diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index 89eee344..ba51400d 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -414,7 +414,7 @@ namespace dxvk { if (cHud != nullptr && !cFrameId) cHud->update(); - m_device->presentImage(m_presenter, cPresentMode, &m_presentStatus); + m_device->presentImage(m_presenter, cPresentMode, 0, &m_presentStatus); }); pContext->FlushCsChunk(); diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 8cc5e954..c40dee48 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -833,7 +833,7 @@ namespace dxvk { if (cHud != nullptr && !cFrameId) cHud->update(); - m_device->presentImage(m_presenter, cPresentMode, &m_presentStatus); + m_device->presentImage(m_presenter, cPresentMode, 0, &m_presentStatus); }); m_parent->FlushCsChunk(); diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 86ae3ccb..9a053791 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -254,12 +254,14 @@ namespace dxvk { void DxvkDevice::presentImage( const Rc& presenter, VkPresentModeKHR presentMode, + uint64_t frameId, DxvkSubmitStatus* status) { status->result = VK_NOT_READY; DxvkPresentInfo presentInfo = { }; presentInfo.presenter = presenter; presentInfo.presentMode = presentMode; + presentInfo.frameId = frameId; m_submissionQueue.present(presentInfo, status); std::lock_guard statLock(m_statLock); @@ -310,10 +312,13 @@ namespace dxvk { void DxvkDevice::waitForIdle() { - this->lockSubmission(); + m_submissionQueue.waitForIdle(); + m_submissionQueue.lockDeviceQueue(); + if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS) Logger::err("DxvkDevice: waitForIdle: Operation failed"); - this->unlockSubmission(); + + m_submissionQueue.unlockDeviceQueue(); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 9085e8d0..d01c1f98 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -455,11 +455,13 @@ namespace dxvk { * can be retrieved with \ref waitForSubmission. * \param [in] presenter The presenter * \param [in] presenteMode Present mode + * \param [in] frameId Optional frame ID * \param [out] status Present status */ void presentImage( const Rc& presenter, VkPresentModeKHR presentMode, + uint64_t frameId, DxvkSubmitStatus* status); /** diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index ef4d757e..41ccf4fa 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -593,7 +593,7 @@ namespace dxvk { VkExtent2D desired) { if (caps.currentExtent.width != std::numeric_limits::max()) return caps.currentExtent; - + VkExtent2D actual; actual.width = clamp(desired.width, caps.minImageExtent.width, caps.maxImageExtent.width); actual.height = clamp(desired.height, caps.minImageExtent.height, caps.maxImageExtent.height); diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index 3c434af9..ca432c80 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -74,6 +74,19 @@ namespace dxvk { } + void DxvkSubmissionQueue::waitForIdle() { + std::unique_lock lock(m_mutex); + + m_submitCond.wait(lock, [this] { + return m_submitQueue.empty(); + }); + + m_finishCond.wait(lock, [this] { + return m_finishQueue.empty(); + }); + } + + void DxvkSubmissionQueue::lockDeviceQueue() { m_mutexQueue.lock(); @@ -107,8 +120,6 @@ namespace dxvk { lock.unlock(); // Submit command buffer to device - VkResult status = VK_NOT_READY; - if (m_lastError != VK_ERROR_DEVICE_LOST) { std::lock_guard lock(m_mutexQueue); @@ -116,31 +127,35 @@ namespace dxvk { m_callback(true); if (entry.submit.cmdList != nullptr) - status = entry.submit.cmdList->submit(); + entry.result = entry.submit.cmdList->submit(); else if (entry.present.presenter != nullptr) - status = entry.present.presenter->presentImage(entry.present.presentMode, 0); + entry.result = entry.present.presenter->presentImage(entry.present.presentMode, entry.present.frameId); if (m_callback) m_callback(false); } else { // Don't submit anything after device loss // so that drivers get a chance to recover - status = VK_ERROR_DEVICE_LOST; + entry.result = VK_ERROR_DEVICE_LOST; } if (entry.status) - entry.status->result = status; + entry.status->result = entry.result; // On success, pass it on to the queue thread lock = std::unique_lock(m_mutex); - if (status == VK_SUCCESS) { - if (entry.submit.cmdList != nullptr) - m_finishQueue.push(std::move(entry)); - } else if (status == VK_ERROR_DEVICE_LOST || entry.submit.cmdList != nullptr) { - Logger::err(str::format("DxvkSubmissionQueue: Command submission failed: ", status)); - m_lastError = status; - m_device->waitForIdle(); + bool doForward = (entry.result == VK_SUCCESS) || + (entry.present.presenter != nullptr && entry.result != VK_ERROR_DEVICE_LOST); + + if (doForward) { + m_finishQueue.push(std::move(entry)); + } else { + Logger::err(str::format("DxvkSubmissionQueue: Command submission failed: ", entry.result)); + m_lastError = entry.result; + + if (m_lastError != VK_ERROR_DEVICE_LOST) + m_device->waitForIdle(); } m_submitQueue.pop(); @@ -172,20 +187,31 @@ namespace dxvk { DxvkSubmitEntry entry = std::move(m_finishQueue.front()); lock.unlock(); - VkResult status = m_lastError.load(); - - if (status != VK_ERROR_DEVICE_LOST) - status = entry.submit.cmdList->synchronizeFence(); - - if (status != VK_SUCCESS) { - m_lastError = status; - m_device->waitForIdle(); + if (entry.submit.cmdList != nullptr) { + VkResult status = m_lastError.load(); + + if (status != VK_ERROR_DEVICE_LOST) + status = entry.submit.cmdList->synchronizeFence(); + + if (status != VK_SUCCESS) { + m_lastError = status; + + if (status != VK_ERROR_DEVICE_LOST) + m_device->waitForIdle(); + } + } else if (entry.present.presenter != nullptr) { + // Signal the frame and then immediately destroy the reference. + // This is necessary since the front-end may want to explicitly + // destroy the presenter object. + entry.present.presenter->signalFrame(entry.result, entry.present.frameId); + entry.present.presenter = nullptr; } // Release resources and signal events, then immediately wake // up any thread that's currently waiting on a resource in // order to reduce delays as much as possible. - entry.submit.cmdList->notifyObjects(); + if (entry.submit.cmdList != nullptr) + entry.submit.cmdList->notifyObjects(); lock.lock(); m_pending -= 1; @@ -195,8 +221,10 @@ namespace dxvk { lock.unlock(); // Free the command list and associated objects now - entry.submit.cmdList->reset(); - m_device->recycleCommandList(entry.submit.cmdList); + if (entry.submit.cmdList != nullptr) { + entry.submit.cmdList->reset(); + m_device->recycleCommandList(entry.submit.cmdList); + } } } diff --git a/src/dxvk/dxvk_queue.h b/src/dxvk/dxvk_queue.h index caacad40..8bedcaa1 100644 --- a/src/dxvk/dxvk_queue.h +++ b/src/dxvk/dxvk_queue.h @@ -44,6 +44,7 @@ namespace dxvk { struct DxvkPresentInfo { Rc presenter; VkPresentModeKHR presentMode; + uint64_t frameId; }; @@ -51,6 +52,7 @@ namespace dxvk { * \brief Submission queue entry */ struct DxvkSubmitEntry { + VkResult result; DxvkSubmitStatus* status; DxvkSubmitInfo submit; DxvkPresentInfo present; @@ -160,6 +162,11 @@ namespace dxvk { m_finishCond.wait(lock, pred); } + /** + * \brief Waits for all submissions to complete + */ + void waitForIdle(); + /** * \brief Locks device queue *