renderer_vulkan: Add fence implementation of MasterSemaphore
This commit is contained in:
@ -21,4 +21,21 @@ MasterSemaphore::~MasterSemaphore() {
|
||||
device.destroySemaphore(semaphore);
|
||||
}
|
||||
|
||||
MasterSemaphoreFence::MasterSemaphoreFence(const Instance& instance) : device{instance.GetDevice()} {
|
||||
fence_reserve.resize(FENCE_RESERVE_COUNT);
|
||||
for (vk::Fence& fence : fence_reserve) {
|
||||
fence = device.createFence({});
|
||||
}
|
||||
}
|
||||
|
||||
MasterSemaphoreFence::~MasterSemaphoreFence() {
|
||||
device.waitIdle();
|
||||
for (const vk::Fence fence : fence_reserve) {
|
||||
device.destroyFence(fence);
|
||||
}
|
||||
for (const Fence& fence : fences) {
|
||||
device.destroyFence(fence.fence);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -92,4 +92,129 @@ private:
|
||||
std::atomic<u64> current_tick{1}; ///< Current logical tick.
|
||||
};
|
||||
|
||||
class MasterSemaphoreFence {
|
||||
static constexpr std::size_t FENCE_RESERVE_COUNT = 8;
|
||||
public:
|
||||
explicit MasterSemaphoreFence(const Instance& instance);
|
||||
~MasterSemaphoreFence();
|
||||
|
||||
/// Returns the current logical tick.
|
||||
[[nodiscard]] u64 CurrentTick() const noexcept {
|
||||
return current_tick.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/// Returns the last known GPU tick.
|
||||
[[nodiscard]] u64 KnownGpuTick() const noexcept {
|
||||
return gpu_tick.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/// Attempts to retrieve a reserved fence
|
||||
[[nodiscard]] vk::Fence TryGetReservedFence() {
|
||||
if (fence_reserve.empty()) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
vk::Fence fence = fence_reserve.back();
|
||||
fence_reserve.pop_back();
|
||||
return fence;
|
||||
}
|
||||
|
||||
/// Returns an available fence for queue submission.
|
||||
[[nodiscard]] vk::Fence Handle() noexcept {
|
||||
vk::Fence fence{};
|
||||
if (fence = TryGetReservedFence(); !fence) {
|
||||
fence = device.createFence({});
|
||||
}
|
||||
|
||||
const u64 tick{CurrentTick()};
|
||||
fences.push_front(Fence{
|
||||
.fence = fence,
|
||||
.gpu_tick = tick
|
||||
});
|
||||
return fence;
|
||||
}
|
||||
|
||||
/// Returns true when a tick has been hit by the GPU.
|
||||
[[nodiscard]] bool IsFree(u64 tick) const noexcept {
|
||||
return KnownGpuTick() >= tick;
|
||||
}
|
||||
|
||||
/// Advance to the logical tick and return the old one
|
||||
[[nodiscard]] u64 NextTick() noexcept {
|
||||
return current_tick.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
/// Returns the tick of the most recent signaled fence
|
||||
[[nodiscard]] u64 FenceCounterValue() {
|
||||
if (fences.empty()) [[unlikely]] {
|
||||
return CurrentTick();
|
||||
}
|
||||
|
||||
u64 tick{KnownGpuTick()};
|
||||
do {
|
||||
Fence fence = fences.back();
|
||||
if (device.getFenceStatus(fence.fence) != vk::Result::eSuccess) {
|
||||
return tick;
|
||||
}
|
||||
tick = fence.gpu_tick;
|
||||
device.resetFences(fence.fence);
|
||||
fence_reserve.push_back(fence.fence);
|
||||
fences.pop_back();
|
||||
} while (!fences.empty());
|
||||
|
||||
return tick;
|
||||
}
|
||||
|
||||
/// Refresh the known GPU tick
|
||||
void Refresh() {
|
||||
u64 this_tick{};
|
||||
u64 counter{};
|
||||
do {
|
||||
this_tick = gpu_tick.load(std::memory_order_acquire);
|
||||
counter = FenceCounterValue();
|
||||
if (counter < this_tick) {
|
||||
return;
|
||||
}
|
||||
} while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
|
||||
std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Waits for a tick to be hit on the GPU
|
||||
void Wait(u64 tick) {
|
||||
// No need to wait if the GPU is ahead of the tick
|
||||
if (IsFree(tick)) {
|
||||
return;
|
||||
}
|
||||
// Update the GPU tick and try again
|
||||
Refresh();
|
||||
if (IsFree(tick)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If none of the above is hit, search for the fence
|
||||
// with the requested tick and wait for it
|
||||
for (const Fence& fence : fences) {
|
||||
if (fence.gpu_tick == tick) {
|
||||
void(device.waitForFences(fence.fence, true, WAIT_TIMEOUT));
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Attempting to wait for tick {} that has not been submitted", tick);
|
||||
}
|
||||
|
||||
private:
|
||||
vk::Device device;
|
||||
std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
|
||||
std::atomic<u64> current_tick{1}; ///< Current logical tick.
|
||||
|
||||
struct Fence {
|
||||
vk::Fence fence;
|
||||
u64 gpu_tick;
|
||||
};
|
||||
std::deque<Fence> fences; ///< List of pending fences
|
||||
std::vector<vk::Fence> fence_reserve; ///< Reserve of unsignaled fences
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
Reference in New Issue
Block a user