diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 45c08222..ecd05e43 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -221,13 +221,16 @@ namespace dxvk { VkResult DxvkDevice::presentSwapImage( const VkPresentInfoKHR& presentInfo) { - { // Queue submissions are not thread safe - std::lock_guard queueLock(m_submissionLock); - std::lock_guard statLock(m_statLock); - + { std::lock_guard statLock(m_statLock); m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1); - return m_vkd->vkQueuePresentKHR(m_presentQueue.queueHandle, &presentInfo); } + + this->lockSubmission(); + VkResult status = m_vkd->vkQueuePresentKHR( + m_presentQueue.queueHandle, &presentInfo); + this->unlockSubmission(); + + return status; } @@ -235,41 +238,17 @@ namespace dxvk { const Rc& commandList, const Rc& waitSync, const Rc& wakeSync) { - VkSemaphore waitSemaphore = VK_NULL_HANDLE; - VkSemaphore wakeSemaphore = VK_NULL_HANDLE; + { std::lock_guard statLock(m_statLock); - if (waitSync != nullptr) { - waitSemaphore = waitSync->handle(); - commandList->trackResource(waitSync); - } - - if (wakeSync != nullptr) { - wakeSemaphore = wakeSync->handle(); - commandList->trackResource(wakeSync); - } - - VkResult status; - - { // Queue submissions are not thread safe - std::lock_guard queueLock(m_submissionLock); - std::lock_guard statLock(m_statLock); - m_statCounters.merge(commandList->statCounters()); m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1); - - status = commandList->submit( - m_graphicsQueue.queueHandle, - waitSemaphore, wakeSemaphore); - } - - if (status == VK_SUCCESS) { - // Add this to the set of running submissions - m_submissionQueue.submit(commandList); - } else { - Logger::err(str::format( - "DxvkDevice: Command buffer submission failed: ", - status)); } + + DxvkSubmission submission; + submission.cmdList = commandList; + submission.semWait = waitSync; + submission.semWake = wakeSync; + m_submissionQueue.submit(std::move(submission)); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 95d5210f..0a55e2cf 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -327,7 +327,6 @@ namespace dxvk { * \param [in] commandList The command list to submit * \param [in] waitSync (Optional) Semaphore to wait on * \param [in] wakeSync (Optional) Semaphore to notify - * \returns Synchronization fence */ void submitCommandList( const Rc& commandList, @@ -342,7 +341,8 @@ namespace dxvk { * to lock the queue before submitting command buffers. */ void lockSubmission() { - m_submissionLock.lock(); + m_submissionQueue.synchronize(); + m_submissionQueue.lock(); } /** @@ -352,7 +352,7 @@ namespace dxvk { * itself can use them for submissions again. */ void unlockSubmission() { - m_submissionLock.unlock(); + m_submissionQueue.unlock(); } /** @@ -399,7 +399,6 @@ namespace dxvk { sync::Spinlock m_statLock; DxvkStatCounters m_statCounters; - std::mutex m_submissionLock; DxvkDeviceQueue m_graphicsQueue; DxvkDeviceQueue m_presentQueue; diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index 9dc6b315..4970da6b 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -4,54 +4,130 @@ namespace dxvk { DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device) - : m_device(device), - m_thread([this] () { threadFunc(); }) { + : m_device (device), + m_queueThread ([this] () { threadQueue(); }), + m_submitThread([this] () { threadSubmit(); }) { } DxvkSubmissionQueue::~DxvkSubmissionQueue() { - { std::unique_lock lock(m_mutex); + { std::unique_lock lockQueue (m_queueLock); + std::unique_lock lockSubmit(m_submitLock); + m_stopped.store(true); } - m_condOnAdd.notify_one(); - m_thread.join(); + m_submitCondOnAdd.notify_one(); + m_submitThread.join(); + + m_queueCond.notify_one(); + m_queueThread.join(); } - void DxvkSubmissionQueue::submit(const Rc& cmdList) { - { std::unique_lock lock(m_mutex); - - m_condOnTake.wait(lock, [this] { - return m_entries.size() < MaxNumQueuedCommandBuffers; - }); - - m_submits += 1; - m_entries.push(cmdList); - m_condOnAdd.notify_one(); + void DxvkSubmissionQueue::submit(DxvkSubmission submission) { + std::unique_lock lock(m_submitLock); + + m_submitCondOnTake.wait(lock, [this] { + return m_submitQueue.size() < MaxNumQueuedCommandBuffers; + }); + + m_submits += 1; + m_submitQueue.push(std::move(submission)); + m_submitCondOnAdd.notify_one(); + } + + + void DxvkSubmissionQueue::synchronize() { + std::unique_lock lock(m_submitLock); + + m_submitCondOnTake.wait(lock, [this] { + return m_submitQueue.size() == 0; + }); + } + + + void DxvkSubmissionQueue::threadSubmit() { + env::setThreadName(L"dxvk-submit"); + + while (!m_stopped.load()) { + DxvkSubmission submission; + + { std::unique_lock lock(m_submitLock); + + m_submitCondOnAdd.wait(lock, [this] { + return m_stopped.load() || (m_submitQueue.size() != 0); + }); + + if (m_submitQueue.size() != 0) + submission = std::move(m_submitQueue.front()); + } + + if (submission.cmdList != nullptr) { + // Make sure that the semaphores do not get deleted or + // reused before the command submission has completed + VkSemaphore waitSemaphore = VK_NULL_HANDLE; + VkSemaphore wakeSemaphore = VK_NULL_HANDLE; + + if (submission.semWait != nullptr) { + waitSemaphore = submission.semWait->handle(); + submission.cmdList->trackResource(submission.semWait); + } + + if (submission.semWake != nullptr) { + wakeSemaphore = submission.semWake->handle(); + submission.cmdList->trackResource(submission.semWake); + } + + // Protect the Vulkan queue itself from concurrent access + { std::unique_lock lock(m_externalLock); + + VkResult status = submission.cmdList->submit( + m_device->m_graphicsQueue.queueHandle, + waitSemaphore, wakeSemaphore); + + if (status != VK_SUCCESS) + Logger::err(str::format("Dxvk: Submission failed: ", status)); + } + + // Process this submission on the 'queue' thread + { std::unique_lock lock(m_queueLock); + + m_queueEntries.push(std::move(submission.cmdList)); + m_queueCond.notify_one(); + } + + // Remove submission now. We cannot do this earlier as + // the synchronize method depends on this behaviour. + { std::unique_lock lock(m_submitLock); + + if (m_submitQueue.size() != 0) + m_submitQueue.pop(); + } + } + + m_submitCondOnTake.notify_one(); } } - - - void DxvkSubmissionQueue::threadFunc() { + + + void DxvkSubmissionQueue::threadQueue() { env::setThreadName(L"dxvk-queue"); while (!m_stopped.load()) { Rc cmdList; - { std::unique_lock lock(m_mutex); + { std::unique_lock lock(m_queueLock); - m_condOnAdd.wait(lock, [this] { - return m_stopped.load() || (m_entries.size() != 0); + m_queueCond.wait(lock, [this] { + return m_stopped.load() || (m_queueEntries.size() != 0); }); - if (m_entries.size() != 0) { - cmdList = std::move(m_entries.front()); - m_entries.pop(); + if (m_queueEntries.size() != 0) { + cmdList = std::move(m_queueEntries.front()); + m_queueEntries.pop(); } - - m_condOnTake.notify_one(); } if (cmdList != nullptr) { diff --git a/src/dxvk/dxvk_queue.h b/src/dxvk/dxvk_queue.h index 01957002..1fe71447 100644 --- a/src/dxvk/dxvk_queue.h +++ b/src/dxvk/dxvk_queue.h @@ -11,6 +11,12 @@ namespace dxvk { class DxvkDevice; + + struct DxvkSubmission { + Rc cmdList; + Rc semWait; + Rc semWake; + }; /** * \brief Submission queue @@ -41,9 +47,33 @@ namespace dxvk { * to finish executing on the GPU and signal * any queries and events that are used by * the command list in question. - * \param [in] cmdList The command list + * \param [in] submission Command submission */ - void submit(const Rc& cmdList); + void submit(DxvkSubmission submission); + + /** + * \brief Synchronizes with submission thread + * + * Waits until all submissions queued prior + * to this call are submitted to the GPU. + */ + void synchronize(); + + /** + * \brief Locks external queue lock + * Protects the Vulkan queue. + */ + void lock() { + m_externalLock.lock(); + } + + /** + * \brief Locks external queue lock + * Releases the Vulkan queue. + */ + void unlock() { + m_externalLock.unlock(); + } private: @@ -51,14 +81,22 @@ namespace dxvk { std::atomic m_stopped = { false }; std::atomic m_submits = { 0u }; + + std::mutex m_externalLock; - std::mutex m_mutex; - std::condition_variable m_condOnAdd; - std::condition_variable m_condOnTake; - std::queue> m_entries; - dxvk::thread m_thread; + std::mutex m_queueLock; + std::condition_variable m_queueCond; + std::queue> m_queueEntries; + dxvk::thread m_queueThread; - void threadFunc(); + std::mutex m_submitLock; + std::condition_variable m_submitCondOnAdd; + std::condition_variable m_submitCondOnTake; + std::queue m_submitQueue; + dxvk::thread m_submitThread; + + void threadSubmit(); + void threadQueue(); };