#include "dxvk_device.h" #include "dxvk_queue.h" namespace dxvk { DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device) : m_device(device), m_submitThread([this] () { submitCmdLists(); }), m_finishThread([this] () { finishCmdLists(); }) { } DxvkSubmissionQueue::~DxvkSubmissionQueue() { { std::unique_lock lock(m_mutex); m_stopped.store(true); } m_appendCond.notify_all(); m_submitCond.notify_all(); m_submitThread.join(); m_finishThread.join(); } void DxvkSubmissionQueue::submit(DxvkSubmitInfo submitInfo) { std::unique_lock lock(m_mutex); m_finishCond.wait(lock, [this] { return m_submitQueue.size() + m_finishQueue.size() <= MaxNumQueuedCommandBuffers; }); DxvkSubmitEntry entry = { }; entry.submit = std::move(submitInfo); m_pending += 1; m_submitQueue.push(std::move(entry)); m_appendCond.notify_all(); } void DxvkSubmissionQueue::present(DxvkPresentInfo presentInfo, DxvkSubmitStatus* status) { std::unique_lock lock(m_mutex); DxvkSubmitEntry entry = { }; entry.status = status; entry.present = std::move(presentInfo); m_submitQueue.push(std::move(entry)); m_appendCond.notify_all(); } void DxvkSubmissionQueue::synchronizeSubmission( DxvkSubmitStatus* status) { std::unique_lock lock(m_mutex); m_submitCond.wait(lock, [status] { return status->result.load() != VK_NOT_READY; }); } void DxvkSubmissionQueue::synchronize() { std::unique_lock lock(m_mutex); m_submitCond.wait(lock, [this] { return m_submitQueue.empty(); }); } void DxvkSubmissionQueue::lockDeviceQueue() { m_mutexQueue.lock(); } void DxvkSubmissionQueue::unlockDeviceQueue() { m_mutexQueue.unlock(); } void DxvkSubmissionQueue::submitCmdLists() { env::setThreadName("dxvk-submit"); std::unique_lock lock(m_mutex); while (!m_stopped.load()) { m_appendCond.wait(lock, [this] { return m_stopped.load() || !m_submitQueue.empty(); }); if (m_stopped.load()) return; DxvkSubmitEntry entry = std::move(m_submitQueue.front()); 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); if (entry.submit.cmdList != nullptr) { status = entry.submit.cmdList->submit( entry.submit.waitSync, entry.submit.wakeSync); } else if (entry.present.presenter != nullptr) { 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) entry.status->result = status; // 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(); } m_submitQueue.pop(); m_submitCond.notify_all(); } } void DxvkSubmissionQueue::finishCmdLists() { env::setThreadName("dxvk-queue"); std::unique_lock lock(m_mutex); while (!m_stopped.load()) { if (m_finishQueue.empty()) { auto t0 = dxvk::high_resolution_clock::now(); m_submitCond.wait(lock, [this] { return m_stopped.load() || !m_finishQueue.empty(); }); auto t1 = dxvk::high_resolution_clock::now(); m_gpuIdle += std::chrono::duration_cast(t1 - t0).count(); } if (m_stopped.load()) return; DxvkSubmitEntry entry = std::move(m_finishQueue.front()); lock.unlock(); VkResult status = m_lastError.load(); 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; m_finishQueue.pop(); m_finishCond.notify_all(); } } }