dxvk/src/dxvk/dxvk_pipemanager.h

313 lines
8.3 KiB
C++

#pragma once
#include <mutex>
#include <queue>
#include <unordered_map>
#include "dxvk_compute.h"
#include "dxvk_graphics.h"
#include "dxvk_state_cache.h"
namespace dxvk {
class DxvkDevice;
/**
* \brief Pipeline count
*
* Stores number of graphics and
* compute pipelines, individually.
*/
struct DxvkPipelineCount {
uint32_t numGraphicsPipelines;
uint32_t numGraphicsLibraries;
uint32_t numComputePipelines;
};
/**
* \brief Pipeline stats
*/
struct DxvkPipelineStats {
std::atomic<uint32_t> numGraphicsPipelines = { 0u };
std::atomic<uint32_t> numGraphicsLibraries = { 0u };
std::atomic<uint32_t> numComputePipelines = { 0u };
};
/**
* \brief Pipeline priority
*/
enum class DxvkPipelinePriority : uint32_t {
Normal = 0,
High = 1,
};
/**
* \brief Pipeline manager worker threads
*
* Spawns worker threads to compile shader pipeline
* libraries and optimized pipelines asynchronously.
*/
class DxvkPipelineWorkers {
public:
DxvkPipelineWorkers(
DxvkDevice* device);
~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
* \param [in] priority Pipeline priority
*/
void compilePipelineLibrary(
DxvkShaderPipelineLibrary* library,
DxvkPipelinePriority priority);
/**
* \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;
};
DxvkDevice* m_device;
std::atomic<uint64_t> m_pendingTasks = { 0ull };
dxvk::mutex m_queueLock;
dxvk::condition_variable m_queueCond;
dxvk::condition_variable m_queueCondPrioritized;
std::queue<PipelineLibraryEntry> m_queuedLibrariesPrioritized;
std::queue<PipelineLibraryEntry> m_queuedLibraries;
std::queue<PipelineEntry> m_queuedPipelines;
bool m_workersRunning = false;
std::vector<dxvk::thread> m_workers;
void startWorkers();
void runWorker();
void runWorkerPrioritized();
};
/**
* \brief Pipeline manager
*
* Creates and stores graphics pipelines and compute
* pipelines for each combination of shaders that is
* used within the application. This is necessary
* because DXVK does not expose the concept of shader
* pipeline objects to the client API.
*/
class DxvkPipelineManager {
friend class DxvkComputePipeline;
friend class DxvkGraphicsPipeline;
friend class DxvkShaderPipelineLibrary;
public:
DxvkPipelineManager(
DxvkDevice* device);
~DxvkPipelineManager();
/**
* \brief Retrieves a compute pipeline object
*
* If a pipeline for the given shader stage object
* already exists, it will be returned. Otherwise,
* a new pipeline will be created.
* \param [in] shaders Shaders for the pipeline
* \returns Compute pipeline object
*/
DxvkComputePipeline* createComputePipeline(
const DxvkComputePipelineShaders& shaders);
/**
* \brief Retrieves a graphics pipeline object
*
* If a pipeline for the given shader stage objects
* already exists, it will be returned. Otherwise,
* a new pipeline will be created.
* \param [in] shaders Shaders for the pipeline
* \returns Graphics pipeline object
*/
DxvkGraphicsPipeline* createGraphicsPipeline(
const DxvkGraphicsPipelineShaders& shaders);
/**
* \brief Retrieves a vertex input pipeline library
*
* \param [in] state Vertex input state
* \returns Pipeline library object
*/
DxvkGraphicsPipelineVertexInputLibrary* createVertexInputLibrary(
const DxvkGraphicsPipelineVertexInputState& state);
/**
* \brief Retrieves a fragment output pipeline library
*
* \param [in] state Fragment output state
* \returns Pipeline library object
*/
DxvkGraphicsPipelineFragmentOutputLibrary* createFragmentOutputLibrary(
const DxvkGraphicsPipelineFragmentOutputState& state);
/**
* \brief Registers a shader
*
* Starts compiling pipelines asynchronously
* in case the state cache contains state
* vectors for this shader.
* \param [in] shader Newly compiled shader
*/
void registerShader(
const Rc<DxvkShader>& shader);
/**
* \brief Prioritizes compilation of a given shader
*
* Adds the pipeline library for the given shader
* to the high-priority queue of the background
* workers to make sure it gets compiled quickly.
* \param [in] shader Newly compiled shader
*/
void requestCompileShader(
const Rc<DxvkShader>& shader);
/**
* \brief Retrieves total pipeline count
* \returns Number of compute/graphics pipelines
*/
DxvkPipelineCount getPipelineCount() const;
/**
* \brief Checks whether async compiler is busy
* \returns \c true if shaders are being compiled
*/
bool isCompilingShaders() const {
return m_workers.isBusy();
}
/**
* \brief Stops async compiler threads
*/
void stopWorkerThreads();
private:
DxvkDevice* m_device;
DxvkPipelineWorkers m_workers;
DxvkStateCache m_stateCache;
DxvkPipelineStats m_stats;
dxvk::mutex m_mutex;
std::unordered_map<
DxvkBindingSetLayoutKey,
DxvkBindingSetLayout,
DxvkHash, DxvkEq> m_descriptorSetLayouts;
std::unordered_map<
DxvkBindingLayout,
DxvkBindingLayoutObjects,
DxvkHash, DxvkEq> m_pipelineLayouts;
std::unordered_map<
DxvkGraphicsPipelineVertexInputState,
DxvkGraphicsPipelineVertexInputLibrary,
DxvkHash, DxvkEq> m_vertexInputLibraries;
std::unordered_map<
DxvkGraphicsPipelineFragmentOutputState,
DxvkGraphicsPipelineFragmentOutputLibrary,
DxvkHash, DxvkEq> m_fragmentOutputLibraries;
std::unordered_map<
DxvkShaderPipelineLibraryKey,
DxvkShaderPipelineLibrary,
DxvkHash, DxvkEq> m_shaderLibraries;
std::unordered_map<
DxvkComputePipelineShaders,
DxvkComputePipeline,
DxvkHash, DxvkEq> m_computePipelines;
std::unordered_map<
DxvkGraphicsPipelineShaders,
DxvkGraphicsPipeline,
DxvkHash, DxvkEq> m_graphicsPipelines;
DxvkBindingSetLayout* createDescriptorSetLayout(
const DxvkBindingSetLayoutKey& key);
DxvkBindingLayoutObjects* createPipelineLayout(
const DxvkBindingLayout& layout);
DxvkShaderPipelineLibrary* createPipelineLibrary(
const Rc<DxvkShader>& shader);
DxvkShaderPipelineLibrary* createNullFsPipelineLibrary();
DxvkShaderPipelineLibrary* findPipelineLibrary(
const Rc<DxvkShader>& shader);
DxvkShaderPipelineLibrary* findPipelineLibraryLocked(
const Rc<DxvkShader>& shader);
bool canPrecompileShader(
const Rc<DxvkShader>& shader) const;
};
}