diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index a27d34ba2..9462ca1fa 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -21,7 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2025-09-29: DirectX12: Rework synchronization logic. (#8961) -// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. +// 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965) +// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-19: Fixed build on MinGW. (#8702, #4594) // 2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. @@ -59,7 +60,7 @@ // DirectX #include -#include +#include #include #ifdef _MSC_VER #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. @@ -89,6 +90,7 @@ struct ImGui_ImplDX12_Texture struct ImGui_ImplDX12_Data { ImGui_ImplDX12_InitInfo InitInfo; + IDXGIFactory5* pdxgiFactory; ID3D12Device* pd3dDevice; ID3D12RootSignature* pRootSignature; ID3D12PipelineState* pPipelineState; @@ -101,6 +103,8 @@ struct ImGui_ImplDX12_Data UINT64 FenceLastSignaledValue; HANDLE FenceEvent; UINT numFramesInFlight; + bool tearingSupport; + bool LegacySingleDescriptorUsed; ID3D12CommandAllocator* pTexCmdAllocator; ID3D12GraphicsCommandList* pTexCmdList; @@ -108,8 +112,6 @@ struct ImGui_ImplDX12_Data ImGui_ImplDX12_RenderBuffers* pFrameResources; UINT frameIndex; - bool LegacySingleDescriptorUsed; - ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -545,6 +547,13 @@ bool ImGui_ImplDX12_CreateDeviceObjects() if (bd->pPipelineState) ImGui_ImplDX12_InvalidateDeviceObjects(); + HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&bd->pdxgiFactory)); + IM_ASSERT(hr == S_OK); + + BOOL allow_tearing = FALSE; + bd->pdxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing)); + bd->tearingSupport = (allow_tearing == TRUE); + // Create the root signature { D3D12_DESCRIPTOR_RANGE descRange = {}; @@ -767,7 +776,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() return false; // Create command allocator and command list for ImGui_ImplDX12_UpdateTexture() - HRESULT hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator)); + hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator)); IM_ASSERT(SUCCEEDED(hr)); hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, bd->pTexCmdAllocator, nullptr, IID_PPV_ARGS(&bd->pTexCmdList)); IM_ASSERT(SUCCEEDED(hr)); @@ -789,6 +798,7 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() if (!bd || !bd->pd3dDevice) return; + SafeRelease(bd->pdxgiFactory); if (bd->commandQueueOwned) SafeRelease(bd->pCommandQueue); bd->commandQueueOwned = false; @@ -853,6 +863,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) bd->DSVFormat = init_info->DSVFormat; bd->numFramesInFlight = init_info->NumFramesInFlight; bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap; + bd->tearingSupport = false; io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx12"; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 56b18de83..cc389c907 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,7 @@ Other Changes: of recreating them each time. (#8963, #8465) [@RT2Code] - Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code] (presumably fixes old hard-to-repro crash issues such as #3463, #5018) +- Backends: DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] - Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] - Backends: GLFW: fixed build on platform that are neither Windows, macOS or diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 665693462..c8578b54c 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -10,7 +10,7 @@ #include "imgui_impl_win32.h" #include "imgui_impl_dx12.h" #include -#include +#include #include #ifdef _DEBUG @@ -92,6 +92,7 @@ static ID3D12Fence* g_fence = nullptr; static HANDLE g_fenceEvent = nullptr; static UINT64 g_fenceLastSignaledValue = 0; static IDXGISwapChain3* g_pSwapChain = nullptr; +static bool g_SwapChainTearingSupport = false; static bool g_SwapChainOccluded = false; static HANDLE g_hSwapChainWaitableObject = nullptr; static ID3D12Resource* g_mainRenderTargetResource[APP_NUM_BACK_BUFFERS] = {}; @@ -287,7 +288,7 @@ int main(int, char**) // Present 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, g_SwapChainTearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); // Present without vsync g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED); g_frameIndex++; } @@ -407,14 +408,24 @@ bool CreateDeviceD3D(HWND hWnd) return false; { - IDXGIFactory4* dxgiFactory = nullptr; + IDXGIFactory5* dxgiFactory = nullptr; IDXGISwapChain1* swapChain1 = nullptr; if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK) return false; + + BOOL allow_tearing = FALSE; + dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing)); + g_SwapChainTearingSupport = (allow_tearing == TRUE); + if (g_SwapChainTearingSupport) + sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + if (dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK) return false; if (swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK) return false; + if (g_SwapChainTearingSupport) + dxgiFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER); + swapChain1->Release(); dxgiFactory->Release(); g_pSwapChain->SetMaximumFrameLatency(APP_NUM_BACK_BUFFERS); @@ -511,7 +522,9 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED) { CleanupRenderTarget(); - HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT); + DXGI_SWAP_CHAIN_DESC1 desc = {}; + g_pSwapChain->GetDesc1(&desc); + HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), desc.Format, desc.Flags); assert(SUCCEEDED(result) && "Failed to resize swapchain."); CreateRenderTarget(); }