diff --git a/src/d3d11/d3d11_context.h b/src/d3d11/d3d11_context.h index 38037d9d..e228641b 100644 --- a/src/d3d11/d3d11_context.h +++ b/src/d3d11/d3d11_context.h @@ -1,12 +1,12 @@ #pragma once +#include "../dxvk/dxvk_adapter.h" +#include "../dxvk/dxvk_device.h" + #include "d3d11_context_state.h" #include "d3d11_device_child.h" #include "d3d11_view.h" -#include -#include - namespace dxvk { class D3D11Device; diff --git a/src/dxvk/dxvk_cs.cpp b/src/dxvk/dxvk_cs.cpp new file mode 100644 index 00000000..cefae9c6 --- /dev/null +++ b/src/dxvk/dxvk_cs.cpp @@ -0,0 +1,90 @@ +#include "dxvk_cs.h" + +namespace dxvk { + + DxvkCsChunk::DxvkCsChunk() { + + } + + + DxvkCsChunk::~DxvkCsChunk() { + for (size_t i = 0; i < m_commandCount; i++) + m_commandList[i]->~DxvkCsCmd(); + } + + + void DxvkCsChunk::executeAll(DxvkContext* ctx) { + for (size_t i = 0; i < m_commandCount; i++) { + m_commandList[i]->exec(ctx); + m_commandList[i]->~DxvkCsCmd(); + } + + m_commandCount = 0; + m_commandOffset = 0; + } + + + DxvkCsThread::DxvkCsThread(const Rc& context) + : m_context(context), + m_curChunk(new DxvkCsChunk()), + m_thread([this] { threadFunc(); }) { + + } + + + DxvkCsThread::~DxvkCsThread() { + m_stopped.store(true); + m_condOnAdd.notify_one(); + m_thread.join(); + } + + + void DxvkCsThread::dispatchChunk(Rc&& chunk) { + { std::lock_guard lock(m_mutex); + m_chunks.push(std::move(m_curChunk)); + } + + m_condOnAdd.notify_one(); + } + + + void DxvkCsThread::flush() { + dispatchChunk(std::move(m_curChunk)); + m_curChunk = new DxvkCsChunk(); + } + + + void DxvkCsThread::synchronize() { + std::unique_lock lock(m_mutex); + + m_condOnSync.wait(lock, [this] { + return m_chunks.size() == 0; + }); + } + + + void DxvkCsThread::threadFunc() { + while (!m_stopped.load()) { + Rc chunk; + + { std::unique_lock lock(m_mutex); + + m_condOnAdd.wait(lock, [this] { + return m_stopped.load() || (m_chunks.size() != 0); + }); + + if (m_chunks.size() != 0) { + chunk = std::move(m_chunks.front()); + m_chunks.pop(); + + if (m_chunks.size() == 0) + m_condOnSync.notify_one(); + } + } + + if (chunk != nullptr) + chunk->executeAll(m_context.ptr()); + } + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_cs.h b/src/dxvk/dxvk_cs.h new file mode 100644 index 00000000..e74ddb4e --- /dev/null +++ b/src/dxvk/dxvk_cs.h @@ -0,0 +1,198 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "dxvk_context.h" + +namespace dxvk { + + /** + * \brief Command stream operation + * + * An abstract representation of an operation + * that can be recorded into a command list. + */ + class DxvkCsCmd { + + public: + + virtual ~DxvkCsCmd() { } + + /** + * \brief Executes embedded commands + * \param [in] ctx The target context + */ + virtual void exec(DxvkContext* ctx) const = 0; + + }; + + + /** + * \brief Typed command + * + * Stores a function object which is + * used to execute an embedded command. + */ + template + class alignas(16) DxvkCsTypedCmd : public DxvkCsCmd { + + public: + + DxvkCsTypedCmd(T&& cmd) + : m_command(std::move(cmd)) { } + + DxvkCsTypedCmd (DxvkCsTypedCmd&&) = delete; + DxvkCsTypedCmd& operator = (DxvkCsTypedCmd&&) = delete; + + void exec(DxvkContext* ctx) const { + m_command(ctx); + } + + private: + + T m_command; + + }; + + + /** + * \brief Command chunk + * + * Stores a list of commands. + */ + class DxvkCsChunk : public RcObject { + constexpr static size_t MaxCommands = 64; + constexpr static size_t MaxBlockSize = 64 * MaxCommands; + public: + + DxvkCsChunk(); + ~DxvkCsChunk(); + + /** + * \brief Tries to add a command to the chunk + * + * If the given command can be added to the chunk, it + * will be consumed. Otherwise, a new chunk must be + * created which is large enough to hold the command. + * \param [in] command The command to add + * \returns \c true on success, \c false if + * a new chunk needs to be allocated + */ + template + bool push(T& command) { + using FuncType = DxvkCsTypedCmd; + + if (m_commandCount >= MaxCommands + || m_commandOffset + sizeof(FuncType) > MaxBlockSize) + return false; + + m_commandList[m_commandCount] = + new (m_data + m_commandOffset) + FuncType(std::move(command)); + + m_commandCount += 1; + m_commandOffset += sizeof(FuncType); + return true; + } + + /** + * \brief Executes all commands + * + * This will also reset the chunk + * so that it can be reused. + * \param [in] ctx The context + */ + void executeAll(DxvkContext* ctx); + + private: + + size_t m_commandCount = 0; + size_t m_commandOffset = 0; + + std::array m_commandList; + + alignas(64) + char m_data[MaxBlockSize]; + + }; + + + /** + * \brief Command stream thread + * + * Spawns a thread that will execute + * commands on a DXVK context. + */ + class DxvkCsThread { + + public: + + DxvkCsThread(const Rc& context); + ~DxvkCsThread(); + + /** + * \brief Dispatches a new command + * + * Adds the command to the current chunk and + * dispatches the chunk in case it is full. + * \param [in] command The command + */ + template + void dispatch(T&& command) { + while (!m_curChunk->push(command)) + this->flush(); + } + + /** + * \brief Dispatches an entire chunk + * + * Can be used to efficiently play back large + * command lists recorded on another thread. + * \param [in] chunk The chunk to dispatch + */ + void dispatchChunk(Rc&& chunk); + + /** + * \brief Dispatches current chunk + * + * Adds the current chunk to the dispatch + * queue and makes an empty chunk current. + * Call this before \ref synchronize. + */ + void flush(); + + /** + * \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. + */ + void synchronize(); + + private: + + const Rc m_context; + + // Chunk that is being recorded + Rc m_curChunk; + + // Chunks that are executing + std::atomic m_stopped = { false }; + std::mutex m_mutex; + std::condition_variable m_condOnAdd; + std::condition_variable m_condOnSync; + std::queue> m_chunks; + + std::thread m_thread; + + void threadFunc(); + + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index d91c9916..fa0c7209 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -5,6 +5,7 @@ dxvk_src = files([ 'dxvk_cmdlist.cpp', 'dxvk_compute.cpp', 'dxvk_context.cpp', + 'dxvk_cs.cpp', 'dxvk_data.cpp', 'dxvk_descriptor.cpp', 'dxvk_device.cpp',