Просмотр исходного кода

Add multi-viewports for DX12. (#2851)

(cherry picked from commit 899e48565d1ecefde06063f99c75e702adcef175)
Ilya.Sevrikov 6 лет назад
Родитель
Сommit
05c1f2795a
3 измененных файлов с 265 добавлено и 102 удалено
  1. 2 2
      examples/example_win32_directx12/main.cpp
  2. 260 98
      examples/imgui_impl_dx12.cpp
  3. 3 2
      examples/imgui_impl_dx12.h

+ 2 - 2
examples/example_win32_directx12/main.cpp

@@ -73,7 +73,7 @@ int main(int, char**)
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;       // Enable Keyboard Controls
     //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
     io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;           // Enable Docking
-    //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows (FIXME: Currently broken in DX12 back-end, need some work!)
+    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;         // Enable Multi-Viewport / Platform Windows
     //io.ConfigViewportsNoAutoMerge = true;
     //io.ConfigViewportsNoTaskBarIcon = true;
 
@@ -92,7 +92,7 @@ int main(int, char**)
     // Setup Platform/Renderer bindings
     ImGui_ImplWin32_Init(hwnd);
     ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT,
-        DXGI_FORMAT_R8G8B8A8_UNORM,
+        DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap,
         g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
         g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
 

+ 260 - 98
examples/imgui_impl_dx12.cpp

@@ -4,8 +4,8 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
+//  [X] Renderer: Multi-viewport.
 // Missing features, issues:
-//  [ ] Renderer: Missing multi-viewport support.
 //  [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
 
 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
@@ -46,6 +46,8 @@ static ID3D12Resource*              g_pFontTextureResource = NULL;
 static D3D12_CPU_DESCRIPTOR_HANDLE  g_hFontSrvCpuDescHandle = {};
 static D3D12_GPU_DESCRIPTOR_HANDLE  g_hFontSrvGpuDescHandle = {};
 
+static ID3D12DescriptorHeap*        g_pd3dSrvDescHeap = NULL;
+
 struct FrameResources
 {
     ID3D12Resource*     IndexBuffer;
@@ -53,9 +55,80 @@ struct FrameResources
     int                 IndexBufferSize;
     int                 VertexBufferSize;
 };
-static FrameResources*  g_pFrameResources = NULL;
+
 static UINT             g_numFramesInFlight = 0;
-static UINT             g_frameIndex = UINT_MAX;
+
+struct FrameContext
+{
+    ID3D12CommandAllocator*     CommandAllocator;
+    UINT64                      FenceValue;
+    ID3D12Resource*             RenderTarget;
+    D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors;
+};
+
+struct ImGuiViewportDataDx12
+{
+    ID3D12CommandQueue*         CommandQueue;
+    ID3D12GraphicsCommandList*  CommandList;
+
+    ID3D12DescriptorHeap*       RtvDescHeap;
+    IDXGISwapChain3*            SwapChain;
+
+    ID3D12Fence*                Fence;
+    UINT64                      FenceSignaledValue;
+    HANDLE                      FenceEvent;
+
+    FrameContext*               FrameCtx;
+    FrameResources*             Resources;
+
+    UINT                        FrameIndex;
+
+    ImGuiViewportDataDx12()
+    {
+        CommandQueue = NULL;
+        CommandList = NULL;
+
+        RtvDescHeap = NULL;
+        SwapChain = NULL;
+        FenceSignaledValue = 0;
+        Fence = NULL;
+        FenceEvent = NULL;
+        FrameIndex = UINT_MAX;
+
+        FrameCtx = new FrameContext[g_numFramesInFlight];
+        Resources = new FrameResources[g_numFramesInFlight];
+
+        for (UINT i = 0; i < g_numFramesInFlight; ++i)
+        {
+            FrameCtx[i].CommandAllocator = NULL;
+            FrameCtx[i].RenderTarget = NULL;
+
+            // Create buffers with a default size (they will later be grown as needed)
+            Resources[i].IndexBuffer = NULL;
+            Resources[i].VertexBuffer = NULL;
+            Resources[i].VertexBufferSize = 5000;
+            Resources[i].IndexBufferSize = 10000;
+        }
+
+    }
+    ~ImGuiViewportDataDx12()
+    {
+        IM_ASSERT(CommandQueue == NULL && CommandList == NULL);
+        IM_ASSERT(RtvDescHeap == NULL);
+        IM_ASSERT(SwapChain == NULL);
+        IM_ASSERT(Fence == NULL);
+        IM_ASSERT(FenceEvent == NULL);
+
+        for (UINT i = 0; i < g_numFramesInFlight; ++i)
+        {
+            IM_ASSERT(FrameCtx[i].CommandAllocator == NULL && FrameCtx[i].RenderTarget == NULL);
+            IM_ASSERT(Resources[i].IndexBuffer == NULL && Resources[i].VertexBuffer == NULL);
+        }
+
+        delete[] FrameCtx; FrameCtx = NULL;
+        delete[] Resources; Resources = NULL;
+    }
+};
 
 struct VERTEX_CONSTANT_BUFFER
 {
@@ -129,10 +202,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
     if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
         return;
 
-    // FIXME: I'm assuming that this only gets called once per frame!
-    // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
-    g_frameIndex = g_frameIndex + 1;
-    FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight];
+    ImGuiViewportDataDx12* render_data = (ImGuiViewportDataDx12*)draw_data->OwnerViewport->RendererUserData;
+    render_data->FrameIndex++;
+    FrameResources* fr = &render_data->Resources[render_data->FrameIndex % g_numFramesInFlight];
 
     // Create and grow vertex/index buffers if needed
     if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount)
@@ -599,15 +671,9 @@ void    ImGui_ImplDX12_InvalidateDeviceObjects()
     if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; }
     if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; }
     if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; io.Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
-    for (UINT i = 0; i < g_numFramesInFlight; i++)
-    {
-        FrameResources* fr = &g_pFrameResources[i];
-        if (fr->IndexBuffer)  { fr->IndexBuffer->Release();  fr->IndexBuffer = NULL; }
-        if (fr->VertexBuffer) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
-    }
 }
 
-bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
+bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
                          D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
 {
     // Setup back-end capabilities flags
@@ -620,20 +686,14 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
     g_RTVFormat = rtv_format;
     g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
     g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
-    g_pFrameResources = new FrameResources[num_frames_in_flight];
     g_numFramesInFlight = num_frames_in_flight;
-    g_frameIndex = UINT_MAX;
+    g_pd3dSrvDescHeap = cbv_srv_heap;
 
-    // Create buffers with a default size (they will later be grown as needed)
-    for (int i = 0; i < num_frames_in_flight; i++)
-    {
-        FrameResources* fr = &g_pFrameResources[i];
-        fr->IndexBuffer = NULL;
-        fr->VertexBuffer = NULL;
-        fr->IndexBufferSize = 10000;
-        fr->VertexBufferSize = 5000;
-    }
+    ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+    main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataDx12);
 
+    // Setup back-end capabilities flags
+    io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports;    // We can create multi-viewports on the Renderer side (optional)
     if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
         ImGui_ImplDX12_InitPlatformInterface();
 
@@ -644,13 +704,17 @@ void ImGui_ImplDX12_Shutdown()
 {
     ImGui_ImplDX12_ShutdownPlatformInterface();
     ImGui_ImplDX12_InvalidateDeviceObjects();
-    delete[] g_pFrameResources;
-    g_pFrameResources = NULL;
+
     g_pd3dDevice = NULL;
     g_hFontSrvCpuDescHandle.ptr = 0;
     g_hFontSrvGpuDescHandle.ptr = 0;
     g_numFramesInFlight = 0;
-    g_frameIndex = UINT_MAX;
+    g_pd3dSrvDescHeap = NULL;
+
+    ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+    if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)main_viewport->RendererUserData)
+        IM_DELETE(data);
+    main_viewport->RendererUserData = NULL;
 }
 
 void ImGui_ImplDX12_NewFrame()
@@ -665,54 +729,118 @@ void ImGui_ImplDX12_NewFrame()
 // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
 //--------------------------------------------------------------------------------------------------------
 
-struct ImGuiViewportDataDx12
-{
-    IDXGISwapChain3*            SwapChain;
-
-    ImGuiViewportDataDx12() { SwapChain = NULL; }
-    ~ImGuiViewportDataDx12() { IM_ASSERT(SwapChain == NULL); }
-};
-
 static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
 {
     ImGuiViewportDataDx12* data = IM_NEW(ImGuiViewportDataDx12)();
     viewport->RendererUserData = data;
-    IM_ASSERT(0);
 
-    /*
-    // FIXME-PLATFORM
     // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
     // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND.
     HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
     IM_ASSERT(hwnd != 0);
 
+    data->FrameIndex = UINT_MAX;
+
+    // Create command queue.
+    D3D12_COMMAND_QUEUE_DESC queue_desc = {};
+    queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+    queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+
+    HRESULT res = S_OK;
+    res = g_pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&data->CommandQueue));
+    IM_ASSERT(res == S_OK);
+
+    // Create command allocator.
+    for (UINT i = 0; i < g_numFramesInFlight; ++i)
+    {
+        res = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&data->FrameCtx[i].CommandAllocator));
+        IM_ASSERT(res == S_OK);
+    }
+
+    // Create command list.
+    res = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, data->FrameCtx[0].CommandAllocator, NULL, IID_PPV_ARGS(&data->CommandList));
+    IM_ASSERT(res == S_OK);
+    data->CommandList->Close();
+
+    // Create fence.
+    res = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&data->Fence));
+    IM_ASSERT(res == S_OK);
+
+    data->FenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    IM_ASSERT(data->FenceEvent != NULL);
+
     // Create swap chain
-    DXGI_SWAP_CHAIN_DESC sd;
-    ZeroMemory(&sd, sizeof(sd));
-    sd.BufferDesc.Width = (UINT)viewport->Size.x;
-    sd.BufferDesc.Height = (UINT)viewport->Size.y;
-    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
-    sd.SampleDesc.Count = 1;
-    sd.SampleDesc.Quality = 0;
-    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
-    sd.BufferCount = 1;
-    sd.OutputWindow = hwnd;
-    sd.Windowed = TRUE;
-    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
-    sd.Flags = 0;
-
-    IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL);
-    g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain);
-
-    // Create the render target
+    DXGI_SWAP_CHAIN_DESC1 sd1;
+    ZeroMemory(&sd1, sizeof(sd1));
+    sd1.BufferCount = g_numFramesInFlight;
+    sd1.Width = (UINT)viewport->Size.x;
+    sd1.Height = (UINT)viewport->Size.y;
+    sd1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    sd1.SampleDesc.Count = 1;
+    sd1.SampleDesc.Quality = 0;
+    sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+    sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
+    sd1.Scaling = DXGI_SCALING_STRETCH;
+    sd1.Stereo = FALSE;
+
+    IM_ASSERT(data->SwapChain == NULL);
+    IM_ASSERT(data->FrameCtx[0].RenderTarget == NULL && data->FrameCtx[1].RenderTarget == NULL && data->FrameCtx[2].RenderTarget == NULL);
+
+    IDXGIFactory4* dxgi_factory = nullptr;
+    res = CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
+    IM_ASSERT(res == S_OK);
+
+    IDXGISwapChain1* swap_chain = nullptr;
+    res = dxgi_factory->CreateSwapChainForHwnd(data->CommandQueue, hwnd, &sd1, NULL, NULL, &swap_chain);
+    IM_ASSERT(res == S_OK);
+
+    dxgi_factory->Release();
+
+    // Or swapChain.As(&mSwapChain)
+    swap_chain->QueryInterface(IID_PPV_ARGS(&data->SwapChain));
+
+    // Create the render targets
     if (data->SwapChain)
     {
-        ID3D11Texture2D* pBackBuffer;
-        data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
-        g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView);
-        pBackBuffer->Release();
+        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
+        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+        desc.NumDescriptors = g_numFramesInFlight;
+        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+        desc.NodeMask = 1;
+
+        IM_ASSERT(g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&data->RtvDescHeap)) == S_OK);
+
+        SIZE_T rtv_descriptor_size = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+        D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = data->RtvDescHeap->GetCPUDescriptorHandleForHeapStart();
+        for (UINT i = 0; i < g_numFramesInFlight; i++)
+        {
+            data->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle;
+            rtv_handle.ptr += rtv_descriptor_size;
+        }
+
+        ID3D12Resource* back_buffer;
+        for (UINT i = 0; i < g_numFramesInFlight; i++)
+        {
+            data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
+            g_pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors);
+            data->FrameCtx[i].RenderTarget = back_buffer;
+        }
+    }
+
+    for (UINT i = 0; i < g_numFramesInFlight; i++)
+    {
+        if (data->Resources[i].IndexBuffer) { data->Resources[i].VertexBuffer->Release(); data->Resources[i].IndexBuffer = NULL; }
+        if (data->Resources[i].VertexBuffer) { data->Resources[i].VertexBuffer->Release(); data->Resources[i].VertexBuffer = NULL; }
     }
-    */
+}
+
+template<typename D12Resource>
+void SafeRelease(D12Resource*& res)
+{
+	if (res)
+		res->Release();
+	res = NULL;
 }
 
 static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
@@ -720,16 +848,22 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
     // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
     if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData)
     {
-        IM_ASSERT(0);
-        /*
-        if (data->SwapChain)
-            data->SwapChain->Release();
-        data->SwapChain = NULL;
-        if (data->RTView)
-            data->RTView->Release();
-        data->RTView = NULL;
+        SafeRelease(data->CommandQueue);
+        SafeRelease(data->CommandList);
+        SafeRelease(data->SwapChain);
+        SafeRelease(data->RtvDescHeap);
+        SafeRelease(data->Fence);
+        CloseHandle(data->FenceEvent); data->FenceEvent = NULL;
+
+        for (UINT i = 0; i < g_numFramesInFlight; i++)
+        {
+            SafeRelease(data->FrameCtx[i].RenderTarget);
+            SafeRelease(data->FrameCtx[i].CommandAllocator);
+            SafeRelease(data->Resources[i].IndexBuffer);
+            SafeRelease(data->Resources[i].VertexBuffer);
+        }
+
         IM_DELETE(data);
-        */
     }
     viewport->RendererUserData = NULL;
 }
@@ -737,51 +871,79 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
 static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
 {
     ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData;
-    IM_ASSERT(0);
-    (void)data; (void)size;
-    /*
-    if (data->RTView)
+
+    for (UINT i = 0; i < g_numFramesInFlight; i++)
     {
-        data->RTView->Release();
-        data->RTView = NULL;
+        SafeRelease(data->FrameCtx[i].RenderTarget);
     }
+
     if (data->SwapChain)
     {
-        ID3D11Texture2D* pBackBuffer = NULL;
+        ID3D12Resource* back_buffer = NULL;
         data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
-        data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
-        g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView);
-        pBackBuffer->Release();
+        for (UINT i = 0; i < g_numFramesInFlight; i++)
+        {
+            data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
+            g_pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors);
+            data->FrameCtx[i].RenderTarget = back_buffer;
+        }
     }
-    */
 }
 
-// arg = ID3D12GraphicsCommandList*
-static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void* renderer_arg)
+static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
 {
     ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData;
-    IM_ASSERT(0);
-    (void)data;
-
-    ID3D12GraphicsCommandList* command_list = (ID3D12GraphicsCommandList*)renderer_arg;
 
-    /*
     ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
-    g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL);
+
+    //--
+    FrameContext* frame_context = &data->FrameCtx[data->FrameIndex % g_numFramesInFlight];
+
+    UINT back_buffer_idx = data->SwapChain->GetCurrentBackBufferIndex();
+    D3D12_RESOURCE_BARRIER barrier = {};
+    barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+    barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+    barrier.Transition.pResource = data->FrameCtx[back_buffer_idx].RenderTarget;
+    barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+    barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+    barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+
+    //-- draw
+    ID3D12GraphicsCommandList* cmd_list = data->CommandList;
+
+    frame_context->CommandAllocator->Reset();
+    cmd_list->Reset(frame_context->CommandAllocator, NULL);
+    cmd_list->ResourceBarrier(1, &barrier);
+    cmd_list->OMSetRenderTargets(1, &data->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, NULL);
     if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
-        g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color);
-    */
-    ImGui_ImplDX12_RenderDrawData(viewport->DrawData, command_list);
+        cmd_list->ClearRenderTargetView(data->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, NULL);
+    cmd_list->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
+
+    ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list);
+
+    barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+    barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
+    cmd_list->ResourceBarrier(1, &barrier);
+    cmd_list->Close();
+
+    //--
+    data->CommandQueue->Wait(data->Fence, data->FenceSignaledValue);
+    //--
+    data->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
+    //--
+    data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue);
 }
 
 static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
 {
     ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData;
-    IM_ASSERT(0);
-    (void)data;
-    /*
-    data->SwapChain->Present(0, 0); // Present without vsync
-    */
+
+    data->SwapChain->Present(0, 0);
+
+    while (data->Fence->GetCompletedValue() < data->FenceSignaledValue)
+    {
+        SwitchToThread();
+    }
 }
 
 void ImGui_ImplDX12_InitPlatformInterface()

+ 3 - 2
examples/imgui_impl_dx12.h

@@ -4,8 +4,8 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
+//  [X] Renderer: Multi-viewport.
 // Missing features, issues:
-//  [ ] Renderer: Missing multi-viewport support.
 //  [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
 
 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
@@ -16,6 +16,7 @@
 
 enum DXGI_FORMAT;
 struct ID3D12Device;
+struct ID3D12DescriptorHeap;
 struct ID3D12GraphicsCommandList;
 struct D3D12_CPU_DESCRIPTOR_HANDLE;
 struct D3D12_GPU_DESCRIPTOR_HANDLE;
@@ -24,7 +25,7 @@ struct D3D12_GPU_DESCRIPTOR_HANDLE;
 // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
 // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
 // font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
-IMGUI_IMPL_API bool     ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
+IMGUI_IMPL_API bool     ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
                                             D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
 IMGUI_IMPL_API void     ImGui_ImplDX12_Shutdown();
 IMGUI_IMPL_API void     ImGui_ImplDX12_NewFrame();