diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index edda53db5..09a851bef 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -169,9 +169,8 @@ namespace dxvk { void DxvkCommandList::reset() { - // Signal resources and events to - // avoid stalling main thread - m_signalTracker.reset(); + // Free resources and other objects + // that are no longer in use m_resources.reset(); // Recycle heavy Vulkan objects @@ -185,6 +184,7 @@ namespace dxvk { m_gpuEventTracker.reset(); // Less important stuff + m_signalTracker.reset(); m_statCounters.reset(); } diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index c8fe75d02..acd526d70 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -194,9 +194,10 @@ namespace dxvk { } /** - * \brief Notifies signals + * \brief Notifies resources and signals */ - void notifySignals() { + void notifyObjects() { + m_resources.notify(); m_signalTracker.notify(); } diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index b5720e52e..bb373e06f 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -246,6 +246,24 @@ namespace dxvk { return result; } + + + void DxvkDevice::waitForResource(const Rc& resource, DxvkAccess access) { + if (resource->isInUse(access)) { + auto t0 = dxvk::high_resolution_clock::now(); + + m_submissionQueue.synchronizeUntil([resource, access] { + return !resource->isInUse(access); + }); + + auto t1 = dxvk::high_resolution_clock::now(); + auto us = std::chrono::duration_cast(t1 - t0); + + std::lock_guard lock(m_statLock); + m_statCounters.addCtr(DxvkStatCounter::GpuSyncCount, 1); + m_statCounters.addCtr(DxvkStatCounter::GpuSyncTicks, us.count()); + } + } void DxvkDevice::waitForIdle() { diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 4e18bd93c..4cee1888d 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -474,6 +474,14 @@ namespace dxvk { * \returns Result of the submission */ VkResult waitForSubmission(DxvkSubmitStatus* status); + + /** + * \brief Waits for resource to become idle + * + * \param [in] resource Resource to wait for + * \param [in] access Access mode to check + */ + void waitForResource(const Rc& resource, DxvkAccess access); /** * \brief Waits until the device becomes idle diff --git a/src/dxvk/dxvk_lifetime.cpp b/src/dxvk/dxvk_lifetime.cpp index 777cf0207..e9439c55d 100644 --- a/src/dxvk/dxvk_lifetime.cpp +++ b/src/dxvk/dxvk_lifetime.cpp @@ -6,9 +6,20 @@ namespace dxvk { DxvkLifetimeTracker::~DxvkLifetimeTracker() { } - void DxvkLifetimeTracker::reset() { + void DxvkLifetimeTracker::notify() { for (const auto& resource : m_resources) resource.first->release(resource.second); + + m_notified = true; + } + + + void DxvkLifetimeTracker::reset() { + // If this gets called without ever being submitted then + // we should at least report the resources as unused + if (!m_notified) + this->notify(); + m_resources.clear(); } diff --git a/src/dxvk/dxvk_lifetime.h b/src/dxvk/dxvk_lifetime.h index 75f3c8df9..fb80d05f6 100644 --- a/src/dxvk/dxvk_lifetime.h +++ b/src/dxvk/dxvk_lifetime.h @@ -30,6 +30,13 @@ namespace dxvk { rc->acquire(Access); m_resources.emplace_back(std::move(rc), Access); } + + /** + * \brief Releases resources + * + * Marks all tracked resources as unused. + */ + void notify(); /** * \brief Resets the command list @@ -42,6 +49,7 @@ namespace dxvk { private: std::vector, DxvkAccess>> m_resources; + bool m_notified = false; }; diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index 5b383fc39..86f0b4e35 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -140,9 +140,9 @@ namespace dxvk { void DxvkSubmissionQueue::finishCmdLists() { env::setThreadName("dxvk-queue"); - std::unique_lock lock(m_mutex); - while (!m_stopped.load()) { + std::unique_lock lock(m_mutex); + if (m_finishQueue.empty()) { auto t0 = dxvk::high_resolution_clock::now(); @@ -171,16 +171,21 @@ namespace dxvk { m_device->waitForIdle(); } - entry.submit.cmdList->notifySignals(); - entry.submit.cmdList->reset(); + // 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(); - m_device->recycleCommandList(entry.submit.cmdList); - - lock = std::unique_lock(m_mutex); + lock.lock(); m_pending -= 1; m_finishQueue.pop(); m_finishCond.notify_all(); + lock.unlock(); + + // Free the command list and associated objects now + 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 337c5face..374da55ed 100644 --- a/src/dxvk/dxvk_queue.h +++ b/src/dxvk/dxvk_queue.h @@ -145,6 +145,18 @@ namespace dxvk { */ void synchronize(); + /** + * \brief Synchronizes until a given condition becomes true + * + * Useful to wait for the GPU without busy-waiting. + * \param [in] pred Predicate to check + */ + template + void synchronizeUntil(const Pred& pred) { + std::unique_lock lock(m_mutex); + m_finishCond.wait(lock, pred); + } + /** * \brief Locks device queue * diff --git a/src/dxvk/dxvk_stats.h b/src/dxvk/dxvk_stats.h index 8a7077b78..9138c7c6f 100644 --- a/src/dxvk/dxvk_stats.h +++ b/src/dxvk/dxvk_stats.h @@ -20,6 +20,8 @@ namespace dxvk { PipeCompilerBusy, ///< Boolean indicating compiler activity QueueSubmitCount, ///< Number of command buffer submissions QueuePresentCount, ///< Number of present calls / frames + GpuSyncCount, ///< Number of GPU synchronizations + GpuSyncTicks, ///< Time spent waiting for GPU GpuIdleTicks, ///< GPU idle time in microseconds CsSyncCount, ///< CS thread synchronizations CsSyncTicks, ///< Time spent waiting on CS