64 KB、1351行 生 Blame 履歴
1// dear imgui: Renderer Backend for DirectX12
2// This needs to be used along with a Platform Backend (e.g. Win32)
3
4// Implemented features:
5// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
6// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
7// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
8// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
9// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
10// FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker.
11
12// The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
13// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
14
15// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
16// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
17// Learn about Dear ImGui:
18// - FAQ https://dearimgui.com/faq
19// - Getting Started https://dearimgui.com/getting-started
20// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
21// - Introduction, links and more at the top of imgui.cpp
22
23// CHANGELOG
24// (minor and older changes stripped away, please see git history for details)
25// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
26// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378)
27// 2025-10-23: [Docking] DirectX12: Fixed an issue in synchronization logic improving rendering throughput for secondary viewports. (#8961, #9025)
28// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002)
29// 2025-09-29: DirectX12: Rework synchronization logic. (#8961)
30// 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965)
31// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963)
32// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
33// 2025-06-19: Fixed build on MinGW. (#8702, #4594)
34// 2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
35// 2025-05-07: DirectX12: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows).
36// 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)
37// 2025-01-15: DirectX12: Texture upload use the command queue provided in ImGui_ImplDX12_InitInfo instead of creating its own.
38// 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat.
39// 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).
40// 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.
41// 2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
42// 2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
43// 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.
44// 2024-10-07: DirectX12: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
45// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
46// 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).
47// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
48// 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer.
49// 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically.
50// 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning.
51// 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID.
52// 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function.
53// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
54// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
55// 2019-03-29: Misc: Various minor tidying up.
56// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
57// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
58// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
59// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
60// 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).
61// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
62
63#include "imgui.h"
64#ifndef IMGUI_DISABLE
65#include "imgui_impl_dx12.h"
66
67// DirectX
68#include <d3d12.h>
69#include <dxgi1_5.h>
70#include <d3dcompiler.h>
71#ifdef _MSC_VER
72#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
73#endif
74
75// Clang/GCC warnings with -Weverything
76#if defined(__clang__)
77#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
78#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
79#endif
80
81// MinGW workaround, see #4594
82typedef decltype(D3D12SerializeRootSignature) *_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE;
83
84// DirectX12 data
85struct ImGui_ImplDX12_RenderBuffers;
86
87struct ImGui_ImplDX12_Texture
88{
89 ID3D12Resource* pTextureResource;
90 D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
91 D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
92
93 ImGui_ImplDX12_Texture() { memset((void*)this, 0, sizeof(*this)); }
94};
95
96struct ImGui_ImplDX12_Data
97{
98 ImGui_ImplDX12_InitInfo InitInfo;
99 ImGui_ImplDX12_RenderState* RenderState;
100
101 IDXGIFactory5* pdxgiFactory;
102 ID3D12Device* pd3dDevice;
103 ID3D12RootSignature* pRootSignatureLinear;
104 ID3D12RootSignature* pRootSignatureNearest;
105 ID3D12PipelineState* pPipelineStateLinear;
106 ID3D12PipelineState* pPipelineStateNearest;
107 ID3D12CommandQueue* pCommandQueue;
108 bool commandQueueOwned;
109 DXGI_FORMAT RTVFormat;
110 DXGI_FORMAT DSVFormat;
111 ID3D12DescriptorHeap* pd3dSrvDescHeap;
112 ID3D12Fence* Fence;
113 UINT64 FenceLastSignaledValue;
114 HANDLE FenceEvent;
115 UINT numFramesInFlight;
116 bool tearingSupport;
117 bool LegacySingleDescriptorUsed;
118
119 ID3D12CommandAllocator* pTexCmdAllocator;
120 ID3D12GraphicsCommandList* pTexCmdList;
121 ID3D12Resource* pTexUploadBuffer;
122 UINT pTexUploadBufferSize;
123 void* pTexUploadBufferMapped;
124
125 ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); }
126};
127
128// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
129// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
130static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData()
131{
132 return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
133}
134
135// Buffers used during the rendering of a frame
136struct ImGui_ImplDX12_RenderBuffers
137{
138 ID3D12Resource* IndexBuffer;
139 ID3D12Resource* VertexBuffer;
140 int IndexBufferSize;
141 int VertexBufferSize;
142};
143
144// Buffers used for secondary viewports created by the multi-viewports systems
145struct ImGui_ImplDX12_FrameContext
146{
147 UINT64 FenceValue;
148 ID3D12CommandAllocator* CommandAllocator;
149 ID3D12Resource* RenderTarget;
150 D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors;
151};
152
153// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
154// Main viewport created by application will only use the Resources field.
155// Secondary viewports created by this backend will use all the fields (including Window fields),
156struct ImGui_ImplDX12_ViewportData
157{
158 // Window
159 ID3D12CommandQueue* CommandQueue;
160 ID3D12GraphicsCommandList* CommandList;
161 ID3D12DescriptorHeap* RtvDescHeap;
162 IDXGISwapChain3* SwapChain;
163 HANDLE SwapChainWaitableObject;
164 UINT NumFramesInFlight;
165 ImGui_ImplDX12_FrameContext* FrameCtx;
166
167 // Render buffers
168 UINT FrameIndex;
169 ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers;
170
171 ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight)
172 {
173 CommandQueue = nullptr;
174 CommandList = nullptr;
175 RtvDescHeap = nullptr;
176 SwapChain = nullptr;
177 SwapChainWaitableObject = nullptr;
178 NumFramesInFlight = num_frames_in_flight;
179 FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
180 FrameIndex = 0;
181 FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight];
182
183 for (UINT i = 0; i < NumFramesInFlight; ++i)
184 {
185 FrameCtx[i].FenceValue = 0;
186 FrameCtx[i].CommandAllocator = nullptr;
187 FrameCtx[i].RenderTarget = nullptr;
188
189 // Create buffers with a default size (they will later be grown as needed)
190 FrameRenderBuffers[i].IndexBuffer = nullptr;
191 FrameRenderBuffers[i].VertexBuffer = nullptr;
192 FrameRenderBuffers[i].VertexBufferSize = 5000;
193 FrameRenderBuffers[i].IndexBufferSize = 10000;
194 }
195 }
196 ~ImGui_ImplDX12_ViewportData()
197 {
198 IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr);
199 IM_ASSERT(RtvDescHeap == nullptr);
200 IM_ASSERT(SwapChain == nullptr);
201 IM_ASSERT(SwapChainWaitableObject == nullptr);
202
203 for (UINT i = 0; i < NumFramesInFlight; ++i)
204 {
205 IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr && FrameCtx[i].RenderTarget == nullptr);
206 IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == nullptr && FrameRenderBuffers[i].VertexBuffer == nullptr);
207 }
208
209 delete[] FrameCtx; FrameCtx = nullptr;
210 delete[] FrameRenderBuffers; FrameRenderBuffers = nullptr;
211 }
212};
213
214struct VERTEX_CONSTANT_BUFFER_DX12
215{
216 float mvp[4][4];
217};
218
219// Forward Declarations
220static void ImGui_ImplDX12_InitMultiViewportSupport();
221static void ImGui_ImplDX12_ShutdownMultiViewportSupport();
222
223// Functions
224static void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list)
225{
226 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
227 command_list->SetPipelineState(bd->pPipelineStateLinear);
228 command_list->SetGraphicsRootSignature(bd->pRootSignatureLinear);
229}
230
231static void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list)
232{
233 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
234 command_list->SetPipelineState(bd->pPipelineStateNearest);
235 command_list->SetGraphicsRootSignature(bd->pRootSignatureNearest);
236}
237
238static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr)
239{
240 // Setup orthographic projection matrix into our constant buffer
241 // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
242 VERTEX_CONSTANT_BUFFER_DX12 vertex_constant_buffer;
243 {
244 float L = draw_data->DisplayPos.x;
245 float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
246 float T = draw_data->DisplayPos.y;
247 float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
248 float mvp[4][4] =
249 {
250 { 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
251 { 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
252 { 0.0f, 0.0f, 0.5f, 0.0f },
253 { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
254 };
255 memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
256 }
257
258 // Setup viewport
259 D3D12_VIEWPORT vp = {};
260 vp.Width = draw_data->DisplaySize.x * draw_data->FramebufferScale.x;
261 vp.Height = draw_data->DisplaySize.y * draw_data->FramebufferScale.y;
262 vp.MinDepth = 0.0f;
263 vp.MaxDepth = 1.0f;
264 vp.TopLeftX = vp.TopLeftY = 0.0f;
265 command_list->RSSetViewports(1, &vp);
266
267 // Bind shader and vertex buffers
268 unsigned int stride = sizeof(ImDrawVert);
269 unsigned int offset = 0;
270 D3D12_VERTEX_BUFFER_VIEW vbv = {};
271 vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
272 vbv.SizeInBytes = fr->VertexBufferSize * stride;
273 vbv.StrideInBytes = stride;
274 command_list->IASetVertexBuffers(0, 1, &vbv);
275 D3D12_INDEX_BUFFER_VIEW ibv = {};
276 ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
277 ibv.SizeInBytes = (UINT)fr->IndexBufferSize * sizeof(ImDrawIdx);
278 ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
279 command_list->IASetIndexBuffer(&ibv);
280 command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
281 ImGui_ImplDX12_SetupSamplerLinear(command_list);
282 command_list->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
283
284 // Setup blend factor
285 const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
286 command_list->OMSetBlendFactor(blend_factor);
287}
288
289template<typename T>
290static inline void SafeRelease(T*& res)
291{
292 if (res)
293 res->Release();
294 res = nullptr;
295}
296
297// Draw callbacks
298static void ImGui_ImplDX12_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
299static void ImGui_ImplDX12_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_SetupSamplerLinear(bd->RenderState->CommandList); }
300static void ImGui_ImplDX12_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_SetupSamplerNearest(bd->RenderState->CommandList); }
301
302// Render function
303void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list)
304{
305 // Avoid rendering when minimized
306 if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
307 return;
308
309 // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
310 // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
311 if (draw_data->Textures != nullptr)
312 for (ImTextureData* tex : *draw_data->Textures)
313 if (tex->Status != ImTextureStatus_OK)
314 ImGui_ImplDX12_UpdateTexture(tex);
315
316 // FIXME: We are assuming that this only gets called once per frame!
317 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
318 ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData;
319 ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight];
320 vd->FrameIndex++;
321
322 // Create and grow vertex/index buffers if needed
323 if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
324 {
325 SafeRelease(fr->VertexBuffer);
326 fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
327 D3D12_HEAP_PROPERTIES props = {};
328 props.Type = D3D12_HEAP_TYPE_UPLOAD;
329 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
330 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
331 D3D12_RESOURCE_DESC desc = {};
332 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
333 desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert);
334 desc.Height = 1;
335 desc.DepthOrArraySize = 1;
336 desc.MipLevels = 1;
337 desc.Format = DXGI_FORMAT_UNKNOWN;
338 desc.SampleDesc.Count = 1;
339 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
340 desc.Flags = D3D12_RESOURCE_FLAG_NONE;
341 if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&fr->VertexBuffer)) < 0)
342 return;
343 }
344 if (fr->IndexBuffer == nullptr || fr->IndexBufferSize < draw_data->TotalIdxCount)
345 {
346 SafeRelease(fr->IndexBuffer);
347 fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
348 D3D12_HEAP_PROPERTIES props = {};
349 props.Type = D3D12_HEAP_TYPE_UPLOAD;
350 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
351 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
352 D3D12_RESOURCE_DESC desc = {};
353 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
354 desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx);
355 desc.Height = 1;
356 desc.DepthOrArraySize = 1;
357 desc.MipLevels = 1;
358 desc.Format = DXGI_FORMAT_UNKNOWN;
359 desc.SampleDesc.Count = 1;
360 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
361 desc.Flags = D3D12_RESOURCE_FLAG_NONE;
362 if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&fr->IndexBuffer)) < 0)
363 return;
364 }
365
366 // Upload vertex/index data into a single contiguous GPU buffer
367 // During Map() we specify a null read range (as per DX12 API, this is informational and for tooling only)
368 void* vtx_resource, *idx_resource;
369 D3D12_RANGE range = { 0, 0 };
370 if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
371 return;
372 if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
373 return;
374 ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
375 ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
376 for (const ImDrawList* draw_list : draw_data->CmdLists)
377 {
378 memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
379 memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
380 vtx_dst += draw_list->VtxBuffer.Size;
381 idx_dst += draw_list->IdxBuffer.Size;
382 }
383
384 // During Unmap() we specify the written range (as per DX12 API, this is informational and for tooling only)
385 range.End = (SIZE_T)((intptr_t)vtx_dst - (intptr_t)vtx_resource);
386 IM_ASSERT(range.End == draw_data->TotalVtxCount * sizeof(ImDrawVert));
387 fr->VertexBuffer->Unmap(0, &range);
388 range.End = (SIZE_T)((intptr_t)idx_dst - (intptr_t)idx_resource);
389 IM_ASSERT(range.End == draw_data->TotalIdxCount * sizeof(ImDrawIdx));
390 fr->IndexBuffer->Unmap(0, &range);
391
392 // Setup desired DX state
393 ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
394
395 // Setup render state structure (for callbacks and custom texture bindings)
396 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
397 ImGui_ImplDX12_RenderState render_state;
398 render_state.Device = bd->pd3dDevice;
399 render_state.CommandList = command_list;
400 platform_io.Renderer_RenderState = bd->RenderState = &render_state;
401
402 // Render command lists
403 // (Because we merged all buffers into a single one, we maintain our own offset into them)
404 int global_vtx_offset = 0;
405 int global_idx_offset = 0;
406 ImVec2 clip_off = draw_data->DisplayPos;
407 ImVec2 clip_scale = draw_data->FramebufferScale;
408 for (const ImDrawList* draw_list : draw_data->CmdLists)
409 {
410 for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
411 {
412 const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
413 if (pcmd->UserCallback != nullptr)
414 {
415 // User callback, registered via ImDrawList::AddCallback()
416 // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
417 if (pcmd->UserCallback == ImGui_ImplDX12_DrawCallback_ResetRenderState)
418 ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
419 else
420 pcmd->UserCallback(draw_list, pcmd);
421 }
422 else
423 {
424 // Project scissor/clipping rectangles into framebuffer space
425 ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
426 ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
427 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
428 continue;
429
430 // Apply scissor/clipping rectangle
431 const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
432 command_list->RSSetScissorRects(1, &r);
433
434 // Bind texture, Draw
435 D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {};
436 texture_handle.ptr = (UINT64)pcmd->GetTexID();
437 command_list->SetGraphicsRootDescriptorTable(1, texture_handle);
438 command_list->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
439 }
440 }
441 global_idx_offset += draw_list->IdxBuffer.Size;
442 global_vtx_offset += draw_list->VtxBuffer.Size;
443 }
444 platform_io.Renderer_RenderState = bd->RenderState = nullptr;
445}
446
447static void ImGui_ImplDX12_DestroyTexture(ImTextureData* tex)
448{
449 if (ImGui_ImplDX12_Texture* backend_tex = (ImGui_ImplDX12_Texture*)tex->BackendUserData)
450 {
451 IM_ASSERT(backend_tex->hFontSrvGpuDescHandle.ptr == (UINT64)tex->TexID);
452 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
453 bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, backend_tex->hFontSrvCpuDescHandle, backend_tex->hFontSrvGpuDescHandle);
454 SafeRelease(backend_tex->pTextureResource);
455 backend_tex->hFontSrvCpuDescHandle.ptr = 0;
456 backend_tex->hFontSrvGpuDescHandle.ptr = 0;
457 IM_DELETE(backend_tex);
458
459 // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
460 tex->SetTexID(ImTextureID_Invalid);
461 tex->BackendUserData = nullptr;
462 }
463 tex->SetStatus(ImTextureStatus_Destroyed);
464}
465
466void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
467{
468 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
469 bool need_barrier_before_copy = true; // Do we need a resource barrier before we copy new data in?
470
471 if (tex->Status == ImTextureStatus_WantCreate)
472 {
473 // Create and upload new texture to graphics system
474 //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
475 IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
476 IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
477 ImGui_ImplDX12_Texture* backend_tex = IM_NEW(ImGui_ImplDX12_Texture)();
478 bd->InitInfo.SrvDescriptorAllocFn(&bd->InitInfo, &backend_tex->hFontSrvCpuDescHandle, &backend_tex->hFontSrvGpuDescHandle); // Allocate a desctriptor handle
479
480 D3D12_HEAP_PROPERTIES props = {};
481 props.Type = D3D12_HEAP_TYPE_DEFAULT;
482 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
483 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
484
485 D3D12_RESOURCE_DESC desc;
486 ZeroMemory(&desc, sizeof(desc));
487 desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
488 desc.Alignment = 0;
489 desc.Width = tex->Width;
490 desc.Height = tex->Height;
491 desc.DepthOrArraySize = 1;
492 desc.MipLevels = 1;
493 desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
494 desc.SampleDesc.Count = 1;
495 desc.SampleDesc.Quality = 0;
496 desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
497 desc.Flags = D3D12_RESOURCE_FLAG_NONE;
498
499 ID3D12Resource* pTexture = nullptr;
500 bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
501 D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture));
502
503 // Create SRV
504 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
505 ZeroMemory(&srvDesc, sizeof(srvDesc));
506 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
507 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
508 srvDesc.Texture2D.MipLevels = desc.MipLevels;
509 srvDesc.Texture2D.MostDetailedMip = 0;
510 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
511 bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, backend_tex->hFontSrvCpuDescHandle);
512 SafeRelease(backend_tex->pTextureResource);
513 backend_tex->pTextureResource = pTexture;
514
515 // Store identifiers
516 tex->SetTexID((ImTextureID)backend_tex->hFontSrvGpuDescHandle.ptr);
517 tex->BackendUserData = backend_tex;
518 need_barrier_before_copy = false; // Because this is a newly-created texture it will be in D3D12_RESOURCE_STATE_COMMON and thus we don't need a barrier
519 // We don't set tex->Status to ImTextureStatus_OK to let the code fallthrough below.
520 }
521
522 if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates)
523 {
524 ImGui_ImplDX12_Texture* backend_tex = (ImGui_ImplDX12_Texture*)tex->BackendUserData;
525 IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
526
527 // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture.
528 // FIXME-OPT: Uploading single box even when using ImTextureStatus_WantUpdates. Could use tex->Updates[]
529 // - Copy all blocks contiguously in upload buffer.
530 // - Barrier before copy, submit all CopyTextureRegion(), barrier after copy.
531 const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x;
532 const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y;
533 const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w;
534 const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h;
535
536 // Update full texture or selected blocks. We only ever write to textures regions which have never been used before!
537 // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions.
538 UINT upload_pitch_src = upload_w * tex->BytesPerPixel;
539 UINT upload_pitch_dst = (upload_pitch_src + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
540 UINT upload_size = upload_pitch_dst * upload_h;
541
542 if (bd->pTexUploadBuffer == nullptr || upload_size > bd->pTexUploadBufferSize)
543 {
544 if (bd->pTexUploadBufferMapped)
545 {
546 D3D12_RANGE range = { 0, bd->pTexUploadBufferSize };
547 bd->pTexUploadBuffer->Unmap(0, &range);
548 bd->pTexUploadBufferMapped = nullptr;
549 }
550 SafeRelease(bd->pTexUploadBuffer);
551
552 D3D12_RESOURCE_DESC desc;
553 ZeroMemory(&desc, sizeof(desc));
554 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
555 desc.Alignment = 0;
556 desc.Width = upload_size;
557 desc.Height = 1;
558 desc.DepthOrArraySize = 1;
559 desc.MipLevels = 1;
560 desc.Format = DXGI_FORMAT_UNKNOWN;
561 desc.SampleDesc.Count = 1;
562 desc.SampleDesc.Quality = 0;
563 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
564 desc.Flags = D3D12_RESOURCE_FLAG_NONE;
565
566 D3D12_HEAP_PROPERTIES props;
567 memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
568 props.Type = D3D12_HEAP_TYPE_UPLOAD;
569 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
570 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
571
572 HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
573 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bd->pTexUploadBuffer));
574 IM_ASSERT(SUCCEEDED(hr));
575
576 D3D12_RANGE range = {0, upload_size};
577 hr = bd->pTexUploadBuffer->Map(0, &range, &bd->pTexUploadBufferMapped);
578 IM_ASSERT(SUCCEEDED(hr));
579 bd->pTexUploadBufferSize = upload_size;
580 }
581
582 bd->pTexCmdAllocator->Reset();
583 bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr);
584 ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList;
585
586 // Copy to upload buffer
587 for (int y = 0; y < upload_h; y++)
588 memcpy((void*)((uintptr_t)bd->pTexUploadBufferMapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src);
589
590 if (need_barrier_before_copy)
591 {
592 D3D12_RESOURCE_BARRIER barrier = {};
593 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
594 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
595 barrier.Transition.pResource = backend_tex->pTextureResource;
596 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
597 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
598 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
599 cmdList->ResourceBarrier(1, &barrier);
600 }
601
602 D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
603 D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
604 {
605 srcLocation.pResource = bd->pTexUploadBuffer;
606 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
607 srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
608 srcLocation.PlacedFootprint.Footprint.Width = upload_w;
609 srcLocation.PlacedFootprint.Footprint.Height = upload_h;
610 srcLocation.PlacedFootprint.Footprint.Depth = 1;
611 srcLocation.PlacedFootprint.Footprint.RowPitch = upload_pitch_dst;
612 dstLocation.pResource = backend_tex->pTextureResource;
613 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
614 dstLocation.SubresourceIndex = 0;
615 }
616 cmdList->CopyTextureRegion(&dstLocation, upload_x, upload_y, 0, &srcLocation, nullptr);
617
618 {
619 D3D12_RESOURCE_BARRIER barrier = {};
620 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
621 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
622 barrier.Transition.pResource = backend_tex->pTextureResource;
623 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
624 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
625 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
626 cmdList->ResourceBarrier(1, &barrier);
627 }
628
629 HRESULT hr = cmdList->Close();
630 IM_ASSERT(SUCCEEDED(hr));
631 ID3D12CommandQueue* cmdQueue = bd->pCommandQueue;
632 cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
633 hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue);
634 IM_ASSERT(SUCCEEDED(hr));
635
636 // FIXME-OPT: Suboptimal?
637 // - To remove this may need to create NumFramesInFlight x ImGui_ImplDX12_FrameContext in backend data (mimick docking version)
638 // - Store per-frame in flight: upload buffer?
639 // - Where do cmdList and cmdAlloc fit?
640 bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent);
641 ::WaitForSingleObject(bd->FenceEvent, INFINITE);
642
643 tex->SetStatus(ImTextureStatus_OK);
644 }
645
646 if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames >= (int)bd->numFramesInFlight)
647 ImGui_ImplDX12_DestroyTexture(tex);
648}
649
650bool ImGui_ImplDX12_CreateDeviceObjects()
651{
652 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
653 if (!bd || !bd->pd3dDevice)
654 return false;
655 if (bd->pPipelineStateLinear)
656 ImGui_ImplDX12_InvalidateDeviceObjects();
657
658 HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&bd->pdxgiFactory));
659 IM_ASSERT(hr == S_OK);
660
661 BOOL allow_tearing = FALSE;
662 bd->pdxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
663 bd->tearingSupport = (allow_tearing == TRUE);
664
665 // Create the root signature
666 {
667 D3D12_DESCRIPTOR_RANGE descRange = {};
668 descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
669 descRange.NumDescriptors = 1;
670 descRange.BaseShaderRegister = 0;
671 descRange.RegisterSpace = 0;
672 descRange.OffsetInDescriptorsFromTableStart = 0;
673
674 D3D12_ROOT_PARAMETER param[2] = {};
675
676 param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
677 param[0].Constants.ShaderRegister = 0;
678 param[0].Constants.RegisterSpace = 0;
679 param[0].Constants.Num32BitValues = 16;
680 param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
681
682 param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
683 param[1].DescriptorTable.NumDescriptorRanges = 1;
684 param[1].DescriptorTable.pDescriptorRanges = &descRange;
685 param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
686
687 // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
688 D3D12_STATIC_SAMPLER_DESC staticSampler[1] = {};
689 staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
690 staticSampler[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
691 staticSampler[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
692 staticSampler[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
693 staticSampler[0].MipLODBias = 0.f;
694 staticSampler[0].MaxAnisotropy = 0;
695 staticSampler[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
696 staticSampler[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
697 staticSampler[0].MinLOD = 0.f;
698 staticSampler[0].MaxLOD = D3D12_FLOAT32_MAX;
699 staticSampler[0].ShaderRegister = 0;
700 staticSampler[0].RegisterSpace = 0;
701 staticSampler[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
702
703 D3D12_ROOT_SIGNATURE_DESC desc = {};
704 desc.NumParameters = _countof(param);
705 desc.pParameters = param;
706 desc.NumStaticSamplers = 1;
707 desc.pStaticSamplers = &staticSampler[0];
708 desc.Flags =
709 D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
710 D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
711 D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
712 D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
713
714 // Load d3d12.dll and D3D12SerializeRootSignature() function address dynamically to facilitate using with D3D12On7.
715 // See if any version of d3d12.dll is already loaded in the process. If so, give preference to that.
716 static HINSTANCE d3d12_dll = ::GetModuleHandleA("d3d12.dll");
717 if (d3d12_dll == nullptr)
718 {
719 // Attempt to load d3d12.dll from local directories. This will only succeed if
720 // (1) the current OS is Windows 7, and
721 // (2) there exists a version of d3d12.dll for Windows 7 (D3D12On7) in one of the following directories.
722 // See https://github.com/ocornut/imgui/pull/3696 for details.
723 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
724 for (int i = 0; i < IM_COUNTOF(localD3d12Paths); i++)
725 if ((d3d12_dll = ::LoadLibraryA(localD3d12Paths[i])) != nullptr)
726 break;
727
728 // If failed, we are on Windows >= 10.
729 if (d3d12_dll == nullptr)
730 d3d12_dll = ::LoadLibraryA("d3d12.dll");
731
732 if (d3d12_dll == nullptr)
733 return false;
734 }
735
736 _PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void*)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
737 if (D3D12SerializeRootSignatureFn == nullptr)
738 return false;
739
740 ID3DBlob* blob = nullptr;
741 if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
742 return false;
743
744 bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignatureLinear));
745 blob->Release();
746
747 // Nearest sampler
748 staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
749 if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
750 return false;
751
752 bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignatureNearest));
753 blob->Release();
754 }
755
756 // By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
757 // If you would like to use this DX12 sample code but remove this dependency you can:
758 // 1) compile once, save the compiled shader blobs into a file or source code and assign them to psoDesc.VS/PS [preferred solution]
759 // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
760 // See https://github.com/ocornut/imgui/pull/638 for sources and details.
761
762 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
763 psoDesc.NodeMask = 1;
764 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
765 psoDesc.pRootSignature = bd->pRootSignatureLinear;
766 psoDesc.SampleMask = UINT_MAX;
767 psoDesc.NumRenderTargets = 1;
768 psoDesc.RTVFormats[0] = bd->RTVFormat;
769 psoDesc.DSVFormat = bd->DSVFormat;
770 psoDesc.SampleDesc.Count = 1;
771 psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
772
773 ID3DBlob* vertexShaderBlob;
774 ID3DBlob* pixelShaderBlob;
775
776 // Create the vertex shader
777 {
778 static const char* vertexShader =
779 "cbuffer vertexBuffer : register(b0) \
780 {\
781 float4x4 ProjectionMatrix; \
782 };\
783 struct VS_INPUT\
784 {\
785 float2 pos : POSITION;\
786 float4 col : COLOR0;\
787 float2 uv : TEXCOORD0;\
788 };\
789 \
790 struct PS_INPUT\
791 {\
792 float4 pos : SV_POSITION;\
793 float4 col : COLOR0;\
794 float2 uv : TEXCOORD0;\
795 };\
796 \
797 PS_INPUT main(VS_INPUT input)\
798 {\
799 PS_INPUT output;\
800 output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
801 output.col = input.col;\
802 output.uv = input.uv;\
803 return output;\
804 }";
805
806 if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), nullptr, nullptr, nullptr, "main", "vs_5_0", 0, 0, &vertexShaderBlob, nullptr)))
807 return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
808 psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() };
809
810 // Create the input layout
811 static D3D12_INPUT_ELEMENT_DESC local_layout[] =
812 {
813 { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
814 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
815 { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
816 };
817 psoDesc.InputLayout = { local_layout, 3 };
818 }
819
820 // Create the pixel shader
821 {
822 static const char* pixelShader =
823 "struct PS_INPUT\
824 {\
825 float4 pos : SV_POSITION;\
826 float4 col : COLOR0;\
827 float2 uv : TEXCOORD0;\
828 };\
829 SamplerState sampler0 : register(s0);\
830 Texture2D texture0 : register(t0);\
831 \
832 float4 main(PS_INPUT input) : SV_Target\
833 {\
834 float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
835 return out_col; \
836 }";
837
838 if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr)))
839 {
840 vertexShaderBlob->Release();
841 return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
842 }
843 psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() };
844 }
845
846 // Create the blending setup
847 {
848 D3D12_BLEND_DESC& desc = psoDesc.BlendState;
849 desc.AlphaToCoverageEnable = false;
850 desc.RenderTarget[0].BlendEnable = true;
851 desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
852 desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
853 desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
854 desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
855 desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
856 desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
857 desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
858 }
859
860 // Create the rasterizer state
861 {
862 D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
863 desc.FillMode = D3D12_FILL_MODE_SOLID;
864 desc.CullMode = D3D12_CULL_MODE_NONE;
865 desc.FrontCounterClockwise = FALSE;
866 desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
867 desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
868 desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
869 desc.DepthClipEnable = true;
870 desc.MultisampleEnable = FALSE;
871 desc.AntialiasedLineEnable = FALSE;
872 desc.ForcedSampleCount = 0;
873 desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
874 }
875
876 // Create depth-stencil State
877 {
878 D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
879 desc.DepthEnable = false;
880 desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
881 desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
882 desc.StencilEnable = false;
883 desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
884 desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
885 desc.BackFace = desc.FrontFace;
886 }
887
888 HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineStateLinear));
889 if (result_pipeline_state != S_OK)
890 {
891 vertexShaderBlob->Release();
892 pixelShaderBlob->Release();
893 return false;
894 }
895
896 psoDesc.pRootSignature = bd->pRootSignatureNearest;
897
898 result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineStateNearest));
899 vertexShaderBlob->Release();
900 pixelShaderBlob->Release();
901 if (result_pipeline_state != S_OK)
902 return false;
903
904 // Create command allocator and command list for ImGui_ImplDX12_UpdateTexture()
905 hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator));
906 IM_ASSERT(SUCCEEDED(hr));
907 hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, bd->pTexCmdAllocator, nullptr, IID_PPV_ARGS(&bd->pTexCmdList));
908 IM_ASSERT(SUCCEEDED(hr));
909 hr = bd->pTexCmdList->Close();
910 IM_ASSERT(SUCCEEDED(hr));
911
912 // Create fence.
913 hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&bd->Fence));
914 IM_ASSERT(hr == S_OK);
915 bd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
916 IM_ASSERT(bd->FenceEvent != nullptr);
917
918 return true;
919}
920
921static void ImGui_ImplDX12_DestroyRenderBuffers(ImGui_ImplDX12_RenderBuffers* render_buffers)
922{
923 SafeRelease(render_buffers->IndexBuffer);
924 SafeRelease(render_buffers->VertexBuffer);
925 render_buffers->IndexBufferSize = render_buffers->VertexBufferSize = 0;
926}
927
928void ImGui_ImplDX12_InvalidateDeviceObjects()
929{
930 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
931 if (!bd || !bd->pd3dDevice)
932 return;
933
934 SafeRelease(bd->pdxgiFactory);
935 if (bd->commandQueueOwned)
936 SafeRelease(bd->pCommandQueue);
937 bd->commandQueueOwned = false;
938 SafeRelease(bd->pRootSignatureLinear);
939 SafeRelease(bd->pRootSignatureNearest);
940 SafeRelease(bd->pPipelineStateLinear);
941 SafeRelease(bd->pPipelineStateNearest);
942
943 if (bd->pTexUploadBufferMapped)
944 {
945 D3D12_RANGE range = { 0, bd->pTexUploadBufferSize };
946 bd->pTexUploadBuffer->Unmap(0, &range);
947 bd->pTexUploadBufferMapped = nullptr;
948 }
949 SafeRelease(bd->pTexUploadBuffer);
950 SafeRelease(bd->pTexCmdList);
951 SafeRelease(bd->pTexCmdAllocator);
952 SafeRelease(bd->Fence);
953 CloseHandle(bd->FenceEvent);
954 bd->FenceEvent = nullptr;
955
956 // Destroy all textures
957 for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
958 if (tex->RefCount == 1)
959 ImGui_ImplDX12_DestroyTexture(tex);
960}
961
962#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
963static void ImGui_ImplDX12_InitLegacySingleDescriptorMode(ImGui_ImplDX12_InitInfo* init_info)
964{
965 // Wrap legacy behavior of passing space for a single descriptor
966 IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
967 init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle)
968 {
969 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
970 IM_ASSERT(bd->LegacySingleDescriptorUsed == false && "Only 1 simultaneous texture allowed with legacy ImGui_ImplDX12_Init() signature!");
971 *out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor;
972 *out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor;
973 bd->LegacySingleDescriptorUsed = true;
974 };
975 init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE)
976 {
977 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
978 IM_ASSERT(bd->LegacySingleDescriptorUsed == true);
979 bd->LegacySingleDescriptorUsed = false;
980 };
981}
982#endif
983
984bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
985{
986 ImGuiIO& io = ImGui::GetIO();
987 IMGUI_CHECKVERSION();
988 IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
989
990 // Setup backend capabilities flags
991 ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
992 bd->InitInfo = *init_info; // Deep copy
993 init_info = &bd->InitInfo;
994
995 bd->pd3dDevice = init_info->Device;
996 IM_ASSERT(init_info->CommandQueue != nullptr);
997 bd->pCommandQueue = init_info->CommandQueue;
998 bd->RTVFormat = init_info->RTVFormat;
999 bd->DSVFormat = init_info->DSVFormat;
1000 bd->numFramesInFlight = init_info->NumFramesInFlight;
1001 bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
1002 bd->tearingSupport = false;
1003
1004 io.BackendRendererUserData = (void*)bd;
1005 io.BackendRendererName = "imgui_impl_dx12";
1006 io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
1007 io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
1008 io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
1009
1010 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
1011 ImGui_ImplDX12_InitMultiViewportSupport();
1012
1013 // Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport,
1014 // Since this is created and managed by the application, we will only use the ->Resources[] fields.
1015 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1016 main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
1017
1018 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1019 platform_io.DrawCallback_ResetRenderState = ImGui_ImplDX12_DrawCallback_ResetRenderState;
1020 platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplDX12_DrawCallback_SetSamplerLinear;
1021 platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplDX12_DrawCallback_SetSamplerNearest;
1022
1023#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1024 if (init_info->SrvDescriptorAllocFn == nullptr)
1025 ImGui_ImplDX12_InitLegacySingleDescriptorMode(init_info);
1026#endif
1027 IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
1028
1029 return true;
1030}
1031
1032#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1033// Legacy initialization API Obsoleted in 1.91.5
1034// 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'
1035bool 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)
1036{
1037 ImGui_ImplDX12_InitInfo init_info;
1038 init_info.Device = device;
1039 init_info.NumFramesInFlight = num_frames_in_flight;
1040 init_info.RTVFormat = rtv_format;
1041 init_info.SrvDescriptorHeap = srv_descriptor_heap;
1042 init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle;
1043 init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle;
1044
1045 D3D12_COMMAND_QUEUE_DESC queueDesc = {};
1046 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
1047 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
1048 queueDesc.NodeMask = 1;
1049 HRESULT hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&init_info.CommandQueue));
1050 IM_ASSERT(SUCCEEDED(hr));
1051
1052 bool ret = ImGui_ImplDX12_Init(&init_info);
1053 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1054 bd->commandQueueOwned = true;
1055
1056 ImGuiIO& io = ImGui::GetIO();
1057 io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; // Using legacy ImGui_ImplDX12_Init() call with 1 SRV descriptor we cannot support multiple textures.
1058
1059 return ret;
1060}
1061#endif
1062
1063void ImGui_ImplDX12_Shutdown()
1064{
1065 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1066 IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
1067 ImGuiIO& io = ImGui::GetIO();
1068 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1069
1070 // Manually delete main viewport render resources in-case we haven't initialized for viewports
1071 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1072 if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)main_viewport->RendererUserData)
1073 {
1074 // We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[]
1075 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1076 ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
1077 IM_DELETE(vd);
1078 main_viewport->RendererUserData = nullptr;
1079 }
1080
1081 ImGui_ImplDX12_ShutdownMultiViewportSupport();
1082 ImGui_ImplDX12_InvalidateDeviceObjects();
1083
1084 io.BackendRendererName = nullptr;
1085 io.BackendRendererUserData = nullptr;
1086 io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
1087 platform_io.ClearRendererHandlers();
1088 IM_DELETE(bd);
1089}
1090
1091void ImGui_ImplDX12_NewFrame()
1092{
1093 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1094 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX12_Init()?");
1095
1096 if (!bd->pPipelineStateLinear)
1097 if (!ImGui_ImplDX12_CreateDeviceObjects())
1098 IM_ASSERT(0 && "ImGui_ImplDX12_CreateDeviceObjects() failed!");
1099}
1100
1101//--------------------------------------------------------------------------------------------------------
1102// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
1103// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
1104// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
1105//--------------------------------------------------------------------------------------------------------
1106
1107static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
1108{
1109 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1110 ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
1111 viewport->RendererUserData = vd;
1112
1113 // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL's WindowID).
1114 // Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
1115 HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
1116 IM_ASSERT(hwnd != 0);
1117
1118 // Use shared command queue from init info
1119 vd->FrameIndex = 0;
1120 vd->CommandQueue = bd->pCommandQueue;
1121
1122 // Create command allocator.
1123 HRESULT res = S_OK;
1124 for (UINT i = 0; i < bd->numFramesInFlight; ++i)
1125 {
1126 res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator));
1127 IM_ASSERT(res == S_OK);
1128 }
1129
1130 // Create command list.
1131 res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vd->FrameCtx[0].CommandAllocator, nullptr, IID_PPV_ARGS(&vd->CommandList));
1132 IM_ASSERT(res == S_OK);
1133 vd->CommandList->Close();
1134
1135 // Create swap chain
1136 // FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application.
1137 DXGI_SWAP_CHAIN_DESC1 sd1;
1138 ZeroMemory(&sd1, sizeof(sd1));
1139 sd1.BufferCount = bd->numFramesInFlight;
1140 sd1.Width = (UINT)viewport->Size.x;
1141 sd1.Height = (UINT)viewport->Size.y;
1142 sd1.Format = bd->RTVFormat;
1143 sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1144 sd1.SampleDesc.Count = 1;
1145 sd1.SampleDesc.Quality = 0;
1146 sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
1147 sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
1148 sd1.Scaling = DXGI_SCALING_NONE;
1149 sd1.Stereo = FALSE;
1150 sd1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
1151
1152 if (bd->tearingSupport)
1153 sd1.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
1154
1155 IDXGISwapChain1* swap_chain = nullptr;
1156 res = bd->pdxgiFactory->CreateSwapChainForHwnd(vd->CommandQueue, hwnd, &sd1, nullptr, nullptr, &swap_chain);
1157 IM_ASSERT(res == S_OK);
1158 res = bd->pdxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); // Disable e.g. Alt+Enter
1159 IM_ASSERT(res == S_OK);
1160
1161 // Or swapChain.As(&mSwapChain)
1162 IM_ASSERT(vd->SwapChain == nullptr);
1163 swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain));
1164 swap_chain->Release();
1165
1166 // Create the render targets and waitable object
1167 if (vd->SwapChain)
1168 {
1169 D3D12_DESCRIPTOR_HEAP_DESC desc = {};
1170 desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
1171 desc.NumDescriptors = bd->numFramesInFlight;
1172 desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
1173 desc.NodeMask = 1;
1174
1175 HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&vd->RtvDescHeap));
1176 IM_ASSERT(hr == S_OK);
1177
1178 SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
1179 D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart();
1180 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1181 {
1182 vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle;
1183 rtv_handle.ptr += rtv_descriptor_size;
1184 }
1185
1186 ID3D12Resource* back_buffer;
1187 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1188 {
1189 IM_ASSERT(vd->FrameCtx[i].RenderTarget == nullptr);
1190 vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
1191 bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
1192 vd->FrameCtx[i].RenderTarget = back_buffer;
1193 }
1194
1195 hr = vd->SwapChain->SetMaximumFrameLatency(bd->numFramesInFlight);
1196 IM_ASSERT(hr == S_OK);
1197 vd->SwapChainWaitableObject = vd->SwapChain->GetFrameLatencyWaitableObject();
1198 }
1199
1200 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1201 ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
1202}
1203
1204static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd)
1205{
1206 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1207 HRESULT hr = vd->CommandQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue);
1208 IM_ASSERT(hr == S_OK);
1209
1210 hr = bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent);
1211 IM_ASSERT(hr == S_OK);
1212 ::WaitForSingleObject(bd->FenceEvent, INFINITE);
1213}
1214
1215static ImGui_ImplDX12_FrameContext* ImGui_WaitForNextFrameContext(ImGui_ImplDX12_ViewportData* vd)
1216{
1217 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1218 ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % vd->NumFramesInFlight];
1219 if (bd->Fence->GetCompletedValue() < frame_context->FenceValue)
1220 {
1221 HRESULT hr = bd->Fence->SetEventOnCompletion(frame_context->FenceValue, bd->FenceEvent);
1222 IM_ASSERT(hr == S_OK);
1223 HANDLE waitableObjects[] = { vd->SwapChainWaitableObject, bd->FenceEvent };
1224 ::WaitForMultipleObjects(2, waitableObjects, TRUE, INFINITE);
1225 }
1226 else
1227 {
1228 ::WaitForSingleObject(vd->SwapChainWaitableObject, INFINITE);
1229 }
1230 return frame_context;
1231}
1232
1233static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
1234{
1235 // The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
1236 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1237 if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData)
1238 {
1239 ImGui_WaitForPendingOperations(vd);
1240
1241 vd->CommandQueue = nullptr;
1242 ::CloseHandle(vd->SwapChainWaitableObject);
1243 vd->SwapChainWaitableObject = nullptr;
1244 SafeRelease(vd->CommandList);
1245 SafeRelease(vd->SwapChain);
1246 SafeRelease(vd->RtvDescHeap);
1247
1248 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1249 {
1250 SafeRelease(vd->FrameCtx[i].RenderTarget);
1251 SafeRelease(vd->FrameCtx[i].CommandAllocator);
1252 ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
1253 }
1254 IM_DELETE(vd);
1255 }
1256 viewport->RendererUserData = nullptr;
1257}
1258
1259static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
1260{
1261 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1262 ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
1263
1264 ImGui_WaitForPendingOperations(vd);
1265
1266 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1267 SafeRelease(vd->FrameCtx[i].RenderTarget);
1268
1269 if (vd->SwapChain)
1270 {
1271 ID3D12Resource* back_buffer = nullptr;
1272 DXGI_SWAP_CHAIN_DESC1 desc = {};
1273 vd->SwapChain->GetDesc1(&desc);
1274 vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, desc.Format, desc.Flags);
1275 for (UINT i = 0; i < bd->numFramesInFlight; i++)
1276 {
1277 vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
1278 bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
1279 vd->FrameCtx[i].RenderTarget = back_buffer;
1280 }
1281 }
1282}
1283
1284static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
1285{
1286 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1287 ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
1288
1289 ImGui_ImplDX12_FrameContext* frame_context = ImGui_WaitForNextFrameContext(vd);
1290 UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex();
1291
1292 const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
1293 D3D12_RESOURCE_BARRIER barrier = {};
1294 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1295 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
1296 barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget;
1297 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1298 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
1299 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
1300
1301 // Draw
1302 ID3D12GraphicsCommandList* cmd_list = vd->CommandList;
1303
1304 frame_context->CommandAllocator->Reset();
1305 cmd_list->Reset(frame_context->CommandAllocator, nullptr);
1306 cmd_list->ResourceBarrier(1, &barrier);
1307 cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr);
1308 if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
1309 cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (const float*)&clear_color, 0, nullptr);
1310 cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap);
1311
1312 ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list);
1313
1314 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
1315 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
1316 cmd_list->ResourceBarrier(1, &barrier);
1317 cmd_list->Close();
1318
1319 vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
1320
1321 HRESULT hr = vd->CommandQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue);
1322 IM_ASSERT(hr == S_OK);
1323 frame_context->FenceValue = bd->FenceLastSignaledValue;
1324}
1325
1326static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
1327{
1328 ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1329 ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
1330
1331 vd->SwapChain->Present(0, bd->tearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0);
1332}
1333
1334void ImGui_ImplDX12_InitMultiViewportSupport()
1335{
1336 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1337 platform_io.Renderer_CreateWindow = ImGui_ImplDX12_CreateWindow;
1338 platform_io.Renderer_DestroyWindow = ImGui_ImplDX12_DestroyWindow;
1339 platform_io.Renderer_SetWindowSize = ImGui_ImplDX12_SetWindowSize;
1340 platform_io.Renderer_RenderWindow = ImGui_ImplDX12_RenderWindow;
1341 platform_io.Renderer_SwapBuffers = ImGui_ImplDX12_SwapBuffers;
1342}
1343
1344void ImGui_ImplDX12_ShutdownMultiViewportSupport()
1345{
1346 ImGui::DestroyPlatformWindows();
1347}
1348
1349//-----------------------------------------------------------------------------
1350
1351#endif // #ifndef IMGUI_DISABLE