RHI: Refactor Fence module to pure timeline semantics

- Remove IsSignaled() from RHIFence interface (semantic inconsistency)
- Remove Reset() from OpenGL implementation (no D3D12 counterpart)
- OpenGL Fence now uses single GLsync + CPU counters for timeline simulation
- OpenGL Fence Initialize() now accepts uint64_t initialValue (was bool)
- Add comprehensive timeline semantics tests for all backends:
  - Signal increment/decrement scenarios
  - Multiple signals
  - Wait smaller than completed value
  - GetCompletedValue stages verification
- Update documentation to reflect actual implementation
This commit is contained in:
2026-03-24 01:53:00 +08:00
parent 92d817e16e
commit 08c01dd143
10 changed files with 769 additions and 95 deletions

View File

@@ -22,7 +22,6 @@ public:
void Signal(uint64_t value) override;
void Wait(uint64_t value) override;
uint64_t GetCompletedValue() const override;
bool IsSignaled() const override { return m_fence->GetCompletedValue() >= m_signalValue; }
void* GetEventHandle() { return m_eventHandle; }
void* GetNativeHandle() override { return m_fence.Get(); }
@@ -31,7 +30,6 @@ public:
private:
ComPtr<ID3D12Fence> m_fence;
void* m_eventHandle;
uint64_t m_signalValue = UINT64_MAX;
};
} // namespace RHI

View File

@@ -1,44 +1,34 @@
#pragma once
#include <cstdint>
#include <atomic>
#include "../RHIFence.h"
namespace XCEngine {
namespace RHI {
enum class FenceStatus {
Signaled,
Unsignaled,
Error
};
class OpenGLFence : public RHIFence {
public:
OpenGLFence();
~OpenGLFence() override;
bool Initialize(bool signaled = false);
bool Initialize(uint64_t initialValue = 0);
void Shutdown() override;
void Signal() override;
void Signal(uint64_t value) override;
void Wait(uint64_t value) override;
void Reset();
bool IsSignaled() const override;
FenceStatus GetStatus() const;
uint64_t GetCompletedValue() const override;
uint64_t GetCurrentValue() const { return m_fenceValue; }
void* GetNativeHandle() override;
private:
void* m_sync;
uint64_t m_fenceValue;
uint64_t m_completedValue;
bool m_signaled;
std::atomic<uint64_t> m_signaledValue;
std::atomic<uint64_t> m_completedValue;
};
} // namespace RHI
} // namespace XCEngine
} // namespace XCEngine

View File

@@ -15,7 +15,6 @@ public:
virtual void Signal(uint64_t value) = 0;
virtual void Wait(uint64_t value) = 0;
virtual uint64_t GetCompletedValue() const = 0;
virtual bool IsSignaled() const = 0;
virtual void* GetNativeHandle() = 0;
};

View File

@@ -38,7 +38,6 @@ void D3D12Fence::Signal() {
}
void D3D12Fence::Signal(uint64_t value) {
m_signalValue = value;
m_fence->Signal(value);
}

View File

@@ -4,21 +4,19 @@
namespace XCEngine {
namespace RHI {
OpenGLFence::OpenGLFence()
OpenGLFence::OpenGLFence()
: m_sync(nullptr)
, m_fenceValue(0)
, m_completedValue(0)
, m_signaled(false) {
, m_signaledValue(0)
, m_completedValue(0) {
}
OpenGLFence::~OpenGLFence() {
Shutdown();
}
bool OpenGLFence::Initialize(bool signaled) {
m_fenceValue = signaled ? 1 : 0;
m_completedValue = m_fenceValue;
m_signaled = signaled;
bool OpenGLFence::Initialize(uint64_t initialValue) {
m_signaledValue.store(initialValue, std::memory_order_release);
m_completedValue.store(initialValue, std::memory_order_release);
return true;
}
@@ -27,64 +25,56 @@ void OpenGLFence::Shutdown() {
glDeleteSync(static_cast<GLsync>(m_sync));
m_sync = nullptr;
}
m_signaledValue.store(0, std::memory_order_release);
m_completedValue.store(0, std::memory_order_release);
}
void OpenGLFence::Signal() {
glFlush();
m_fenceValue++;
m_signaled = true;
Signal(1);
}
void OpenGLFence::Signal(uint64_t value) {
glFlush();
m_fenceValue = value;
m_completedValue = value;
m_signaled = true;
if (m_sync) {
glDeleteSync(static_cast<GLsync>(m_sync));
}
m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
void OpenGLFence::Wait(uint64_t timeoutNs) {
if (m_signaled && m_sync) {
GLsync sync = static_cast<GLsync>(m_sync);
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
}
glFinish();
m_completedValue = m_fenceValue;
m_signaled = true;
}
void OpenGLFence::Reset() {
if (m_sync) {
glClientWaitSync(static_cast<GLsync>(m_sync), GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(static_cast<GLsync>(m_sync));
m_sync = nullptr;
}
m_signaled = false;
glFlush();
m_signaledValue.store(value, std::memory_order_release);
m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
bool OpenGLFence::IsSignaled() const {
return m_signaled;
}
FenceStatus OpenGLFence::GetStatus() const {
if (!m_sync) {
return m_signaled ? FenceStatus::Signaled : FenceStatus::Unsignaled;
void OpenGLFence::Wait(uint64_t value) {
uint64_t currentCompleted = m_completedValue.load(std::memory_order_acquire);
if (currentCompleted >= value) {
return;
}
GLsync sync = static_cast<GLsync>(m_sync);
GLint status = 0;
glGetSynciv(sync, GL_SYNC_STATUS, sizeof(status), nullptr, &status);
if (status == GL_SIGNALED) {
return FenceStatus::Signaled;
if (m_sync) {
glClientWaitSync(static_cast<GLsync>(m_sync), GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(static_cast<GLsync>(m_sync));
m_sync = nullptr;
}
return FenceStatus::Unsignaled;
uint64_t signaled = m_signaledValue.load(std::memory_order_acquire);
m_completedValue.store(signaled, std::memory_order_release);
}
uint64_t OpenGLFence::GetCompletedValue() const {
return m_completedValue;
if (!m_sync) {
return m_completedValue.load(std::memory_order_acquire);
}
GLint status = 0;
glGetSynciv(static_cast<GLsync>(m_sync), GL_SYNC_STATUS, sizeof(status), nullptr, &status);
if (status == GL_SIGNALED) {
return m_signaledValue.load(std::memory_order_acquire);
}
return m_completedValue.load(std::memory_order_acquire);
}
void* OpenGLFence::GetNativeHandle() {
@@ -95,4 +85,4 @@ void* OpenGLFence::GetNativeHandle() {
}
} // namespace RHI
} // namespace XCEngine
} // namespace XCEngine