Merge pull request #10388 from GPUCode/fence-wait

vk_master_semaphore: Move fence wait on separate thread
This commit is contained in:
liamwhite 2023-05-23 09:42:56 -04:00 committed by GitHub
commit a5d4c3e5ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 6 deletions

View File

@ -10,11 +10,16 @@
namespace Vulkan { namespace Vulkan {
constexpr u64 FENCE_RESERVE_SIZE = 8;
MasterSemaphore::MasterSemaphore(const Device& device_) : device(device_) { MasterSemaphore::MasterSemaphore(const Device& device_) : device(device_) {
if (!device.HasTimelineSemaphore()) { if (!device.HasTimelineSemaphore()) {
static constexpr VkFenceCreateInfo fence_ci{ static constexpr VkFenceCreateInfo fence_ci{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0}; .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0};
fence = device.GetLogical().CreateFence(fence_ci); 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; return;
} }
@ -167,16 +172,53 @@ VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphor
.pSignalSemaphores = &signal_semaphore, .pSignalSemaphores = &signal_semaphore,
}; };
auto fence = GetFreeFence();
auto result = device.GetGraphicsQueue().Submit(submit_info, *fence); auto result = device.GetGraphicsQueue().Submit(submit_info, *fence);
if (result == VK_SUCCESS) { if (result == VK_SUCCESS) {
fence.Wait(); std::scoped_lock lock{wait_mutex};
fence.Reset(); wait_queue.emplace(host_tick, std::move(fence));
gpu_tick.store(host_tick); wait_cv.notify_one();
gpu_tick.notify_all();
} }
return result; 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();
gpu_tick.store(host_tick);
gpu_tick.notify_all();
std::scoped_lock lock{free_mutex};
free_queue.push_front(std::move(fence));
}
}
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 } // namespace Vulkan

View File

@ -5,8 +5,10 @@
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <deque>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <queue>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
@ -17,6 +19,8 @@ namespace Vulkan {
class Device; class Device;
class MasterSemaphore { class MasterSemaphore {
using Waitable = std::pair<u64, vk::Fence>;
public: public:
explicit MasterSemaphore(const Device& device); explicit MasterSemaphore(const Device& device);
~MasterSemaphore(); ~MasterSemaphore();
@ -57,13 +61,22 @@ private:
VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick); VkSemaphore wait_semaphore, u64 host_tick);
void WaitThread(std::stop_token token);
vk::Fence GetFreeFence();
private: private:
const Device& device; ///< Device. const Device& device; ///< Device.
vk::Fence fence; ///< Fence.
vk::Semaphore semaphore; ///< Timeline semaphore. vk::Semaphore semaphore; ///< Timeline semaphore.
std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick. std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
std::atomic<u64> current_tick{1}; ///< Current logical tick. std::atomic<u64> current_tick{1}; ///< Current logical tick.
std::mutex wait_mutex;
std::mutex free_mutex;
std::condition_variable_any wait_cv;
std::queue<Waitable> wait_queue; ///< Queue for the fences to be waited on by the wait thread.
std::deque<vk::Fence> free_queue; ///< Holds available fences for submission.
std::jthread debug_thread; ///< Debug thread to workaround validation layer bugs. std::jthread debug_thread; ///< Debug thread to workaround validation layer bugs.
std::jthread wait_thread; ///< Helper thread that waits for submitted fences.
}; };
} // namespace Vulkan } // namespace Vulkan