From 778aadca6586da13abba2364ac09cff8156d6218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Tassoux?= Date: Mon, 29 Sep 2025 16:23:50 +0200 Subject: [PATCH] Backends: DX12: Rework synchronization logic. (docking) (#8961) --- backends/imgui_impl_dx12.cpp | 76 ++++++++++++++--------- examples/example_win32_directx12/main.cpp | 5 +- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index f16bd8cd0..9fbdc4426 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -135,6 +135,7 @@ struct ImGui_ImplDX12_RenderBuffers // Buffers used for secondary viewports created by the multi-viewports systems struct ImGui_ImplDX12_FrameContext { + UINT64 FenceValue; ID3D12CommandAllocator* CommandAllocator; ID3D12Resource* RenderTarget; D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; @@ -150,8 +151,9 @@ struct ImGui_ImplDX12_ViewportData ID3D12GraphicsCommandList* CommandList; ID3D12DescriptorHeap* RtvDescHeap; IDXGISwapChain3* SwapChain; + HANDLE SwapChainWaitableObject; ID3D12Fence* Fence; - UINT64 FenceSignaledValue; + UINT64 FenceLastSignaledValue; HANDLE FenceEvent; UINT NumFramesInFlight; ImGui_ImplDX12_FrameContext* FrameCtx; @@ -166,16 +168,18 @@ struct ImGui_ImplDX12_ViewportData CommandList = nullptr; RtvDescHeap = nullptr; SwapChain = nullptr; + SwapChainWaitableObject = nullptr; Fence = nullptr; - FenceSignaledValue = 0; + FenceLastSignaledValue = 0; FenceEvent = nullptr; NumFramesInFlight = num_frames_in_flight; FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight]; - FrameIndex = UINT_MAX; + FrameIndex = 0; FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight]; for (UINT i = 0; i < NumFramesInFlight; ++i) { + FrameCtx[i].FenceValue = 0; FrameCtx[i].CommandAllocator = nullptr; FrameCtx[i].RenderTarget = nullptr; @@ -191,6 +195,7 @@ struct ImGui_ImplDX12_ViewportData IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr); IM_ASSERT(RtvDescHeap == nullptr); IM_ASSERT(SwapChain == nullptr); + IM_ASSERT(SwapChainWaitableObject == nullptr); IM_ASSERT(Fence == 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; 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. + HRESULT res = S_OK; for (UINT i = 0; i < bd->numFramesInFlight; ++i) { 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.Scaling = DXGI_SCALING_NONE; sd1.Stereo = FALSE; + sd1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; IDXGIFactory4* dxgi_factory = nullptr; 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->Release(); - // Create the render targets + // Create the render targets and waitable object if (vd->SwapChain) { 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); 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++) @@ -1142,16 +1146,29 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) 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); - ::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) @@ -1162,7 +1179,9 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { ImGui_WaitForPendingOperations(vd); - SafeRelease(vd->CommandQueue); + vd->CommandQueue = nullptr; + ::CloseHandle(vd->SwapChainWaitableObject); + vd->SwapChainWaitableObject = nullptr; SafeRelease(vd->CommandList); SafeRelease(vd->SwapChain); SafeRelease(vd->RtvDescHeap); @@ -1194,7 +1213,7 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) if (vd->SwapChain) { 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++) { 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_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(); 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->Close(); - vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue); 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*) @@ -1249,8 +1270,7 @@ static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; vd->SwapChain->Present(0, 0); - while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue) - ::SwitchToThread(); + vd->FrameIndex++; } void ImGui_ImplDX12_InitMultiViewportSupport() diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 68cb68a07..4fdf5da9e 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -295,8 +295,6 @@ int main(int, char**) g_pd3dCommandList->Close(); 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 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) @@ -305,6 +303,9 @@ int main(int, char**) ImGui::RenderPlatformWindowsDefault(); } + g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue); + frameCtx->FenceValue = g_fenceLastSignaledValue; + // Present HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync