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
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            continue;
        }

195
196
        app.run_simulations();

197
198
199
200
201
        // Start the Dear ImGui frame
        ImGui_ImplDX12_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();

202
        if (!app.show())
203
            ::PostMessage(hwnd, WM_CLOSE, 0, 0);
204

Gauthier Quesnel's avatar
Gauthier Quesnel committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
        // 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(
222
223
        //      "This is some useful text."); // Display some text (you can use
        //      a
Gauthier Quesnel's avatar
Gauthier Quesnel committed
224
225
        //                                    // format strings too)
        //    ImGui::Checkbox("Demo Window",
226
227
        //                    &show_demo_window); // Edit bools storing our
        //                    window
Gauthier Quesnel's avatar
Gauthier Quesnel committed
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
264
265
        //                                        // 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();
        }

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

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

        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
276
277
278
279
        barrier.Transition.pResource =
          g_mainRenderTargetResource[backBufferIdx];
        barrier.Transition.Subresource =
          D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
280
281
        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
        barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
282
        g_pd3dCommandList->Reset(frameCtx->CommandAllocator, NULL);
283
        g_pd3dCommandList->ResourceBarrier(1, &barrier);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
284
285
286
287
288
289
290
291
292
293
294
295
296

        // 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);
297
298
299
300
301
302
303
        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
304
305
        g_pd3dCommandQueue->ExecuteCommandLists(
          1, (ID3D12CommandList* const*)&g_pd3dCommandList);
306
307

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

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
316
    WaitForLastSubmittedFrame();
317

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

    // Cleanup
322
323
324
325
326
327
328
329
330
331
332
333
334
    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
335
336
bool
CreateDeviceD3D(HWND hWnd)
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
{
    // 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
356
    // [DEBUG] Enable debug interface
357
358
359
360
361
362
#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
363
    // Create device
364
    D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
365
366
    if (D3D12CreateDevice(NULL, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) !=
        S_OK)
367
368
        return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
369
370
371
372
373
374
375
376
377
378
379
380
381
        // [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

382
383
384
385
386
387
    {
        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
388
389
        if (g_pd3dDevice->CreateDescriptorHeap(
              &desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
390
391
            return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
392
393
394
395
396
397
        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++) {
398
399
400
401
402
403
404
405
406
407
            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
408
409
        if (g_pd3dDevice->CreateDescriptorHeap(
              &desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
410
411
412
413
414
415
416
417
            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
418
419
        if (g_pd3dDevice->CreateCommandQueue(
              &desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
420
421
422
423
            return false;
    }

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

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
438
439
    if (g_pd3dDevice->CreateFence(
          0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
440
441
442
443
444
445
446
447
448
        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
449
450
451
452
453
454
        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)
455
456
457
458
            return false;
        swapChain1->Release();
        dxgiFactory->Release();
        g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
459
460
        g_hSwapChainWaitableObject =
          g_pSwapChain->GetFrameLatencyWaitableObject();
461
462
463
464
465
466
    }

    CreateRenderTarget();
    return true;
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
467
468
void
CleanupDeviceD3D()
469
470
{
    CleanupRenderTarget();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
471
472
473
474
475
476
477
    if (g_pSwapChain) {
        g_pSwapChain->Release();
        g_pSwapChain = NULL;
    }
    if (g_hSwapChainWaitableObject != NULL) {
        CloseHandle(g_hSwapChainWaitableObject);
    }
478
    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
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
509
510
        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;
    }
511
512
513

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

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

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

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

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

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

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

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

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

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

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

    WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
585
    return frameCtx;
586
587
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
588
589
void
ResizeSwapChain(HWND hWnd, int width, int height)
590
591
592
593
594
595
596
597
598
599
600
601
602
{
    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
603
604
    dxgiFactory->CreateSwapChainForHwnd(
      g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1);
605
606
607
608
609
610
611
612
613
614
615
    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
616
617
618
619
620
extern IMGUI_IMPL_API LRESULT
ImGui_ImplWin32_WndProcHandler(HWND hWnd,
                               UINT msg,
                               WPARAM wParam,
                               LPARAM lParam);
621
622

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
629
    switch (msg) {
630
    case WM_SIZE:
Gauthier Quesnel's avatar
Gauthier Quesnel committed
631
632
        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) {
            WaitForLastSubmittedFrame();
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
            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);
}