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 // Create application window
113 //ImGui_ImplWin32_EnableDpiAwareness();
114 WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
115 ::RegisterClassExW(&wc);
116 HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr);
117
118 // Initialize Direct3D
119 if (!CreateDeviceD3D(hwnd))
120 {
121 CleanupDeviceD3D();
122 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
123 return 1;
124 }
125
126 // Show the window
127 ::ShowWindow(hwnd, SW_SHOWDEFAULT);
128 ::UpdateWindow(hwnd);
129
130 // Setup Dear ImGui context
131 IMGUI_CHECKVERSION();
132 ImGui::CreateContext();
133 ImGuiIO& io = ImGui::GetIO(); (void)io;
134 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
135 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
136 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
137 io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
138 //io.ConfigViewportsNoAutoMerge = true;
139 //io.ConfigViewportsNoTaskBarIcon = true;
140
141 // Setup Dear ImGui style
142 ImGui::StyleColorsDark();
143 //ImGui::StyleColorsLight();
144
145 // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
146 ImGuiStyle& style = ImGui::GetStyle();
147 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
148 {
149 style.WindowRounding = 0.0f;
150 style.Colors[ImGuiCol_WindowBg].w = 1.0f;
151 }
152
153 // Setup Platform/Renderer backends
154 ImGui_ImplWin32_Init(hwnd);
155
156 ImGui_ImplDX12_InitInfo init_info = {};
157 init_info.Device = g_pd3dDevice;
158 init_info.CommandQueue = g_pd3dCommandQueue;
159 init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
160 init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
161 init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
162 // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks.
163 // (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
164 init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap;
165 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); };
166 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); };
167 ImGui_ImplDX12_Init(info: &init_info);
168
169 // 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.
170 //ImGui_ImplDX12_Init(g_pd3dDevice, APP_NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
171
172 // Load Fonts
173 // - 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.
174 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
175 // - 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).
176 // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
177 // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
178 // - Read 'docs/FONTS.md' for more instructions and details.
179 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
180 //io.Fonts->AddFontDefault();
181 //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
182 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
183 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
184 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
185 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
186 //IM_ASSERT(font != nullptr);
187
188 // Our state
189 bool show_demo_window = true;
190 bool show_another_window = false;
191 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
192
193 // Main loop
194 bool done = false;
195 while (!done)
196 {
197 // Poll and handle messages (inputs, window resize, etc.)
198 // See the WndProc() function below for our to dispatch events to the Win32 backend.
199 MSG msg;
200 while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
201 {
202 ::TranslateMessage(&msg);
203 ::DispatchMessage(&msg);
204 if (msg.message == WM_QUIT)
205 done = true;
206 }
207 if (done)
208 break;
209
210 // Handle window screen locked
211 if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
212 {
213 ::Sleep(10);
214 continue;
215 }
216 g_SwapChainOccluded = false;
217
218 // Start the Dear ImGui frame
219 ImGui_ImplDX12_NewFrame();
220 ImGui_ImplWin32_NewFrame();
221 ImGui::NewFrame();
222
223 // 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!).
224 if (show_demo_window)
225 ImGui::ShowDemoWindow(p_open: &show_demo_window);
226
227 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
228 {
229 static float f = 0.0f;
230 static int counter = 0;
231
232 ImGui::Begin(name: "Hello, world!"); // Create a window called "Hello, world!" and append into it.
233
234 ImGui::Text(fmt: "This is some useful text."); // Display some text (you can use a format strings too)
235 ImGui::Checkbox(label: "Demo Window", v: &show_demo_window); // Edit bools storing our window open/close state
236 ImGui::Checkbox(label: "Another Window", v: &show_another_window);
237
238 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
239 ImGui::ColorEdit3(label: "clear color", col: (float*)&clear_color); // Edit 3 floats representing a color
240
241 if (ImGui::Button(label: "Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
242 counter++;
243 ImGui::SameLine();
244 ImGui::Text(fmt: "counter = %d", counter);
245
246 ImGui::Text(fmt: "Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
247 ImGui::End();
248 }
249
250 // 3. Show another simple window.
251 if (show_another_window)
252 {
253 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)
254 ImGui::Text(fmt: "Hello from another window!");
255 if (ImGui::Button(label: "Close Me"))
256 show_another_window = false;
257 ImGui::End();
258 }
259
260 // Rendering
261 ImGui::Render();
262
263 FrameContext* frameCtx = WaitForNextFrameResources();
264 UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
265 frameCtx->CommandAllocator->Reset();
266
267 D3D12_RESOURCE_BARRIER barrier = {};
268 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
269 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
270 barrier.Transition.pResource = g_mainRenderTargetResource[backBufferIdx];
271 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
272 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
273 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
274 g_pd3dCommandList->Reset(frameCtx->CommandAllocator, nullptr);
275 g_pd3dCommandList->ResourceBarrier(1, &barrier);
276
277 // Render Dear ImGui graphics
278 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 };
279 g_pd3dCommandList->ClearRenderTargetView(g_mainRenderTargetDescriptor[backBufferIdx], clear_color_with_alpha, 0, nullptr);
280 g_pd3dCommandList->OMSetRenderTargets(1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, nullptr);
281 g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
282 ImGui_ImplDX12_RenderDrawData(draw_data: ImGui::GetDrawData(), graphics_command_list: g_pd3dCommandList);
283 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
284 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
285 g_pd3dCommandList->ResourceBarrier(1, &barrier);
286 g_pd3dCommandList->Close();
287
288 g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
289
290 // Update and Render additional Platform Windows
291 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
292 {
293 ImGui::UpdatePlatformWindows();
294 ImGui::RenderPlatformWindowsDefault();
295 }
296
297 // Present
298 HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
299 //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
300 g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
301
302 UINT64 fenceValue = g_fenceLastSignaledValue + 1;
303 g_pd3dCommandQueue->Signal(g_fence, fenceValue);
304 g_fenceLastSignaledValue = fenceValue;
305 frameCtx->FenceValue = fenceValue;
306 }
307
308 WaitForLastSubmittedFrame();
309
310 // Cleanup
311 ImGui_ImplDX12_Shutdown();
312 ImGui_ImplWin32_Shutdown();
313 ImGui::DestroyContext();
314
315 CleanupDeviceD3D();
316 ::DestroyWindow(hwnd);
317 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
318
319 return 0;
320}
321
322// Helper functions
323bool CreateDeviceD3D(HWND hWnd)
324{
325 // Setup swap chain
326 DXGI_SWAP_CHAIN_DESC1 sd;
327 {
328 ZeroMemory(&sd, sizeof(sd));
329 sd.BufferCount = APP_NUM_BACK_BUFFERS;
330 sd.Width = 0;
331 sd.Height = 0;
332 sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
333 sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
334 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
335 sd.SampleDesc.Count = 1;
336 sd.SampleDesc.Quality = 0;
337 sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
338 sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
339 sd.Scaling = DXGI_SCALING_STRETCH;
340 sd.Stereo = FALSE;
341 }
342
343 // [DEBUG] Enable debug interface
344#ifdef DX12_ENABLE_DEBUG_LAYER
345 ID3D12Debug* pdx12Debug = nullptr;
346 if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pdx12Debug))))
347 pdx12Debug->EnableDebugLayer();
348#endif
349
350 // Create device
351 D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
352 if (D3D12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) != S_OK)
353 return false;
354
355 // [DEBUG] Setup debug interface to break on any warnings/errors
356#ifdef DX12_ENABLE_DEBUG_LAYER
357 if (pdx12Debug != nullptr)
358 {
359 ID3D12InfoQueue* pInfoQueue = nullptr;
360 g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pInfoQueue));
361 pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
362 pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
363 pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
364 pInfoQueue->Release();
365 pdx12Debug->Release();
366 }
367#endif
368
369 {
370 D3D12_DESCRIPTOR_HEAP_DESC desc = {};
371 desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
372 desc.NumDescriptors = APP_NUM_BACK_BUFFERS;
373 desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
374 desc.NodeMask = 1;
375 if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
376 return false;
377
378 SIZE_T rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
379 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
380 for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
381 {
382 g_mainRenderTargetDescriptor[i] = rtvHandle;
383 rtvHandle.ptr += rtvDescriptorSize;
384 }
385 }
386
387 {
388 D3D12_DESCRIPTOR_HEAP_DESC desc = {};
389 desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
390 desc.NumDescriptors = APP_SRV_HEAP_SIZE;
391 desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
392 if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
393 return false;
394 g_pd3dSrvDescHeapAlloc.Create(device: g_pd3dDevice, heap: g_pd3dSrvDescHeap);
395 }
396
397 {
398 D3D12_COMMAND_QUEUE_DESC desc = {};
399 desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
400 desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
401 desc.NodeMask = 1;
402 if (g_pd3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
403 return false;
404 }
405
406 for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
407 if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
408 return false;
409
410 if (g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frameContext[0].CommandAllocator, nullptr, IID_PPV_ARGS(&g_pd3dCommandList)) != S_OK ||
411 g_pd3dCommandList->Close() != S_OK)
412 return false;
413
414 if (g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
415 return false;
416
417 g_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
418 if (g_fenceEvent == nullptr)
419 return false;
420
421 {
422 IDXGIFactory4* dxgiFactory = nullptr;
423 IDXGISwapChain1* swapChain1 = nullptr;
424 if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK)
425 return false;
426 if (dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK)
427 return false;
428 if (swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK)
429 return false;
430 swapChain1->Release();
431 dxgiFactory->Release();
432 g_pSwapChain->SetMaximumFrameLatency(APP_NUM_BACK_BUFFERS);
433 g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
434 }
435
436 CreateRenderTarget();
437 return true;
438}
439
440void CleanupDeviceD3D()
441{
442 CleanupRenderTarget();
443 if (g_pSwapChain) { g_pSwapChain->SetFullscreenState(false, nullptr); g_pSwapChain->Release(); g_pSwapChain = nullptr; }
444 if (g_hSwapChainWaitableObject != nullptr) { CloseHandle(g_hSwapChainWaitableObject); }
445 for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
446 if (g_frameContext[i].CommandAllocator) { g_frameContext[i].CommandAllocator->Release(); g_frameContext[i].CommandAllocator = nullptr; }
447 if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = nullptr; }
448 if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = nullptr; }
449 if (g_pd3dRtvDescHeap) { g_pd3dRtvDescHeap->Release(); g_pd3dRtvDescHeap = nullptr; }
450 if (g_pd3dSrvDescHeap) { g_pd3dSrvDescHeap->Release(); g_pd3dSrvDescHeap = nullptr; }
451 if (g_fence) { g_fence->Release(); g_fence = nullptr; }
452 if (g_fenceEvent) { CloseHandle(g_fenceEvent); g_fenceEvent = nullptr; }
453 if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
454
455#ifdef DX12_ENABLE_DEBUG_LAYER
456 IDXGIDebug1* pDebug = nullptr;
457 if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&pDebug))))
458 {
459 pDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
460 pDebug->Release();
461 }
462#endif
463}
464
465void CreateRenderTarget()
466{
467 for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
468 {
469 ID3D12Resource* pBackBuffer = nullptr;
470 g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
471 g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, g_mainRenderTargetDescriptor[i]);
472 g_mainRenderTargetResource[i] = pBackBuffer;
473 }
474}
475
476void CleanupRenderTarget()
477{
478 WaitForLastSubmittedFrame();
479
480 for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
481 if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
482}
483
484void WaitForLastSubmittedFrame()
485{
486 FrameContext* frameCtx = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT];
487
488 UINT64 fenceValue = frameCtx->FenceValue;
489 if (fenceValue == 0)
490 return; // No fence was signaled
491
492 frameCtx->FenceValue = 0;
493 if (g_fence->GetCompletedValue() >= fenceValue)
494 return;
495
496 g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
497 WaitForSingleObject(g_fenceEvent, INFINITE);
498}
499
500FrameContext* WaitForNextFrameResources()
501{
502 UINT nextFrameIndex = g_frameIndex + 1;
503 g_frameIndex = nextFrameIndex;
504
505 HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr };
506 DWORD numWaitableObjects = 1;
507
508 FrameContext* frameCtx = &g_frameContext[nextFrameIndex % APP_NUM_FRAMES_IN_FLIGHT];
509 UINT64 fenceValue = frameCtx->FenceValue;
510 if (fenceValue != 0) // means no fence was signaled
511 {
512 frameCtx->FenceValue = 0;
513 g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
514 waitableObjects[1] = g_fenceEvent;
515 numWaitableObjects = 2;
516 }
517
518 WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);
519
520 return frameCtx;
521}
522
523// Forward declare message handler from imgui_impl_win32.cpp
524extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
525
526// Win32 message handler
527// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
528// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
529// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
530// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
531LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
532{
533 if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
534 return true;
535
536 switch (msg)
537 {
538 case WM_SIZE:
539 if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
540 {
541 WaitForLastSubmittedFrame();
542 CleanupRenderTarget();
543 HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
544 assert(SUCCEEDED(result) && "Failed to resize swapchain.");
545 CreateRenderTarget();
546 }
547 return 0;
548 case WM_SYSCOMMAND:
549 if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
550 return 0;
551 break;
552 case WM_DESTROY:
553 ::PostQuitMessage(0);
554 return 0;
555 }
556 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
557}
558

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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