mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-24 18:16:27 +02:00
Overlay in HDR
- ImGui draws sRGB colours, and the overlay wrote them straight into the
game's HDR buffer, where they came out far too bright and washed out.
The DX12 backend's pixel shader now converts them: to scRGB for an FP16
buffer, and to HDR10 (BT.2020, PQ) for a 10-bit buffer while the display
is in HDR, with white at 200 nits. An SDR buffer is drawn as before.
- The log says which was chosen ("Overlay output: ..."), and now also says
when the game presents in a format the overlay does not draw to, which
left its menus hidden with no reason given.
- Not seen on an HDR display. The changed shader compiles with the D3D
compiler.
DualSense
- While a menu holds the game's input, the reports the game reads from a
PlayStation pad are rewritten as untouched. USB and Bluetooth lay a
report out differently, and the two were told apart by the size of the
buffer the game read into. A USB DualSense read into a larger buffer had
its d-pad byte written as "up", which placed a session marker whenever T
or Insert opened something. The device's own report length decides now.
- The log line for the first rewritten report gives the pad, USB or
Bluetooth, the report and the two lengths.
- Not tested with a DualSense.
Launcher
- The controller pointer learned where the right stick rests while no pad
was being read, which includes the launcher being in the background, and
took the middle for it. A stick that rests off the middle, or a wheel or
throttle on that axis, then steered the pointer for good once the
launcher had the focus again, and the mouse stopped responding until a
restart. Nothing is learned while no pad is read, and a pad that goes
away gives the pointer back. Two tests for it.
Item search
- The cosmetic search bar showed in the world: a grid keeps its focused
item after its screen has gone. It needs the game's menu to be up, and
to have been for a second, as well.
831 lines
40 KiB
C++
831 lines
40 KiB
C++
// dear imgui: Renderer Backend for DirectX12
|
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
|
|
|
// Implemented features:
|
|
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
|
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
|
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
|
|
|
// The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
|
|
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
|
|
|
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
|
// Learn about Dear ImGui:
|
|
// - FAQ https://dearimgui.com/faq
|
|
// - Getting Started https://dearimgui.com/getting-started
|
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
|
// - Introduction, links and more at the top of imgui.cpp
|
|
|
|
// CHANGELOG
|
|
// (minor and older changes stripped away, please see git history for details)
|
|
// 2025-02-24: DirectX12: Fixed an issue where ImGui_ImplDX12_Init() signature change from 2024-11-15 combined with change from 2025-01-15 made legacy ImGui_ImplDX12_Init() crash. (#8429)
|
|
// 2025-01-15: DirectX12: Texture upload use the command queue provided in ImGui_ImplDX12_InitInfo instead of creating its own.
|
|
// 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat.
|
|
// 2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
|
|
// 2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple.
|
|
// 2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
|
|
// 2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
|
// 2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
|
// 2024-10-07: DirectX12: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
|
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
|
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
|
// 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer.
|
|
// 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically.
|
|
// 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning.
|
|
// 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID.
|
|
// 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function.
|
|
// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
|
// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
|
// 2019-03-29: Misc: Various minor tidying up.
|
|
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
|
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
|
// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
|
|
// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
|
|
// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport).
|
|
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
|
|
|
|
#include "imgui.h"
|
|
#ifndef IMGUI_DISABLE
|
|
#include "imgui_impl_dx12.h"
|
|
|
|
// DirectX
|
|
#include <d3d12.h>
|
|
#include <wrl/client.h> // ReSkate bounded upload ownership
|
|
#include <dxgi1_4.h>
|
|
#include <d3dcompiler.h>
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
|
#endif
|
|
|
|
// DirectX12 data
|
|
struct ImGui_ImplDX12_RenderBuffers;
|
|
|
|
struct ImGui_ImplDX12_Texture
|
|
{
|
|
ID3D12Resource* pTextureResource;
|
|
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
|
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
|
|
|
ImGui_ImplDX12_Texture() { memset((void*)this, 0, sizeof(*this)); }
|
|
};
|
|
|
|
struct ImGui_ImplDX12_Data
|
|
{
|
|
ImGui_ImplDX12_InitInfo InitInfo;
|
|
ID3D12Device* pd3dDevice;
|
|
ID3D12RootSignature* pRootSignature;
|
|
ID3D12PipelineState* pPipelineState;
|
|
ID3D12CommandQueue* pCommandQueue;
|
|
bool commandQueueOwned;
|
|
DXGI_FORMAT RTVFormat;
|
|
DXGI_FORMAT DSVFormat;
|
|
ID3D12DescriptorHeap* pd3dSrvDescHeap;
|
|
UINT numFramesInFlight;
|
|
|
|
ImGui_ImplDX12_RenderBuffers* pFrameResources;
|
|
UINT frameIndex;
|
|
|
|
ImGui_ImplDX12_Texture FontTexture;
|
|
bool LegacySingleDescriptorUsed;
|
|
|
|
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; }
|
|
};
|
|
|
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
|
static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData()
|
|
{
|
|
return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
|
}
|
|
|
|
// Buffers used during the rendering of a frame
|
|
struct ImGui_ImplDX12_RenderBuffers
|
|
{
|
|
ID3D12Resource* IndexBuffer;
|
|
ID3D12Resource* VertexBuffer;
|
|
int IndexBufferSize;
|
|
int VertexBufferSize;
|
|
};
|
|
|
|
struct VERTEX_CONSTANT_BUFFER_DX12
|
|
{
|
|
float mvp[4][4];
|
|
};
|
|
|
|
// [ReSkate] mode, white in nits, unused, unused: see ImGui_ImplDX12_SetOutputTransfer.
|
|
static float g_ReSkateOutputTransfer[4] = { 0.0f, 200.0f, 0.0f, 0.0f };
|
|
void ImGui_ImplDX12_SetOutputTransfer(int mode, float white_nits)
|
|
{
|
|
g_ReSkateOutputTransfer[0] = (float)mode;
|
|
g_ReSkateOutputTransfer[1] = white_nits;
|
|
}
|
|
|
|
// Functions
|
|
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr)
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
|
|
// Setup orthographic projection matrix into our constant buffer
|
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
|
VERTEX_CONSTANT_BUFFER_DX12 vertex_constant_buffer;
|
|
{
|
|
float L = draw_data->DisplayPos.x;
|
|
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
|
float T = draw_data->DisplayPos.y;
|
|
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
|
float mvp[4][4] =
|
|
{
|
|
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
|
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
|
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
|
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
|
};
|
|
memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
|
|
}
|
|
|
|
// Setup viewport
|
|
D3D12_VIEWPORT vp = {};
|
|
vp.Width = draw_data->DisplaySize.x;
|
|
vp.Height = draw_data->DisplaySize.y;
|
|
vp.MinDepth = 0.0f;
|
|
vp.MaxDepth = 1.0f;
|
|
vp.TopLeftX = vp.TopLeftY = 0.0f;
|
|
command_list->RSSetViewports(1, &vp);
|
|
|
|
// Bind shader and vertex buffers
|
|
unsigned int stride = sizeof(ImDrawVert);
|
|
unsigned int offset = 0;
|
|
D3D12_VERTEX_BUFFER_VIEW vbv = {};
|
|
vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
|
|
vbv.SizeInBytes = fr->VertexBufferSize * stride;
|
|
vbv.StrideInBytes = stride;
|
|
command_list->IASetVertexBuffers(0, 1, &vbv);
|
|
D3D12_INDEX_BUFFER_VIEW ibv = {};
|
|
ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
|
|
ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx);
|
|
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
|
|
command_list->IASetIndexBuffer(&ibv);
|
|
command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
command_list->SetPipelineState(bd->pPipelineState);
|
|
command_list->SetGraphicsRootSignature(bd->pRootSignature);
|
|
command_list->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
|
|
command_list->SetGraphicsRoot32BitConstants(0, 4, g_ReSkateOutputTransfer, 16); // [ReSkate] see ImGui_ImplDX12_SetOutputTransfer
|
|
|
|
// Setup blend factor
|
|
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
|
command_list->OMSetBlendFactor(blend_factor);
|
|
}
|
|
|
|
template<typename T>
|
|
static inline void SafeRelease(T*& res)
|
|
{
|
|
if (res)
|
|
res->Release();
|
|
res = nullptr;
|
|
}
|
|
|
|
// Render function
|
|
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list)
|
|
{
|
|
// Avoid rendering when minimized
|
|
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
|
return;
|
|
|
|
// FIXME: We are assuming that this only gets called once per frame!
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
bd->frameIndex = bd->frameIndex + 1;
|
|
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
|
|
|
|
// Create and grow vertex/index buffers if needed
|
|
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
|
|
{
|
|
SafeRelease(fr->VertexBuffer);
|
|
fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
|
D3D12_HEAP_PROPERTIES props = {};
|
|
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
D3D12_RESOURCE_DESC desc = {};
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert);
|
|
desc.Height = 1;
|
|
desc.DepthOrArraySize = 1;
|
|
desc.MipLevels = 1;
|
|
desc.Format = DXGI_FORMAT_UNKNOWN;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&fr->VertexBuffer)) < 0)
|
|
return;
|
|
}
|
|
if (fr->IndexBuffer == nullptr || fr->IndexBufferSize < draw_data->TotalIdxCount)
|
|
{
|
|
SafeRelease(fr->IndexBuffer);
|
|
fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
|
D3D12_HEAP_PROPERTIES props = {};
|
|
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
D3D12_RESOURCE_DESC desc = {};
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx);
|
|
desc.Height = 1;
|
|
desc.DepthOrArraySize = 1;
|
|
desc.MipLevels = 1;
|
|
desc.Format = DXGI_FORMAT_UNKNOWN;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&fr->IndexBuffer)) < 0)
|
|
return;
|
|
}
|
|
|
|
// Upload vertex/index data into a single contiguous GPU buffer
|
|
// During Map() we specify a null read range (as per DX12 API, this is informational and for tooling only)
|
|
void* vtx_resource, *idx_resource;
|
|
D3D12_RANGE range = { 0, 0 };
|
|
if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
|
|
return;
|
|
if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
|
|
return;
|
|
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
|
|
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
|
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
|
{
|
|
const ImDrawList* draw_list = draw_data->CmdLists[n];
|
|
memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
|
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
|
vtx_dst += draw_list->VtxBuffer.Size;
|
|
idx_dst += draw_list->IdxBuffer.Size;
|
|
}
|
|
|
|
// During Unmap() we specify the written range (as per DX12 API, this is informational and for tooling only)
|
|
range.End = (SIZE_T)((intptr_t)vtx_dst - (intptr_t)vtx_resource);
|
|
IM_ASSERT(range.End == draw_data->TotalVtxCount * sizeof(ImDrawVert));
|
|
fr->VertexBuffer->Unmap(0, &range);
|
|
range.End = (SIZE_T)((intptr_t)idx_dst - (intptr_t)idx_resource);
|
|
IM_ASSERT(range.End == draw_data->TotalIdxCount * sizeof(ImDrawIdx));
|
|
fr->IndexBuffer->Unmap(0, &range);
|
|
|
|
// Setup desired DX state
|
|
ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
|
|
|
|
// Setup render state structure (for callbacks and custom texture bindings)
|
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
|
ImGui_ImplDX12_RenderState render_state;
|
|
render_state.Device = bd->pd3dDevice;
|
|
render_state.CommandList = command_list;
|
|
platform_io.Renderer_RenderState = &render_state;
|
|
|
|
// Render command lists
|
|
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
|
int global_vtx_offset = 0;
|
|
int global_idx_offset = 0;
|
|
ImVec2 clip_off = draw_data->DisplayPos;
|
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
|
{
|
|
const ImDrawList* draw_list = draw_data->CmdLists[n];
|
|
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
|
{
|
|
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
|
if (pcmd->UserCallback != nullptr)
|
|
{
|
|
// User callback, registered via ImDrawList::AddCallback()
|
|
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
|
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
|
ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
|
|
else
|
|
pcmd->UserCallback(draw_list, pcmd);
|
|
}
|
|
else
|
|
{
|
|
// Project scissor/clipping rectangles into framebuffer space
|
|
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
|
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
|
continue;
|
|
|
|
// Apply scissor/clipping rectangle
|
|
const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
|
command_list->RSSetScissorRects(1, &r);
|
|
|
|
// Bind texture, Draw
|
|
D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {};
|
|
texture_handle.ptr = (UINT64)pcmd->GetTexID();
|
|
command_list->SetGraphicsRootDescriptorTable(1, texture_handle);
|
|
command_list->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
|
}
|
|
}
|
|
global_idx_offset += draw_list->IdxBuffer.Size;
|
|
global_vtx_offset += draw_list->VtxBuffer.Size;
|
|
}
|
|
platform_io.Renderer_RenderState = nullptr;
|
|
}
|
|
|
|
// ReSkate bounded upload adaptation of the upstream font texture helper.
|
|
// See third_party/README.md. Retain in-flight objects on a failed/expired GPU
|
|
// fence instead of hanging Present or releasing memory the GPU may still use.
|
|
static bool ImGui_ImplDX12_CreateFontsTexture()
|
|
{
|
|
using Microsoft::WRL::ComPtr;
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
unsigned char* pixels = nullptr;
|
|
int width = 0, height = 0;
|
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
|
if (!pixels || width <= 0 || height <= 0 || width > 16384 || height > 16384)
|
|
return false;
|
|
const UINT pitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u)
|
|
& ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
|
|
const UINT size = height * pitch;
|
|
D3D12_HEAP_PROPERTIES props = {};
|
|
props.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
D3D12_RESOURCE_DESC desc = {};
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
desc.Width = width;
|
|
desc.Height = height;
|
|
desc.DepthOrArraySize = 1;
|
|
desc.MipLevels = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.SampleDesc.Count = 1;
|
|
ComPtr<ID3D12Resource> texture, upload;
|
|
if (FAILED(bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE,
|
|
&desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&texture))))
|
|
return false;
|
|
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
desc.Width = size;
|
|
desc.Height = 1;
|
|
desc.Format = DXGI_FORMAT_UNKNOWN;
|
|
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
if (FAILED(bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE,
|
|
&desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&upload))))
|
|
return false;
|
|
void* mapped = nullptr;
|
|
D3D12_RANGE range = { 0, 0 };
|
|
if (FAILED(upload->Map(0, &range, &mapped)) || !mapped)
|
|
return false;
|
|
for (int y = 0; y < height; ++y)
|
|
memcpy((char*)mapped + y * pitch, pixels + y * width * 4, width * 4);
|
|
range.End = size;
|
|
upload->Unmap(0, &range);
|
|
ComPtr<ID3D12Fence> fence;
|
|
ComPtr<ID3D12CommandAllocator> allocator;
|
|
ComPtr<ID3D12GraphicsCommandList> commands;
|
|
if (FAILED(bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))
|
|
|| FAILED(bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
IID_PPV_ARGS(&allocator)))
|
|
|| FAILED(bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
allocator.Get(), nullptr, IID_PPV_ARGS(&commands))))
|
|
return false;
|
|
D3D12_TEXTURE_COPY_LOCATION source = {};
|
|
source.pResource = upload.Get();
|
|
source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
source.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
source.PlacedFootprint.Footprint.Width = width;
|
|
source.PlacedFootprint.Footprint.Height = height;
|
|
source.PlacedFootprint.Footprint.Depth = 1;
|
|
source.PlacedFootprint.Footprint.RowPitch = pitch;
|
|
D3D12_TEXTURE_COPY_LOCATION destination = {};
|
|
destination.pResource = texture.Get();
|
|
destination.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
commands->CopyTextureRegion(&destination, 0, 0, 0, &source, nullptr);
|
|
D3D12_RESOURCE_BARRIER barrier = {};
|
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
barrier.Transition.pResource = texture.Get();
|
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
|
commands->ResourceBarrier(1, &barrier);
|
|
if (FAILED(commands->Close())) return false;
|
|
HANDLE event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
|
if (!event) return false;
|
|
ID3D12CommandList* lists[] = { commands.Get() };
|
|
bd->pCommandQueue->ExecuteCommandLists(1, lists);
|
|
const bool signaled = SUCCEEDED(bd->pCommandQueue->Signal(fence.Get(), 1))
|
|
&& SUCCEEDED(fence->SetEventOnCompletion(1, event));
|
|
if (!signaled || ::WaitForSingleObject(event, 5000) != WAIT_OBJECT_0)
|
|
{
|
|
// Submission already occurred. Keep its resource references and event
|
|
// until process exit. Rendering is disabled by the caller on false.
|
|
texture.Detach(); upload.Detach(); commands.Detach(); allocator.Detach(); fence.Detach();
|
|
return false;
|
|
}
|
|
::CloseHandle(event);
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srv = {};
|
|
srv.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
|
srv.Texture2D.MipLevels = 1;
|
|
srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
auto* font = &bd->FontTexture;
|
|
bd->pd3dDevice->CreateShaderResourceView(texture.Get(), &srv, font->hFontSrvCpuDescHandle);
|
|
SafeRelease(font->pTextureResource);
|
|
font->pTextureResource = texture.Detach();
|
|
io.Fonts->SetTexID((ImTextureID)font->hFontSrvGpuDescHandle.ptr);
|
|
return true;
|
|
}
|
|
|
|
bool ImGui_ImplDX12_CreateDeviceObjects()
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
if (!bd || !bd->pd3dDevice)
|
|
return false;
|
|
if (bd->pPipelineState)
|
|
ImGui_ImplDX12_InvalidateDeviceObjects();
|
|
|
|
// Create the root signature
|
|
{
|
|
D3D12_DESCRIPTOR_RANGE descRange = {};
|
|
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descRange.NumDescriptors = 1;
|
|
descRange.BaseShaderRegister = 0;
|
|
descRange.RegisterSpace = 0;
|
|
descRange.OffsetInDescriptorsFromTableStart = 0;
|
|
|
|
D3D12_ROOT_PARAMETER param[2] = {};
|
|
|
|
param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
|
|
param[0].Constants.ShaderRegister = 0;
|
|
param[0].Constants.RegisterSpace = 0;
|
|
param[0].Constants.Num32BitValues = 20; // [ReSkate] the matrix, then the output transfer
|
|
param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // [ReSkate] the pixel shader reads the transfer
|
|
|
|
param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
param[1].DescriptorTable.NumDescriptorRanges = 1;
|
|
param[1].DescriptorTable.pDescriptorRanges = &descRange;
|
|
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
|
|
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
|
|
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
|
|
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
|
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
staticSampler.MipLODBias = 0.f;
|
|
staticSampler.MaxAnisotropy = 0;
|
|
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
|
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
|
staticSampler.MinLOD = 0.f;
|
|
staticSampler.MaxLOD = 0.f;
|
|
staticSampler.ShaderRegister = 0;
|
|
staticSampler.RegisterSpace = 0;
|
|
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
|
|
D3D12_ROOT_SIGNATURE_DESC desc = {};
|
|
desc.NumParameters = _countof(param);
|
|
desc.pParameters = param;
|
|
desc.NumStaticSamplers = 1;
|
|
desc.pStaticSamplers = &staticSampler;
|
|
desc.Flags =
|
|
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
|
|
|
|
// Load d3d12.dll and D3D12SerializeRootSignature() function address dynamically to facilitate using with D3D12On7.
|
|
// See if any version of d3d12.dll is already loaded in the process. If so, give preference to that.
|
|
static HINSTANCE d3d12_dll = ::GetModuleHandleA("d3d12.dll");
|
|
if (d3d12_dll == nullptr)
|
|
{
|
|
// Attempt to load d3d12.dll from local directories. This will only succeed if
|
|
// (1) the current OS is Windows 7, and
|
|
// (2) there exists a version of d3d12.dll for Windows 7 (D3D12On7) in one of the following directories.
|
|
// See https://github.com/ocornut/imgui/pull/3696 for details.
|
|
const char* localD3d12Paths[] = { ".\\d3d12.dll", ".\\d3d12on7\\d3d12.dll", ".\\12on7\\d3d12.dll" }; // A. current directory, B. used by some games, C. used in Microsoft D3D12On7 sample
|
|
for (int i = 0; i < IM_ARRAYSIZE(localD3d12Paths); i++)
|
|
if ((d3d12_dll = ::LoadLibraryA(localD3d12Paths[i])) != nullptr)
|
|
break;
|
|
|
|
// If failed, we are on Windows >= 10.
|
|
if (d3d12_dll == nullptr)
|
|
d3d12_dll = ::LoadLibraryA("d3d12.dll");
|
|
|
|
if (d3d12_dll == nullptr)
|
|
return false;
|
|
}
|
|
|
|
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
|
|
if (D3D12SerializeRootSignatureFn == nullptr)
|
|
return false;
|
|
|
|
ID3DBlob* blob = nullptr;
|
|
if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
|
|
return false;
|
|
|
|
bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
|
|
blob->Release();
|
|
}
|
|
|
|
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
|
// If you would like to use this DX12 sample code but remove this dependency you can:
|
|
// 1) compile once, save the compiled shader blobs into a file or source code and assign them to psoDesc.VS/PS [preferred solution]
|
|
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
|
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
|
|
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
|
psoDesc.NodeMask = 1;
|
|
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
|
psoDesc.pRootSignature = bd->pRootSignature;
|
|
psoDesc.SampleMask = UINT_MAX;
|
|
psoDesc.NumRenderTargets = 1;
|
|
psoDesc.RTVFormats[0] = bd->RTVFormat;
|
|
psoDesc.DSVFormat = bd->DSVFormat;
|
|
psoDesc.SampleDesc.Count = 1;
|
|
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
|
|
|
ID3DBlob* vertexShaderBlob;
|
|
ID3DBlob* pixelShaderBlob;
|
|
|
|
// Create the vertex shader
|
|
{
|
|
static const char* vertexShader =
|
|
"cbuffer vertexBuffer : register(b0) \
|
|
{\
|
|
float4x4 ProjectionMatrix; \
|
|
};\
|
|
struct VS_INPUT\
|
|
{\
|
|
float2 pos : POSITION;\
|
|
float4 col : COLOR0;\
|
|
float2 uv : TEXCOORD0;\
|
|
};\
|
|
\
|
|
struct PS_INPUT\
|
|
{\
|
|
float4 pos : SV_POSITION;\
|
|
float4 col : COLOR0;\
|
|
float2 uv : TEXCOORD0;\
|
|
};\
|
|
\
|
|
PS_INPUT main(VS_INPUT input)\
|
|
{\
|
|
PS_INPUT output;\
|
|
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
|
output.col = input.col;\
|
|
output.uv = input.uv;\
|
|
return output;\
|
|
}";
|
|
|
|
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), nullptr, nullptr, nullptr, "main", "vs_5_0", 0, 0, &vertexShaderBlob, nullptr)))
|
|
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
|
psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() };
|
|
|
|
// Create the input layout
|
|
static D3D12_INPUT_ELEMENT_DESC local_layout[] =
|
|
{
|
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
|
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
|
};
|
|
psoDesc.InputLayout = { local_layout, 3 };
|
|
}
|
|
|
|
// Create the pixel shader
|
|
{
|
|
static const char* pixelShader =
|
|
"struct PS_INPUT\
|
|
{\
|
|
float4 pos : SV_POSITION;\
|
|
float4 col : COLOR0;\
|
|
float2 uv : TEXCOORD0;\
|
|
};\
|
|
cbuffer vertexBuffer : register(b0) \
|
|
{\
|
|
float4x4 ProjectionMatrix; \
|
|
float4 OutputTransfer; \
|
|
};\
|
|
SamplerState sampler0 : register(s0);\
|
|
Texture2D texture0 : register(t0);\
|
|
\
|
|
float3 srgb_to_linear(float3 c)\
|
|
{\
|
|
return lerp(c / 12.92, pow(abs((c + 0.055) / 1.055), 2.4), step(0.04045, c)); \
|
|
}\
|
|
float4 main(PS_INPUT input) : SV_Target\
|
|
{\
|
|
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
|
if (OutputTransfer.x > 0.5) \
|
|
{\
|
|
float3 lin = srgb_to_linear(saturate(out_col.rgb)); \
|
|
if (OutputTransfer.x < 1.5) \
|
|
{\
|
|
out_col.rgb = lin * (OutputTransfer.y / 80.0); \
|
|
}\
|
|
else \
|
|
{\
|
|
float3 wide = float3(dot(lin, float3(0.6274, 0.3293, 0.0433)), dot(lin, float3(0.0691, 0.9195, 0.0114)), dot(lin, float3(0.0164, 0.0880, 0.8956))); \
|
|
float3 y = pow(saturate(wide * (OutputTransfer.y / 10000.0)), 0.1593017578125); \
|
|
out_col.rgb = pow((0.8359375 + 18.8515625 * y) / (1.0 + 18.6875 * y), 78.84375); \
|
|
}\
|
|
}\
|
|
return out_col; \
|
|
}";
|
|
|
|
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr)))
|
|
{
|
|
vertexShaderBlob->Release();
|
|
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
|
}
|
|
psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() };
|
|
}
|
|
|
|
// Create the blending setup
|
|
{
|
|
D3D12_BLEND_DESC& desc = psoDesc.BlendState;
|
|
desc.AlphaToCoverageEnable = false;
|
|
desc.RenderTarget[0].BlendEnable = true;
|
|
desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
|
desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
|
desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
|
|
desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
|
|
desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
|
|
desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
|
desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
|
}
|
|
|
|
// Create the rasterizer state
|
|
{
|
|
D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
|
|
desc.FillMode = D3D12_FILL_MODE_SOLID;
|
|
desc.CullMode = D3D12_CULL_MODE_NONE;
|
|
desc.FrontCounterClockwise = FALSE;
|
|
desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
|
desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
|
desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
|
desc.DepthClipEnable = true;
|
|
desc.MultisampleEnable = FALSE;
|
|
desc.AntialiasedLineEnable = FALSE;
|
|
desc.ForcedSampleCount = 0;
|
|
desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
|
}
|
|
|
|
// Create depth-stencil State
|
|
{
|
|
D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
|
|
desc.DepthEnable = false;
|
|
desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
|
|
desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
|
desc.StencilEnable = false;
|
|
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
|
|
desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
|
desc.BackFace = desc.FrontFace;
|
|
}
|
|
|
|
HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState));
|
|
vertexShaderBlob->Release();
|
|
pixelShaderBlob->Release();
|
|
if (result_pipeline_state != S_OK)
|
|
return false;
|
|
|
|
return ImGui_ImplDX12_CreateFontsTexture();
|
|
}
|
|
|
|
void ImGui_ImplDX12_InvalidateDeviceObjects()
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
if (!bd || !bd->pd3dDevice)
|
|
return;
|
|
|
|
if (bd->commandQueueOwned)
|
|
SafeRelease(bd->pCommandQueue);
|
|
bd->commandQueueOwned = false;
|
|
SafeRelease(bd->pRootSignature);
|
|
SafeRelease(bd->pPipelineState);
|
|
|
|
// Free SRV descriptor used by texture
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture;
|
|
bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, font_tex->hFontSrvCpuDescHandle, font_tex->hFontSrvGpuDescHandle);
|
|
SafeRelease(font_tex->pTextureResource);
|
|
io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well.
|
|
|
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
|
{
|
|
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
|
|
SafeRelease(fr->IndexBuffer);
|
|
SafeRelease(fr->VertexBuffer);
|
|
}
|
|
}
|
|
|
|
bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
IMGUI_CHECKVERSION();
|
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
|
|
|
// Setup backend capabilities flags
|
|
ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
|
|
bd->InitInfo = *init_info; // Deep copy
|
|
init_info = &bd->InitInfo;
|
|
|
|
bd->pd3dDevice = init_info->Device;
|
|
IM_ASSERT(init_info->CommandQueue != NULL);
|
|
bd->pCommandQueue = init_info->CommandQueue;
|
|
bd->RTVFormat = init_info->RTVFormat;
|
|
bd->DSVFormat = init_info->DSVFormat;
|
|
bd->numFramesInFlight = init_info->NumFramesInFlight;
|
|
bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
|
|
|
|
io.BackendRendererUserData = (void*)bd;
|
|
io.BackendRendererName = "imgui_impl_dx12";
|
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
|
|
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
|
if (init_info->SrvDescriptorAllocFn == nullptr)
|
|
{
|
|
// Wrap legacy behavior of passing space for a single descriptor
|
|
IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
|
|
init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle)
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
IM_ASSERT(bd->LegacySingleDescriptorUsed == false && "Only 1 simultaneous texture allowed with legacy ImGui_ImplDX12_Init() signature!");
|
|
*out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor;
|
|
*out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor;
|
|
bd->LegacySingleDescriptorUsed = true;
|
|
};
|
|
init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE)
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
IM_ASSERT(bd->LegacySingleDescriptorUsed == true);
|
|
bd->LegacySingleDescriptorUsed = false;
|
|
};
|
|
}
|
|
#endif
|
|
|
|
// Allocate 1 SRV descriptor for the font texture
|
|
IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
|
|
init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle);
|
|
|
|
// Create buffers with a default size (they will later be grown as needed)
|
|
bd->frameIndex = UINT_MAX;
|
|
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[bd->numFramesInFlight];
|
|
for (int i = 0; i < (int)bd->numFramesInFlight; i++)
|
|
{
|
|
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
|
|
fr->IndexBuffer = nullptr;
|
|
fr->VertexBuffer = nullptr;
|
|
fr->IndexBufferSize = 10000;
|
|
fr->VertexBufferSize = 5000;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
|
// Legacy initialization API Obsoleted in 1.91.5
|
|
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
|
|
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
|
|
{
|
|
ImGui_ImplDX12_InitInfo init_info;
|
|
init_info.Device = device;
|
|
init_info.NumFramesInFlight = num_frames_in_flight;
|
|
init_info.RTVFormat = rtv_format;
|
|
init_info.SrvDescriptorHeap = srv_descriptor_heap;
|
|
init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle;
|
|
init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle;
|
|
|
|
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
|
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
|
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
queueDesc.NodeMask = 1;
|
|
HRESULT hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&init_info.CommandQueue));
|
|
IM_ASSERT(SUCCEEDED(hr));
|
|
|
|
bool ret = ImGui_ImplDX12_Init(&init_info);
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
bd->commandQueueOwned = true;
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
void ImGui_ImplDX12_Shutdown()
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
// Clean up windows and device objects
|
|
ImGui_ImplDX12_InvalidateDeviceObjects();
|
|
delete[] bd->pFrameResources;
|
|
|
|
io.BackendRendererName = nullptr;
|
|
io.BackendRendererUserData = nullptr;
|
|
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
|
IM_DELETE(bd);
|
|
}
|
|
|
|
void ImGui_ImplDX12_NewFrame()
|
|
{
|
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX12_Init()?");
|
|
|
|
if (!bd->pPipelineState)
|
|
ImGui_ImplDX12_CreateDeviceObjects();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#endif // #ifndef IMGUI_DISABLE
|