From a1c8f3a528811be9c110d600f10bc31d5f6778c0 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 8 Mar 2025 19:03:05 +0300 Subject: [PATCH] rsx/prog: Use a proper cache hint key instead of disjointed counters --- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 5 +- rpcs3/Emu/RSX/GL/GLProgramBuffer.h | 8 +- rpcs3/Emu/RSX/Program/ProgramStateCache.cpp | 14 ++ rpcs3/Emu/RSX/Program/ProgramStateCache.h | 156 ++++++++++---------- rpcs3/Emu/RSX/RSXThread.cpp | 8 +- rpcs3/Emu/RSX/RSXThread.h | 5 +- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 5 +- rpcs3/Emu/RSX/VK/VKProgramBuffer.h | 8 +- rpcs3/Emu/RSX/rsx_cache.h | 2 +- 9 files changed, 110 insertions(+), 101 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 0a8b78bf51..79c93b2cd4 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -782,8 +782,9 @@ bool GLGSRender::load_program() { void* pipeline_properties = nullptr; std::tie(m_program, m_vertex_prog, m_fragment_prog) = m_prog_buffer.get_graphics_pipeline( - current_vertex_program, vertex_program_invalidation_count, - current_fragment_program, fragment_program_invalidation_count, + &m_program_cache_hint, + current_vertex_program, + current_fragment_program, pipeline_properties, shadermode != shader_mode::recompiler, true); diff --git a/rpcs3/Emu/RSX/GL/GLProgramBuffer.h b/rpcs3/Emu/RSX/GL/GLProgramBuffer.h index 9a1d249f59..8dd34caacd 100644 --- a/rpcs3/Emu/RSX/GL/GLProgramBuffer.h +++ b/rpcs3/Emu/RSX/GL/GLProgramBuffer.h @@ -135,13 +135,13 @@ struct GLProgramBuffer : public program_state_cache template void add_pipeline_entry(const RSXVertexProgram& vp, const RSXFragmentProgram& fp, void* &props, Args&& ...args) { - get_graphics_pipeline(vp, umax, fp, umax, props, false, false, std::forward(args)...); + get_graphics_pipeline(nullptr, vp, fp, props, false, false, std::forward(args)...); } - void preload_programs(const RSXVertexProgram& vp, const RSXFragmentProgram& fp) + void preload_programs(rsx::program_cache_hint_t* cache_hint, const RSXVertexProgram& vp, const RSXFragmentProgram& fp) { - search_vertex_program(vp, umax); - search_fragment_program(fp, umax); + search_vertex_program(cache_hint, vp); + search_fragment_program(cache_hint, fp); } bool check_cache_missed() const diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index a6979a8f2b..9cac524d61 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "ProgramStateCache.h" #include "Emu/system_config.h" +#include "Emu/RSX/Core/RSXDriverState.h" #include "util/sysinfo.hpp" #include @@ -851,4 +852,17 @@ namespace rsx write_fragment_constants_to_buffer_fallback(buffer, rsx_prog, offsets_cache, sanitize); #endif } + + void program_cache_hint_t::invalidate(u32 flags) + { + if (flags & rsx::vertex_program_dirty) + { + cached_vertex_program = nullptr; + } + + if (flags & rsx::fragment_program_dirty) + { + cached_fragment_program = nullptr; + } + } } diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.h b/rpcs3/Emu/RSX/Program/ProgramStateCache.h index 6e460ab580..ff1082dc36 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.h +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.h @@ -7,6 +7,7 @@ #include "util/logs.hpp" #include "util/fnv_hash.hpp" #include "util/v128.hpp" +#include #include #include @@ -86,6 +87,47 @@ namespace program_hash_util namespace rsx { + struct program_cache_hint_t + { + template + T* get_fragment_program() const + { + return utils::bless(cached_fragment_program); + } + + template + T* get_vertex_program() const + { + return utils::bless(cached_vertex_program); + } + + bool has_vertex_program() const + { + return cached_vertex_program != nullptr; + } + + bool has_fragment_program() const + { + return cached_fragment_program != nullptr; + } + + void invalidate(u32 flags); + + static inline void cache_vertex_program(program_cache_hint_t* cache, void* vertex_program) + { + if (cache) cache->cached_vertex_program = vertex_program; + } + + static inline void cache_fragment_program(program_cache_hint_t* cache, void* fragment_program) + { + if (cache) cache->cached_fragment_program = fragment_program; + } + + protected: + void* cached_fragment_program = nullptr; + void* cached_vertex_program = nullptr; + }; + void write_fragment_constants_to_buffer(const std::span& buffer, const RSXFragmentProgram& rsx_prog, const std::vector& offsets_cache, bool sanitize = true); } @@ -97,13 +139,13 @@ namespace rsx * - a typedef VertexProgramData to a type that encapsulate vertex program info. It should provide an Id member. * - a typedef FragmentProgramData to a types that encapsulate fragment program info. It should provide an Id member and a fragment constant offset vector. * - a typedef PipelineData encapsulating monolithic program. -* - a typedef PipelineProperties to a type that encapsulate various state info relevant to program compilation (alpha test, primitive type,...) +* - a typedef pipeline_properties to a type that encapsulate various state info relevant to program compilation (alpha test, primitive type,...) * - a typedef ExtraData type that will be passed to the buildProgram function. * It should also contains the following function member : * - static void recompile_fragment_program(RSXFragmentProgram *RSXFP, FragmentProgramData& fragmentProgramData, usz ID); * - static void recompile_vertex_program(RSXVertexProgram *RSXVP, VertexProgramData& vertexProgramData, usz ID); -* - static PipelineData build_program(VertexProgramData &vertexProgramData, FragmentProgramData &fragmentProgramData, const PipelineProperties &pipelineProperties, const ExtraData& extraData); -* - static void validate_pipeline_properties(const VertexProgramData &vertexProgramData, const FragmentProgramData &fragmentProgramData, PipelineProperties& props); +* - static PipelineData build_program(VertexProgramData &vertexProgramData, FragmentProgramData &fragmentProgramData, const pipeline_properties &pipeline_properties, const ExtraData& extraData); +* - static void validate_pipeline_properties(const VertexProgramData &vertexProgramData, const FragmentProgramData &fragmentProgramData, pipeline_properties& props); */ template class program_state_cache @@ -168,41 +210,25 @@ protected: pipeline_storage_type __null_pipeline_handle; /// bool here to inform that the program was preexisting. - std::tuple search_vertex_program(const RSXVertexProgram& rsx_vp, usz rsx_vp_invalidation_count) + std::tuple search_vertex_program( + rsx::program_cache_hint_t* cache_hint, + const RSXVertexProgram& rsx_vp) { + if (cache_hint && cache_hint->has_vertex_program()) + { + // The caller guarantees that the cached vertex program is correct. + return std::forward_as_tuple(*cache_hint->get_vertex_program(), true); + } + bool recompile = false; vertex_program_type* new_shader; { - thread_local const std::pair* prev_vp = nullptr; - thread_local usz prev_map_count = umax, prev_rsx_count = umax; - static atomic_t map_invl_count = 0; - reader_lock lock(m_vertex_mutex); - if (prev_map_count == map_invl_count) - { - // prev_vp must be non-null here - if (prev_vp->first.data.size() == rsx_vp.data.size() && prev_vp->first.output_mask == rsx_vp.output_mask) - { - if (rsx_vp_invalidation_count != umax && prev_rsx_count == rsx_vp_invalidation_count) - { - return std::forward_as_tuple(prev_vp->second, true); - } - - if (program_hash_util::vertex_program_compare()(prev_vp->first, rsx_vp)) - { - prev_rsx_count = rsx_vp_invalidation_count; - return std::forward_as_tuple(prev_vp->second, true); - } - } - } - const auto& I = m_vertex_shader_cache.find(rsx_vp); if (I != m_vertex_shader_cache.end()) { - prev_vp = &*I; - prev_map_count = map_invl_count; - prev_rsx_count = rsx_vp_invalidation_count; + rsx::program_cache_hint_t::cache_vertex_program(cache_hint, &(I->second)); return std::forward_as_tuple(I->second, true); } @@ -212,10 +238,6 @@ protected: auto [it, inserted] = m_vertex_shader_cache.try_emplace(rsx_vp); new_shader = &(it->second); recompile = inserted; - prev_map_count = umax; - prev_rsx_count = umax; - prev_vp = nullptr; - map_invl_count++; } if (recompile) @@ -223,51 +245,29 @@ protected: backend_traits::recompile_vertex_program(rsx_vp, *new_shader, m_next_id++); } + rsx::program_cache_hint_t::cache_vertex_program(cache_hint, new_shader); return std::forward_as_tuple(*new_shader, false); } /// bool here to inform that the program was preexisting. - std::tuple search_fragment_program(const RSXFragmentProgram& rsx_fp, usz rsx_fp_invalidation_count) + std::tuple search_fragment_program(rsx::program_cache_hint_t* cache_hint, const RSXFragmentProgram& rsx_fp) { + if (cache_hint && cache_hint->has_fragment_program()) + { + // The caller guarantees that the cached fragemnt program is correct. + return std::forward_as_tuple(*cache_hint->get_fragment_program(), true); + } + bool recompile = false; typename binary_to_fragment_program::iterator it; fragment_program_type* new_shader; { - thread_local const std::pair* prev_fp = nullptr; - thread_local usz prev_map_count = umax, prev_rsx_count = umax; - static atomic_t map_invl_count = 0; - reader_lock lock(m_fragment_mutex); - if (prev_map_count == map_invl_count) - { - // prev_vp must be non-null here - if (prev_fp->first.ucode_length == rsx_fp.ucode_length && prev_fp->first.texcoord_control_mask == rsx_fp.texcoord_control_mask) - { - if (rsx_fp_invalidation_count != umax && prev_rsx_count == rsx_fp_invalidation_count) - { - // Shader UCODE must be the same. - // Shader config changes are not tracked at the moment - // Compare manually - if (program_hash_util::fragment_program_compare::config_only(prev_fp->first, rsx_fp)) - { - return std::forward_as_tuple(prev_fp->second, true); - } - } - else if (program_hash_util::fragment_program_compare()(prev_fp->first, rsx_fp)) - { - prev_rsx_count = rsx_fp_invalidation_count; - return std::forward_as_tuple(prev_fp->second, true); - } - } - } - const auto& I = m_fragment_shader_cache.find(rsx_fp); if (I != m_fragment_shader_cache.end()) { - prev_fp = &*I; - prev_rsx_count = rsx_fp_invalidation_count; - prev_map_count = map_invl_count; + rsx::program_cache_hint_t::cache_fragment_program(cache_hint, &(I->second)); return std::forward_as_tuple(I->second, true); } @@ -276,10 +276,6 @@ protected: lock.upgrade(); std::tie(it, recompile) = m_fragment_shader_cache.try_emplace(rsx_fp); new_shader = &(it->second); - prev_map_count = umax; - prev_rsx_count = umax; - prev_fp = nullptr; - map_invl_count++; } if (recompile) @@ -288,6 +284,7 @@ protected: backend_traits::recompile_fragment_program(rsx_fp, *new_shader, m_next_id++); } + rsx::program_cache_hint_t::cache_fragment_program(cache_hint, new_shader); return std::forward_as_tuple(*new_shader, false); } @@ -342,31 +339,30 @@ public: template pipeline_data_type get_graphics_pipeline( - const RSXVertexProgram& vertexShader, - usz vertexShaderInvalidationCount, - const RSXFragmentProgram& fragmentShader, - usz fragmentShaderInvalidationCount, - pipeline_properties& pipelineProperties, + rsx::program_cache_hint_t* cache_hint, + const RSXVertexProgram& vertex_shader, + const RSXFragmentProgram& fragment_shader, + pipeline_properties& pipeline_properties, bool compile_async, bool allow_notification, Args&& ...args ) { - const auto& vp_search = search_vertex_program(vertexShader, vertexShaderInvalidationCount); - const auto& fp_search = search_fragment_program(fragmentShader, fragmentShaderInvalidationCount); + const auto& vp_search = search_vertex_program(cache_hint, vertex_shader); + const auto& fp_search = search_fragment_program(cache_hint, fragment_shader); const bool already_existing_fragment_program = std::get<1>(fp_search); const bool already_existing_vertex_program = std::get<1>(vp_search); const vertex_program_type& vertex_program = std::get<0>(vp_search); const fragment_program_type& fragment_program = std::get<0>(fp_search); - const pipeline_key key = { vertex_program.id, fragment_program.id, pipelineProperties }; + const pipeline_key key = { vertex_program.id, fragment_program.id, pipeline_properties }; m_cache_miss_flag = true; if (already_existing_vertex_program && already_existing_fragment_program) { // There is a high chance the pipeline object was compiled if the two shaders already existed before - backend_traits::validate_pipeline_properties(vertex_program, fragment_program, pipelineProperties); + backend_traits::validate_pipeline_properties(vertex_program, fragment_program, pipeline_properties); reader_lock lock(m_pipeline_mutex); if (const auto I = m_storage.find(key); I != m_storage.end()) @@ -396,7 +392,7 @@ public: if (allow_notification) { - callback = [this, vertexShader, fragmentShader_ = RSXFragmentProgram::clone(fragmentShader), key] + callback = [this, vertex_shader, fragment_shader_ = RSXFragmentProgram::clone(fragment_shader), key] (pipeline_storage_type& pipeline) -> pipeline_type* { if (!pipeline) @@ -405,7 +401,7 @@ public: } rsx_log.success("Program compiled successfully"); - notify_pipeline_compiled(key.properties, vertexShader, fragmentShader_); + notify_pipeline_compiled(key.properties, vertex_shader, fragment_shader_); std::lock_guard lock(m_pipeline_mutex); auto& pipe_result = m_storage[key]; @@ -432,7 +428,7 @@ public: auto result = backend_traits::build_pipeline( vertex_program, // VS, must already be decompiled and recompiled above fragment_program, // FS, must already be decompiled and recompiled above - pipelineProperties, // Pipeline state + pipeline_properties, // Pipeline state compile_async, // Allow asynchronous compilation callback, // Insertion and notification callback std::forward(args)...); // Other arguments diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index c12066ffb8..860136e187 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1904,7 +1904,6 @@ namespace rsx } m_graphics_state.clear(rsx::pipeline_state::fragment_program_ucode_dirty); - fragment_program_invalidation_count++; // Request for update of fragment constants if the program block is invalidated m_graphics_state |= rsx::pipeline_state::fragment_constants_dirty; @@ -1954,7 +1953,6 @@ namespace rsx } m_graphics_state.clear(rsx::pipeline_state::vertex_program_ucode_dirty); - vertex_program_invalidation_count++; // Reload transform constants unconditionally for now m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; @@ -1990,6 +1988,8 @@ namespace rsx void thread::analyse_current_rsx_pipeline() { + m_program_cache_hint.invalidate(m_graphics_state.load()); + prefetch_vertex_program(); prefetch_fragment_program(); } @@ -2012,7 +2012,6 @@ namespace rsx return; } - vertex_program_invalidation_count++; ensure(!m_graphics_state.test(rsx::pipeline_state::vertex_program_ucode_dirty)); current_vertex_program.output_mask = rsx::method_registers.vertex_attrib_output_mask(); @@ -2048,9 +2047,6 @@ namespace rsx m_graphics_state.clear(rsx::pipeline_state::fragment_program_dirty); - // FP config is always checked for now (see get_graphics_pipeline) - //fragment_program_invalidation_count++; - current_fragment_program.ctrl = m_ctx->register_state->shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); current_fragment_program.texcoord_control_mask = m_ctx->register_state->texcoord_control_mask(); current_fragment_program.two_sided_lighting = m_ctx->register_state->two_side_light_en(); diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 19b40d3087..6c45e3e48a 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -242,8 +242,7 @@ namespace rsx rsx::atomic_bitmask_t m_eng_interrupt_mask; rsx::bitmask_t m_graphics_state; - u64 fragment_program_invalidation_count = umax; - u64 vertex_program_invalidation_count = umax; + u64 ROP_sync_timestamp = 0; program_hash_util::fragment_program_utils::fragment_program_metadata current_fp_metadata = {}; @@ -262,6 +261,8 @@ namespace rsx vertex_program_texture_state current_vp_texture_state = {}; fragment_program_texture_state current_fp_texture_state = {}; + program_cache_hint_t m_program_cache_hint; + // Runs shader prefetch and resolves pipeline status flags void analyse_current_rsx_pipeline(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 2513b070eb..c81df01ff3 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1961,8 +1961,9 @@ bool VKGSRender::load_program() // Load current program from cache std::tie(m_program, m_vertex_prog, m_fragment_prog) = m_prog_buffer->get_graphics_pipeline( - vertex_program, vertex_program_invalidation_count, - fragment_program, fragment_program_invalidation_count, + &m_program_cache_hint, + vertex_program, + fragment_program, m_pipeline_properties, shadermode != shader_mode::recompiler, true, m_pipeline_layout); diff --git a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h index 6483176c43..4f9f535a76 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h +++ b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h @@ -88,13 +88,13 @@ namespace vk template void add_pipeline_entry(RSXVertexProgram& vp, RSXFragmentProgram& fp, vk::pipeline_props& props, Args&& ...args) { - get_graphics_pipeline(vp, umax, fp, umax, props, false, false, std::forward(args)...); + get_graphics_pipeline(nullptr, vp, fp, props, false, false, std::forward(args)...); } - void preload_programs(const RSXVertexProgram& vp, const RSXFragmentProgram& fp) + void preload_programs(rsx::program_cache_hint_t* cache_hint, const RSXVertexProgram& vp, const RSXFragmentProgram& fp) { - search_vertex_program(vp, umax); - search_fragment_program(fp, umax); + search_vertex_program(cache_hint, vp); + search_fragment_program(cache_hint, fp); } bool check_cache_missed() const diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 4bbec32d99..fcbb7869a7 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -111,7 +111,7 @@ namespace rsx continue; } - m_storage.preload_programs(std::get<1>(entry), std::get<2>(entry)); + m_storage.preload_programs(nullptr, std::get<1>(entry), std::get<2>(entry)); unpacked[unpacked.push_begin()] = std::move(entry); }