main-windows.cpp 21.6 KB
Newer Older
Gauthier Quesnel's avatar
Gauthier Quesnel committed
1
2
3
4
// Dear ImGui: standalone example application for DirectX 12
// If you are new to Dear ImGui, read documentation from the docs/ folder + read
// the top of imgui.cpp. Read online:
// https://github.com/ocornut/imgui/tree/master/docs
5

Gauthier Quesnel's avatar
Gauthier Quesnel committed
6
7
8
9
10
// Important: to compile on 32-bit systems, the DirectX12 backend requires code
// to be compiled with '#define ImTextureID ImU64'. This is because we need
// ImTextureID to carry a 64-bit value and by default ImTextureID is defined as
// void*. This define is set in the example .vcxproj file and need to be
// replicated in your app or by adding it to your imconfig.h file.
11

12
#include "application.hpp"
13
#include "imnodes.hpp"
Gauthier Quesnel's avatar
Gauthier Quesnel committed
14
15

#include "imgui.h"
16
#include "imgui_impl_dx12.h"
Gauthier Quesnel's avatar
Gauthier Quesnel committed
17
#include "imgui_impl_win32.h"
18
19
20
21
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>

Gauthier Quesnel's avatar
Gauthier Quesnel committed
22
23
24
#ifdef _DEBUG
#define DX12_ENABLE_DEBUG_LAYER
#endif
25
26
27
28
29
30
31
32
33

#ifdef DX12_ENABLE_DEBUG_LAYER
#include <dxgidebug.h>
#pragma comment(lib, "dxguid.lib")
#endif

struct FrameContext
{
    ID3D12CommandAllocator* CommandAllocator;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
34
    UINT64 FenceValue;
35
36
37
};

// Data
Gauthier Quesnel's avatar
Gauthier Quesnel committed
38
39
40
static int const NUM_FRAMES_IN_FLIGHT = 3;
static FrameContext g_frameContext[NUM_FRAMES_IN_FLIGHT] = {};
static UINT g_frameIndex = 0;
41

Gauthier Quesnel's avatar
Gauthier Quesnel committed
42
static int const NUM_BACK_BUFFERS = 3;
43
44
45
46
47
48
static ID3D12Device* g_pd3dDevice = NULL;
static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = NULL;
static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = NULL;
static ID3D12CommandQueue* g_pd3dCommandQueue = NULL;
static ID3D12GraphicsCommandList* g_pd3dCommandList = NULL;
static ID3D12Fence* g_fence = NULL;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
49
50
static HANDLE g_fenceEvent = NULL;
static UINT64 g_fenceLastSignaledValue = 0;
51
static IDXGISwapChain3* g_pSwapChain = NULL;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
52
static HANDLE g_hSwapChainWaitableObject = NULL;
53
static ID3D12Resource* g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {};
Gauthier Quesnel's avatar
Gauthier Quesnel committed
54
55
static D3D12_CPU_DESCRIPTOR_HANDLE
  g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {};
56
57

// Forward declarations of helper functions
Gauthier Quesnel's avatar
Gauthier Quesnel committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
bool
CreateDeviceD3D(HWND hWnd);
void
CleanupDeviceD3D();
void
CreateRenderTarget();
void
CleanupRenderTarget();
void
WaitForLastSubmittedFrame();
FrameContext*
WaitForNextFrameResources();
void
ResizeSwapChain(HWND hWnd, int width, int height);
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
74
75

// Main code
Gauthier Quesnel's avatar
Gauthier Quesnel committed
76
77
int
main(int, char**)
78
79
{
    // Create application window
Gauthier Quesnel's avatar
Gauthier Quesnel committed
80
81
82
83
    // ImGui_ImplWin32_EnableDpiAwareness();
    WNDCLASSEX wc = { sizeof(WNDCLASSEX),    CS_CLASSDC, WndProc, 0L,   0L,
                      GetModuleHandle(NULL), NULL,       NULL,    NULL, NULL,
                      _T("ImGui Example"),   NULL };
84
    ::RegisterClassEx(&wc);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
85
86
87
88
89
90
91
92
93
94
95
    HWND hwnd = ::CreateWindow(wc.lpszClassName,
                               _T("Irritator"),
                               WS_OVERLAPPEDWINDOW,
                               100,
                               100,
                               1280,
                               800,
                               NULL,
                               NULL,
                               wc.hInstance,
                               NULL);
96
97

    // Initialize Direct3D
Gauthier Quesnel's avatar
Gauthier Quesnel committed
98
    if (!CreateDeviceD3D(hwnd)) {
99
100
101
102
103
104
105
106
107
108
109
110
        CleanupDeviceD3D();
        ::UnregisterClass(wc.lpszClassName, wc.hInstance);
        return 1;
    }

    // Show the window
    ::ShowWindow(hwnd, SW_SHOWDEFAULT);
    ::UpdateWindow(hwnd);

    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
111
112
113
114
115
    ImGuiIO& io = ImGui::GetIO();
    (void)io;
    // io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable
    // Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; //
    // Enable Gamepad Controls
116
117
118

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
119
    // ImGui::StyleColorsClassic();
120

Gauthier Quesnel's avatar
Gauthier Quesnel committed
121
    // Setup Platform/Renderer backends
122
    ImGui_ImplWin32_Init(hwnd);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
123
124
125
126
127
128
129
    ImGui_ImplDX12_Init(
      g_pd3dDevice,
      NUM_FRAMES_IN_FLIGHT,
      DXGI_FORMAT_R8G8B8A8_UNORM,
      g_pd3dSrvDescHeap,
      g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
      g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
130

131
    // Load Fonts
Gauthier Quesnel's avatar
Gauthier Quesnel committed
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    // - 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.
    // - AddFontFromFileTTF() will return the ImFont* so you can store it if you
    // need to select the font among multiple.
    // - If the file cannot be loaded, the function will return NULL. Please
    // handle those errors in your application (e.g. use an assertion, or
    // display an error and quit).
    // - 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.
    // - Read 'docs/FONTS.md' for more instructions and details.
    // - Remember that in C/C++ if you want to include a backslash \ in a string
    // literal you need to write a double backslash \\ !
    // io.Fonts->AddFontDefault();
    // io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
    // io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
    // io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
    // io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
    // ImFont* font =
    // io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f,
    // NULL, io.Fonts->GetGlyphRangesJapanese()); IM_ASSERT(font != NULL);
155
156

    // Our state
Gauthier Quesnel's avatar
Gauthier Quesnel committed
157
158
    bool show_demo_window = false;
    bool show_another_window = false;
159
160
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
161
    imnodes::CreateContext();
162
163
164
165
166
167
168
169
170
171
172
173
174
    irt::application app;
    if (!app.init()) {
        imnodes::DestroyContext();

        ImGui_ImplDX12_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyContext();

        CleanupDeviceD3D();
        ::DestroyWindow(hwnd);
        ::UnregisterClass(wc.lpszClassName, wc.hInstance);
        return 0;
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
175

176
177
178
    // Main loop
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
179
    while (msg.message != WM_QUIT) {
180
        // Poll and handle messages (inputs, window resize, etc.)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
181
182
183
184
185
186
187
188
189
        // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to
        // tell if dear imgui wants to use your inputs.
        // - When io.WantCaptureMouse is true, do not dispatch mouse input data
        // to your main application.
        // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input
        // data to your main application. Generally you may always pass all
        // inputs to dear imgui, and hide them from your application based on
        // those two flags.
        if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
190
191
192
193
194
195
196
197
198
199
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            continue;
        }

        // Start the Dear ImGui frame
        ImGui_ImplDX12_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();

200
        if (!app.show())
201
            ::PostMessage(hwnd, WM_CLOSE, 0, 0);
202

Gauthier Quesnel's avatar
Gauthier Quesnel committed
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
        // 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!).
        if (show_demo_window)
            ImGui::ShowDemoWindow(&show_demo_window);

        // 2. Show a simple window that we create ourselves. We use a Begin/End
        // pair to created a named window.

        //{
        //    static float f = 0.0f;
        //    static int counter = 0;

        //    ImGui::Begin("Hello, world!"); // Create a window called "Hello,
        //                                   // world!" and append into it.

        //    ImGui::Text(
220
221
        //      "This is some useful text."); // Display some text (you can use
        //      a
Gauthier Quesnel's avatar
Gauthier Quesnel committed
222
223
        //                                    // format strings too)
        //    ImGui::Checkbox("Demo Window",
224
225
        //                    &show_demo_window); // Edit bools storing our
        //                    window
Gauthier Quesnel's avatar
Gauthier Quesnel committed
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
        //                                        // open/close state
        //    ImGui::Checkbox("Another Window", &show_another_window);

        //    ImGui::SliderFloat(
        //      "float",
        //      &f,
        //      0.0f,
        //      1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
        //    ImGui::ColorEdit3(
        //      "clear color",
        //      (float*)&clear_color); // Edit 3 floats representing a color

        //    if (ImGui::Button(
        //          "Button")) // Buttons return true when clicked (most widgets
        //                     // return true when edited/activated)
        //        counter++;
        //    ImGui::SameLine();
        //    ImGui::Text("counter = %d", counter);

        //    ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
        //                1000.0f / ImGui::GetIO().Framerate,
        //                ImGui::GetIO().Framerate);
        //    ImGui::End();
        //}

        // 3. Show another simple window.
        if (show_another_window) {
            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)
            ImGui::Text("Hello from another window!");
            if (ImGui::Button("Close Me"))
                show_another_window = false;
            ImGui::End();
        }

264
        // Rendering
Gauthier Quesnel's avatar
Gauthier Quesnel committed
265
266
267
        ImGui::Render();

        FrameContext* frameCtx = WaitForNextFrameResources();
268
        UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
269
        frameCtx->CommandAllocator->Reset();
270
271
272
273

        D3D12_RESOURCE_BARRIER barrier = {};
        barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
        barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
274
275
276
277
        barrier.Transition.pResource =
          g_mainRenderTargetResource[backBufferIdx];
        barrier.Transition.Subresource =
          D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
278
279
        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
        barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
280
        g_pd3dCommandList->Reset(frameCtx->CommandAllocator, NULL);
281
        g_pd3dCommandList->ResourceBarrier(1, &barrier);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
282
283
284
285
286
287
288
289
290
291
292
293
294

        // Render Dear ImGui graphics
        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 };
        g_pd3dCommandList->ClearRenderTargetView(
          g_mainRenderTargetDescriptor[backBufferIdx],
          clear_color_with_alpha,
          0,
          NULL);
        g_pd3dCommandList->OMSetRenderTargets(
          1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, NULL);
295
296
297
298
299
300
301
        g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
        ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
        barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
        g_pd3dCommandList->ResourceBarrier(1, &barrier);
        g_pd3dCommandList->Close();

Gauthier Quesnel's avatar
Gauthier Quesnel committed
302
303
        g_pd3dCommandQueue->ExecuteCommandLists(
          1, (ID3D12CommandList* const*)&g_pd3dCommandList);
304
305

        g_pSwapChain->Present(1, 0); // Present with vsync
Gauthier Quesnel's avatar
Gauthier Quesnel committed
306
        // g_pSwapChain->Present(0, 0); // Present without vsync
307
308
309
310

        UINT64 fenceValue = g_fenceLastSignaledValue + 1;
        g_pd3dCommandQueue->Signal(g_fence, fenceValue);
        g_fenceLastSignaledValue = fenceValue;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
311
        frameCtx->FenceValue = fenceValue;
312
313
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
314
    WaitForLastSubmittedFrame();
315

316
    app.shutdown();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
317
    imnodes::DestroyContext();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
318
319

    // Cleanup
320
321
322
323
324
325
326
327
328
329
330
331
332
    ImGui_ImplDX12_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
    ::DestroyWindow(hwnd);
    ::UnregisterClass(wc.lpszClassName, wc.hInstance);

    return 0;
}

// Helper functions

Gauthier Quesnel's avatar
Gauthier Quesnel committed
333
334
bool
CreateDeviceD3D(HWND hWnd)
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
{
    // Setup swap chain
    DXGI_SWAP_CHAIN_DESC1 sd;
    {
        ZeroMemory(&sd, sizeof(sd));
        sd.BufferCount = NUM_BACK_BUFFERS;
        sd.Width = 0;
        sd.Height = 0;
        sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
        sd.Scaling = DXGI_SCALING_STRETCH;
        sd.Stereo = FALSE;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
354
    // [DEBUG] Enable debug interface
355
356
357
358
359
360
#ifdef DX12_ENABLE_DEBUG_LAYER
    ID3D12Debug* pdx12Debug = NULL;
    if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pdx12Debug))))
        pdx12Debug->EnableDebugLayer();
#endif

Gauthier Quesnel's avatar
Gauthier Quesnel committed
361
    // Create device
362
    D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
363
364
    if (D3D12CreateDevice(NULL, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) !=
        S_OK)
365
366
        return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
367
368
369
370
371
372
373
374
375
376
377
378
379
        // [DEBUG] Setup debug interface to break on any warnings/errors
#ifdef DX12_ENABLE_DEBUG_LAYER
    if (pdx12Debug != NULL) {
        ID3D12InfoQueue* pInfoQueue = NULL;
        g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pInfoQueue));
        pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
        pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
        pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
        pInfoQueue->Release();
        pdx12Debug->Release();
    }
#endif

380
381
382
383
384
385
    {
        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
        desc.NumDescriptors = NUM_BACK_BUFFERS;
        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
        desc.NodeMask = 1;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
386
387
        if (g_pd3dDevice->CreateDescriptorHeap(
              &desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
388
389
            return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
390
391
392
393
394
395
        SIZE_T rtvDescriptorSize =
          g_pd3dDevice->GetDescriptorHandleIncrementSize(
            D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
        D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle =
          g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
        for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
396
397
398
399
400
401
402
403
404
405
            g_mainRenderTargetDescriptor[i] = rtvHandle;
            rtvHandle.ptr += rtvDescriptorSize;
        }
    }

    {
        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        desc.NumDescriptors = 1;
        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
406
407
        if (g_pd3dDevice->CreateDescriptorHeap(
              &desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
408
409
410
411
412
413
414
415
            return false;
    }

    {
        D3D12_COMMAND_QUEUE_DESC desc = {};
        desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
        desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
        desc.NodeMask = 1;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
416
417
        if (g_pd3dDevice->CreateCommandQueue(
              &desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
418
419
420
421
            return false;
    }

    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
422
423
424
        if (g_pd3dDevice->CreateCommandAllocator(
              D3D12_COMMAND_LIST_TYPE_DIRECT,
              IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
425
426
            return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
427
428
429
430
431
432
    if (g_pd3dDevice->CreateCommandList(0,
                                        D3D12_COMMAND_LIST_TYPE_DIRECT,
                                        g_frameContext[0].CommandAllocator,
                                        NULL,
                                        IID_PPV_ARGS(&g_pd3dCommandList)) !=
          S_OK ||
433
434
435
        g_pd3dCommandList->Close() != S_OK)
        return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
436
437
    if (g_pd3dDevice->CreateFence(
          0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
438
439
440
441
442
443
444
445
446
        return false;

    g_fenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (g_fenceEvent == NULL)
        return false;

    {
        IDXGIFactory4* dxgiFactory = NULL;
        IDXGISwapChain1* swapChain1 = NULL;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
447
448
449
450
451
452
        if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK)
            return false;
        if (dxgiFactory->CreateSwapChainForHwnd(
              g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1) != S_OK)
            return false;
        if (swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK)
453
454
455
456
            return false;
        swapChain1->Release();
        dxgiFactory->Release();
        g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
457
458
        g_hSwapChainWaitableObject =
          g_pSwapChain->GetFrameLatencyWaitableObject();
459
460
461
462
463
464
    }

    CreateRenderTarget();
    return true;
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
465
466
void
CleanupDeviceD3D()
467
468
{
    CleanupRenderTarget();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
469
470
471
472
473
474
475
    if (g_pSwapChain) {
        g_pSwapChain->Release();
        g_pSwapChain = NULL;
    }
    if (g_hSwapChainWaitableObject != NULL) {
        CloseHandle(g_hSwapChainWaitableObject);
    }
476
    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
        if (g_frameContext[i].CommandAllocator) {
            g_frameContext[i].CommandAllocator->Release();
            g_frameContext[i].CommandAllocator = NULL;
        }
    if (g_pd3dCommandQueue) {
        g_pd3dCommandQueue->Release();
        g_pd3dCommandQueue = NULL;
    }
    if (g_pd3dCommandList) {
        g_pd3dCommandList->Release();
        g_pd3dCommandList = NULL;
    }
    if (g_pd3dRtvDescHeap) {
        g_pd3dRtvDescHeap->Release();
        g_pd3dRtvDescHeap = NULL;
    }
    if (g_pd3dSrvDescHeap) {
        g_pd3dSrvDescHeap->Release();
        g_pd3dSrvDescHeap = NULL;
    }
    if (g_fence) {
        g_fence->Release();
        g_fence = NULL;
    }
    if (g_fenceEvent) {
        CloseHandle(g_fenceEvent);
        g_fenceEvent = NULL;
    }
    if (g_pd3dDevice) {
        g_pd3dDevice->Release();
        g_pd3dDevice = NULL;
    }
509
510
511

#ifdef DX12_ENABLE_DEBUG_LAYER
    IDXGIDebug1* pDebug = NULL;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
512
    if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&pDebug)))) {
513
514
515
516
517
518
        pDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
        pDebug->Release();
    }
#endif
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
519
520
void
CreateRenderTarget()
521
{
Gauthier Quesnel's avatar
Gauthier Quesnel committed
522
    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
523
524
        ID3D12Resource* pBackBuffer = NULL;
        g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
525
526
        g_pd3dDevice->CreateRenderTargetView(
          pBackBuffer, NULL, g_mainRenderTargetDescriptor[i]);
527
528
529
530
        g_mainRenderTargetResource[i] = pBackBuffer;
    }
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
531
532
void
CleanupRenderTarget()
533
534
535
536
{
    WaitForLastSubmittedFrame();

    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
537
538
539
540
        if (g_mainRenderTargetResource[i]) {
            g_mainRenderTargetResource[i]->Release();
            g_mainRenderTargetResource[i] = NULL;
        }
541
542
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
543
544
void
WaitForLastSubmittedFrame()
545
{
Gauthier Quesnel's avatar
Gauthier Quesnel committed
546
547
    FrameContext* frameCtx =
      &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT];
548

Gauthier Quesnel's avatar
Gauthier Quesnel committed
549
    UINT64 fenceValue = frameCtx->FenceValue;
550
551
552
    if (fenceValue == 0)
        return; // No fence was signaled

Gauthier Quesnel's avatar
Gauthier Quesnel committed
553
    frameCtx->FenceValue = 0;
554
555
556
557
558
559
560
    if (g_fence->GetCompletedValue() >= fenceValue)
        return;

    g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
    WaitForSingleObject(g_fenceEvent, INFINITE);
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
561
562
FrameContext*
WaitForNextFrameResources()
563
564
565
566
567
568
569
{
    UINT nextFrameIndex = g_frameIndex + 1;
    g_frameIndex = nextFrameIndex;

    HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, NULL };
    DWORD numWaitableObjects = 1;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
570
571
572
    FrameContext* frameCtx =
      &g_frameContext[nextFrameIndex % NUM_FRAMES_IN_FLIGHT];
    UINT64 fenceValue = frameCtx->FenceValue;
573
574
    if (fenceValue != 0) // means no fence was signaled
    {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
575
        frameCtx->FenceValue = 0;
576
577
578
579
580
581
582
        g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
        waitableObjects[1] = g_fenceEvent;
        numWaitableObjects = 2;
    }

    WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
583
    return frameCtx;
584
585
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
586
587
void
ResizeSwapChain(HWND hWnd, int width, int height)
588
589
590
591
592
593
594
595
596
597
598
599
600
{
    DXGI_SWAP_CHAIN_DESC1 sd;
    g_pSwapChain->GetDesc1(&sd);
    sd.Width = width;
    sd.Height = height;

    IDXGIFactory4* dxgiFactory = NULL;
    g_pSwapChain->GetParent(IID_PPV_ARGS(&dxgiFactory));

    g_pSwapChain->Release();
    CloseHandle(g_hSwapChainWaitableObject);

    IDXGISwapChain1* swapChain1 = NULL;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
601
602
    dxgiFactory->CreateSwapChainForHwnd(
      g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1);
603
604
605
606
607
608
609
610
611
612
613
    swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain));
    swapChain1->Release();
    dxgiFactory->Release();

    g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);

    g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
    assert(g_hSwapChainWaitableObject != NULL);
}

// Forward declare message handler from imgui_impl_win32.cpp
Gauthier Quesnel's avatar
Gauthier Quesnel committed
614
615
616
617
618
extern IMGUI_IMPL_API LRESULT
ImGui_ImplWin32_WndProcHandler(HWND hWnd,
                               UINT msg,
                               WPARAM wParam,
                               LPARAM lParam);
619
620

// Win32 message handler
Gauthier Quesnel's avatar
Gauthier Quesnel committed
621
622
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
623
624
625
626
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
627
    switch (msg) {
628
    case WM_SIZE:
Gauthier Quesnel's avatar
Gauthier Quesnel committed
629
630
        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) {
            WaitForLastSubmittedFrame();
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
            ImGui_ImplDX12_InvalidateDeviceObjects();
            CleanupRenderTarget();
            ResizeSwapChain(hWnd, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam));
            CreateRenderTarget();
            ImGui_ImplDX12_CreateDeviceObjects();
        }
        return 0;
    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
            return 0;
        break;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        return 0;
    }
    return ::DefWindowProc(hWnd, msg, wParam, lParam);
}