mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-20 19:54:19 +01:00
[dxvk] Introduce new pipeline manager worker thread system
This commit is contained in:
parent
1c573a7fd5
commit
02e6a212bb
@ -1,9 +1,166 @@
|
||||
#include <optional>
|
||||
|
||||
#include "dxvk_device.h"
|
||||
#include "dxvk_pipemanager.h"
|
||||
#include "dxvk_state_cache.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkPipelineWorkers::DxvkPipelineWorkers(
|
||||
DxvkDevice* device,
|
||||
DxvkPipelineCache* cache)
|
||||
: m_cache(cache) {
|
||||
// Use a reasonably large number of threads for compiling, but
|
||||
// leave some cores to the application to avoid excessive stutter
|
||||
uint32_t numCpuCores = dxvk::thread::hardware_concurrency();
|
||||
m_workerCount = ((std::max(1u, numCpuCores) - 1) * 5) / 7;
|
||||
|
||||
if (m_workerCount < 1) m_workerCount = 1;
|
||||
if (m_workerCount > 32) m_workerCount = 32;
|
||||
|
||||
if (device->config().numCompilerThreads > 0)
|
||||
m_workerCount = device->config().numCompilerThreads;
|
||||
}
|
||||
|
||||
|
||||
DxvkPipelineWorkers::~DxvkPipelineWorkers() {
|
||||
this->stopWorkers();
|
||||
}
|
||||
|
||||
|
||||
void DxvkPipelineWorkers::compilePipelineLibrary(
|
||||
DxvkShaderPipelineLibrary* library) {
|
||||
std::unique_lock lock(m_queueLock);
|
||||
this->startWorkers();
|
||||
|
||||
m_pendingTasks += 1;
|
||||
|
||||
PipelineLibraryEntry e = { };
|
||||
e.pipelineLibrary = library;
|
||||
|
||||
m_queuedLibraries.push(e);
|
||||
m_queueCond.notify_one();
|
||||
}
|
||||
|
||||
|
||||
void DxvkPipelineWorkers::compileComputePipeline(
|
||||
DxvkComputePipeline* pipeline,
|
||||
const DxvkComputePipelineStateInfo& state) {
|
||||
std::unique_lock lock(m_queueLock);
|
||||
this->startWorkers();
|
||||
|
||||
m_pendingTasks += 1;
|
||||
|
||||
PipelineEntry e = { };
|
||||
e.computePipeline = pipeline;
|
||||
e.computeState = state;
|
||||
|
||||
m_queuedPipelines.push(e);
|
||||
m_queueCond.notify_one();
|
||||
}
|
||||
|
||||
|
||||
void DxvkPipelineWorkers::compileGraphicsPipeline(
|
||||
DxvkGraphicsPipeline* pipeline,
|
||||
const DxvkGraphicsPipelineStateInfo& state) {
|
||||
std::unique_lock lock(m_queueLock);
|
||||
this->startWorkers();
|
||||
|
||||
m_pendingTasks += 1;
|
||||
|
||||
PipelineEntry e = { };
|
||||
e.graphicsPipeline = pipeline;
|
||||
e.graphicsState = state;
|
||||
|
||||
m_queuedPipelines.push(e);
|
||||
m_queueCond.notify_one();
|
||||
}
|
||||
|
||||
|
||||
bool DxvkPipelineWorkers::isBusy() const {
|
||||
return m_pendingTasks.load() != 0ull;
|
||||
}
|
||||
|
||||
|
||||
void DxvkPipelineWorkers::stopWorkers() {
|
||||
{ std::unique_lock lock(m_queueLock);
|
||||
|
||||
if (!m_workersRunning)
|
||||
return;
|
||||
|
||||
m_workersRunning = false;
|
||||
m_queueCond.notify_all();
|
||||
}
|
||||
|
||||
for (auto& worker : m_workers)
|
||||
worker.join();
|
||||
|
||||
m_workers.clear();
|
||||
}
|
||||
|
||||
|
||||
void DxvkPipelineWorkers::startWorkers() {
|
||||
if (!m_workersRunning) {
|
||||
m_workersRunning = true;
|
||||
|
||||
Logger::info(str::format("DXVK: Using ", m_workerCount, " compiler threads"));
|
||||
m_workers.resize(m_workerCount);
|
||||
|
||||
for (auto& worker : m_workers) {
|
||||
worker = dxvk::thread([this] { runWorker(); });
|
||||
worker.set_priority(ThreadPriority::Lowest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxvkPipelineWorkers::runWorker() {
|
||||
env::setThreadName("dxvk-shader");
|
||||
|
||||
while (true) {
|
||||
std::optional<PipelineEntry> p;
|
||||
std::optional<PipelineLibraryEntry> l;
|
||||
|
||||
{ std::unique_lock lock(m_queueLock);
|
||||
|
||||
m_queueCond.wait(lock, [this] {
|
||||
return !m_workersRunning
|
||||
|| !m_queuedLibraries.empty()
|
||||
|| !m_queuedPipelines.empty();
|
||||
});
|
||||
|
||||
if (!m_workersRunning) {
|
||||
// Skip pending work, exiting early is
|
||||
// more important in this case.
|
||||
break;
|
||||
} else if (!m_queuedLibraries.empty()) {
|
||||
l = m_queuedLibraries.front();
|
||||
m_queuedLibraries.pop();
|
||||
} else if (!m_queuedPipelines.empty()) {
|
||||
p = m_queuedPipelines.front();
|
||||
m_queuedPipelines.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (l) {
|
||||
if (l->pipelineLibrary)
|
||||
l->pipelineLibrary->compilePipeline(m_cache->handle());
|
||||
|
||||
m_pendingTasks -= 1;
|
||||
}
|
||||
|
||||
if (p) {
|
||||
if (p->computePipeline)
|
||||
p->computePipeline->compilePipeline(p->computeState);
|
||||
else if (p->graphicsPipeline)
|
||||
p->graphicsPipeline->compilePipeline(p->graphicsState);
|
||||
|
||||
m_pendingTasks -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxvkPipelineManager::DxvkPipelineManager(
|
||||
DxvkDevice* device)
|
||||
: m_device (device),
|
||||
|
@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "dxvk_compute.h"
|
||||
@ -10,7 +11,7 @@
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class DxvkStateCache;
|
||||
class DxvkDevice;
|
||||
|
||||
/**
|
||||
* \brief Pipeline count
|
||||
@ -22,7 +23,101 @@ namespace dxvk {
|
||||
uint32_t numGraphicsPipelines;
|
||||
uint32_t numComputePipelines;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Pipeline manager worker threads
|
||||
*
|
||||
* Spawns worker threads to compile shader pipeline
|
||||
* libraries and optimized pipelines asynchronously.
|
||||
*/
|
||||
class DxvkPipelineWorkers {
|
||||
|
||||
public:
|
||||
|
||||
DxvkPipelineWorkers(
|
||||
DxvkDevice* device,
|
||||
DxvkPipelineCache* cache);
|
||||
|
||||
~DxvkPipelineWorkers();
|
||||
|
||||
/**
|
||||
* \brief Compiles a pipeline library
|
||||
*
|
||||
* Asynchronously compiles a basic variant of
|
||||
* the pipeline with default compile arguments.
|
||||
* Note that pipeline libraries are high priority.
|
||||
* \param [in] library The pipeline library
|
||||
*/
|
||||
void compilePipelineLibrary(
|
||||
DxvkShaderPipelineLibrary* library);
|
||||
|
||||
/**
|
||||
* \brief Compiles an optimized compute pipeline
|
||||
*
|
||||
* \param [in] pipeline Compute pipeline
|
||||
* \param [in] state Pipeline state
|
||||
*/
|
||||
void compileComputePipeline(
|
||||
DxvkComputePipeline* pipeline,
|
||||
const DxvkComputePipelineStateInfo& state);
|
||||
|
||||
/**
|
||||
* \brief Compiles an optimized graphics pipeline
|
||||
*
|
||||
* \param [in] pipeline Compute pipeline
|
||||
* \param [in] state Pipeline state
|
||||
*/
|
||||
void compileGraphicsPipeline(
|
||||
DxvkGraphicsPipeline* pipeline,
|
||||
const DxvkGraphicsPipelineStateInfo& state);
|
||||
|
||||
/**
|
||||
* \brief Checks whether workers are busy
|
||||
* \returns \c true if there is unfinished work
|
||||
*/
|
||||
bool isBusy() const;
|
||||
|
||||
/**
|
||||
* \brief Stops all worker threads
|
||||
*
|
||||
* Stops threads and waits for their current work
|
||||
* to complete. Queued work will be discarded.
|
||||
*/
|
||||
void stopWorkers();
|
||||
|
||||
private:
|
||||
|
||||
struct PipelineEntry {
|
||||
DxvkComputePipeline* computePipeline;
|
||||
DxvkGraphicsPipeline* graphicsPipeline;
|
||||
DxvkComputePipelineStateInfo computeState;
|
||||
DxvkGraphicsPipelineStateInfo graphicsState;
|
||||
};
|
||||
|
||||
struct PipelineLibraryEntry {
|
||||
DxvkShaderPipelineLibrary* pipelineLibrary;
|
||||
};
|
||||
|
||||
DxvkPipelineCache* m_cache;
|
||||
std::atomic<uint64_t> m_pendingTasks = { 0ull };
|
||||
|
||||
dxvk::mutex m_queueLock;
|
||||
dxvk::condition_variable m_queueCond;
|
||||
|
||||
std::queue<PipelineLibraryEntry> m_queuedLibraries;
|
||||
std::queue<PipelineEntry> m_queuedPipelines;
|
||||
|
||||
uint32_t m_workerCount = 0;
|
||||
bool m_workersRunning = false;
|
||||
std::vector<dxvk::thread> m_workers;
|
||||
|
||||
void startWorkers();
|
||||
|
||||
void runWorker();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Pipeline manager
|
||||
|
Loading…
x
Reference in New Issue
Block a user