From 37f3d9208bb4025e8f00e5ef05eece0cd57b8626 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 8 Feb 2022 22:36:02 +0100 Subject: [PATCH] [dxvk] Introduce sequence numbers for CS submissions --- src/d3d11/d3d11_context_imm.cpp | 3 +-- src/d3d9/d3d9_device.cpp | 3 +-- src/dxvk/dxvk_cs.cpp | 30 +++++++++++++++++--------- src/dxvk/dxvk_cs.h | 38 ++++++++++++++++++--------------- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index a4de6f0c..b254f41c 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -548,8 +548,7 @@ namespace dxvk { // recorded prior to this function will be run FlushCsChunk(); - if (m_csThread.isBusy()) - m_csThread.synchronize(); + m_csThread.synchronize(DxvkCsThread::SynchronizeAll); } diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index ffba18d1..b3baee71 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -4759,8 +4759,7 @@ namespace dxvk { // recorded prior to this function will be run FlushCsChunk(); - if (m_csThread.isBusy()) - m_csThread.synchronize(); + m_csThread.synchronize(DxvkCsThread::SynchronizeAll); } diff --git a/src/dxvk/dxvk_cs.cpp b/src/dxvk/dxvk_cs.cpp index 290c3eba..5b70c725 100644 --- a/src/dxvk/dxvk_cs.cpp +++ b/src/dxvk/dxvk_cs.cpp @@ -111,22 +111,32 @@ namespace dxvk { } - void DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) { + uint64_t DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) { + uint64_t seq; + { std::unique_lock lock(m_mutex); + seq = ++m_chunksDispatched; m_chunksQueued.push(std::move(chunk)); - m_chunksPending += 1; } m_condOnAdd.notify_one(); + return seq; } - void DxvkCsThread::synchronize() { - std::unique_lock lock(m_mutex); - - m_condOnSync.wait(lock, [this] { - return !m_chunksPending.load(); - }); + void DxvkCsThread::synchronize(uint64_t seq) { + // Avoid locking if we know the sync is a no-op, may + // reduce overhead if this is being called frequently + if (seq > m_chunksExecuted.load(std::memory_order_acquire)) { + std::unique_lock lock(m_mutex); + + if (seq == SynchronizeAll) + seq = m_chunksDispatched.load(); + + m_condOnSync.wait(lock, [this, seq] { + return m_chunksExecuted.load() >= seq; + }); + } } @@ -139,8 +149,8 @@ namespace dxvk { while (!m_stopped.load()) { { std::unique_lock lock(m_mutex); if (chunk) { - if (--m_chunksPending == 0) - m_condOnSync.notify_one(); + m_chunksExecuted++; + m_condOnSync.notify_one(); chunk = DxvkCsChunkRef(); } diff --git a/src/dxvk/dxvk_cs.h b/src/dxvk/dxvk_cs.h index ca34fa23..b59873a7 100644 --- a/src/dxvk/dxvk_cs.h +++ b/src/dxvk/dxvk_cs.h @@ -379,7 +379,9 @@ namespace dxvk { class DxvkCsThread { public: - + + constexpr static uint64_t SynchronizeAll = ~0ull; + DxvkCsThread(const Rc& context); ~DxvkCsThread(); @@ -389,41 +391,43 @@ namespace dxvk { * Can be used to efficiently play back large * command lists recorded on another thread. * \param [in] chunk The chunk to dispatch + * \returns Sequence number of the submission */ - void dispatchChunk(DxvkCsChunkRef&& chunk); + uint64_t dispatchChunk(DxvkCsChunkRef&& chunk); /** * \brief Synchronizes with the thread * - * This waits for all chunks in the dispatch - * queue to be processed by the thread. Note - * that this does \e not implicitly call - * \ref flush. + * This waits for all chunks in the dispatch queue to + * be processed by the thread, up to the given sequence + * number. If the sequence number is 0, this will wait + * for all pending chunks to complete execution. + * \param [in] seq Sequence number to wait for. */ - void synchronize(); + void synchronize(uint64_t seq); /** - * \brief Checks whether the worker thread is busy - * - * Note that this information is only reliable if - * only the calling thread dispatches jobs to the - * worker queue and if the result is \c false. - * \returns \c true if there is still work to do + * \brief Retrieves last executed sequence number + * + * Can be used to avoid synchronization in some cases. + * \returns Sequence number of last executed chunk */ - bool isBusy() const { - return m_chunksPending.load() != 0; + uint64_t lastSequenceNumber() const { + return m_chunksExecuted.load(); } - + private: const Rc m_context; + + std::atomic m_chunksDispatched = { 0ull }; + std::atomic m_chunksExecuted = { 0ull }; std::atomic m_stopped = { false }; dxvk::mutex m_mutex; dxvk::condition_variable m_condOnAdd; dxvk::condition_variable m_condOnSync; std::queue m_chunksQueued; - std::atomic m_chunksPending = { 0u }; dxvk::thread m_thread; void threadFunc();