#pragma once #include #include #include #include #include #include #include #include "dxvk_state_cache_types.h" namespace dxvk { class DxvkDevice; /** * \brief State cache * * The shader state cache stores state vectors and * render pass formats of all pipelines used in a * game, which allows DXVK to compile them ahead * of time instead of compiling them on the first * draw. */ class DxvkStateCache : public RcObject { public: DxvkStateCache( const DxvkDevice* device, DxvkPipelineManager* pipeManager, DxvkRenderPassPool* passManager); ~DxvkStateCache(); /** * Adds a graphics pipeline to the cache * * If the pipeline is not already cached, this * will write a new pipeline to the cache file. * \param [in] shaders Shader keys * \param [in] state Graphics pipeline state * \param [in] format Render pass format */ void addGraphicsPipeline( const DxvkStateCacheKey& shaders, const DxvkGraphicsPipelineStateInfo& state, const DxvkRenderPassFormat& format); /** * Adds a compute pipeline to the cache * * If the pipeline is not already cached, this * will write a new pipeline to the cache file. * \param [in] shaders Shader keys * \param [in] state Compute pipeline state */ void addComputePipeline( const DxvkStateCacheKey& shaders, const DxvkComputePipelineStateInfo& state); /** * \brief Registers a newly compiled shader * * Makes the shader available to the pipeline * compiler, and starts compiling all pipelines * for which all shaders become available. * \param [in] shader The shader to add */ void registerShader( const Rc& shader); /** * \brief Checks whether compiler threads are busy * \returns \c true if we're compiling shaders */ bool isCompilingShaders() { return m_workerBusy.load() > 0; } private: using WriterItem = DxvkStateCacheEntry; struct WorkerItem { Rc vs; Rc tcs; Rc tes; Rc gs; Rc fs; Rc cs; }; DxvkPipelineManager* m_pipeManager; DxvkRenderPassPool* m_passManager; std::vector m_entries; std::atomic m_stopThreads = { false }; std::mutex m_entryLock; std::unordered_multimap< DxvkStateCacheKey, size_t, DxvkHash, DxvkEq> m_entryMap; std::unordered_multimap< DxvkShaderKey, DxvkStateCacheKey, DxvkHash, DxvkEq> m_pipelineMap; std::unordered_map< DxvkShaderKey, Rc, DxvkHash, DxvkEq> m_shaderMap; std::mutex m_workerLock; std::condition_variable m_workerCond; std::queue m_workerQueue; std::atomic m_workerBusy; std::vector m_workerThreads; std::mutex m_writerLock; std::condition_variable m_writerCond; std::queue m_writerQueue; dxvk::thread m_writerThread; DxvkShaderKey getShaderKey( const Rc& shader) const; bool getShaderByKey( const DxvkShaderKey& key, Rc& shader) const; void mapPipelineToEntry( const DxvkStateCacheKey& key, size_t entryId); void mapShaderToPipeline( const DxvkShaderKey& shader, const DxvkStateCacheKey& key); void compilePipelines( const WorkerItem& item); bool readCacheFile(); bool readCacheHeader( std::istream& stream, DxvkStateCacheHeader& header) const; bool readCacheEntry( uint32_t version, std::istream& stream, DxvkStateCacheEntry& entry) const; void writeCacheEntry( std::ostream& stream, DxvkStateCacheEntry& entry) const; bool convertEntryV2( DxvkStateCacheEntryV4& entry) const; bool convertEntryV4( const DxvkStateCacheEntryV4& in, DxvkStateCacheEntry& out) const; void workerFunc(); void writerFunc(); std::string getCacheFileName() const; std::string getCacheDir() const; }; }