renderer_vulkan: Add fence implementation of MasterSemaphore
This commit is contained in:
@ -21,4 +21,21 @@ MasterSemaphore::~MasterSemaphore() {
|
|||||||
device.destroySemaphore(semaphore);
|
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
|
} // namespace Vulkan
|
||||||
|
@ -92,4 +92,129 @@ private:
|
|||||||
std::atomic<u64> current_tick{1}; ///< Current logical tick.
|
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
|
} // namespace Vulkan
|
||||||
|
Reference in New Issue
Block a user