// Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include #include #include #include #include "common/assert.h" #include "common/common_types.h" #include "core/file_sys/vfs_vector.h" #include "video_core/renderer_opengl/gl_shader_gen.h" #include "video_core/shader/const_buffer_locker.h" namespace Core { class System; } namespace FileUtil { class IOFile; } namespace OpenGL { struct ShaderDiskCacheUsage; struct ShaderDiskCacheDump; using ProgramCode = std::vector; using ShaderDumpsMap = std::unordered_map; /// Allocated bindings used by an OpenGL shader program struct BaseBindings { u32 cbuf{}; u32 gmem{}; u32 sampler{}; u32 image{}; bool operator==(const BaseBindings& rhs) const noexcept { return std::tie(cbuf, gmem, sampler, image) == std::tie(rhs.cbuf, rhs.gmem, rhs.sampler, rhs.image); } bool operator!=(const BaseBindings& rhs) const noexcept { return !operator==(rhs); } }; static_assert(std::is_trivially_copyable_v); /// Describes the different variants a program can be compiled with. struct ProgramVariant final { ProgramVariant() = default; /// Graphics constructor. explicit constexpr ProgramVariant(BaseBindings base_bindings, GLenum primitive_mode) noexcept : base_bindings{base_bindings}, primitive_mode{primitive_mode} {} /// Compute constructor. explicit constexpr ProgramVariant(u32 block_x, u32 block_y, u32 block_z, u32 shared_memory_size, u32 local_memory_size) noexcept : block_x{block_x}, block_y{static_cast(block_y)}, block_z{static_cast(block_z)}, shared_memory_size{shared_memory_size}, local_memory_size{local_memory_size} {} // Graphics specific parameters. BaseBindings base_bindings{}; GLenum primitive_mode{}; // Compute specific parameters. u32 block_x{}; u16 block_y{}; u16 block_z{}; u32 shared_memory_size{}; u32 local_memory_size{}; bool operator==(const ProgramVariant& rhs) const noexcept { return std::tie(base_bindings, primitive_mode, block_x, block_y, block_z, shared_memory_size, local_memory_size) == std::tie(rhs.base_bindings, rhs.primitive_mode, rhs.block_x, rhs.block_y, rhs.block_z, rhs.shared_memory_size, rhs.local_memory_size); } bool operator!=(const ProgramVariant& rhs) const noexcept { return !operator==(rhs); } }; static_assert(std::is_trivially_copyable_v); /// Describes how a shader is used. struct ShaderDiskCacheUsage { u64 unique_identifier{}; ProgramVariant variant; VideoCommon::Shader::KeyMap keys; VideoCommon::Shader::BoundSamplerMap bound_samplers; VideoCommon::Shader::BindlessSamplerMap bindless_samplers; bool operator==(const ShaderDiskCacheUsage& rhs) const { return std::tie(unique_identifier, variant, keys, bound_samplers, bindless_samplers) == std::tie(rhs.unique_identifier, rhs.variant, rhs.keys, rhs.bound_samplers, rhs.bindless_samplers); } bool operator!=(const ShaderDiskCacheUsage& rhs) const { return !operator==(rhs); } }; } // namespace OpenGL namespace std { template <> struct hash { std::size_t operator()(const OpenGL::BaseBindings& bindings) const noexcept { return static_cast(bindings.cbuf) ^ (static_cast(bindings.gmem) << 8) ^ (static_cast(bindings.sampler) << 16) ^ (static_cast(bindings.image) << 24); } }; template <> struct hash { std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept { return std::hash{}(variant.base_bindings) ^ (static_cast(variant.primitive_mode) << 6) ^ static_cast(variant.block_x) ^ (static_cast(variant.block_y) << 32) ^ (static_cast(variant.block_z) << 48) ^ (static_cast(variant.shared_memory_size) << 16) ^ (static_cast(variant.local_memory_size) << 36); } }; template <> struct hash { std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept { return static_cast(usage.unique_identifier) ^ std::hash{}(usage.variant); } }; } // namespace std namespace OpenGL { /// Describes a shader how it's used by the guest GPU class ShaderDiskCacheRaw { public: explicit ShaderDiskCacheRaw(u64 unique_identifier, ProgramType program_type, ProgramCode program_code, ProgramCode program_code_b = {}); ShaderDiskCacheRaw(); ~ShaderDiskCacheRaw(); bool Load(FileUtil::IOFile& file); bool Save(FileUtil::IOFile& file) const; u64 GetUniqueIdentifier() const { return unique_identifier; } bool HasProgramA() const { return program_type == ProgramType::VertexA; } ProgramType GetProgramType() const { return program_type; } const ProgramCode& GetProgramCode() const { return program_code; } const ProgramCode& GetProgramCodeB() const { return program_code_b; } private: u64 unique_identifier{}; ProgramType program_type{}; ProgramCode program_code; ProgramCode program_code_b; }; /// Contains an OpenGL dumped binary program struct ShaderDiskCacheDump { GLenum binary_format{}; std::vector binary; }; class ShaderDiskCacheOpenGL { public: explicit ShaderDiskCacheOpenGL(Core::System& system); ~ShaderDiskCacheOpenGL(); /// Loads transferable cache. If file has a old version or on failure, it deletes the file. std::optional, std::vector>> LoadTransferable(); /// Loads current game's precompiled cache. Invalidates on failure. std::unordered_map LoadPrecompiled(); /// Removes the transferable (and precompiled) cache file. void InvalidateTransferable(); /// Removes the precompiled cache file and clears virtual precompiled cache file. void InvalidatePrecompiled(); /// Saves a raw dump to the transferable file. Checks for collisions. void SaveRaw(const ShaderDiskCacheRaw& entry); /// Saves shader usage to the transferable file. Does not check for collisions. void SaveUsage(const ShaderDiskCacheUsage& usage); /// Saves a dump entry to the precompiled file. Does not check for collisions. void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); /// Serializes virtual precompiled shader cache file to real file void SaveVirtualPrecompiledFile(); private: /// Loads the transferable cache. Returns empty on failure. std::optional> LoadPrecompiledFile(FileUtil::IOFile& file); /// Opens current game's transferable file and write it's header if it doesn't exist FileUtil::IOFile AppendTransferableFile() const; /// Save precompiled header to precompiled_cache_in_memory void SavePrecompiledHeaderToVirtualPrecompiledCache(); /// Create shader disk cache directories. Returns true on success. bool EnsureDirectories() const; /// Gets current game's transferable file path std::string GetTransferablePath() const; /// Gets current game's precompiled file path std::string GetPrecompiledPath() const; /// Get user's transferable directory path std::string GetTransferableDir() const; /// Get user's precompiled directory path std::string GetPrecompiledDir() const; /// Get user's shader directory path std::string GetBaseDir() const; /// Get current game's title id std::string GetTitleID() const; template bool SaveArrayToPrecompiled(const T* data, std::size_t length) { const std::size_t write_length = precompiled_cache_virtual_file.WriteArray( data, length, precompiled_cache_virtual_file_offset); precompiled_cache_virtual_file_offset += write_length; return write_length == sizeof(T) * length; } template bool LoadArrayFromPrecompiled(T* data, std::size_t length) { const std::size_t read_length = precompiled_cache_virtual_file.ReadArray( data, length, precompiled_cache_virtual_file_offset); precompiled_cache_virtual_file_offset += read_length; return read_length == sizeof(T) * length; } template bool SaveObjectToPrecompiled(const T& object) { return SaveArrayToPrecompiled(&object, 1); } bool SaveObjectToPrecompiled(bool object) { const auto value = static_cast(object); return SaveArrayToPrecompiled(&value, 1); } template bool LoadObjectFromPrecompiled(T& object) { return LoadArrayFromPrecompiled(&object, 1); } Core::System& system; // Stores whole precompiled cache which will be read from or saved to the precompiled chache // file FileSys::VectorVfsFile precompiled_cache_virtual_file; // Stores the current offset of the precompiled cache file for IO purposes std::size_t precompiled_cache_virtual_file_offset = 0; // Stored transferable shaders std::unordered_map> transferable; // The cache has been loaded at boot bool is_usable{}; }; } // namespace OpenGL