From 247e90b3d09d9dca3c67a9f773a4515f10da083b Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 2 Jan 2021 07:20:11 +0200 Subject: [PATCH] rsx: Shaders cache loading and saving bugfixes * Fixed crash whenever files are missing from the cache. * Fixed crash whenever files are empty. * Fixed crash whenever file creation/overwrite of cache files failed. (handled by fs::write_file) * Fixed crash whenever there are any subdirectories inside the pipelines cache directories. * Overwrite invalid shader cache files if encountered such. * Optimizations have been added. --- Utilities/File.h | 15 ++++++-- rpcs3/Emu/RSX/rsx_cache.h | 79 +++++++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/Utilities/File.h b/Utilities/File.h index 74db5ff8f2..54b98f1db1 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -733,9 +733,18 @@ namespace fs // Always use write flag, remove read flag if (fs::file f{path, mode + fs::write - fs::read}) { - // Write args sequentially - (f.write(args), ...); - return true; + if constexpr (sizeof...(args) == 2u && (std::is_pointer_v || ...)) + { + // Specialization for [const void*, usz] args + f.write(args...); + return true; + } + else + { + // Write args sequentially + (f.write(args), ...); + return true; + } } return false; diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 6fedcf5753..1cea35906c 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -447,20 +447,34 @@ namespace rsx fs::dir_entry tmp = entries[pos]; const auto filename = directory_path + "/" + tmp.name; - std::vector bytes; fs::file f(filename); + + if (!f) + { + // Unexpected error, but avoid crash + continue; + } + if (f.size() != sizeof(pipeline_data)) { rsx_log.error("Removing cached pipeline object %s since it's not binary compatible with the current shader cache", tmp.name.c_str()); fs::remove_file(filename); continue; } - f.read(bytes, f.size()); - auto entry = unpack(*reinterpret_cast(bytes.data())); + pipeline_data pdata{}; + f.read(&pdata, f.size()); + + auto entry = unpack(pdata); + + if (std::get<1>(entry).data.empty() || !std::get<2>(entry).ucode_length) + { + continue; + } + m_storage.preload_programs(std::get<1>(entry), std::get<2>(entry)); - unpacked[unpacked.push_begin()] = entry; + unpacked[unpacked.push_begin()] = std::move(entry); } // Do not account for an extra shader that was never processed processed--; @@ -573,23 +587,20 @@ namespace rsx std::string directory_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix; - if (!fs::is_dir(directory_path)) + fs::dir root = fs::dir(directory_path); + + if (!root) { fs::create_path(directory_path); fs::create_path(root_path + "/raw"); - return; } - fs::dir root = fs::dir(directory_path); - u32 entry_count = 0; std::vector entries; for (auto It = root.begin(); It != root.end(); ++It, entry_count++) { - fs::dir_entry tmp = *It; - - if (tmp.name == "." || tmp.name == "..") + if (tmp.is_directory) continue; entries.push_back(tmp); @@ -643,17 +654,20 @@ namespace rsx } pipeline_data data = pack(pipeline, vp, fp); + std::string fp_name = root_path + "/raw/" + fmt::format("%llX.fp", data.fragment_program_hash); std::string vp_name = root_path + "/raw/" + fmt::format("%llX.vp", data.vertex_program_hash); - if (!fs::is_file(fp_name)) + // Writeback to cache either if file does not exist or it is invalid (unexpected size) + // Note: fs::write_file is not atomic, if the process is terminated in the middle an empty file is created + if (fs::stat_t s{}; !fs::stat(fp_name, s) || s.size != fp.ucode_length) { - fs::file(fp_name, fs::rewrite).write(fp.get_data(), fp.ucode_length); + fs::write_file(fp_name, fs::rewrite, fp.get_data(), fp.ucode_length); } - if (!fs::is_file(vp_name)) + if (fs::stat_t s{}; !fs::stat(vp_name, s) || s.size != vp.data.size() * sizeof(u32)) { - fs::file(vp_name, fs::rewrite).write(vp.data); + fs::write_file(vp_name, fs::rewrite, vp.data); } u64 state_hash = 0; @@ -669,21 +683,20 @@ namespace rsx state_hash ^= rpcs3::hash_base(data.fp_shadow_textures); state_hash ^= rpcs3::hash_base(data.fp_redirected_textures); - std::string pipeline_file_name = fmt::format("%llX+%llX+%llX+%llX.bin", data.vertex_program_hash, data.fragment_program_hash, data.pipeline_storage_hash, state_hash); - std::string pipeline_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix + "/" + pipeline_file_name; - fs::file(pipeline_path, fs::rewrite).write(&data, sizeof(pipeline_data)); + const std::string pipeline_file_name = fmt::format("%llX+%llX+%llX+%llX.bin", data.vertex_program_hash, data.fragment_program_hash, data.pipeline_storage_hash, state_hash); + const std::string pipeline_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix + "/" + pipeline_file_name; + fs::write_file(pipeline_path, fs::rewrite, &data, sizeof(data)); } RSXVertexProgram load_vp_raw(u64 program_hash) { - std::vector data; + RSXVertexProgram vp = {}; + std::string filename = fmt::format("%llX.vp", program_hash); fs::file f(root_path + "/raw/" + filename); - f.read(data, f.size() / sizeof(u32)); + if (f) f.read(vp.data, f.size() / sizeof(u32)); - RSXVertexProgram vp = {}; - vp.data = data; vp.skip_vertex_input_check = true; return vp; @@ -695,24 +708,26 @@ namespace rsx std::string filename = fmt::format("%llX.fp", program_hash); fs::file f(root_path + "/raw/" + filename); - f.read(data, f.size()); + if (f) f.read(data, f.size()); RSXFragmentProgram fp = {}; - { - std::lock_guard lock(fpd_mutex); - fragment_program_data[program_hash] = data; - fp.data = fragment_program_data[program_hash].data(); - } fp.ucode_length = ::size32(data); + { + std::lock_guard lock(fpd_mutex); + fp.data = fragment_program_data.insert_or_assign(program_hash, std::move(data)).first->second.data(); + } return fp; } std::tuple unpack(pipeline_data &data) { - RSXVertexProgram vp = load_vp_raw(data.vertex_program_hash); - RSXFragmentProgram fp = load_fp_raw(data.fragment_program_hash); - pipeline_storage_type pipeline = data.pipeline_properties; + std::tuple result; + auto& [pipeline, vp, fp] = result; + + vp = load_vp_raw(data.vertex_program_hash); + fp = load_fp_raw(data.fragment_program_hash); + pipeline = data.pipeline_properties; vp.output_mask = data.vp_ctrl; vp.texture_dimensions = data.vp_texture_dimensions; @@ -741,7 +756,7 @@ namespace rsx fp.shadow_textures = data.fp_shadow_textures; fp.redirected_textures = data.fp_redirected_textures; - return std::make_tuple(pipeline, vp, fp); + return result; } pipeline_data pack(const pipeline_storage_type &pipeline, const RSXVertexProgram &vp, const RSXFragmentProgram &fp)