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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of imgui/backends/imgui_impl_dx12.cpp