[dxvk] Introduce sequence numbers for CS submissions

This commit is contained in:
Philip Rebohle 2022-02-08 22:36:02 +01:00
parent bc137fdf37
commit 37f3d9208b
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
4 changed files with 43 additions and 31 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -111,22 +111,32 @@ namespace dxvk {
}
void DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) {
uint64_t DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) {
uint64_t seq;
{ std::unique_lock<dxvk::mutex> 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<dxvk::mutex> 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<dxvk::mutex> 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<dxvk::mutex> lock(m_mutex);
if (chunk) {
if (--m_chunksPending == 0)
m_condOnSync.notify_one();
m_chunksExecuted++;
m_condOnSync.notify_one();
chunk = DxvkCsChunkRef();
}

View File

@ -379,7 +379,9 @@ namespace dxvk {
class DxvkCsThread {
public:
constexpr static uint64_t SynchronizeAll = ~0ull;
DxvkCsThread(const Rc<DxvkContext>& 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<DxvkContext> m_context;
std::atomic<uint64_t> m_chunksDispatched = { 0ull };
std::atomic<uint64_t> m_chunksExecuted = { 0ull };
std::atomic<bool> m_stopped = { false };
dxvk::mutex m_mutex;
dxvk::condition_variable m_condOnAdd;
dxvk::condition_variable m_condOnSync;
std::queue<DxvkCsChunkRef> m_chunksQueued;
std::atomic<uint32_t> m_chunksPending = { 0u };
dxvk::thread m_thread;
void threadFunc();