#include "dxvk_cs.h" namespace dxvk { DxvkCsChunk::DxvkCsChunk() { } DxvkCsChunk::~DxvkCsChunk() { this->reset(); } void DxvkCsChunk::init(DxvkCsChunkFlags flags) { m_flags = flags; } void DxvkCsChunk::executeAll(DxvkContext* ctx) { auto cmd = m_head; if (m_flags.test(DxvkCsChunkFlag::SingleUse)) { m_commandOffset = 0; while (cmd != nullptr) { auto next = cmd->next(); cmd->exec(ctx); cmd->~DxvkCsCmd(); cmd = next; } m_head = nullptr; m_tail = nullptr; } else { while (cmd != nullptr) { cmd->exec(ctx); cmd = cmd->next(); } } } void DxvkCsChunk::reset() { auto cmd = m_head; while (cmd != nullptr) { auto next = cmd->next(); cmd->~DxvkCsCmd(); cmd = next; } m_head = nullptr; m_tail = nullptr; m_commandOffset = 0; } DxvkCsChunkPool::DxvkCsChunkPool() { } DxvkCsChunkPool::~DxvkCsChunkPool() { for (DxvkCsChunk* chunk : m_chunks) delete chunk; } DxvkCsChunk* DxvkCsChunkPool::allocChunk(DxvkCsChunkFlags flags) { DxvkCsChunk* chunk = nullptr; { std::lock_guard lock(m_mutex); if (m_chunks.size() != 0) { chunk = m_chunks.back(); m_chunks.pop_back(); } } if (!chunk) chunk = new DxvkCsChunk(); chunk->init(flags); return chunk; } void DxvkCsChunkPool::freeChunk(DxvkCsChunk* chunk) { chunk->reset(); std::lock_guard lock(m_mutex); m_chunks.push_back(chunk); } DxvkCsThread::DxvkCsThread( const Rc& device, const Rc& context) : m_device(device), m_context(context), m_thread([this] { threadFunc(); }) { } DxvkCsThread::~DxvkCsThread() { { std::unique_lock lock(m_mutex); m_stopped.store(true); } m_condOnAdd.notify_one(); m_thread.join(); } 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_condOnAdd.notify_one(); return seq; } 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(); auto t0 = dxvk::high_resolution_clock::now(); m_condOnSync.wait(lock, [this, seq] { return m_chunksExecuted.load() >= seq; }); auto t1 = dxvk::high_resolution_clock::now(); auto ticks = std::chrono::duration_cast(t1 - t0); m_device->addStatCtr(DxvkStatCounter::CsSyncCount, 1); m_device->addStatCtr(DxvkStatCounter::CsSyncTicks, ticks.count()); } } void DxvkCsThread::threadFunc() { env::setThreadName("dxvk-cs"); DxvkCsChunkRef chunk; try { while (!m_stopped.load()) { { std::unique_lock lock(m_mutex); if (chunk) { m_chunksExecuted++; m_condOnSync.notify_one(); chunk = DxvkCsChunkRef(); } if (m_chunksQueued.size() == 0) { m_condOnAdd.wait(lock, [this] { return (m_chunksQueued.size() != 0) || (m_stopped.load()); }); } if (m_chunksQueued.size() != 0) { chunk = std::move(m_chunksQueued.front()); m_chunksQueued.pop(); } } if (chunk) { m_context->addStatCtr(DxvkStatCounter::CsChunkCount, 1); chunk->executeAll(m_context.ptr()); } } } catch (const DxvkError& e) { Logger::err("Exception on CS thread!"); Logger::err(e.message()); } } }