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 |
26 | static const int APP_NUM_FRAMES_IN_FLIGHT = 2; |
27 | static const int APP_NUM_BACK_BUFFERS = 2; |
28 | static const int APP_SRV_HEAP_SIZE = 64; |
29 | |
30 | struct FrameContext |
31 | { |
32 | ID3D12CommandAllocator* CommandAllocator; |
33 | UINT64 FenceValue; |
34 | }; |
35 | |
36 | // Simple free list based allocator |
37 | struct 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 |
82 | static FrameContext g_frameContext[APP_NUM_FRAMES_IN_FLIGHT] = {}; |
83 | static UINT g_frameIndex = 0; |
84 | |
85 | static ID3D12Device* g_pd3dDevice = nullptr; |
86 | static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = nullptr; |
87 | static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr; |
88 | static ExampleDescriptorHeapAllocator g_pd3dSrvDescHeapAlloc; |
89 | static ID3D12CommandQueue* g_pd3dCommandQueue = nullptr; |
90 | static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr; |
91 | static ID3D12Fence* g_fence = nullptr; |
92 | static HANDLE g_fenceEvent = nullptr; |
93 | static UINT64 g_fenceLastSignaledValue = 0; |
94 | static IDXGISwapChain3* g_pSwapChain = nullptr; |
95 | static bool g_SwapChainOccluded = false; |
96 | static HANDLE g_hSwapChainWaitableObject = nullptr; |
97 | static ID3D12Resource* g_mainRenderTargetResource[APP_NUM_BACK_BUFFERS] = {}; |
98 | static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[APP_NUM_BACK_BUFFERS] = {}; |
99 | |
100 | // Forward declarations of helper functions |
101 | bool CreateDeviceD3D(HWND hWnd); |
102 | void CleanupDeviceD3D(); |
103 | void CreateRenderTarget(); |
104 | void CleanupRenderTarget(); |
105 | void WaitForLastSubmittedFrame(); |
106 | FrameContext* WaitForNextFrameResources(); |
107 | LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
108 | |
109 | // Main code |
110 | int 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 |
332 | bool 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 | |
449 | void 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 | |
474 | void 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 | |
485 | void 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 | |
493 | void 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 | |
509 | FrameContext* 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 |
533 | extern 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. |
540 | LRESULT 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 | |