1
0
mirror of https://github.com/EduApps-CDG/OpenDX synced 2024-12-30 09:45:37 +01:00
OpenDX/src/dxvk/dxvk_cs.h
Philip Rebohle 3148155c35
[d3d11] Implemented CS chunk submission for deferred contexts
Changes were necessary due to the fact that chunks in deferred
contexts are not directly submitted to the CS thread.
2018-01-23 12:03:26 +01:00

185 lines
4.2 KiB
C++

#pragma once
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#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<typename T>
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 = 1024;
constexpr static size_t MaxBlockSize = 64 * MaxCommands;
public:
DxvkCsChunk();
~DxvkCsChunk();
/**
* \brief Number of commands recorded to the chunk
*
* Can be used to check whether the chunk needs to
* be dispatched or just to keep track of statistics.
*/
size_t commandCount() const {
return m_commandCount;
}
/**
* \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<typename T>
bool push(T& command) {
using FuncType = DxvkCsTypedCmd<T>;
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<DxvkCsCmd*, MaxCommands> 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 {
// Limit the number of chunks in the queue
// to prevent memory leaks, stuttering etc.
constexpr static uint32_t MaxChunksInFlight = 16;
public:
DxvkCsThread(const Rc<DxvkContext>& context);
~DxvkCsThread();
/**
* \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<DxvkCsChunk>&& 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.
*/
void synchronize();
private:
const Rc<DxvkContext> m_context;
std::atomic<bool> m_stopped = { false };
std::mutex m_mutex;
std::condition_variable m_condOnAdd;
std::condition_variable m_condOnSync;
std::queue<Rc<DxvkCsChunk>> m_chunksQueued;
std::thread m_thread;
uint32_t m_chunksPending = 0;
void threadFunc();
};
}