From 2bc6a699dc53baf55b0ccbb40750a40036ee184f Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 15 Jan 2019 15:22:25 -0300 Subject: [PATCH] gl_shader_disk_cache: Guard reads and writes against failure --- .../renderer_opengl/gl_shader_disk_cache.cpp | 531 +++++++++++------- .../renderer_opengl/gl_shader_disk_cache.h | 30 +- 2 files changed, 342 insertions(+), 219 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 5157e319a..f8bdb7779 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -24,6 +24,8 @@ namespace OpenGL { +using ShaderCacheVersionHash = std::array; + enum class TransferableEntryKind : u32 { Raw, Usage, @@ -35,7 +37,6 @@ enum class PrecompiledEntryKind : u32 { }; constexpr u32 NativeVersion = 1; -constexpr u32 ShaderHashSize = 64; // Making sure sizes doesn't change by accident static_assert(sizeof(BaseBindings) == 12); @@ -46,10 +47,11 @@ std::string GetTitleID() { return fmt::format("{:016X}", Core::CurrentProcess()->GetTitleID()); } -std::string GetShaderHash() { - std::array hash{}; - std::strncpy(hash.data(), Common::g_shader_cache_version, ShaderHashSize); - return std::string(hash.data(), hash.size()); +ShaderCacheVersionHash GetShaderCacheVersionHash() { + ShaderCacheVersionHash hash{}; + const std::size_t length = std::min(std::strlen(Common::g_shader_cache_version), hash.size()); + std::memcpy(hash.data(), Common::g_shader_cache_version, length); + return hash; } template @@ -82,24 +84,6 @@ std::vector DecompressData(const std::vector& compressed, std::size_t un } } // namespace -ShaderDiskCacheRaw::ShaderDiskCacheRaw(FileUtil::IOFile& file) { - file.ReadBytes(&unique_identifier, sizeof(u64)); - file.ReadBytes(&program_type, sizeof(u32)); - - u32 program_code_size{}; - u32 program_code_size_b{}; - file.ReadBytes(&program_code_size, sizeof(u32)); - file.ReadBytes(&program_code_size_b, sizeof(u32)); - - program_code.resize(program_code_size); - program_code_b.resize(program_code_size_b); - - file.ReadArray(program_code.data(), program_code_size); - if (HasProgramA()) { - file.ReadArray(program_code_b.data(), program_code_size_b); - } -} - ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, u32 program_code_size, u32 program_code_size_b, ProgramCode program_code, ProgramCode program_code_b) @@ -107,25 +91,57 @@ ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderPro program_code_size{program_code_size}, program_code_size_b{program_code_size_b}, program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {} +ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default; + ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default; -void ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { - file.WriteObject(unique_identifier); - file.WriteObject(static_cast(program_type)); - file.WriteObject(program_code_size); - file.WriteObject(program_code_size_b); - - file.WriteArray(program_code.data(), program_code_size); - if (HasProgramA()) { - file.WriteArray(program_code_b.data(), program_code_size_b); +bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) { + if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) || + file.ReadBytes(&program_type, sizeof(u32)) != sizeof(u32)) { + return false; } + u32 program_code_size{}; + u32 program_code_size_b{}; + if (file.ReadBytes(&program_code_size, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&program_code_size_b, sizeof(u32)) != sizeof(u32)) { + return false; + } + + program_code.resize(program_code_size); + program_code_b.resize(program_code_size_b); + + if (file.ReadArray(program_code.data(), program_code_size) != program_code_size) + return false; + + if (HasProgramA() && + file.ReadArray(program_code_b.data(), program_code_size_b) != program_code_size_b) { + return false; + } + return true; +} + +bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { + if (file.WriteObject(unique_identifier) != 1 || + file.WriteObject(static_cast(program_type)) != 1 || + file.WriteObject(program_code_size) != 1 || file.WriteObject(program_code_size_b) != 1) { + return false; + } + + if (file.WriteArray(program_code.data(), program_code_size) != program_code_size) + return false; + + if (HasProgramA() && + file.WriteArray(program_code_b.data(), program_code_size_b) != program_code_size_b) { + return false; + } + return true; } std::optional, std::vector>> ShaderDiskCacheOpenGL::LoadTransferable() { - if (!Settings::values.use_disk_shader_cache) { + if (!Settings::values.use_disk_shader_cache) return {}; - } + tried_to_load = true; FileUtil::IOFile file(GetTransferablePath(), "rb"); if (!file.IsOpen()) { @@ -133,15 +149,19 @@ ShaderDiskCacheOpenGL::LoadTransferable() { GetTitleID()); return {}; } - const u64 file_size = file.GetSize(); u32 version{}; - file.ReadBytes(&version, sizeof(version)); + if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { + LOG_ERROR(Render_OpenGL, + "Failed to get transferable cache version for title id={} - skipping", + GetTitleID()); + return {}; + } if (version < NativeVersion) { LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing"); file.Close(); - FileUtil::Delete(GetTransferablePath()); + InvalidateTransferable(); return {}; } if (version > NativeVersion) { @@ -153,25 +173,35 @@ ShaderDiskCacheOpenGL::LoadTransferable() { // Version is valid, load the shaders std::vector raws; std::vector usages; - while (file.Tell() < file_size) { + while (file.Tell() < file.GetSize()) { TransferableEntryKind kind{}; - file.ReadBytes(&kind, sizeof(u32)); + if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { + LOG_ERROR(Render_OpenGL, "Failed to read transferable file - skipping"); + return {}; + } switch (kind) { case TransferableEntryKind::Raw: { - ShaderDiskCacheRaw entry{file}; + ShaderDiskCacheRaw entry; + if (!entry.Load(file)) { + LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry - skipping"); + return {}; + } transferable.insert({entry.GetUniqueIdentifier(), {}}); raws.push_back(std::move(entry)); break; } case TransferableEntryKind::Usage: { ShaderDiskCacheUsage usage{}; - file.ReadBytes(&usage, sizeof(usage)); + if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) { + LOG_ERROR(Render_OpenGL, "Failed to load transferable usage entry - skipping"); + return {}; + } usages.push_back(std::move(usage)); break; } default: - LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={} - aborting", + LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={} - skipping", static_cast(kind)); return {}; } @@ -182,9 +212,8 @@ ShaderDiskCacheOpenGL::LoadTransferable() { std::pair, std::map> ShaderDiskCacheOpenGL::LoadPrecompiled() { - if (!Settings::values.use_disk_shader_cache) { + if (!IsUsable()) return {}; - } FileUtil::IOFile file(GetPrecompiledPath(), "rb"); if (!file.IsOpen()) { @@ -192,119 +221,75 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() { GetTitleID()); return {}; } - const u64 file_size = file.GetSize(); - char precompiled_hash[ShaderHashSize]; - file.ReadBytes(&precompiled_hash, ShaderHashSize); - if (precompiled_hash != GetShaderHash()) { - LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of yuzu - removing"); + const auto result = LoadPrecompiledFile(file); + if (!result) { + LOG_INFO(Render_OpenGL, + "Failed to load precompiled cache for game with title id={} - removing", + GetTitleID()); file.Close(); InvalidatePrecompiled(); return {}; } + return *result; +} + +std::optional, + std::map>> +ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { + ShaderCacheVersionHash file_hash{}; + if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) { + return {}; + } + if (GetShaderCacheVersionHash() != file_hash) { + LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); + return {}; + } std::map decompiled; std::map dumps; - while (file.Tell() < file_size) { + while (file.Tell() < file.GetSize()) { PrecompiledEntryKind kind{}; - file.ReadBytes(&kind, sizeof(u32)); + if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { + return {}; + } switch (kind) { case PrecompiledEntryKind::Decompiled: { - ShaderDiskCacheDecompiled entry; - u64 unique_identifier{}; - file.ReadBytes(&unique_identifier, sizeof(u64)); - - u32 code_size{}; - u32 compressed_code_size{}; - file.ReadBytes(&code_size, sizeof(u32)); - file.ReadBytes(&compressed_code_size, sizeof(u32)); - - std::vector compressed_code(compressed_code_size); - file.ReadArray(compressed_code.data(), compressed_code.size()); - - const std::vector code = DecompressData(compressed_code, code_size); - if (code.empty()) { - LOG_ERROR(Render_OpenGL, - "Failed to decompress GLSL code in precompiled shader={:016x} - removing", - unique_identifier); - InvalidatePrecompiled(); + if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64)) return {}; - } - entry.code = std::string(reinterpret_cast(code.data()), code_size); - u32 const_buffers_count{}; - file.ReadBytes(&const_buffers_count, sizeof(u32)); - for (u32 i = 0; i < const_buffers_count; ++i) { - u32 max_offset{}, index{}; - u8 is_indirect{}; - file.ReadBytes(&max_offset, sizeof(u32)); - file.ReadBytes(&index, sizeof(u32)); - file.ReadBytes(&is_indirect, sizeof(u8)); - - entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); - } - - u32 samplers_count{}; - file.ReadBytes(&samplers_count, sizeof(u32)); - for (u32 i = 0; i < samplers_count; ++i) { - u64 offset{}, index{}; - u32 type{}; - u8 is_array{}, is_shadow{}; - file.ReadBytes(&offset, sizeof(u64)); - file.ReadBytes(&index, sizeof(u64)); - file.ReadBytes(&type, sizeof(u32)); - file.ReadBytes(&is_array, sizeof(u8)); - file.ReadBytes(&is_shadow, sizeof(u8)); - - entry.entries.samplers.emplace_back( - static_cast(offset), static_cast(index), - static_cast(type), is_array != 0, is_shadow != 0); - } - - u32 global_memory_count{}; - file.ReadBytes(&global_memory_count, sizeof(u32)); - for (u32 i = 0; i < global_memory_count; ++i) { - u32 cbuf_index{}, cbuf_offset{}; - file.ReadBytes(&cbuf_index, sizeof(u32)); - file.ReadBytes(&cbuf_offset, sizeof(u32)); - entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); - } - - for (auto& clip_distance : entry.entries.clip_distances) { - u8 clip_distance_raw{}; - file.ReadBytes(&clip_distance_raw, sizeof(u8)); - clip_distance = clip_distance_raw != 0; - } - - u64 shader_length{}; - file.ReadBytes(&shader_length, sizeof(u64)); - entry.entries.shader_length = static_cast(shader_length); - - decompiled.insert({unique_identifier, std::move(entry)}); + const auto entry = LoadDecompiledEntry(file); + if (!entry) + return {}; + decompiled.insert({unique_identifier, std::move(*entry)}); break; } case PrecompiledEntryKind::Dump: { ShaderDiskCacheUsage usage; - file.ReadBytes(&usage, sizeof(usage)); + if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) + return {}; ShaderDiskCacheDump dump; - file.ReadBytes(&dump.binary_format, sizeof(u32)); + if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32)) + return {}; u32 binary_length{}; u32 compressed_size{}; - file.ReadBytes(&binary_length, sizeof(u32)); - file.ReadBytes(&compressed_size, sizeof(u32)); + if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) { + return {}; + } std::vector compressed_binary(compressed_size); - file.ReadArray(compressed_binary.data(), compressed_binary.size()); + if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) != + compressed_binary.size()) { + return {}; + } dump.binary = DecompressData(compressed_binary, binary_length); if (dump.binary.empty()) { - LOG_ERROR(Render_OpenGL, - "Failed to decompress precompiled binary program - removing"); - InvalidatePrecompiled(); return {}; } @@ -312,28 +297,165 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() { break; } default: - LOG_ERROR(Render_OpenGL, "Unknown precompiled shader cache entry kind={} - removing", - static_cast(kind)); - InvalidatePrecompiled(); return {}; } } - return {decompiled, dumps}; + return {{decompiled, dumps}}; } -bool ShaderDiskCacheOpenGL::InvalidateTransferable() const { - const bool success = FileUtil::Delete(GetTransferablePath()); - return InvalidatePrecompiled() && success; +std::optional ShaderDiskCacheOpenGL::LoadDecompiledEntry( + FileUtil::IOFile& file) { + u32 code_size{}; + u32 compressed_code_size{}; + if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) { + return {}; + } + + std::vector compressed_code(compressed_code_size); + if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { + return {}; + } + + const std::vector code = DecompressData(compressed_code, code_size); + if (code.empty()) { + return {}; + } + ShaderDiskCacheDecompiled entry; + entry.code = std::string(reinterpret_cast(code.data()), code_size); + + u32 const_buffers_count{}; + if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32)) + return {}; + for (u32 i = 0; i < const_buffers_count; ++i) { + u32 max_offset{}; + u32 index{}; + u8 is_indirect{}; + if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) { + return {}; + } + entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); + } + + u32 samplers_count{}; + if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32)) + return {}; + for (u32 i = 0; i < samplers_count; ++i) { + u64 offset{}; + u64 index{}; + u32 type{}; + u8 is_array{}; + u8 is_shadow{}; + if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) || + file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) || + file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) || + file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8)) { + return {}; + } + entry.entries.samplers.emplace_back( + static_cast(offset), static_cast(index), + static_cast(type), is_array != 0, is_shadow != 0); + } + + u32 global_memory_count{}; + if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32)) + return {}; + for (u32 i = 0; i < global_memory_count; ++i) { + u32 cbuf_index{}; + u32 cbuf_offset{}; + if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) { + return {}; + } + entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); + } + + for (auto& clip_distance : entry.entries.clip_distances) { + u8 clip_distance_raw{}; + if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8)) + return {}; + clip_distance = clip_distance_raw != 0; + } + + u64 shader_length{}; + if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64)) + return {}; + entry.entries.shader_length = static_cast(shader_length); + + return entry; } -bool ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { - return FileUtil::Delete(GetPrecompiledPath()); +bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, + const std::string& code, + const std::vector& compressed_code, + const GLShader::ShaderEntries& entries) { + if (file.WriteObject(static_cast(PrecompiledEntryKind::Decompiled)) != 1 || + file.WriteObject(unique_identifier) != 1 || + file.WriteObject(static_cast(code.size())) != 1 || + file.WriteObject(static_cast(compressed_code.size())) != 1 || + file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { + return false; + } + + if (file.WriteObject(static_cast(entries.const_buffers.size())) != 1) + return false; + for (const auto& cbuf : entries.const_buffers) { + if (file.WriteObject(static_cast(cbuf.GetMaxOffset())) != 1 || + file.WriteObject(static_cast(cbuf.GetIndex())) != 1 || + file.WriteObject(static_cast(cbuf.IsIndirect() ? 1 : 0)) != 1) { + return false; + } + } + + if (file.WriteObject(static_cast(entries.samplers.size())) != 1) + return false; + for (const auto& sampler : entries.samplers) { + if (file.WriteObject(static_cast(sampler.GetOffset())) != 1 || + file.WriteObject(static_cast(sampler.GetIndex())) != 1 || + file.WriteObject(static_cast(sampler.GetType())) != 1 || + file.WriteObject(static_cast(sampler.IsArray() ? 1 : 0)) != 1 || + file.WriteObject(static_cast(sampler.IsShadow() ? 1 : 0)) != 1) { + return false; + } + } + + if (file.WriteObject(static_cast(entries.global_memory_entries.size())) != 1) + return false; + for (const auto& gmem : entries.global_memory_entries) { + if (file.WriteObject(static_cast(gmem.GetCbufIndex())) != 1 || + file.WriteObject(static_cast(gmem.GetCbufOffset())) != 1) { + return false; + } + } + + for (const bool clip_distance : entries.clip_distances) { + if (file.WriteObject(static_cast(clip_distance ? 1 : 0)) != 1) + return false; + } + + return file.WriteObject(static_cast(entries.shader_length)) == 1; +} + +void ShaderDiskCacheOpenGL::InvalidateTransferable() const { + if (!FileUtil::Delete(GetTransferablePath())) { + LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", + GetTransferablePath()); + } + InvalidatePrecompiled(); +} + +void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { + if (!FileUtil::Delete(GetPrecompiledPath())) { + LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); + } } void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { - if (!Settings::values.use_disk_shader_cache) { + if (!IsUsable()) return; - } const u64 id = entry.GetUniqueIdentifier(); if (transferable.find(id) != transferable.end()) { @@ -342,47 +464,44 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { } FileUtil::IOFile file = AppendTransferableFile(); - if (!file.IsOpen()) { + if (!file.IsOpen()) + return; + if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) { + LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry - removing"); + file.Close(); + InvalidateTransferable(); return; } - file.WriteObject(TransferableEntryKind::Raw); - entry.Save(file); - transferable.insert({id, {}}); } void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { - if (!Settings::values.use_disk_shader_cache) { + if (!IsUsable()) return; - } const auto it = transferable.find(usage.unique_identifier); - if (it == transferable.end()) { - LOG_CRITICAL(Render_OpenGL, "Saving shader usage without storing raw previously"); - UNREACHABLE(); - } + ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); + auto& usages{it->second}; ASSERT(usages.find(usage) == usages.end()); usages.insert(usage); FileUtil::IOFile file = AppendTransferableFile(); - if (!file.IsOpen()) { + if (!file.IsOpen()) + return; + + if (file.WriteObject(TransferableEntryKind::Usage) != 1 || file.WriteObject(usage) != 1) { + LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry - removing"); + file.Close(); + InvalidateTransferable(); return; } - file.WriteObject(TransferableEntryKind::Usage); - file.WriteObject(usage); } void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code, const GLShader::ShaderEntries& entries) { - if (!Settings::values.use_disk_shader_cache) { + if (!IsUsable()) return; - } - - FileUtil::IOFile file = AppendPrecompiledFile(); - if (!file.IsOpen()) { - return; - } const std::vector compressed_code{CompressData(code.data(), code.size())}; if (compressed_code.empty()) { @@ -391,52 +510,21 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str return; } - file.WriteObject(static_cast(PrecompiledEntryKind::Decompiled)); + FileUtil::IOFile file = AppendPrecompiledFile(); + if (!file.IsOpen()) + return; - file.WriteObject(unique_identifier); - - file.WriteObject(static_cast(code.size())); - file.WriteObject(static_cast(compressed_code.size())); - file.WriteArray(compressed_code.data(), compressed_code.size()); - - file.WriteObject(static_cast(entries.const_buffers.size())); - for (const auto& cbuf : entries.const_buffers) { - file.WriteObject(static_cast(cbuf.GetMaxOffset())); - file.WriteObject(static_cast(cbuf.GetIndex())); - file.WriteObject(static_cast(cbuf.IsIndirect() ? 1 : 0)); + if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) { + LOG_ERROR(Render_OpenGL, + "Failed to save decompiled entry to the precompiled file - removing"); + file.Close(); + InvalidatePrecompiled(); } - - file.WriteObject(static_cast(entries.samplers.size())); - for (const auto& sampler : entries.samplers) { - file.WriteObject(static_cast(sampler.GetOffset())); - file.WriteObject(static_cast(sampler.GetIndex())); - file.WriteObject(static_cast(sampler.GetType())); - file.WriteObject(static_cast(sampler.IsArray() ? 1 : 0)); - file.WriteObject(static_cast(sampler.IsShadow() ? 1 : 0)); - } - - file.WriteObject(static_cast(entries.global_memory_entries.size())); - for (const auto& gmem : entries.global_memory_entries) { - file.WriteObject(static_cast(gmem.GetCbufIndex())); - file.WriteObject(static_cast(gmem.GetCbufOffset())); - } - - for (const bool clip_distance : entries.clip_distances) { - file.WriteObject(static_cast(clip_distance ? 1 : 0)); - } - - file.WriteObject(static_cast(entries.shader_length)); } void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) { - if (!Settings::values.use_disk_shader_cache) { + if (!IsUsable()) return; - } - - FileUtil::IOFile file = AppendPrecompiledFile(); - if (!file.IsOpen()) { - return; - } GLint binary_length{}; glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length); @@ -452,20 +540,31 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p return; } - file.WriteObject(static_cast(PrecompiledEntryKind::Dump)); + FileUtil::IOFile file = AppendPrecompiledFile(); + if (!file.IsOpen()) + return; - file.WriteObject(usage); + if (file.WriteObject(static_cast(PrecompiledEntryKind::Dump)) != 1 || + file.WriteObject(usage) != 1 || file.WriteObject(static_cast(binary_format)) != 1 || + file.WriteObject(static_cast(binary_length)) != 1 || + file.WriteObject(static_cast(compressed_binary.size())) != 1 || + file.WriteArray(compressed_binary.data(), compressed_binary.size()) != + compressed_binary.size()) { + LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", + usage.unique_identifier); + file.Close(); + InvalidatePrecompiled(); + return; + } +} - file.WriteObject(static_cast(binary_format)); - file.WriteObject(static_cast(binary_length)); - file.WriteObject(static_cast(compressed_binary.size())); - file.WriteArray(compressed_binary.data(), compressed_binary.size()); +bool ShaderDiskCacheOpenGL::IsUsable() const { + return tried_to_load && Settings::values.use_disk_shader_cache; } FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { - if (!EnsureDirectories()) { + if (!EnsureDirectories()) return {}; - } const auto transferable_path{GetTransferablePath()}; const bool existed = FileUtil::Exists(transferable_path); @@ -477,15 +576,18 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { } if (!existed || file.GetSize() == 0) { // If the file didn't exist, write its version - file.WriteObject(NativeVersion); + if (file.WriteObject(NativeVersion) != 1) { + LOG_ERROR(Render_OpenGL, "Failed to write transferable cache version in path={}", + transferable_path); + return {}; + } } return file; } FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { - if (!EnsureDirectories()) { + if (!EnsureDirectories()) return {}; - } const auto precompiled_path{GetPrecompiledPath()}; const bool existed = FileUtil::Exists(precompiled_path); @@ -497,9 +599,12 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { } if (!existed || file.GetSize() == 0) { - std::array hash{}; - std::strcpy(hash.data(), GetShaderHash().c_str()); - file.WriteArray(hash.data(), hash.size()); + const auto hash{GetShaderCacheVersionHash()}; + if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { + LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}", + precompiled_path); + return {}; + } } return file; } diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 4bffe4307..ddcd4cf51 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h @@ -53,15 +53,15 @@ struct BaseBindings { /// Describes a shader how it's used by the guest GPU class ShaderDiskCacheRaw { public: - explicit ShaderDiskCacheRaw(FileUtil::IOFile& file); - explicit ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, u32 program_code_size, u32 program_code_size_b, ProgramCode program_code, ProgramCode program_code_b); - + ShaderDiskCacheRaw(); ~ShaderDiskCacheRaw(); - void Save(FileUtil::IOFile& file) const; + bool Load(FileUtil::IOFile& file); + + bool Save(FileUtil::IOFile& file) const; u64 GetUniqueIdentifier() const { return unique_identifier; @@ -158,10 +158,10 @@ public: LoadPrecompiled(); /// Removes the transferable (and precompiled) cache file. - bool InvalidateTransferable() const; + void InvalidateTransferable() const; /// Removes the precompiled cache file. - bool InvalidatePrecompiled() const; + void InvalidatePrecompiled() const; /// Saves a raw dump to the transferable file. Checks for collisions. void SaveRaw(const ShaderDiskCacheRaw& entry); @@ -177,6 +177,22 @@ public: void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); private: + /// Loads the transferable cache. Returns empty on failure. + std::optional, + std::map>> + LoadPrecompiledFile(FileUtil::IOFile& file); + + /// Loads a decompiled cache entry from the passed file. Returns empty on failure. + std::optional LoadDecompiledEntry(FileUtil::IOFile& file); + + /// Saves a decompiled entry to the passed file. Returns true on success. + bool SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, const std::string& code, + const std::vector& compressed_code, + const GLShader::ShaderEntries& entries); + + /// Returns if the cache can be used + bool IsUsable() const; + /// Opens current game's transferable file and write it's header if it doesn't exist FileUtil::IOFile AppendTransferableFile() const; @@ -203,6 +219,8 @@ private: // Stored transferable shaders std::map> transferable; + // The cache has been loaded at boot + bool tried_to_load{}; }; } // namespace OpenGL \ No newline at end of file