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
This commit is contained in:
2026-03-20 02:51:34 +08:00
parent b7d66a09de
commit 4c6e7af02e
5 changed files with 37 additions and 9 deletions

View File

@@ -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<ID3D12CommandQueue> m_commandQueue;
CommandQueueType m_type;
uint64_t m_timestampFrequency;
ComPtr<ID3D12Fence> m_frameFence;
uint64_t m_currentFrame = 0;
void* m_frameEvent = nullptr;
};
} // namespace RHI

View File

@@ -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

View File

@@ -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

View File

@@ -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