diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 5a389ae3c..b324bb53e 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -174,18 +174,6 @@ namespace dxvk { const auto& graphicsQueue = m_device->queues().graphics; const auto& transferQueue = m_device->queues().transfer; - VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - - if (m_vkd->vkCreateSemaphore(m_vkd->device(), &semaphoreInfo, nullptr, &m_bindSemaphore) - || m_vkd->vkCreateSemaphore(m_vkd->device(), &semaphoreInfo, nullptr, &m_postSemaphore) - || m_vkd->vkCreateSemaphore(m_vkd->device(), &semaphoreInfo, nullptr, &m_sdmaSemaphore)) - throw DxvkError("DxvkCommandList: Failed to create semaphore"); - - VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; - - if (m_vkd->vkCreateFence(m_vkd->device(), &fenceInfo, nullptr, &m_fence)) - throw DxvkError("DxvkCommandList: Failed to create fence"); - m_graphicsPool = new DxvkCommandPool(device, graphicsQueue.queueFamily); if (transferQueue.queueFamily != graphicsQueue.queueFamily) @@ -197,16 +185,12 @@ namespace dxvk { DxvkCommandList::~DxvkCommandList() { this->reset(); - - m_vkd->vkDestroySemaphore(m_vkd->device(), m_bindSemaphore, nullptr); - m_vkd->vkDestroySemaphore(m_vkd->device(), m_postSemaphore, nullptr); - m_vkd->vkDestroySemaphore(m_vkd->device(), m_sdmaSemaphore, nullptr); - - m_vkd->vkDestroyFence(m_vkd->device(), m_fence, nullptr); } - VkResult DxvkCommandList::submit() { + VkResult DxvkCommandList::submit( + const DxvkTimelineSemaphores& semaphores, + DxvkTimelineSemaphoreValues& timelines) { VkResult status = VK_SUCCESS; const auto& graphics = m_device->queues().graphics; @@ -236,18 +220,16 @@ namespace dxvk { if (sparseBind) { // Sparse binding needs to serialize command execution, so wait // for any prior submissions, then block any subsequent ones - m_commandSubmission.signalSemaphore(m_bindSemaphore, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + sparseBind->waitSemaphore(semaphores.graphics, timelines.graphics); + sparseBind->waitSemaphore(semaphores.transfer, timelines.transfer); - if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle))) - return status; - - sparseBind->waitSemaphore(m_bindSemaphore, 0); - sparseBind->signalSemaphore(m_postSemaphore, 0); + sparseBind->signalSemaphore(semaphores.graphics, ++timelines.graphics); if ((status = sparseBind->submit(m_device, sparse.queueHandle))) return status; - m_commandSubmission.waitSemaphore(m_postSemaphore, 0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT); + m_commandSubmission.waitSemaphore(semaphores.graphics, + timelines.graphics, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT); } // Submit transfer commands as necessary @@ -257,12 +239,14 @@ namespace dxvk { // If we had either a transfer command or a semaphore wait, submit to the // transfer queue so that all subsequent commands get stalled as necessary. if (m_device->hasDedicatedTransferQueue() && !m_commandSubmission.isEmpty()) { - m_commandSubmission.signalSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + m_commandSubmission.signalSemaphore(semaphores.transfer, + ++timelines.transfer, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); if ((status = m_commandSubmission.submit(m_device, transfer.queueHandle))) return status; - m_commandSubmission.waitSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT); + m_commandSubmission.waitSemaphore(semaphores.transfer, + timelines.transfer, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT); } // We promise to never do weird stuff to WSI images on @@ -291,14 +275,25 @@ namespace dxvk { m_commandSubmission.signalSemaphore(m_wsiSemaphores.present, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); } - - // Signal synchronization fence on final submission - m_commandSubmission.signalFence(m_fence); } + m_commandSubmission.signalSemaphore(semaphores.graphics, + ++timelines.graphics, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + // Finally, submit all graphics commands of the current submission if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle))) return status; + + // If there are WSI semaphores involved, do another submit only + // containing a timeline semaphore signal so that we can be sure + // that they are safe to use afterwards. + if ((m_wsiSemaphores.present || m_wsiSemaphores.acquire) && isLast) { + m_commandSubmission.signalSemaphore(semaphores.graphics, + ++timelines.graphics, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + + if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle))) + return status; + } } return VK_SUCCESS; @@ -358,11 +353,6 @@ namespace dxvk { } - VkResult DxvkCommandList::synchronizeFence() { - return m_vkd->vkWaitForFences(m_vkd->device(), 1, &m_fence, VK_TRUE, ~0ull); - } - - void DxvkCommandList::reset() { // Free resources and other objects // that are no longer in use @@ -395,10 +385,6 @@ namespace dxvk { // Reset actual command buffers and pools m_graphicsPool->reset(); m_transferPool->reset(); - - // Reset fence - if (m_vkd->vkResetFences(m_vkd->device(), 1, &m_fence)) - Logger::err("DxvkCommandList: Failed to reset fence"); } diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index 6484f5c31..be5c9d5e8 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -18,6 +18,26 @@ namespace dxvk { + /** + * \brief Timeline semaphore pair + * + * One semaphore for each queue. + */ + struct DxvkTimelineSemaphores { + VkSemaphore graphics = VK_NULL_HANDLE; + VkSemaphore transfer = VK_NULL_HANDLE; + }; + + + /** + * \brief Timeline semaphore values + */ + struct DxvkTimelineSemaphoreValues { + uint64_t graphics = 0u; + uint64_t transfer = 0u; + }; + + /** * \brief Command buffer flags * @@ -195,9 +215,14 @@ namespace dxvk { /** * \brief Submits command list + * + * \param [in] semaphores Timeline semaphore pair + * \param [in] timelines Timeline semaphore values * \returns Submission status */ - VkResult submit(); + VkResult submit( + const DxvkTimelineSemaphores& semaphores, + DxvkTimelineSemaphoreValues& timelines); /** * \brief Stat counters @@ -341,12 +366,6 @@ namespace dxvk { m_wsiSemaphores = wsiSemaphores; } - /** - * \brief Synchronizes with command list fence - * \returns Return value of vkWaitForFences call - */ - VkResult synchronizeFence(); - /** * \brief Resets the command list * @@ -1047,11 +1066,6 @@ namespace dxvk { Rc m_graphicsPool; Rc m_transferPool; - VkSemaphore m_bindSemaphore = VK_NULL_HANDLE; - VkSemaphore m_postSemaphore = VK_NULL_HANDLE; - VkSemaphore m_sdmaSemaphore = VK_NULL_HANDLE; - VkFence m_fence = VK_NULL_HANDLE; - DxvkCommandSubmissionInfo m_cmd; PresenterSync m_wsiSemaphores = { }; diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index c3b64630c..b810c1210 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -141,10 +141,12 @@ namespace dxvk { if (m_callback) m_callback(true); - if (entry.submit.cmdList != nullptr) - entry.result = entry.submit.cmdList->submit(); - else if (entry.present.presenter != nullptr) + if (entry.submit.cmdList != nullptr) { + entry.result = entry.submit.cmdList->submit(m_semaphores, m_timelines); + entry.timelines = m_timelines; + } else if (entry.present.presenter != nullptr) { entry.result = entry.present.presenter->presentImage(entry.present.presentMode, entry.present.frameId); + } if (m_callback) m_callback(false); @@ -182,6 +184,8 @@ namespace dxvk { void DxvkSubmissionQueue::finishCmdLists() { env::setThreadName("dxvk-queue"); + auto vk = m_device->vkd(); + while (!m_stopped.load()) { std::unique_lock lock(m_mutex); @@ -204,10 +208,19 @@ namespace dxvk { if (entry.submit.cmdList != nullptr) { VkResult status = m_lastError.load(); - - if (status != VK_ERROR_DEVICE_LOST) - status = entry.submit.cmdList->synchronizeFence(); - + + if (status != VK_ERROR_DEVICE_LOST) { + std::array semaphores = { m_semaphores.graphics, m_semaphores.transfer }; + std::array timelines = { entry.timelines.graphics, entry.timelines.transfer }; + + VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO }; + waitInfo.semaphoreCount = semaphores.size(); + waitInfo.pSemaphores = semaphores.data(); + waitInfo.pValues = timelines.data(); + + status = vk->vkWaitSemaphores(vk->device(), &waitInfo, ~0ull); + } + if (status != VK_SUCCESS) { m_lastError = status; diff --git a/src/dxvk/dxvk_queue.h b/src/dxvk/dxvk_queue.h index 662d843ad..b7b9114b9 100644 --- a/src/dxvk/dxvk_queue.h +++ b/src/dxvk/dxvk_queue.h @@ -56,26 +56,7 @@ namespace dxvk { DxvkSubmitStatus* status; DxvkSubmitInfo submit; DxvkPresentInfo present; - }; - - - /** - * \brief Timeline semaphore pair - * - * One semaphore for each queue. - */ - struct DxvkTimelineSemaphores { - VkSemaphore graphics = VK_NULL_HANDLE; - VkSemaphore transfer = VK_NULL_HANDLE; - }; - - - /** - * \brief Timeline semaphore values - */ - struct DxvkTimelineSemaphoreValues { - uint64_t graphics = 0u; - uint64_t transfer = 0u; + DxvkTimelineSemaphoreValues timelines; };