|
|
|
@ -10,7 +10,14 @@ |
|
|
|
|
|
|
|
namespace Vulkan { |
|
|
|
|
|
|
|
MasterSemaphore::MasterSemaphore(const Device& device) { |
|
|
|
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}; |
|
|
|
fence = device.GetLogical().CreateFence(fence_ci); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{ |
|
|
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, |
|
|
|
.pNext = nullptr, |
|
|
|
@ -42,4 +49,134 @@ 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, use an atomic wait
|
|
|
|
while (true) { |
|
|
|
u64 current_value = gpu_tick.load(std::memory_order_relaxed); |
|
|
|
if (current_value >= tick) { |
|
|
|
return; |
|
|
|
} |
|
|
|
gpu_tick.wait(current_value); |
|
|
|
} |
|
|
|
|
|
|
|
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 ? 2 : 1; |
|
|
|
const std::array wait_values{host_tick - 1, u64(1)}; |
|
|
|
const std::array wait_semaphores{timeline_semaphore, wait_semaphore}; |
|
|
|
|
|
|
|
const VkTimelineSemaphoreSubmitInfo timeline_si{ |
|
|
|
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, |
|
|
|
.pNext = nullptr, |
|
|
|
.waitSemaphoreValueCount = num_wait_semaphores, |
|
|
|
.pWaitSemaphoreValues = wait_values.data(), |
|
|
|
.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_semaphores.data(), |
|
|
|
.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 result = device.GetGraphicsQueue().Submit(submit_info, *fence); |
|
|
|
|
|
|
|
if (result == VK_SUCCESS) { |
|
|
|
fence.Wait(); |
|
|
|
fence.Reset(); |
|
|
|
gpu_tick.store(host_tick); |
|
|
|
gpu_tick.notify_all(); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace Vulkan
|