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