summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp175
1 files changed, 174 insertions, 1 deletions
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index 8aa07ef9d..6b288b994 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -3,6 +3,7 @@
#include <thread>
+#include "common/polyfill_ranges.h"
#include "common/settings.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/vulkan_common/vulkan_device.h"
@@ -10,7 +11,19 @@
namespace Vulkan {
-MasterSemaphore::MasterSemaphore(const Device& device) {
+constexpr u64 FENCE_RESERVE_SIZE = 8;
+
+MasterSemaphore::MasterSemaphore(const Device& device_) : device(device_) {
+ if (!device.HasTimelineSemaphore()) {
+ static constexpr VkFenceCreateInfo fence_ci{
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0};
+ free_queue.resize(FENCE_RESERVE_SIZE);
+ std::ranges::generate(free_queue,
+ [&] { return device.GetLogical().CreateFence(fence_ci); });
+ wait_thread = std::jthread([this](std::stop_token token) { WaitThread(token); });
+ return;
+ }
+
static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = nullptr,
@@ -42,4 +55,164 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
MasterSemaphore::~MasterSemaphore() = default;
+void MasterSemaphore::Refresh() {
+ if (!semaphore) {
+ // If we don't support timeline semaphores, there's nothing to refresh
+ return;
+ }
+
+ u64 this_tick{};
+ u64 counter{};
+ do {
+ this_tick = gpu_tick.load(std::memory_order_acquire);
+ counter = semaphore.GetCounter();
+ if (counter < this_tick) {
+ return;
+ }
+ } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
+ std::memory_order_relaxed));
+}
+
+void MasterSemaphore::Wait(u64 tick) {
+ if (!semaphore) {
+ // If we don't support timeline semaphores, wait for the value normally
+ std::unique_lock lk{free_mutex};
+ free_cv.wait(lk, [&] { return gpu_tick.load(std::memory_order_relaxed) >= tick; });
+ return;
+ }
+
+ // 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, fallback to a regular wait
+ while (!semaphore.Wait(tick)) {
+ }
+
+ Refresh();
+}
+
+VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick) {
+ if (semaphore) {
+ return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
+ } else {
+ return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
+ }
+}
+
+static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+};
+
+VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
+ VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick) {
+ const VkSemaphore timeline_semaphore = *semaphore;
+
+ const u32 num_signal_semaphores = signal_semaphore ? 2 : 1;
+ const std::array signal_values{host_tick, u64(0)};
+ const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
+
+ const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
+ const VkTimelineSemaphoreSubmitInfo timeline_si{
+ .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
+ .pNext = nullptr,
+ .waitSemaphoreValueCount = 0,
+ .pWaitSemaphoreValues = nullptr,
+ .signalSemaphoreValueCount = num_signal_semaphores,
+ .pSignalSemaphoreValues = signal_values.data(),
+ };
+ const VkSubmitInfo submit_info{
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = &timeline_si,
+ .waitSemaphoreCount = num_wait_semaphores,
+ .pWaitSemaphores = &wait_semaphore,
+ .pWaitDstStageMask = wait_stage_masks.data(),
+ .commandBufferCount = 1,
+ .pCommandBuffers = cmdbuf.address(),
+ .signalSemaphoreCount = num_signal_semaphores,
+ .pSignalSemaphores = signal_semaphores.data(),
+ };
+
+ return device.GetGraphicsQueue().Submit(submit_info);
+}
+
+VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick) {
+ const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
+ const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
+
+ const VkSubmitInfo submit_info{
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = nullptr,
+ .waitSemaphoreCount = num_wait_semaphores,
+ .pWaitSemaphores = &wait_semaphore,
+ .pWaitDstStageMask = wait_stage_masks.data(),
+ .commandBufferCount = 1,
+ .pCommandBuffers = cmdbuf.address(),
+ .signalSemaphoreCount = num_signal_semaphores,
+ .pSignalSemaphores = &signal_semaphore,
+ };
+
+ auto fence = GetFreeFence();
+ auto result = device.GetGraphicsQueue().Submit(submit_info, *fence);
+
+ if (result == VK_SUCCESS) {
+ std::scoped_lock lock{wait_mutex};
+ wait_queue.emplace(host_tick, std::move(fence));
+ wait_cv.notify_one();
+ }
+
+ return result;
+}
+
+void MasterSemaphore::WaitThread(std::stop_token token) {
+ while (!token.stop_requested()) {
+ u64 host_tick;
+ vk::Fence fence;
+ {
+ std::unique_lock lock{wait_mutex};
+ Common::CondvarWait(wait_cv, lock, token, [this] { return !wait_queue.empty(); });
+ if (token.stop_requested()) {
+ return;
+ }
+ std::tie(host_tick, fence) = std::move(wait_queue.front());
+ wait_queue.pop();
+ }
+
+ fence.Wait();
+ fence.Reset();
+
+ {
+ std::scoped_lock lock{free_mutex};
+ free_queue.push_front(std::move(fence));
+ gpu_tick.store(host_tick);
+ }
+ free_cv.notify_one();
+ }
+}
+
+vk::Fence MasterSemaphore::GetFreeFence() {
+ std::scoped_lock lock{free_mutex};
+ if (free_queue.empty()) {
+ static constexpr VkFenceCreateInfo fence_ci{
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0};
+ return device.GetLogical().CreateFence(fence_ci);
+ }
+
+ auto fence = std::move(free_queue.back());
+ free_queue.pop_back();
+ return fence;
+}
+
} // namespace Vulkan