From 447f29285fbf9d148c7cbccdf0f6fb08bbe566e8 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Sun, 8 Jan 2023 15:04:28 +0200 Subject: [PATCH] renderer_vulkan: Add fence implementation of MasterSemaphore --- .../renderer_vulkan/vk_master_semaphore.cpp | 17 +++ .../renderer_vulkan/vk_master_semaphore.h | 125 ++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 6af474304..0445ba28f 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -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 diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 0f2f352a8..f6ac46ce4 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -92,4 +92,129 @@ private: std::atomic 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 gpu_tick{0}; ///< Current known GPU tick. + std::atomic current_tick{1}; ///< Current logical tick. + + struct Fence { + vk::Fence fence; + u64 gpu_tick; + }; + std::deque fences; ///< List of pending fences + std::vector fence_reserve; ///< Reserve of unsignaled fences +}; + } // namespace Vulkan