1// Dear ImGui: standalone example application for DirectX 12
2
3// Learn about Dear ImGui:
4// - FAQ https://dearimgui.com/faq
5// - Getting Started https://dearimgui.com/getting-started
6// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
7// - Introduction, links and more at the top of imgui.cpp
8
9#include "imgui.h"
10#include "imgui_impl_win32.h"
11#include "imgui_impl_dx12.h"
12#include <d3d12.h>
13#include <dxgi1_4.h>
14#include <tchar.h>
15
16#ifdef _DEBUG
17#define DX12_ENABLE_DEBUG_LAYER
18#endif
19
20#ifdef DX12_ENABLE_DEBUG_LAYER
21#include <dxgidebug.h>
22#pragma comment(lib, "dxguid.lib")
23#endif
24
25// Config for example app
26static const int APP_NUM_FRAMES_IN_FLIGHT = 2;
27static const int APP_NUM_BACK_BUFFERS = 2;
28static const int APP_SRV_HEAP_SIZE = 64;
29
30struct FrameContext
31{
32 ID3D12CommandAllocator* CommandAllocator;
33 UINT64 FenceValue;
34};
35
36// Simple free list based allocator
37struct ExampleDescriptorHeapAllocator
38{
39 ID3D12DescriptorHeap* Heap = nullptr;
40 D3D12_DESCRIPTOR_HEAP_TYPE HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
41 D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu;
42 D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu;
43 UINT HeapHandleIncrement;
44 ImVector<int> FreeIndices;
45
46 void Create(ID3D12Device* device, ID3D12DescriptorHeap* heap)
47 {
48 IM_ASSERT(Heap == nullptr && FreeIndices.empty());
49 Heap = heap;
50 D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
51 HeapType = desc.Type;
52 HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart();
53 HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart();
54 HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType);
55 FreeIndices.reserve(new_capacity: (int)desc.NumDescriptors);
56 for (int n = desc.NumDescriptors; n > 0; n--)
57 FreeIndices.push_back(v: n - 1);
58 }
59 void Destroy()
60 {
61 Heap = nullptr;
62 FreeIndices.clear();
63 }
64 void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle)
65 {
66 IM_ASSERT(FreeIndices.Size > 0);
67 int idx = FreeIndices.back();
68 FreeIndices.pop_back();
69 out_cpu_desc_handle->ptr = HeapStartCpu.ptr + (idx * HeapHandleIncrement);
70 out_gpu_desc_handle->ptr = HeapStartGpu.ptr + (idx * HeapHandleIncrement);
71 }
72 void Free(D3D12_CPU_DESCRIPTOR_HANDLE out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE out_gpu_desc_handle)
73 {
74 int cpu_idx = (int)((out_cpu_desc_handle.ptr - HeapStartCpu.ptr) / HeapHandleIncrement);
75 int gpu_idx = (int)((out_gpu_desc_handle.ptr - HeapStartGpu.ptr) / HeapHandleIncrement);
76 IM_ASSERT(cpu_idx == gpu_idx);
77 FreeIndices.push_back(v: cpu_idx);
78 }
79};
80
81// Data
82static FrameContext g_frameContext[APP_NUM_FRAMES_IN_FLIGHT] = {};
83static UINT g_frameIndex = 0;
84
85static ID3D12Device* g_pd3dDevice = nullptr;
86static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = nullptr;
87static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr;
88static ExampleDescriptorHeapAllocator g_pd3dSrvDescHeapAlloc;
89static ID3D12CommandQueue* g_pd3dCommandQueue = nullptr;
90static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr;
91static ID3D12Fence* g_fence = nullptr;
92static HANDLE g_fenceEvent = nullptr;
93static UINT64 g_fenceLastSignaledValue = 0;
94static IDXGISwapChain3* g_pSwapChain = nullptr;
95static bool g_SwapChainOccluded = false;
96static HANDLE g_hSwapChainWaitableObject = nullptr;
97static ID3D12Resource* g_mainRenderTargetResource[APP_NUM_BACK_BUFFERS] = {};
98static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[APP_NUM_BACK_BUFFERS] = {};
99
100// Forward declarations of helper functions
101bool CreateDeviceD3D(HWND hWnd);
102void CleanupDeviceD3D();
103void CreateRenderTarget();
104void CleanupRenderTarget();
105void WaitForLastSubmittedFrame();
106FrameContext* WaitForNextFrameResources();
107LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
108
109// Main code
110int main(int, char**)
111{
112 // Make process DPI aware and obtain main monitor scale
113 ImGui_ImplWin32_EnableDpiAwareness();
114 float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
115
116 // Create application window
117 WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
118 ::RegisterClassExW(&wc);
119 HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);
120
121 // Initialize Direct3D
122 if (!CreateDeviceD3D(hwnd))
123 {
124 CleanupDeviceD3D();
125 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
126 return 1;
127 }
128
129 // Show the window
130 ::ShowWindow(hwnd, SW_SHOWDEFAULT);
131 ::UpdateWindow(hwnd);
132
133 // Setup Dear ImGui context
134 IMGUI_CHECKVERSION();
135 ImGui::CreateContext();
136 ImGuiIO& io = ImGui::GetIO(); (void)io;
137 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
138 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
139 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
140 io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
141 //io.ConfigViewportsNoAutoMerge = true;
142 //io.ConfigViewportsNoTaskBarIcon = true;
143
144 // Setup Dear ImGui style
145 ImGui::StyleColorsDark();
146 //ImGui::StyleColorsLight();
147
148 // Setup scaling
149 ImGuiStyle& style = ImGui::GetStyle();
150 style.ScaleAllSizes(scale_factor: main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
151 style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose)
152 io.ConfigDpiScaleFonts = true; // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
153 io.ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes.
154
155 // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
156 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
157 {
158 style.WindowRounding = 0.0f;
159 style.Colors[ImGuiCol_WindowBg].w = 1.0f;
160 }
161
162 // Setup Platform/Renderer backends
163 ImGui_ImplWin32_Init(hwnd);
164
165 ImGui_ImplDX12_InitInfo init_info = {};
166 init_info.Device = g_pd3dDevice;
167 init_info.CommandQueue = g_pd3dCommandQueue;
168 init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
169 init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
170 init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
171 // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks.
172 // (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
173 init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap;
174 init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) { return g_pd3dSrvDescHeapAlloc.Alloc(out_cpu_desc_handle: out_cpu_handle, out_gpu_desc_handle: out_gpu_handle); };
175 init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle) { return g_pd3dSrvDescHeapAlloc.Free(out_cpu_desc_handle: cpu_handle, out_gpu_desc_handle: gpu_handle); };
176 ImGui_ImplDX12_Init(info: &init_info);
177
178 // Before 1.91.6: our signature was using a single descriptor. From 1.92, specifying SrvDescriptorAllocFn/SrvDescriptorFreeFn will be required to benefit from new features.
179 //ImGui_ImplDX12_Init(g_pd3dDevice, APP_NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
180
181 // Load Fonts
182 // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
183 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
184 // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
185 // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
186 // - Read 'docs/FONTS.md' for more instructions and details.
187 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
188 //style.FontSizeBase = 20.0f;
189 //io.Fonts->AddFontDefault();
190 //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
191 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
192 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
193 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
194 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
195 //IM_ASSERT(font != nullptr);
196
197 // Our state
198 bool show_demo_window = true;
199 bool show_another_window = false;
200 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
201
202 // Main loop
203 bool done = false;
204 while (!done)
205 {
206 // Poll and handle messages (inputs, window resize, etc.)
207 // See the WndProc() function below for our to dispatch events to the Win32 backend.
208 MSG msg;
209 while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
210 {
211 ::TranslateMessage(&msg);
212 ::DispatchMessage(&msg);
213 if (msg.message == WM_QUIT)
214 done = true;
215 }
216 if (done)
217 break;
218
219 // Handle window screen locked
220 if ((g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED) || ::IsIconic(hwnd))
221 {
222 ::Sleep(10);
223 continue;
224 }
225 g_SwapChainOccluded = false;
226
227 // Start the Dear ImGui frame
228 ImGui_ImplDX12_NewFrame();
229 ImGui_ImplWin32_NewFrame();
230 ImGui::NewFrame();
231
232 // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
233 if (show_demo_window)
234 ImGui::ShowDemoWindow(p_open: &show_demo_window);
235
236 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
237 {
238 static float f = 0.0f;
239 static int counter = 0;
240
241 ImGui::Begin(name: "Hello, world!"); // Create a window called "Hello, world!" and append into it.
242
243 ImGui::Text(fmt: "This is some useful text."); // Display some text (you can use a format strings too)
244 ImGui::Checkbox(label: "Demo Window", v: &show_demo_window); // Edit bools storing our window open/close state
245 ImGui::Checkbox(label: "Another Window", v: &show_another_window);
246
247 ImGui::SliderFloat(label: "float", v: &f, v_min: 0.0f, v_max: 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
248 ImGui::ColorEdit3(label: "clear color", col: (float*)&clear_color); // Edit 3 floats representing a color
249
250 if (ImGui::Button(label: "Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
251 counter++;
252 ImGui::SameLine();
253 ImGui::Text(fmt: "counter = %d", counter);
254
255 ImGui::Text(fmt: "Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
256 ImGui::End();
257 }
258
259 // 3. Show another simple window.
260 if (show_another_window)
261 {
262 ImGui::Begin(name: "Another Window", p_open: &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
263 ImGui::Text(fmt: "Hello from another window!");
264 if (ImGui::Button(label: "Close Me"))
265 show_another_window = false;
266 ImGui::End();
267 }
268
269 // Rendering
270 ImGui::Render();
271
272 FrameContext* frameCtx = WaitForNextFrameResources();
273 UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
274 frameCtx->CommandAllocator->Reset();
275
276 D3D12_RESOURCE_BARRIER barrier = {};
277 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
278 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
279 barrier.Transition.pResource = g_mainRenderTargetResource[backBufferIdx];
280 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
281 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
282 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
283 g_pd3dCommandList->Reset(frameCtx->CommandAllocator, nullptr);
284 g_pd3dCommandList->ResourceBarrier(1, &barrier);
285
286 // Render Dear ImGui graphics
287 const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
288 g_pd3dCommandList->ClearRenderTargetView(g_mainRenderTargetDescriptor[backBufferIdx], clear_color_with_alpha, 0, nullptr);
289 g_pd3dCommandList->OMSetRenderTargets(1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, nullptr);
290 g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
291 ImGui_ImplDX12_RenderDrawData(draw_data: ImGui::GetDrawData(), graphics_command_list: g_pd3dCommandList);
292 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
293 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
294 g_pd3dCommandList->ResourceBarrier(1, &barrier);
295 g_pd3dCommandList->Close();
296
297 g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
298
299 // Update and Render additional Platform Windows
300 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
301 {
302 ImGui::UpdatePlatformWindows();
303 ImGui::RenderPlatformWindowsDefault();
304 }
305
306 // Present
307 HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
308 //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
309 g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
310
311 UINT64 fenceValue = g_fenceLastSignaledValue + 1;
312 g_pd3dCommandQueue->Signal(g_fence, fenceValue);
313 g_fenceLastSignaledValue = fenceValue;
314 frameCtx->FenceValue = fenceValue;
315 }
316
317 WaitForLastSubmittedFrame();
318
319 // Cleanup
320 ImGui_ImplDX12_Shutdown();
321 ImGui_ImplWin32_Shutdown();
322 ImGui::DestroyContext();
323
324 CleanupDeviceD3D();
325 ::DestroyWindow(hwnd);
326 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
327
328 return 0;
329}
330
331// Helper functions
332bool CreateDeviceD3D(HWND hWnd)
333{
334 // Setup swap chain
335 DXGI_SWAP_CHAIN_DESC1 sd;
336 {
337 ZeroMemory(&sd, sizeof(sd));
338 sd.BufferCount = APP_NUM_BACK_BUFFERS;
339 sd.Width = 0;
340 sd.Height = 0;
341 sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
342 sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
343 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
344 sd.SampleDesc.Count = 1;
345 sd.SampleDesc.Quality = 0;
346 sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
347 sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
348 sd.Scaling = DXGI_SCALING_STRETCH;
349 sd.Stereo = FALSE;
350 }
351
352 // [DEBUG] Enable debug interface
353#ifdef DX12_ENABLE_DEBUG_LAYER
354 ID3D12Debug* pdx12Debug = nullptr;
355 if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pdx12Debug))))
356 pdx12Debug->EnableDebugLayer();
357#endif
358
359 // Create device
360 D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
361 if (D3D12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) != S_OK)
362 return false;
363
364 // [DEBUG] Setup debug interface to break on any warnings/errors
365#ifdef DX12_ENABLE_DEBUG_LAYER
366 if (pdx12Debug != nullptr)
367 {
368 ID3D12InfoQueue* pInfoQueue = nullptr;
369 g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pInfoQueue));
370 pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
371 pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
372 pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
373 pInfoQueue->Release();
374 pdx12Debug->Release();
375 }
376#endif
377
378 {
379 D3D12_DESCRIPTOR_HEAP_DESC desc = {};
380 desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
381 desc.NumDescriptors = APP_NUM_BACK_BUFFERS;
382 desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
383 desc.NodeMask = 1;
384 if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
385 return false;
386
387 SIZE_T rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
388 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
389 for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
390 {
391 g_mainRenderTargetDescriptor[i] = rtvHandle;
392 rtvHandle.ptr += rtvDescriptorSize;
393 }
394 }
395
396 {
397 D3D12_DESCRIPTOR_HEAP_DESC desc = {};
398 desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
399 desc.NumDescriptors = APP_SRV_HEAP_SIZE;
400 desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
401 if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
402 return false;
403 g_pd3dSrvDescHeapAlloc.Create(device: g_pd3dDevice, heap: g_pd3dSrvDescHeap);
404 }
405
406 {
407 D3D12_COMMAND_QUEUE_DESC desc = {};
408 desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
409 desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
410 desc.NodeMask = 1;
411 if (g_pd3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
412 return false;
413 }
414
415 for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
416 if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
417 return false;
418
419 if (g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frameContext[0].CommandAllocator, nullptr, IID_PPV_ARGS(&g_pd3dCommandList)) != S_OK ||
420 g_pd3dCommandList->Close() != S_OK)
421 return false;
422
423 if (g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
424 return false;
425
426 g_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
427 if (g_fenceEvent == nullptr)
428 return false;
429
430 {
431 IDXGIFactory4* dxgiFactory = nullptr;
432 IDXGISwapChain1* swapChain1 = nullptr;
433 if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK)
434 return false;
435 if (dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK)
436 return false;
437 if (swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK)
438 return false;
439 swapChain1->Release();
440 dxgiFactory->Release();
441 g_pSwapChain->SetMaximumFrameLatency(APP_NUM_BACK_BUFFERS);
442 g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
443 }
444
445 CreateRenderTarget();
446 return true;
447}
448
449void CleanupDeviceD3D()
450{
451 CleanupRenderTarget();
452 if (g_pSwapChain) { g_pSwapChain->SetFullscreenState(false, nullptr); g_pSwapChain->Release(); g_pSwapChain = nullptr; }
453 if (g_hSwapChainWaitableObject != nullptr) { CloseHandle(g_hSwapChainWaitableObject); }
454 for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
455 if (g_frameContext[i].CommandAllocator) { g_frameContext[i].CommandAllocator->Release(); g_frameContext[i].CommandAllocator = nullptr; }
456 if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = nullptr; }
457 if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = nullptr; }
458 if (g_pd3dRtvDescHeap) { g_pd3dRtvDescHeap->Release(); g_pd3dRtvDescHeap = nullptr; }
459 if (g_pd3dSrvDescHeap) { g_pd3dSrvDescHeap->Release(); g_pd3dSrvDescHeap = nullptr; }
460 if (g_fence) { g_fence->Release(); g_fence = nullptr; }
461 if (g_fenceEvent) { CloseHandle(g_fenceEvent); g_fenceEvent = nullptr; }
462 if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
463
464#ifdef DX12_ENABLE_DEBUG_LAYER
465 IDXGIDebug1* pDebug = nullptr;
466 if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&pDebug))))
467 {
468 pDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
469 pDebug->Release();
470 }
471#endif
472}
473
474void CreateRenderTarget()
475{
476 for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
477 {
478 ID3D12Resource* pBackBuffer = nullptr;
479 g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
480 g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, g_mainRenderTargetDescriptor[i]);
481 g_mainRenderTargetResource[i] = pBackBuffer;
482 }
483}
484
485void CleanupRenderTarget()
486{
487 WaitForLastSubmittedFrame();
488
489 for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
490 if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
491}
492
493void WaitForLastSubmittedFrame()
494{
495 FrameContext* frameCtx = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT];
496
497 UINT64 fenceValue = frameCtx->FenceValue;
498 if (fenceValue == 0)
499 return; // No fence was signaled
500
501 frameCtx->FenceValue = 0;
502 if (g_fence->GetCompletedValue() >= fenceValue)
503 return;
504
505 g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
506 WaitForSingleObject(g_fenceEvent, INFINITE);
507}
508
509FrameContext* WaitForNextFrameResources()
510{
511 UINT nextFrameIndex = g_frameIndex + 1;
512 g_frameIndex = nextFrameIndex;
513
514 HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr };
515 DWORD numWaitableObjects = 1;
516
517 FrameContext* frameCtx = &g_frameContext[nextFrameIndex % APP_NUM_FRAMES_IN_FLIGHT];
518 UINT64 fenceValue = frameCtx->FenceValue;
519 if (fenceValue != 0) // means no fence was signaled
520 {
521 frameCtx->FenceValue = 0;
522 g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
523 waitableObjects[1] = g_fenceEvent;
524 numWaitableObjects = 2;
525 }
526
527 WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);
528
529 return frameCtx;
530}
531
532// Forward declare message handler from imgui_impl_win32.cpp
533extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
534
535// Win32 message handler
536// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
537// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
538// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
539// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
540LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
541{
542 if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
543 return true;
544
545 switch (msg)
546 {
547 case WM_SIZE:
548 if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
549 {
550 WaitForLastSubmittedFrame();
551 CleanupRenderTarget();
552 HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
553 assert(SUCCEEDED(result) && "Failed to resize swapchain.");
554 CreateRenderTarget();
555 }
556 return 0;
557 case WM_SYSCOMMAND:
558 if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
559 return 0;
560 break;
561 case WM_DESTROY:
562 ::PostQuitMessage(0);
563 return 0;
564 }
565 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
566}
567

source code of imgui/examples/example_win32_directx12/main.cpp