diff --git a/Utilities/File.h b/Utilities/File.h index 74db5ff8f2dd..54b98f1db101 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 6fedcf5753c9..587f0819a157 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -423,8 +423,7 @@ namespace rsx std::string version_prefix; std::string root_path; std::string pipeline_class_name; - std::mutex fpd_mutex; - std::unordered_map> fragment_program_data; + lf_fifo, 100> fragment_program_data; backend_storage& m_storage; @@ -447,20 +446,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,29 +586,28 @@ 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 == "..") + for (auto&& tmp : root) + { + if (tmp.is_directory) continue; entries.push_back(tmp); } - if ((entry_count = ::size32(entries)) <= 2) + u32 entry_count = ::size32(entries); + + if (!entry_count) return; root.rewind(); @@ -643,17 +655,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 +684,18 @@ 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; - std::string filename = fmt::format("%llX.vp", program_hash); + RSXVertexProgram vp = {}; - fs::file f(root_path + "/raw/" + filename); - f.read(data, f.size() / sizeof(u32)); + fs::file f(fmt::format("%s/raw/%llX.vp", root_path, program_hash)); + if (f) f.read(vp.data, f.size() / sizeof(u32)); - RSXVertexProgram vp = {}; - vp.data = data; vp.skip_vertex_input_check = true; return vp; @@ -691,28 +703,32 @@ namespace rsx RSXFragmentProgram load_fp_raw(u64 program_hash) { - std::vector data; - std::string filename = fmt::format("%llX.fp", program_hash); - - fs::file f(root_path + "/raw/" + filename); - f.read(data, f.size()); + fs::file f(fmt::format("%s/raw/%llX.fp", root_path, program_hash)); RSXFragmentProgram fp = {}; + + const u32 size = fp.ucode_length = f ? ::size32(f) : 0; + + if (!size) { - std::lock_guard lock(fpd_mutex); - fragment_program_data[program_hash] = data; - fp.data = fragment_program_data[program_hash].data(); + return fp; } - fp.ucode_length = ::size32(data); + auto buf = std::make_unique(size); + fp.data = buf.get(); + f.read(buf.get(), size); + fragment_program_data[fragment_program_data.push_begin()] = std::move(buf); 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 +757,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)