diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_master_semaphore.cpp | 139 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_master_semaphore.h | 48 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_scheduler.cpp | 36 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_device.h | 6 |
4 files changed, 161 insertions, 68 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..47c74e4d8 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp | |||
| @@ -10,7 +10,14 @@ | |||
| 10 | 10 | ||
| 11 | namespace Vulkan { | 11 | namespace Vulkan { |
| 12 | 12 | ||
| 13 | MasterSemaphore::MasterSemaphore(const Device& device) { | 13 | MasterSemaphore::MasterSemaphore(const Device& device_) : device(device_) { |
| 14 | if (!device.HasTimelineSemaphore()) { | ||
| 15 | static constexpr VkFenceCreateInfo fence_ci{ | ||
| 16 | .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0}; | ||
| 17 | fence = device.GetLogical().CreateFence(fence_ci); | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | |||
| 14 | static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{ | 21 | static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{ |
| 15 | .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, | 22 | .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, |
| 16 | .pNext = nullptr, | 23 | .pNext = nullptr, |
| @@ -42,4 +49,134 @@ MasterSemaphore::MasterSemaphore(const Device& device) { | |||
| 42 | 49 | ||
| 43 | MasterSemaphore::~MasterSemaphore() = default; | 50 | MasterSemaphore::~MasterSemaphore() = default; |
| 44 | 51 | ||
| 52 | void MasterSemaphore::Refresh() { | ||
| 53 | if (!semaphore) { | ||
| 54 | // If we don't support timeline semaphores, there's nothing to refresh | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | |||
| 58 | u64 this_tick{}; | ||
| 59 | u64 counter{}; | ||
| 60 | do { | ||
| 61 | this_tick = gpu_tick.load(std::memory_order_acquire); | ||
| 62 | counter = semaphore.GetCounter(); | ||
| 63 | if (counter < this_tick) { | ||
| 64 | return; | ||
| 65 | } | ||
| 66 | } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release, | ||
| 67 | std::memory_order_relaxed)); | ||
| 68 | } | ||
| 69 | |||
| 70 | void MasterSemaphore::Wait(u64 tick) { | ||
| 71 | if (!semaphore) { | ||
| 72 | // If we don't support timeline semaphores, use an atomic wait | ||
| 73 | while (true) { | ||
| 74 | u64 current_value = gpu_tick.load(std::memory_order_relaxed); | ||
| 75 | if (current_value >= tick) { | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | gpu_tick.wait(current_value); | ||
| 79 | } | ||
| 80 | |||
| 81 | return; | ||
| 82 | } | ||
| 83 | |||
| 84 | // No need to wait if the GPU is ahead of the tick | ||
| 85 | if (IsFree(tick)) { | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | |||
| 89 | // Update the GPU tick and try again | ||
| 90 | Refresh(); | ||
| 91 | |||
| 92 | if (IsFree(tick)) { | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | |||
| 96 | // If none of the above is hit, fallback to a regular wait | ||
| 97 | while (!semaphore.Wait(tick)) { | ||
| 98 | } | ||
| 99 | |||
| 100 | Refresh(); | ||
| 101 | } | ||
| 102 | |||
| 103 | VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | ||
| 104 | VkSemaphore wait_semaphore, u64 host_tick) { | ||
| 105 | if (semaphore) { | ||
| 106 | return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); | ||
| 107 | } else { | ||
| 108 | return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{ | ||
| 113 | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 114 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||
| 115 | }; | ||
| 116 | |||
| 117 | VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, | ||
| 118 | VkSemaphore signal_semaphore, | ||
| 119 | VkSemaphore wait_semaphore, u64 host_tick) { | ||
| 120 | const VkSemaphore timeline_semaphore = *semaphore; | ||
| 121 | |||
| 122 | const u32 num_signal_semaphores = signal_semaphore ? 2 : 1; | ||
| 123 | const std::array signal_values{host_tick, u64(0)}; | ||
| 124 | const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; | ||
| 125 | |||
| 126 | const u32 num_wait_semaphores = wait_semaphore ? 2 : 1; | ||
| 127 | const std::array wait_values{host_tick - 1, u64(1)}; | ||
| 128 | const std::array wait_semaphores{timeline_semaphore, wait_semaphore}; | ||
| 129 | |||
| 130 | const VkTimelineSemaphoreSubmitInfo timeline_si{ | ||
| 131 | .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, | ||
| 132 | .pNext = nullptr, | ||
| 133 | .waitSemaphoreValueCount = num_wait_semaphores, | ||
| 134 | .pWaitSemaphoreValues = wait_values.data(), | ||
| 135 | .signalSemaphoreValueCount = num_signal_semaphores, | ||
| 136 | .pSignalSemaphoreValues = signal_values.data(), | ||
| 137 | }; | ||
| 138 | const VkSubmitInfo submit_info{ | ||
| 139 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | ||
| 140 | .pNext = &timeline_si, | ||
| 141 | .waitSemaphoreCount = num_wait_semaphores, | ||
| 142 | .pWaitSemaphores = wait_semaphores.data(), | ||
| 143 | .pWaitDstStageMask = wait_stage_masks.data(), | ||
| 144 | .commandBufferCount = 1, | ||
| 145 | .pCommandBuffers = cmdbuf.address(), | ||
| 146 | .signalSemaphoreCount = num_signal_semaphores, | ||
| 147 | .pSignalSemaphores = signal_semaphores.data(), | ||
| 148 | }; | ||
| 149 | |||
| 150 | return device.GetGraphicsQueue().Submit(submit_info); | ||
| 151 | } | ||
| 152 | |||
| 153 | VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | ||
| 154 | VkSemaphore wait_semaphore, u64 host_tick) { | ||
| 155 | const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; | ||
| 156 | const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; | ||
| 157 | |||
| 158 | const VkSubmitInfo submit_info{ | ||
| 159 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | ||
| 160 | .pNext = nullptr, | ||
| 161 | .waitSemaphoreCount = num_wait_semaphores, | ||
| 162 | .pWaitSemaphores = &wait_semaphore, | ||
| 163 | .pWaitDstStageMask = wait_stage_masks.data(), | ||
| 164 | .commandBufferCount = 1, | ||
| 165 | .pCommandBuffers = cmdbuf.address(), | ||
| 166 | .signalSemaphoreCount = num_signal_semaphores, | ||
| 167 | .pSignalSemaphores = &signal_semaphore, | ||
| 168 | }; | ||
| 169 | |||
| 170 | auto result = device.GetGraphicsQueue().Submit(submit_info, *fence); | ||
| 171 | |||
| 172 | if (result == VK_SUCCESS) { | ||
| 173 | fence.Wait(); | ||
| 174 | fence.Reset(); | ||
| 175 | gpu_tick.store(host_tick); | ||
| 176 | gpu_tick.notify_all(); | ||
| 177 | } | ||
| 178 | |||
| 179 | return result; | ||
| 180 | } | ||
| 181 | |||
| 45 | } // namespace Vulkan | 182 | } // 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 689f02ea5..f2f61f781 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <atomic> | 6 | #include <atomic> |
| 7 | #include <condition_variable> | ||
| 8 | #include <mutex> | ||
| 7 | #include <thread> | 9 | #include <thread> |
| 8 | 10 | ||
| 9 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -29,11 +31,6 @@ public: | |||
| 29 | return gpu_tick.load(std::memory_order_acquire); | 31 | return gpu_tick.load(std::memory_order_acquire); |
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | /// Returns the timeline semaphore handle. | ||
| 33 | [[nodiscard]] VkSemaphore Handle() const noexcept { | ||
| 34 | return *semaphore; | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Returns true when a tick has been hit by the GPU. | 34 | /// Returns true when a tick has been hit by the GPU. |
| 38 | [[nodiscard]] bool IsFree(u64 tick) const noexcept { | 35 | [[nodiscard]] bool IsFree(u64 tick) const noexcept { |
| 39 | return KnownGpuTick() >= tick; | 36 | return KnownGpuTick() >= tick; |
| @@ -45,37 +42,24 @@ public: | |||
| 45 | } | 42 | } |
| 46 | 43 | ||
| 47 | /// Refresh the known GPU tick | 44 | /// Refresh the known GPU tick |
| 48 | void Refresh() { | 45 | void Refresh(); |
| 49 | u64 this_tick{}; | ||
| 50 | u64 counter{}; | ||
| 51 | do { | ||
| 52 | this_tick = gpu_tick.load(std::memory_order_acquire); | ||
| 53 | counter = semaphore.GetCounter(); | ||
| 54 | if (counter < this_tick) { | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release, | ||
| 58 | std::memory_order_relaxed)); | ||
| 59 | } | ||
| 60 | 46 | ||
| 61 | /// Waits for a tick to be hit on the GPU | 47 | /// Waits for a tick to be hit on the GPU |
| 62 | void Wait(u64 tick) { | 48 | void Wait(u64 tick); |
| 63 | // No need to wait if the GPU is ahead of the tick | 49 | |
| 64 | if (IsFree(tick)) { | 50 | /// Submits the device graphics queue, updating the tick as necessary |
| 65 | return; | 51 | VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, |
| 66 | } | 52 | VkSemaphore wait_semaphore, u64 host_tick); |
| 67 | // Update the GPU tick and try again | 53 | |
| 68 | Refresh(); | 54 | private: |
| 69 | if (IsFree(tick)) { | 55 | VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, |
| 70 | return; | 56 | VkSemaphore wait_semaphore, u64 host_tick); |
| 71 | } | 57 | VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, |
| 72 | // If none of the above is hit, fallback to a regular wait | 58 | VkSemaphore wait_semaphore, u64 host_tick); |
| 73 | while (!semaphore.Wait(tick)) { | ||
| 74 | } | ||
| 75 | Refresh(); | ||
| 76 | } | ||
| 77 | 59 | ||
| 78 | private: | 60 | private: |
| 61 | const Device& device; ///< Device. | ||
| 62 | vk::Fence fence; ///< Fence. | ||
| 79 | vk::Semaphore semaphore; ///< Timeline semaphore. | 63 | vk::Semaphore semaphore; ///< Timeline semaphore. |
| 80 | std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick. | 64 | std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick. |
| 81 | std::atomic<u64> current_tick{1}; ///< Current logical tick. | 65 | std::atomic<u64> current_tick{1}; ///< Current logical tick. |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index b264e6ada..057e16967 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -212,45 +212,13 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s | |||
| 212 | const u64 signal_value = master_semaphore->NextTick(); | 212 | const u64 signal_value = master_semaphore->NextTick(); |
| 213 | Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { | 213 | Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { |
| 214 | cmdbuf.End(); | 214 | cmdbuf.End(); |
| 215 | const VkSemaphore timeline_semaphore = master_semaphore->Handle(); | ||
| 216 | |||
| 217 | const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U; | ||
| 218 | const std::array signal_values{signal_value, u64(0)}; | ||
| 219 | const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; | ||
| 220 | |||
| 221 | const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U; | ||
| 222 | const std::array wait_values{signal_value - 1, u64(1)}; | ||
| 223 | const std::array wait_semaphores{timeline_semaphore, wait_semaphore}; | ||
| 224 | static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{ | ||
| 225 | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 226 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||
| 227 | }; | ||
| 228 | |||
| 229 | const VkTimelineSemaphoreSubmitInfo timeline_si{ | ||
| 230 | .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, | ||
| 231 | .pNext = nullptr, | ||
| 232 | .waitSemaphoreValueCount = num_wait_semaphores, | ||
| 233 | .pWaitSemaphoreValues = wait_values.data(), | ||
| 234 | .signalSemaphoreValueCount = num_signal_semaphores, | ||
| 235 | .pSignalSemaphoreValues = signal_values.data(), | ||
| 236 | }; | ||
| 237 | const VkSubmitInfo submit_info{ | ||
| 238 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | ||
| 239 | .pNext = &timeline_si, | ||
| 240 | .waitSemaphoreCount = num_wait_semaphores, | ||
| 241 | .pWaitSemaphores = wait_semaphores.data(), | ||
| 242 | .pWaitDstStageMask = wait_stage_masks.data(), | ||
| 243 | .commandBufferCount = 1, | ||
| 244 | .pCommandBuffers = cmdbuf.address(), | ||
| 245 | .signalSemaphoreCount = num_signal_semaphores, | ||
| 246 | .pSignalSemaphores = signal_semaphores.data(), | ||
| 247 | }; | ||
| 248 | 215 | ||
| 249 | if (on_submit) { | 216 | if (on_submit) { |
| 250 | on_submit(); | 217 | on_submit(); |
| 251 | } | 218 | } |
| 252 | 219 | ||
| 253 | switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) { | 220 | switch (const VkResult result = master_semaphore->SubmitQueue( |
| 221 | cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { | ||
| 254 | case VK_SUCCESS: | 222 | case VK_SUCCESS: |
| 255 | break; | 223 | break; |
| 256 | case VK_ERROR_DEVICE_LOST: | 224 | case VK_ERROR_DEVICE_LOST: |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 41b5da18a..7d5018151 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -145,7 +145,6 @@ | |||
| 145 | FEATURE_NAME(robustness2, robustImageAccess2) \ | 145 | FEATURE_NAME(robustness2, robustImageAccess2) \ |
| 146 | FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \ | 146 | FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \ |
| 147 | FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \ | 147 | FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \ |
| 148 | FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ | ||
| 149 | FEATURE_NAME(variable_pointer, variablePointers) \ | 148 | FEATURE_NAME(variable_pointer, variablePointers) \ |
| 150 | FEATURE_NAME(variable_pointer, variablePointersStorageBuffer) | 149 | FEATURE_NAME(variable_pointer, variablePointersStorageBuffer) |
| 151 | 150 | ||
| @@ -158,6 +157,7 @@ | |||
| 158 | FEATURE_NAME(provoking_vertex, provokingVertexLast) \ | 157 | FEATURE_NAME(provoking_vertex, provokingVertexLast) \ |
| 159 | FEATURE_NAME(shader_float16_int8, shaderFloat16) \ | 158 | FEATURE_NAME(shader_float16_int8, shaderFloat16) \ |
| 160 | FEATURE_NAME(shader_float16_int8, shaderInt8) \ | 159 | FEATURE_NAME(shader_float16_int8, shaderInt8) \ |
| 160 | FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ | ||
| 161 | FEATURE_NAME(transform_feedback, transformFeedback) \ | 161 | FEATURE_NAME(transform_feedback, transformFeedback) \ |
| 162 | FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \ | 162 | FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \ |
| 163 | FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState) | 163 | FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState) |
| @@ -493,6 +493,10 @@ public: | |||
| 493 | return extensions.shader_atomic_int64; | 493 | return extensions.shader_atomic_int64; |
| 494 | } | 494 | } |
| 495 | 495 | ||
| 496 | bool HasTimelineSemaphore() const { | ||
| 497 | return features.timeline_semaphore.timelineSemaphore; | ||
| 498 | } | ||
| 499 | |||
| 496 | /// Returns the minimum supported version of SPIR-V. | 500 | /// Returns the minimum supported version of SPIR-V. |
| 497 | u32 SupportedSpirvVersion() const { | 501 | u32 SupportedSpirvVersion() const { |
| 498 | if (instance_version >= VK_API_VERSION_1_3) { | 502 | if (instance_version >= VK_API_VERSION_1_3) { |