vk_master_semaphore.cpp
1 // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 #include <limits> 5 #include <mutex> 6 #include "video_core/renderer_vulkan/vk_instance.h" 7 #include "video_core/renderer_vulkan/vk_master_semaphore.h" 8 #include "video_core/renderer_vulkan/vk_scheduler.h" 9 10 namespace Vulkan { 11 12 constexpr u64 WAIT_TIMEOUT = std::numeric_limits<u64>::max(); 13 14 MasterSemaphoreTimeline::MasterSemaphoreTimeline(const Instance& instance_) : instance{instance_} { 15 const vk::StructureChain semaphore_chain = { 16 vk::SemaphoreCreateInfo{}, 17 vk::SemaphoreTypeCreateInfoKHR{ 18 .semaphoreType = vk::SemaphoreType::eTimeline, 19 .initialValue = 0, 20 }, 21 }; 22 semaphore = instance.GetDevice().createSemaphoreUnique(semaphore_chain.get()); 23 } 24 25 MasterSemaphoreTimeline::~MasterSemaphoreTimeline() = default; 26 27 void MasterSemaphoreTimeline::Refresh() { 28 u64 this_tick{}; 29 u64 counter{}; 30 do { 31 this_tick = gpu_tick.load(std::memory_order_acquire); 32 counter = instance.GetDevice().getSemaphoreCounterValueKHR(*semaphore); 33 if (counter < this_tick) { 34 return; 35 } 36 } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release, 37 std::memory_order_relaxed)); 38 } 39 40 void MasterSemaphoreTimeline::Wait(u64 tick) { 41 // No need to wait if the GPU is ahead of the tick 42 if (IsFree(tick)) { 43 return; 44 } 45 // Update the GPU tick and try again 46 Refresh(); 47 if (IsFree(tick)) { 48 return; 49 } 50 51 // If none of the above is hit, fallback to a regular wait 52 const vk::SemaphoreWaitInfoKHR wait_info = { 53 .semaphoreCount = 1, 54 .pSemaphores = &semaphore.get(), 55 .pValues = &tick, 56 }; 57 58 while (instance.GetDevice().waitSemaphoresKHR(&wait_info, WAIT_TIMEOUT) != 59 vk::Result::eSuccess) { 60 } 61 Refresh(); 62 } 63 64 void MasterSemaphoreTimeline::SubmitWork(vk::CommandBuffer cmdbuf, vk::Semaphore wait, 65 vk::Semaphore signal, u64 signal_value) { 66 cmdbuf.end(); 67 68 const u32 num_signal_semaphores = signal ? 2U : 1U; 69 const std::array signal_values{signal_value, u64(0)}; 70 const std::array signal_semaphores{Handle(), signal}; 71 72 const u32 num_wait_semaphores = wait ? 2U : 1U; 73 const std::array wait_values{signal_value - 1, u64(1)}; 74 const std::array wait_semaphores{Handle(), wait}; 75 76 static constexpr std::array<vk::PipelineStageFlags, 2> wait_stage_masks = { 77 vk::PipelineStageFlagBits::eAllCommands, 78 vk::PipelineStageFlagBits::eColorAttachmentOutput, 79 }; 80 81 const vk::TimelineSemaphoreSubmitInfoKHR timeline_si = { 82 .waitSemaphoreValueCount = num_wait_semaphores, 83 .pWaitSemaphoreValues = wait_values.data(), 84 .signalSemaphoreValueCount = num_signal_semaphores, 85 .pSignalSemaphoreValues = signal_values.data(), 86 }; 87 88 const vk::SubmitInfo submit_info = { 89 .pNext = &timeline_si, 90 .waitSemaphoreCount = num_wait_semaphores, 91 .pWaitSemaphores = wait_semaphores.data(), 92 .pWaitDstStageMask = wait_stage_masks.data(), 93 .commandBufferCount = 1u, 94 .pCommandBuffers = &cmdbuf, 95 .signalSemaphoreCount = num_signal_semaphores, 96 .pSignalSemaphores = signal_semaphores.data(), 97 }; 98 99 try { 100 instance.GetGraphicsQueue().submit(submit_info); 101 } catch (vk::DeviceLostError& err) { 102 LOG_CRITICAL(Render_Vulkan, "Device lost during submit: {}", err.what()); 103 UNREACHABLE(); 104 } 105 } 106 107 constexpr u64 FENCE_RESERVE = 8; 108 109 MasterSemaphoreFence::MasterSemaphoreFence(const Instance& instance_) : instance{instance_} { 110 const vk::Device device{instance.GetDevice()}; 111 for (u64 i = 0; i < FENCE_RESERVE; i++) { 112 free_queue.push(device.createFenceUnique({})); 113 } 114 wait_thread = std::jthread([this](std::stop_token token) { WaitThread(token); }); 115 } 116 117 MasterSemaphoreFence::~MasterSemaphoreFence() = default; 118 119 void MasterSemaphoreFence::Refresh() {} 120 121 void MasterSemaphoreFence::Wait(u64 tick) { 122 while (true) { 123 u64 current_value = gpu_tick.load(std::memory_order_relaxed); 124 if (current_value >= tick) { 125 return; 126 } 127 gpu_tick.wait(current_value); 128 } 129 } 130 131 void MasterSemaphoreFence::SubmitWork(vk::CommandBuffer cmdbuf, vk::Semaphore wait, 132 vk::Semaphore signal, u64 signal_value) { 133 cmdbuf.end(); 134 135 const u32 num_signal_semaphores = signal ? 1U : 0U; 136 const u32 num_wait_semaphores = wait ? 1U : 0U; 137 138 static constexpr std::array<vk::PipelineStageFlags, 1> wait_stage_masks = { 139 vk::PipelineStageFlagBits::eColorAttachmentOutput, 140 }; 141 142 const vk::SubmitInfo submit_info = { 143 .waitSemaphoreCount = num_wait_semaphores, 144 .pWaitSemaphores = &wait, 145 .pWaitDstStageMask = wait_stage_masks.data(), 146 .commandBufferCount = 1u, 147 .pCommandBuffers = &cmdbuf, 148 .signalSemaphoreCount = num_signal_semaphores, 149 .pSignalSemaphores = &signal, 150 }; 151 152 vk::UniqueFence fence{GetFreeFence()}; 153 try { 154 instance.GetGraphicsQueue().submit(submit_info, *fence); 155 } catch (vk::DeviceLostError& err) { 156 LOG_CRITICAL(Render_Vulkan, "Device lost during submit: {}", err.what()); 157 UNREACHABLE(); 158 } 159 160 std::scoped_lock lock{wait_mutex}; 161 wait_queue.push({ 162 .handle = std::move(fence), 163 .signal_value = signal_value, 164 }); 165 wait_cv.notify_one(); 166 } 167 168 void MasterSemaphoreFence::WaitThread(std::stop_token token) { 169 const vk::Device device{instance.GetDevice()}; 170 while (!token.stop_requested()) { 171 Fence fence; 172 { 173 std::unique_lock lock{wait_mutex}; 174 Common::CondvarWait(wait_cv, lock, token, [this] { return !wait_queue.empty(); }); 175 if (token.stop_requested()) { 176 return; 177 } 178 fence = std::move(wait_queue.front()); 179 wait_queue.pop(); 180 } 181 182 const vk::Result result = device.waitForFences(*fence.handle, true, WAIT_TIMEOUT); 183 if (result != vk::Result::eSuccess) { 184 LOG_CRITICAL(Render_Vulkan, "Fence wait failed with error {}", vk::to_string(result)); 185 UNREACHABLE(); 186 } 187 device.resetFences(*fence.handle); 188 189 gpu_tick.store(fence.signal_value); 190 gpu_tick.notify_all(); 191 192 std::scoped_lock lock{free_mutex}; 193 free_queue.push(std::move(fence.handle)); 194 } 195 } 196 197 vk::UniqueFence MasterSemaphoreFence::GetFreeFence() { 198 std::scoped_lock lock{free_mutex}; 199 if (free_queue.empty()) { 200 return instance.GetDevice().createFenceUnique({}); 201 } 202 203 vk::UniqueFence fence{std::move(free_queue.front())}; 204 free_queue.pop(); 205 return fence; 206 } 207 208 } // namespace Vulkan