Browse Source

Backends: DX12: Rework synchronization logic. (docking) (#8961)

Rémy Tassoux 1 week ago
parent
commit
778aadca65
2 changed files with 51 additions and 30 deletions
  1. 48 28
      backends/imgui_impl_dx12.cpp
  2. 3 2
      examples/example_win32_directx12/main.cpp

+ 48 - 28
backends/imgui_impl_dx12.cpp

@@ -135,6 +135,7 @@ struct ImGui_ImplDX12_RenderBuffers
 // Buffers used for secondary viewports created by the multi-viewports systems
 // Buffers used for secondary viewports created by the multi-viewports systems
 struct ImGui_ImplDX12_FrameContext
 struct ImGui_ImplDX12_FrameContext
 {
 {
+    UINT64                          FenceValue;
     ID3D12CommandAllocator*         CommandAllocator;
     ID3D12CommandAllocator*         CommandAllocator;
     ID3D12Resource*                 RenderTarget;
     ID3D12Resource*                 RenderTarget;
     D3D12_CPU_DESCRIPTOR_HANDLE     RenderTargetCpuDescriptors;
     D3D12_CPU_DESCRIPTOR_HANDLE     RenderTargetCpuDescriptors;
@@ -150,8 +151,9 @@ struct ImGui_ImplDX12_ViewportData
     ID3D12GraphicsCommandList*      CommandList;
     ID3D12GraphicsCommandList*      CommandList;
     ID3D12DescriptorHeap*           RtvDescHeap;
     ID3D12DescriptorHeap*           RtvDescHeap;
     IDXGISwapChain3*                SwapChain;
     IDXGISwapChain3*                SwapChain;
+    HANDLE                          SwapChainWaitableObject;
     ID3D12Fence*                    Fence;
     ID3D12Fence*                    Fence;
-    UINT64                          FenceSignaledValue;
+    UINT64                          FenceLastSignaledValue;
     HANDLE                          FenceEvent;
     HANDLE                          FenceEvent;
     UINT                            NumFramesInFlight;
     UINT                            NumFramesInFlight;
     ImGui_ImplDX12_FrameContext*    FrameCtx;
     ImGui_ImplDX12_FrameContext*    FrameCtx;
@@ -166,16 +168,18 @@ struct ImGui_ImplDX12_ViewportData
         CommandList = nullptr;
         CommandList = nullptr;
         RtvDescHeap = nullptr;
         RtvDescHeap = nullptr;
         SwapChain = nullptr;
         SwapChain = nullptr;
+        SwapChainWaitableObject = nullptr;
         Fence = nullptr;
         Fence = nullptr;
-        FenceSignaledValue = 0;
+        FenceLastSignaledValue = 0;
         FenceEvent = nullptr;
         FenceEvent = nullptr;
         NumFramesInFlight = num_frames_in_flight;
         NumFramesInFlight = num_frames_in_flight;
         FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
         FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
-        FrameIndex = UINT_MAX;
+        FrameIndex = 0;
         FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight];
         FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight];
 
 
         for (UINT i = 0; i < NumFramesInFlight; ++i)
         for (UINT i = 0; i < NumFramesInFlight; ++i)
         {
         {
+            FrameCtx[i].FenceValue = 0;
             FrameCtx[i].CommandAllocator = nullptr;
             FrameCtx[i].CommandAllocator = nullptr;
             FrameCtx[i].RenderTarget = nullptr;
             FrameCtx[i].RenderTarget = nullptr;
 
 
@@ -191,6 +195,7 @@ struct ImGui_ImplDX12_ViewportData
         IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr);
         IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr);
         IM_ASSERT(RtvDescHeap == nullptr);
         IM_ASSERT(RtvDescHeap == nullptr);
         IM_ASSERT(SwapChain == nullptr);
         IM_ASSERT(SwapChain == nullptr);
+        IM_ASSERT(SwapChainWaitableObject == nullptr);
         IM_ASSERT(Fence == nullptr);
         IM_ASSERT(Fence == nullptr);
         IM_ASSERT(FenceEvent == nullptr);
         IM_ASSERT(FenceEvent == nullptr);
 
 
@@ -1043,18 +1048,12 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
     HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
     HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
     IM_ASSERT(hwnd != 0);
     IM_ASSERT(hwnd != 0);
 
 
-    vd->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 = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&vd->CommandQueue));
-    IM_ASSERT(res == S_OK);
+    // Use shared command queue from init info
+    vd->FrameIndex = 0;
+    vd->CommandQueue = bd->pCommandQueue;
 
 
     // Create command allocator.
     // Create command allocator.
+    HRESULT res = S_OK;
     for (UINT i = 0; i < bd->numFramesInFlight; ++i)
     for (UINT i = 0; i < bd->numFramesInFlight; ++i)
     {
     {
         res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator));
         res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator));
@@ -1088,6 +1087,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
     sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
     sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
     sd1.Scaling = DXGI_SCALING_NONE;
     sd1.Scaling = DXGI_SCALING_NONE;
     sd1.Stereo = FALSE;
     sd1.Stereo = FALSE;
+    sd1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
 
 
     IDXGIFactory4* dxgi_factory = nullptr;
     IDXGIFactory4* dxgi_factory = nullptr;
     res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
     res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
@@ -1106,7 +1106,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
     swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain));
     swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain));
     swap_chain->Release();
     swap_chain->Release();
 
 
-    // Create the render targets
+    // Create the render targets and waitable object
     if (vd->SwapChain)
     if (vd->SwapChain)
     {
     {
         D3D12_DESCRIPTOR_HEAP_DESC desc = {};
         D3D12_DESCRIPTOR_HEAP_DESC desc = {};
@@ -1134,6 +1134,10 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
             bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
             bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
             vd->FrameCtx[i].RenderTarget = back_buffer;
             vd->FrameCtx[i].RenderTarget = back_buffer;
         }
         }
+
+        hr = vd->SwapChain->SetMaximumFrameLatency(bd->numFramesInFlight);
+        IM_ASSERT(hr == S_OK);
+        vd->SwapChainWaitableObject = vd->SwapChain->GetFrameLatencyWaitableObject();
     }
     }
 
 
     for (UINT i = 0; i < bd->numFramesInFlight; i++)
     for (UINT i = 0; i < bd->numFramesInFlight; i++)
@@ -1142,16 +1146,29 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
 
 
 static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd)
 static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd)
 {
 {
-    HRESULT hr = S_FALSE;
-    if (vd && vd->CommandQueue && vd->Fence && vd->FenceEvent)
+    HRESULT hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceLastSignaledValue);
+    IM_ASSERT(hr == S_OK);
+
+    hr = vd->Fence->SetEventOnCompletion(vd->FenceLastSignaledValue, vd->FenceEvent);
+    IM_ASSERT(hr == S_OK);
+    ::WaitForSingleObject(vd->FenceEvent, INFINITE);
+}
+
+static ImGui_ImplDX12_FrameContext* ImGui_WaitForNextFrameContext(ImGui_ImplDX12_ViewportData* vd)
+{
+    ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % vd->NumFramesInFlight];
+    if (vd->Fence->GetCompletedValue() < frame_context->FenceValue)
     {
     {
-        hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue);
+        HRESULT hr = vd->Fence->SetEventOnCompletion(frame_context->FenceValue, vd->FenceEvent);
         IM_ASSERT(hr == S_OK);
         IM_ASSERT(hr == S_OK);
-        ::WaitForSingleObject(vd->FenceEvent, 0); // Reset any forgotten waits
-        hr = vd->Fence->SetEventOnCompletion(vd->FenceSignaledValue, vd->FenceEvent);
-        IM_ASSERT(hr == S_OK);
-        ::WaitForSingleObject(vd->FenceEvent, INFINITE);
+        HANDLE waitableObjects[] = { vd->SwapChainWaitableObject, vd->FenceEvent };
+        ::WaitForMultipleObjects(2, waitableObjects, TRUE, INFINITE);
     }
     }
+    else
+    {
+        ::WaitForSingleObject(vd->SwapChainWaitableObject, INFINITE);
+    }
+    return frame_context;
 }
 }
 
 
 static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
 static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
@@ -1162,7 +1179,9 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
     {
     {
         ImGui_WaitForPendingOperations(vd);
         ImGui_WaitForPendingOperations(vd);
 
 
-        SafeRelease(vd->CommandQueue);
+        vd->CommandQueue = nullptr;
+        ::CloseHandle(vd->SwapChainWaitableObject);
+        vd->SwapChainWaitableObject = nullptr;
         SafeRelease(vd->CommandList);
         SafeRelease(vd->CommandList);
         SafeRelease(vd->SwapChain);
         SafeRelease(vd->SwapChain);
         SafeRelease(vd->RtvDescHeap);
         SafeRelease(vd->RtvDescHeap);
@@ -1194,7 +1213,7 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
     if (vd->SwapChain)
     if (vd->SwapChain)
     {
     {
         ID3D12Resource* back_buffer = nullptr;
         ID3D12Resource* back_buffer = nullptr;
-        vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
+        vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
         for (UINT i = 0; i < bd->numFramesInFlight; i++)
         for (UINT i = 0; i < bd->numFramesInFlight; i++)
         {
         {
             vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
             vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
@@ -1209,7 +1228,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
     ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
     ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
     ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
     ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
 
 
-    ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % bd->numFramesInFlight];
+    ImGui_ImplDX12_FrameContext* frame_context = ImGui_WaitForNextFrameContext(vd);
     UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex();
     UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex();
 
 
     const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
     const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
@@ -1239,9 +1258,11 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
     cmd_list->ResourceBarrier(1, &barrier);
     cmd_list->ResourceBarrier(1, &barrier);
     cmd_list->Close();
     cmd_list->Close();
 
 
-    vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue);
     vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
     vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
-    vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue);
+
+    HRESULT hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceLastSignaledValue);
+    IM_ASSERT(hr == S_OK);
+    frame_context->FenceValue = vd->FenceLastSignaledValue;
 }
 }
 
 
 static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
 static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
@@ -1249,8 +1270,7 @@ static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
     ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
     ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
 
 
     vd->SwapChain->Present(0, 0);
     vd->SwapChain->Present(0, 0);
-    while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue)
-        ::SwitchToThread();
+    vd->FrameIndex++;
 }
 }
 
 
 void ImGui_ImplDX12_InitMultiViewportSupport()
 void ImGui_ImplDX12_InitMultiViewportSupport()

+ 3 - 2
examples/example_win32_directx12/main.cpp

@@ -295,8 +295,6 @@ int main(int, char**)
         g_pd3dCommandList->Close();
         g_pd3dCommandList->Close();
 
 
         g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
         g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
-        g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
-        frameCtx->FenceValue = g_fenceLastSignaledValue;
 
 
         // Update and Render additional Platform Windows
         // Update and Render additional Platform Windows
         if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
         if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@@ -305,6 +303,9 @@ int main(int, char**)
             ImGui::RenderPlatformWindowsDefault();
             ImGui::RenderPlatformWindowsDefault();
         }
         }
 
 
+        g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
+        frameCtx->FenceValue = g_fenceLastSignaledValue;
+
         // Present
         // Present
         HRESULT hr = g_pSwapChain->Present(1, 0);   // Present with vsync
         HRESULT hr = g_pSwapChain->Present(1, 0);   // Present with vsync
         //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
         //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync