diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 58ef3cc98..a692d13c6 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -173,6 +173,17 @@ namespace dxvk { return m_properties; } + /** + * \brief Get device status + * + * This may report device loss in + * case a submission failed. + * \returns Device status + */ + VkResult getDeviceStatus() const { + return m_submissionQueue.getLastError(); + } + /** * \brief Queries supported shader stages * \returns Supported shader pipeline stages diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index 0c88f87b7..8daa0267a 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -114,7 +114,8 @@ namespace dxvk { // Submit command buffer to device VkResult status = VK_NOT_READY; - { std::lock_guard lock(m_mutexQueue); + if (m_lastError != VK_ERROR_DEVICE_LOST) { + std::lock_guard lock(m_mutexQueue); if (entry.submit.cmdList != nullptr) { status = entry.submit.cmdList->submit( @@ -124,6 +125,10 @@ namespace dxvk { status = entry.present.presenter->presentImage( entry.present.waitSync); } + } else { + // Don't submit anything after device loss + // so that drivers get a chance to recover + status = VK_ERROR_DEVICE_LOST; } if (entry.status) @@ -135,8 +140,10 @@ namespace dxvk { if (status == VK_SUCCESS) { if (entry.submit.cmdList != nullptr) m_finishQueue.push(std::move(entry)); - } else if (entry.submit.cmdList != nullptr) { + } 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(); } m_submitQueue.pop(); @@ -168,19 +175,22 @@ namespace dxvk { DxvkSubmitEntry entry = std::move(m_finishQueue.front()); lock.unlock(); - VkResult status = entry.submit.cmdList->synchronize(); + VkResult status = m_lastError.load(); - if (status == VK_SUCCESS) { - entry.submit.cmdList->notifySignals(); - entry.submit.cmdList->reset(); - - m_device->recycleCommandList(entry.submit.cmdList); - } else { - Logger::err(str::format( - "DxvkSubmissionQueue: Failed to sync fence: ", - status)); + if (status != VK_ERROR_DEVICE_LOST) + status = entry.submit.cmdList->synchronize(); + + if (status != VK_SUCCESS) { + Logger::err(str::format("DxvkSubmissionQueue: Failed to sync fence: ", status)); + m_lastError = status; + m_device->waitForIdle(); } + entry.submit.cmdList->notifySignals(); + entry.submit.cmdList->reset(); + + m_device->recycleCommandList(entry.submit.cmdList); + lock = std::unique_lock(m_mutex); m_pending -= 1; diff --git a/src/dxvk/dxvk_queue.h b/src/dxvk/dxvk_queue.h index bef9494ed..9387510e9 100644 --- a/src/dxvk/dxvk_queue.h +++ b/src/dxvk/dxvk_queue.h @@ -92,6 +92,17 @@ namespace dxvk { uint64_t gpuIdleTicks() const { return m_gpuIdle.load(); } + + /** + * \brief Retrieves last submission error + * + * In case an error occured during asynchronous command + * submission, it will be returned by this function. + * \returns Last error from command submission + */ + VkResult getLastError() const { + return m_lastError.load(); + } /** * \brief Submits a command list asynchronously @@ -157,6 +168,8 @@ namespace dxvk { DxvkDevice* m_device; bool m_asyncPresent; + + std::atomic m_lastError = { VK_SUCCESS }; std::atomic m_stopped = { false }; std::atomic m_pending = { 0u };