From 4c6e7af02e5f53302c1b585794b300e1c3c45d22 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 20 Mar 2026 02:51:34 +0800 Subject: [PATCH] refactor: encapsulate frame fence synchronization in CommandQueue - Add WaitForPreviousFrame() and GetCurrentFrame() to RHICommandQueue interface - D3D12CommandQueue now manages frame fence internally - ExecuteCommandLists automatically signals fence after command execution - OpenGLCommandQueue provides stub implementations for interface compliance - Minimal test now uses CommandQueue::WaitForPreviousFrame() instead of manual fence --- .../XCEngine/RHI/D3D12/D3D12CommandQueue.h | 6 +++++ .../XCEngine/RHI/OpenGL/OpenGLCommandQueue.h | 2 ++ engine/include/XCEngine/RHI/RHICommandQueue.h | 2 ++ engine/src/RHI/D3D12/D3D12CommandQueue.cpp | 26 +++++++++++++++++++ tests/RHI/D3D12/integration/main_minimal.cpp | 10 +------ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12CommandQueue.h b/engine/include/XCEngine/RHI/D3D12/D3D12CommandQueue.h index ede4d599..261d73bf 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12CommandQueue.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12CommandQueue.h @@ -38,10 +38,16 @@ public: void Signal(ID3D12Fence* fence, uint64_t value); void Wait(ID3D12Fence* fence, uint64_t value); + void WaitForPreviousFrame(); + uint64_t GetCurrentFrame() const { return m_currentFrame; } + private: ComPtr m_commandQueue; CommandQueueType m_type; uint64_t m_timestampFrequency; + ComPtr m_frameFence; + uint64_t m_currentFrame = 0; + void* m_frameEvent = nullptr; }; } // namespace RHI diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLCommandQueue.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLCommandQueue.h index 75bbeb5d..63d0248d 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLCommandQueue.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLCommandQueue.h @@ -22,6 +22,8 @@ public: uint64_t GetTimestampFrequency() const override { return 0; } void* GetNativeHandle() override { return nullptr; } + void WaitForPreviousFrame() override {} + uint64_t GetCurrentFrame() const override { return 0; } }; } // namespace RHI diff --git a/engine/include/XCEngine/RHI/RHICommandQueue.h b/engine/include/XCEngine/RHI/RHICommandQueue.h index a1cd81c5..927d1ea5 100644 --- a/engine/include/XCEngine/RHI/RHICommandQueue.h +++ b/engine/include/XCEngine/RHI/RHICommandQueue.h @@ -24,6 +24,8 @@ public: virtual uint64_t GetTimestampFrequency() const = 0; virtual void* GetNativeHandle() = 0; + virtual void WaitForPreviousFrame() = 0; + virtual uint64_t GetCurrentFrame() const = 0; }; } // namespace RHI diff --git a/engine/src/RHI/D3D12/D3D12CommandQueue.cpp b/engine/src/RHI/D3D12/D3D12CommandQueue.cpp index ebe8128b..075795bc 100644 --- a/engine/src/RHI/D3D12/D3D12CommandQueue.cpp +++ b/engine/src/RHI/D3D12/D3D12CommandQueue.cpp @@ -27,12 +27,27 @@ bool D3D12CommandQueue::Initialize(ID3D12Device* device, CommandQueueType type) return false; } + hResult = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_frameFence)); + if (FAILED(hResult)) { + return false; + } + + m_frameEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (!m_frameEvent) { + return false; + } + m_type = type; m_commandQueue->GetTimestampFrequency(&m_timestampFrequency); return true; } void D3D12CommandQueue::Shutdown() { + if (m_frameEvent) { + CloseHandle(m_frameEvent); + m_frameEvent = nullptr; + } + m_frameFence.Reset(); m_commandQueue.Reset(); } @@ -42,6 +57,8 @@ void D3D12CommandQueue::ExecuteCommandLists(uint32_t count, void** lists) { void D3D12CommandQueue::ExecuteCommandListsInternal(uint32_t count, ID3D12CommandList** lists) { m_commandQueue->ExecuteCommandLists(count, lists); + m_currentFrame++; + m_commandQueue->Signal(m_frameFence.Get(), m_currentFrame); } void D3D12CommandQueue::Signal(RHIFence* fence, uint64_t value) { @@ -93,5 +110,14 @@ void D3D12CommandQueue::WaitForIdle() { } } +void D3D12CommandQueue::WaitForPreviousFrame() { + if (m_currentFrame > 0 && m_frameFence) { + if (m_frameFence->GetCompletedValue() < m_currentFrame) { + m_frameFence->SetEventOnCompletion(m_currentFrame, m_frameEvent); + WaitForSingleObject(m_frameEvent, INFINITE); + } + } +} + } // namespace RHI } // namespace XCEngine diff --git a/tests/RHI/D3D12/integration/main_minimal.cpp b/tests/RHI/D3D12/integration/main_minimal.cpp index c69b1d16..b906277d 100644 --- a/tests/RHI/D3D12/integration/main_minimal.cpp +++ b/tests/RHI/D3D12/integration/main_minimal.cpp @@ -42,7 +42,6 @@ D3D12CommandQueue gCommandQueue; D3D12SwapChain gSwapChain; D3D12CommandAllocator gCommandAllocator; D3D12CommandList gCommandList; -D3D12Fence gFence; // Render targets D3D12Texture gColorRTs[2]; @@ -55,7 +54,6 @@ D3D12DepthStencilView gDSV; UINT gRTVDescriptorSize = 0; UINT gDSVDescriptorSize = 0; int gCurrentRTIndex = 0; -UINT64 gFenceValue = 0; // Window HWND gHWND = nullptr; @@ -162,9 +160,6 @@ bool InitD3D12() { gCommandAllocator.Initialize(device, CommandQueueType::Direct); gCommandList.Initialize(device, CommandQueueType::Direct, gCommandAllocator.GetCommandAllocator()); - // Create fence - gFence.Initialize(device, 0); - Log("[INFO] D3D12 initialized successfully"); return true; } @@ -179,8 +174,6 @@ void ExecuteCommandList() { gCommandList.Close(); void* commandLists[] = { gCommandList.GetCommandList() }; gCommandQueue.ExecuteCommandLists(1, commandLists); - gFenceValue += 1; - gCommandQueue.Signal(gFence.GetFence(), gFenceValue); } // Begin rendering @@ -277,7 +270,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } else { // Wait for previous frame to complete before resetting if (frameCount > 0) { - gFence.Wait(gFenceValue); + gCommandQueue.WaitForPreviousFrame(); } // Reset command list for this frame @@ -324,7 +317,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // Shutdown gCommandList.Shutdown(); gCommandAllocator.Shutdown(); - gFence.Shutdown(); gSwapChain.Shutdown(); gDevice.Shutdown();