From aaa4f770e293d42ef1fd0517cbef0811aabd2d62 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 23 Aug 2026 14:32:12 +0200 Subject: [PATCH] Fix regression in D3D12 on multiple swap chains in use --- .../DirectX/DX12/GPUContextDX12.cpp | 60 ++++++++++++++++--- .../DirectX/DX12/GPUContextDX12.h | 2 +- .../DirectX/DX12/GPUSwapChainDX12.cpp | 4 ++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index bb1cbbb27..9210a98cc 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -32,7 +32,6 @@ #include "Engine/Graphics/GPUResourceAccess.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/Shaders/GPUShader.h" -#include "Engine/Threading/Threading.h" #define DX12_ENABLE_RESOURCE_BARRIERS_BATCHING 1 #define DX12_ENABLE_RESOURCE_BARRIERS_DEBUGGING (0 && LOG_ENABLE) @@ -88,6 +87,44 @@ FORCE_INLINE D3D12_RESOURCE_STATES GetResourceState(GPUResourceAccess access) return D3D12_RESOURCE_STATE_COMMON; } +#if DX12_ENABLE_RESOURCE_BARRIERS_DEBUGGING + +String ResourceStateToString(D3D12_RESOURCE_STATES state) +{ + if (state == D3D12_RESOURCE_STATE_COMMON) + return TEXT("Common"); + String result; + while (state) + { + if (result.HasChars()) + result += TEXT('|'); +#define CHECK_STATE(type, name) if ((type & state) == type) { state &= ~type; result += TEXT(name); continue; } + CHECK_STATE(D3D12_RESOURCE_STATE_GENERIC_READ, "GenericRead"); + CHECK_STATE(D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, "VertexConstantBuffer"); + CHECK_STATE(D3D12_RESOURCE_STATE_INDEX_BUFFER, "IndexBuffer"); + CHECK_STATE(D3D12_RESOURCE_STATE_RENDER_TARGET, "RenderTarget"); + CHECK_STATE(D3D12_RESOURCE_STATE_UNORDERED_ACCESS, "UAV"); + CHECK_STATE(D3D12_RESOURCE_STATE_DEPTH_WRITE, "DepthWrite"); + CHECK_STATE(D3D12_RESOURCE_STATE_DEPTH_READ, "DepthRead"); + CHECK_STATE(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, "SRV-PS"); + CHECK_STATE(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, "SRV-VS"); + CHECK_STATE(D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, "IndirectArg"); + CHECK_STATE(D3D12_RESOURCE_STATE_COPY_DEST, "CopyDst"); + CHECK_STATE(D3D12_RESOURCE_STATE_COPY_SOURCE, "CopySrc"); + CHECK_STATE(D3D12_RESOURCE_STATE_RESOLVE_DEST, "ResolveDst"); + CHECK_STATE(D3D12_RESOURCE_STATE_RESOLVE_SOURCE, "ResolveSrc"); + CHECK_STATE(D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE, "ShadingRate"); + CHECK_STATE(D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, "RtxAcceleration"); + CHECK_STATE(D3D12_RESOURCE_STATE_PRESENT, "Present"); +#undef CHECK_STATE + result += TEXT("Unknown"); + break; + } + return result; +} + +#endif + // Ensure to match the indirect commands arguments layout static_assert(sizeof(GPUDispatchIndirectArgs) == sizeof(D3D12_DISPATCH_ARGUMENTS), "Wrong size of GPUDrawIndirectArgs."); static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountX) == OFFSET_OF(D3D12_DISPATCH_ARGUMENTS, ThreadGroupCountX), "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountX"); @@ -163,7 +200,7 @@ void GPUContextDX12::AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D resourceName = gpuResource->GetName(); else resourceName = StringUtils::ToString((uint32)(uint64)resource->GetResource()); - const auto info = String::Format(TEXT("[DX12 Resource Barrier]: 0x{0:x} -> 0x{1:x}: {2} (subresource: {3})"), before, after, resourceName, subresourceIndex); + const auto info = String::Format(TEXT("[DX12 Resource Barrier]: {0} -> {1}: {2} (subresource: {3})"), ResourceStateToString(before), ResourceStateToString(after), resourceName, subresourceIndex); Log::Logger::Write(LogType::Info, info); #endif @@ -349,6 +386,7 @@ void GPUContextDX12::OnSwapChainFlush(ResourceOwnerDX12* backBuffer) if (_swapChains.Count() > 1) { // Flush GPU commands + flushSwapChains(); Flush(); } } @@ -362,6 +400,17 @@ void GPUContextDX12::GetActiveHeapDescriptor(const D3D12_CPU_DESCRIPTOR_HANDLE& _device->GetDevice()->CopyDescriptorsSimple(1, descriptor.CPU, cpuHandle, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); } +void GPUContextDX12::flushSwapChains() +{ + // Transition swapchains to the present state + for (ResourceOwnerDX12* backBuffer : _swapChains) + { + SetResourceState(backBuffer, D3D12_RESOURCE_STATE_PRESENT); + } + _swapChains.Clear(); + flushRBs(); +} + void GPUContextDX12::flushSRVs() { uint32 srMask; @@ -753,12 +802,7 @@ void GPUContextDX12::FrameBegin() void GPUContextDX12::FrameEnd() { - // Transition swapchains to the present state - for (ResourceOwnerDX12* backBuffer : _swapChains) - { - SetResourceState(backBuffer, D3D12_RESOURCE_STATE_PRESENT); - } - _swapChains.Clear(); + flushSwapChains(); // Base GPUContext::FrameEnd(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 6144d0ea3..e1bb0d1e5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -152,7 +152,7 @@ protected: void GetActiveHeapDescriptor(const D3D12_CPU_DESCRIPTOR_HANDLE& cpuHandle, Descriptor& descriptor); private: - + void flushSwapChains(); void flushSRVs(); void flushRTVs(); void flushUAVs(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp index 5c29ee656..de0cd5e83 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp @@ -381,6 +381,10 @@ void GPUSwapChainDX12::Present(bool vsync) { presentFlags |= DXGI_PRESENT_ALLOW_TEARING; } +#if GPU_ENABLE_ASSERTION + //ASSERT(_currentFrameIndex == _swapChain->GetCurrentBackBufferIndex()); + ASSERT(_backBuffers[_currentFrameIndex].State.CheckResourceState(D3D12_RESOURCE_STATE_PRESENT)); +#endif const HRESULT res = _swapChain->Present(vsync ? 1 : 0, presentFlags); LOG_DIRECTX_RESULT(res);