diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index ff039126..c035e65d 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -886,6 +886,15 @@ namespace dxvk { } + void D3D11ImmediateContext::EmitCsChunkExternal( + DxvkCsChunkRef&& Chunk, + bool Synchronize) { + // Do not update the sequence number when emitting a chunk + // from an external source since that would break tracking + m_csThread.injectChunk(std::move(Chunk), Synchronize); + } + + void D3D11ImmediateContext::EmitCsChunk(DxvkCsChunkRef&& chunk) { m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk)); } diff --git a/src/d3d11/d3d11_context_imm.h b/src/d3d11/d3d11_context_imm.h index 3e683250..e4c6d9ab 100644 --- a/src/d3d11/d3d11_context_imm.h +++ b/src/d3d11/d3d11_context_imm.h @@ -97,6 +97,10 @@ namespace dxvk { return m_multithread.AcquireLock(); } + void EmitCsChunkExternal( + DxvkCsChunkRef&& Chunk, + bool Synchronize); + private: DxvkCsThread m_csThread; diff --git a/src/dxvk/dxvk_cs.cpp b/src/dxvk/dxvk_cs.cpp index 5788ba82..6a6716ab 100644 --- a/src/dxvk/dxvk_cs.cpp +++ b/src/dxvk/dxvk_cs.cpp @@ -125,6 +125,22 @@ namespace dxvk { m_condOnAdd.notify_one(); return seq; } + + + void DxvkCsThread::injectChunk(DxvkCsChunkRef&& chunk, bool synchronize) { + std::unique_lock lock(m_mutex); + + uint64_t timeline = ++m_chunksInjectedCount; + m_chunksInjected.push_back(std::move(chunk)); + + m_condOnAdd.notify_one(); + + if (synchronize) { + m_condOnSync.wait(lock, [this, timeline] { + return m_chunksInjectedComplete.load() >= timeline; + }); + } + } void DxvkCsThread::synchronize(uint64_t seq) { @@ -163,14 +179,18 @@ namespace dxvk { try { while (!m_stopped.load()) { + bool injected = false; + { std::unique_lock lock(m_mutex); m_condOnAdd.wait(lock, [this] { return (!m_chunksQueued.empty()) + || (!m_chunksInjected.empty()) || (m_stopped.load()); }); - std::swap(chunks, m_chunksQueued); + injected = !m_chunksInjected.empty(); + std::swap(chunks, injected ? m_chunksInjected : m_chunksQueued); } for (auto& chunk : chunks) { @@ -182,7 +202,7 @@ namespace dxvk { // will only ever be contested if synchronization is // actually necessary. { std::unique_lock lock(m_counterMutex); - m_chunksExecuted += 1; + (injected ? m_chunksInjectedComplete : m_chunksExecuted) += 1u; m_condOnSync.notify_one(); } diff --git a/src/dxvk/dxvk_cs.h b/src/dxvk/dxvk_cs.h index ae20746a..463cd742 100644 --- a/src/dxvk/dxvk_cs.h +++ b/src/dxvk/dxvk_cs.h @@ -183,6 +183,11 @@ namespace dxvk { return true; } + template + bool push(T&& command) { + return push(command); + } + /** * \brief Adds a command with data to the chunk * @@ -398,7 +403,20 @@ namespace dxvk { * \returns Sequence number of the submission */ uint64_t dispatchChunk(DxvkCsChunkRef&& chunk); - + + /** + * \brief Injects chunk into the command stream + * + * This is meant to be used when serialized execution is required + * from a thread other than the main thread recording rendering + * commands. The context can still be safely accessed, but chunks + * will not be executed in any particular oder. These chunks also + * do not contribute to the main timeline. + * \param [in] chunk The chunk to dispatch + * \param [in] synchronize Whether to wait for execution to complete + */ + void injectChunk(DxvkCsChunkRef&& chunk, bool synchronize); + /** * \brief Synchronizes with the thread * @@ -428,12 +446,16 @@ namespace dxvk { dxvk::mutex m_counterMutex; std::atomic m_chunksDispatched = { 0ull }; std::atomic m_chunksExecuted = { 0ull }; + + std::atomic m_chunksInjectedCount = { 0ull }; + std::atomic m_chunksInjectedComplete = { 0ull }; std::atomic m_stopped = { false }; dxvk::mutex m_mutex; dxvk::condition_variable m_condOnAdd; dxvk::condition_variable m_condOnSync; std::vector m_chunksQueued; + std::vector m_chunksInjected; dxvk::thread m_thread; void threadFunc();