#pragma once #include #include #include #include #include #include #include #include "dxvk_pipemanager.h" #include "dxvk_renderpass.h" namespace dxvk { class DxvkDevice; /** * \brief State cache entry key * * Stores the shader keys for all * graphics shader stages. Used to * look up cached state entries. */ struct DxvkStateCacheKey { DxvkShaderKey vs; DxvkShaderKey tcs; DxvkShaderKey tes; DxvkShaderKey gs; DxvkShaderKey fs; DxvkShaderKey cs; bool eq(const DxvkStateCacheKey& key) const; size_t hash() const; }; /** * \brief State entry * * Stores the shaders used in a pipeline, as well * as the full state vector, including its render * pass format. This also includes a SHA-1 hash * that is used as a check sum to verify integrity. */ struct DxvkStateCacheEntry { DxvkStateCacheKey shaders; DxvkGraphicsPipelineStateInfo gpState; DxvkComputePipelineStateInfo cpState; DxvkRenderPassFormat format; Sha1Hash hash; }; /** * \brief State cache header * * Stores the state cache format version. If an * existing cache file is incompatible to the * current version, it will be discarded. */ struct DxvkStateCacheHeader { char magic[4] = { 'D', 'X', 'V', 'K' }; uint32_t version = 3; uint32_t entrySize = sizeof(DxvkStateCacheEntry); }; static_assert(sizeof(DxvkStateCacheHeader) == 12); /** * \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); 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::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( std::istream& stream, DxvkStateCacheEntry& entry) const; void writeCacheEntry( std::ostream& stream, DxvkStateCacheEntry& entry) const; bool convertEntryV2( DxvkStateCacheEntry& entry) const; void workerFunc(); void writerFunc(); std::string getCacheFileName() const; std::string getCacheDir() const; }; }