main-windows.cpp 21.3 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 "imnodes.hpp"
Gauthier Quesnel's avatar
Gauthier Quesnel committed
13
14
15
#include "gui.hpp"

#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
162
163
    imnodes::CreateContext();
    irt::application_initialize();

164
165
166
    // Main loop
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
167
    while (msg.message != WM_QUIT) {
168
        // Poll and handle messages (inputs, window resize, etc.)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
169
170
171
172
173
174
175
176
177
        // 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)) {
178
179
180
181
182
183
184
185
186
187
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            continue;
        }

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

188
        if (!irt::application_show())
189
            ::PostMessage(hwnd, WM_CLOSE, 0, 0);
190

Gauthier Quesnel's avatar
Gauthier Quesnel committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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
        // 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(
        //      "This is some useful text."); // Display some text (you can use a
        //                                    // format strings too)
        //    ImGui::Checkbox("Demo Window",
        //                    &show_demo_window); // Edit bools storing our window
        //                                        // 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();
        }

251
        // Rendering
Gauthier Quesnel's avatar
Gauthier Quesnel committed
252
253
254
        ImGui::Render();

        FrameContext* frameCtx = WaitForNextFrameResources();
255
        UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
256
        frameCtx->CommandAllocator->Reset();
257
258
259
260

        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
261
262
263
264
        barrier.Transition.pResource =
          g_mainRenderTargetResource[backBufferIdx];
        barrier.Transition.Subresource =
          D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
265
266
        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
        barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
267
        g_pd3dCommandList->Reset(frameCtx->CommandAllocator, NULL);
268
        g_pd3dCommandList->ResourceBarrier(1, &barrier);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
269
270
271
272
273
274
275
276
277
278
279
280
281

        // 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);
282
283
284
285
286
287
288
        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
289
290
        g_pd3dCommandQueue->ExecuteCommandLists(
          1, (ID3D12CommandList* const*)&g_pd3dCommandList);
291
292

        g_pSwapChain->Present(1, 0); // Present with vsync
Gauthier Quesnel's avatar
Gauthier Quesnel committed
293
        // g_pSwapChain->Present(0, 0); // Present without vsync
294
295
296
297

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
301
    WaitForLastSubmittedFrame();
302

Gauthier Quesnel's avatar
Gauthier Quesnel committed
303
    irt::application_shutdown();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
304
    imnodes::DestroyContext();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
305
306

    // Cleanup
307
308
309
310
311
312
313
314
315
316
317
318
319
    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
320
321
bool
CreateDeviceD3D(HWND hWnd)
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
{
    // 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
341
    // [DEBUG] Enable debug interface
342
343
344
345
346
347
#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
348
    // Create device
349
    D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
350
351
    if (D3D12CreateDevice(NULL, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) !=
        S_OK)
352
353
        return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
354
355
356
357
358
359
360
361
362
363
364
365
366
        // [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

367
368
369
370
371
372
    {
        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
373
374
        if (g_pd3dDevice->CreateDescriptorHeap(
              &desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
375
376
            return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
377
378
379
380
381
382
        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++) {
383
384
385
386
387
388
389
390
391
392
            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
393
394
        if (g_pd3dDevice->CreateDescriptorHeap(
              &desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
395
396
397
398
399
400
401
402
            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
403
404
        if (g_pd3dDevice->CreateCommandQueue(
              &desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
405
406
407
408
            return false;
    }

    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
409
410
411
        if (g_pd3dDevice->CreateCommandAllocator(
              D3D12_COMMAND_LIST_TYPE_DIRECT,
              IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
412
413
            return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
414
415
416
417
418
419
    if (g_pd3dDevice->CreateCommandList(0,
                                        D3D12_COMMAND_LIST_TYPE_DIRECT,
                                        g_frameContext[0].CommandAllocator,
                                        NULL,
                                        IID_PPV_ARGS(&g_pd3dCommandList)) !=
          S_OK ||
420
421
422
        g_pd3dCommandList->Close() != S_OK)
        return false;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
423
424
    if (g_pd3dDevice->CreateFence(
          0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
425
426
427
428
429
430
431
432
433
        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
434
435
436
437
438
439
        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)
440
441
442
443
            return false;
        swapChain1->Release();
        dxgiFactory->Release();
        g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
444
445
        g_hSwapChainWaitableObject =
          g_pSwapChain->GetFrameLatencyWaitableObject();
446
447
448
449
450
451
    }

    CreateRenderTarget();
    return true;
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
452
453
void
CleanupDeviceD3D()
454
455
{
    CleanupRenderTarget();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
456
457
458
459
460
461
462
    if (g_pSwapChain) {
        g_pSwapChain->Release();
        g_pSwapChain = NULL;
    }
    if (g_hSwapChainWaitableObject != NULL) {
        CloseHandle(g_hSwapChainWaitableObject);
    }
463
    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
        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;
    }
496
497
498

#ifdef DX12_ENABLE_DEBUG_LAYER
    IDXGIDebug1* pDebug = NULL;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
499
    if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&pDebug)))) {
500
501
502
503
504
505
        pDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
        pDebug->Release();
    }
#endif
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
506
507
void
CreateRenderTarget()
508
{
Gauthier Quesnel's avatar
Gauthier Quesnel committed
509
    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
510
511
        ID3D12Resource* pBackBuffer = NULL;
        g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
512
513
        g_pd3dDevice->CreateRenderTargetView(
          pBackBuffer, NULL, g_mainRenderTargetDescriptor[i]);
514
515
516
517
        g_mainRenderTargetResource[i] = pBackBuffer;
    }
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
518
519
void
CleanupRenderTarget()
520
521
522
523
{
    WaitForLastSubmittedFrame();

    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
524
525
526
527
        if (g_mainRenderTargetResource[i]) {
            g_mainRenderTargetResource[i]->Release();
            g_mainRenderTargetResource[i] = NULL;
        }
528
529
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
530
531
void
WaitForLastSubmittedFrame()
532
{
Gauthier Quesnel's avatar
Gauthier Quesnel committed
533
534
    FrameContext* frameCtx =
      &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT];
535

Gauthier Quesnel's avatar
Gauthier Quesnel committed
536
    UINT64 fenceValue = frameCtx->FenceValue;
537
538
539
    if (fenceValue == 0)
        return; // No fence was signaled

Gauthier Quesnel's avatar
Gauthier Quesnel committed
540
    frameCtx->FenceValue = 0;
541
542
543
544
545
546
547
    if (g_fence->GetCompletedValue() >= fenceValue)
        return;

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
548
549
FrameContext*
WaitForNextFrameResources()
550
551
552
553
554
555
556
{
    UINT nextFrameIndex = g_frameIndex + 1;
    g_frameIndex = nextFrameIndex;

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
557
558
559
    FrameContext* frameCtx =
      &g_frameContext[nextFrameIndex % NUM_FRAMES_IN_FLIGHT];
    UINT64 fenceValue = frameCtx->FenceValue;
560
561
    if (fenceValue != 0) // means no fence was signaled
    {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
562
        frameCtx->FenceValue = 0;
563
564
565
566
567
568
569
        g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
        waitableObjects[1] = g_fenceEvent;
        numWaitableObjects = 2;
    }

    WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
570
    return frameCtx;
571
572
}

Gauthier Quesnel's avatar
Gauthier Quesnel committed
573
574
void
ResizeSwapChain(HWND hWnd, int width, int height)
575
576
577
578
579
580
581
582
583
584
585
586
587
{
    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
588
589
    dxgiFactory->CreateSwapChainForHwnd(
      g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1);
590
591
592
593
594
595
596
597
598
599
600
    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
601
602
603
604
605
extern IMGUI_IMPL_API LRESULT
ImGui_ImplWin32_WndProcHandler(HWND hWnd,
                               UINT msg,
                               WPARAM wParam,
                               LPARAM lParam);
606
607

// Win32 message handler
Gauthier Quesnel's avatar
Gauthier Quesnel committed
608
609
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
610
611
612
613
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
614
    switch (msg) {
615
    case WM_SIZE:
Gauthier Quesnel's avatar
Gauthier Quesnel committed
616
617
        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) {
            WaitForLastSubmittedFrame();
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
            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);
}