From 27c56cde2241b0056df7433a8fbb040bdb95dc07 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 9 Jan 2025 03:22:12 +0300 Subject: [PATCH] rsx/shaders: Track active MRT count per shader - Also use more robust hashing to avoid collisions --- rpcs3/Emu/RSX/Common/surface_store.cpp | 16 ++++++- rpcs3/Emu/RSX/Common/surface_store.h | 1 + rpcs3/Emu/RSX/GL/GLGSRender.cpp | 7 ++- rpcs3/Emu/RSX/GL/GLGSRender.h | 3 ++ rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp | 2 +- rpcs3/Emu/RSX/GL/GLShaderInterpreter.h | 2 +- .../RSX/Program/FragmentProgramDecompiler.cpp | 8 +++- rpcs3/Emu/RSX/Program/ProgramStateCache.cpp | 43 +++++++++++-------- rpcs3/Emu/RSX/Program/RSXFragmentProgram.h | 4 +- rpcs3/Emu/RSX/RSXThread.cpp | 17 ++++++-- rpcs3/Emu/RSX/RSXThread.h | 2 + rpcs3/Emu/RSX/VK/VKGSRender.cpp | 7 ++- rpcs3/Emu/RSX/VK/VKGSRender.h | 3 ++ rpcs3/Emu/RSX/rsx_cache.h | 38 +++++++++------- rpcs3/util/fnv_hash.hpp | 25 +++++++++++ 15 files changed, 135 insertions(+), 43 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/surface_store.cpp b/rpcs3/Emu/RSX/Common/surface_store.cpp index 53981ac6db..43f4b4161f 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.cpp +++ b/rpcs3/Emu/RSX/Common/surface_store.cpp @@ -18,7 +18,21 @@ namespace rsx case surface_target::surfaces_a_b_c: return{ 0, 1, 2 }; case surface_target::surfaces_a_b_c_d: return{ 0, 1, 2, 3 }; } - fmt::throw_exception("Wrong color_target"); + fmt::throw_exception("Invalid color target %d", static_cast(color_target)); + } + + u8 get_mrt_buffers_count(surface_target color_target) + { + switch (color_target) + { + case surface_target::none: return 0; + case surface_target::surface_a: return 1; + case surface_target::surface_b: return 1; + case surface_target::surfaces_a_b: return 2; + case surface_target::surfaces_a_b_c: return 3; + case surface_target::surfaces_a_b_c_d: return 4; + } + fmt::throw_exception("Invalid color target %d", static_cast(color_target)); } usz get_aligned_pitch(surface_color_format format, u32 width) diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index e4062707d0..e4d3f2d3fb 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -17,6 +17,7 @@ namespace rsx namespace utility { std::vector get_rtt_indexes(surface_target color_target); + u8 get_mrt_buffers_count(surface_target color_target); usz get_aligned_pitch(surface_color_format format, u32 width); usz get_packed_pitch(surface_color_format format, u32 width); } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index a2d080c86e..90bc7cacbd 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -39,7 +39,7 @@ u64 GLGSRender::get_cycles() GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) { - m_shaders_cache = std::make_unique(m_prog_buffer, "opengl", "v1.94"); + m_shaders_cache = std::make_unique(m_prog_buffer, "opengl", "v1.95"); if (g_cfg.video.disable_vertex_cache) m_vertex_cache = std::make_unique(); @@ -986,6 +986,11 @@ void GLGSRender::load_program_env() rsx::pipeline_state::fragment_texture_state_dirty); } +bool GLGSRender::is_current_program_interpreted() const +{ + return m_program && m_shader_interpreter.is_interpreter(m_program); +} + void GLGSRender::upload_transform_constants(const rsx::io_buffer& buffer) { const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index c8c6ba89dd..01579a445b 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -206,6 +206,9 @@ public: // GRAPH backend void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override; + // Misc + bool is_current_program_interpreted() const override; + protected: void clear_surface(u32 arg) override; void begin() override; diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index 1dfd0f4996..0687798efd 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -347,7 +347,7 @@ namespace gl return data; } - bool shader_interpreter::is_interpreter(const glsl::program* program) + bool shader_interpreter::is_interpreter(const glsl::program* program) const { return (program == &m_current_interpreter->prog); } diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h index 6a46035742..2c15010192 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h @@ -84,6 +84,6 @@ namespace gl void update_fragment_textures(const std::array, 16>& descriptors, u16 reference_mask, u32* out); glsl::program* get(const interpreter::program_metadata& fp_metadata); - bool is_interpreter(const glsl::program* program); + bool is_interpreter(const glsl::program* program) const; }; } diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index 4de2100ff2..4a2215d9f9 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -795,7 +795,7 @@ std::string FragmentProgramDecompiler::BuildCode() output_register_names = { "h0", "h4", "h6", "h8" }; } - for (int n = 0; n < 4; ++n) + for (u32 n = 0; n < 4; ++n) { const auto& reg_name = output_register_names[n]; if (!m_parr.HasParam(PF_PARAM_NONE, float4_type, reg_name)) @@ -803,6 +803,12 @@ std::string FragmentProgramDecompiler::BuildCode() m_parr.AddParam(PF_PARAM_NONE, float4_type, reg_name, init_value); } + if (n >= m_prog.mrt_buffers_count) + { + // Skip gather + continue; + } + const auto block_index = ouput_register_indices[n]; auto& r = temp_registers[block_index]; diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index 8bc851ec28..6a1bcffc7a 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -340,12 +340,16 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert usz vertex_program_storage_hash::operator()(const RSXVertexProgram &program) const { - usz hash = vertex_program_utils::get_vertex_program_ucode_hash(program); - hash ^= program.ctrl; - hash ^= program.output_mask; - hash ^= program.texture_state.texture_dimensions; - hash ^= program.texture_state.multisampled_textures; - return hash; + const usz ucode_hash = vertex_program_utils::get_vertex_program_ucode_hash(program); + const u32 state_params[] = + { + program.ctrl, + program.output_mask, + program.texture_state.texture_dimensions, + program.texture_state.multisampled_textures, + }; + const usz metadata_hash = rpcs3::hash_array(state_params); + return rpcs3::hash64(ucode_hash, metadata_hash); } bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const RSXVertexProgram &binary2) const @@ -541,16 +545,20 @@ usz fragment_program_utils::get_fragment_program_ucode_hash(const RSXFragmentPro usz fragment_program_storage_hash::operator()(const RSXFragmentProgram& program) const { - usz hash = fragment_program_utils::get_fragment_program_ucode_hash(program); - hash ^= program.ctrl; - hash ^= +program.two_sided_lighting; - hash ^= program.texture_state.texture_dimensions; - hash ^= program.texture_state.shadow_textures; - hash ^= program.texture_state.redirected_textures; - hash ^= program.texture_state.multisampled_textures; - hash ^= program.texcoord_control_mask; - - return hash; + const usz ucode_hash = fragment_program_utils::get_fragment_program_ucode_hash(program); + const u32 state_params[] = + { + program.ctrl, + program.two_sided_lighting ? 1u : 0u, + program.texture_state.texture_dimensions, + program.texture_state.shadow_textures, + program.texture_state.redirected_textures, + program.texture_state.multisampled_textures, + program.texcoord_control_mask, + program.mrt_buffers_count + }; + const usz metadata_hash = rpcs3::hash_array(state_params); + return rpcs3::hash64(ucode_hash, metadata_hash); } bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, const RSXFragmentProgram& binary2) const @@ -559,7 +567,8 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con binary1.ctrl != binary2.ctrl || binary1.texture_state != binary2.texture_state || binary1.texcoord_control_mask != binary2.texcoord_control_mask || - binary1.two_sided_lighting != binary2.two_sided_lighting) + binary1.two_sided_lighting != binary2.two_sided_lighting || + binary1.mrt_buffers_count != binary2.mrt_buffers_count) { return false; } diff --git a/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h b/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h index a9e95531aa..f834b7c7f5 100644 --- a/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h +++ b/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h @@ -300,8 +300,10 @@ struct RSXFragmentProgram u32 ucode_length = 0; u32 total_length = 0; u32 ctrl = 0; - bool two_sided_lighting = false; u32 texcoord_control_mask = 0; + u32 mrt_buffers_count = 0; + + bool two_sided_lighting = false; rsx::fragment_program_texture_state texture_state; rsx::fragment_program_texture_config texture_params; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 29c2d8e865..57ffc0e11f 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1719,7 +1719,7 @@ namespace rsx for (uint i = 0; i < mrt_buffers.size(); ++i) { - if (rsx::method_registers.color_write_enabled(i)) + if (m_ctx->register_state->color_write_enabled(i)) { const auto real_index = mrt_buffers[i]; m_framebuffer_layout.color_write_enabled[real_index] = true; @@ -1727,6 +1727,14 @@ namespace rsx } } + if (::size32(mrt_buffers) != current_fragment_program.mrt_buffers_count && + !m_graphics_state.test(rsx::pipeline_state::fragment_program_dirty) && + !is_current_program_interpreted()) + { + // Notify that we should recompile the FS + m_graphics_state |= rsx::pipeline_state::fragment_program_state_dirty; + } + return any_found; }; @@ -2038,9 +2046,10 @@ namespace rsx m_graphics_state.clear(rsx::pipeline_state::fragment_program_dirty); - current_fragment_program.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); - current_fragment_program.texcoord_control_mask = rsx::method_registers.texcoord_control_mask(); - current_fragment_program.two_sided_lighting = rsx::method_registers.two_side_light_en(); + 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(); + current_fragment_program.mrt_buffers_count = rsx::utility::get_mrt_buffers_count(m_ctx->register_state->surface_color_target()); if (method_registers.current_draw_clause.classify_mode() == primitive_class::polygon) { diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 4bcd08c9f4..257c70e4a5 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -436,6 +436,8 @@ namespace rsx bool is_current_vertex_program_instanced() const { return !!(current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS); } + virtual bool is_current_program_interpreted() const { return false; } + public: void reset(); void init(u32 ctrlAddress); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 51d0df3580..6e53891fbf 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -730,7 +730,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) else m_vertex_cache = std::make_unique(); - m_shaders_cache = std::make_unique(*m_prog_buffer, "vulkan", "v1.94"); + m_shaders_cache = std::make_unique(*m_prog_buffer, "vulkan", "v1.95"); for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i) { @@ -2363,6 +2363,11 @@ void VKGSRender::load_program_env() m_graphics_state.clear(handled_flags); } +bool VKGSRender::is_current_program_interpreted() const +{ + return m_program && m_shader_interpreter.is_interpreter(m_program); +} + void VKGSRender::upload_transform_constants(const rsx::io_buffer& buffer) { const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 55c4b029bb..c245a2677f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -288,6 +288,9 @@ public: // GRAPH backend void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override; + // Misc + bool is_current_program_interpreted() const override; + protected: void clear_surface(u32 mask) override; void begin() override; diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 843789d442..7f82c210d9 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -51,7 +51,10 @@ namespace rsx u16 fp_shadow_textures; u16 fp_redirected_textures; u16 fp_multisampled_textures; - u64 fp_reserved_0; + u8 fp_mrt_count; + u8 fp_reserved0; + u16 fp_reserved1; + u32 fp_reserved2; pipeline_storage_type pipeline_properties; }; @@ -306,20 +309,24 @@ namespace rsx fs::write_file(vp_name, fs::rewrite, vp.data); } - u64 state_hash = 0; - state_hash ^= rpcs3::hash_base(data.vp_ctrl0); - state_hash ^= rpcs3::hash_base(data.vp_ctrl1); - state_hash ^= rpcs3::hash_base(data.fp_ctrl); - state_hash ^= rpcs3::hash_base(data.vp_texture_dimensions); - state_hash ^= rpcs3::hash_base(data.fp_texture_dimensions); - state_hash ^= rpcs3::hash_base(data.fp_texcoord_control); - state_hash ^= rpcs3::hash_base(data.fp_height); - state_hash ^= rpcs3::hash_base(data.fp_pixel_layout); - state_hash ^= rpcs3::hash_base(data.fp_lighting_flags); - state_hash ^= rpcs3::hash_base(data.fp_shadow_textures); - state_hash ^= rpcs3::hash_base(data.fp_redirected_textures); - state_hash ^= rpcs3::hash_base(data.vp_multisampled_textures); - state_hash ^= rpcs3::hash_base(data.fp_multisampled_textures); + const u32 state_params[] = + { + data.vp_ctrl0, + data.vp_ctrl1, + data.fp_ctrl, + data.vp_texture_dimensions, + data.fp_texture_dimensions, + data.fp_texcoord_control, + data.fp_height, + data.fp_pixel_layout, + data.fp_lighting_flags, + data.fp_shadow_textures, + data.fp_redirected_textures, + data.vp_multisampled_textures, + data.fp_multisampled_textures, + data.fp_mrt_count, + }; + const usz state_hash = rpcs3::hash_array(state_params); 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; @@ -439,6 +446,7 @@ namespace rsx data_block.fp_shadow_textures = fp.texture_state.shadow_textures; data_block.fp_redirected_textures = fp.texture_state.redirected_textures; data_block.fp_multisampled_textures = fp.texture_state.multisampled_textures; + data_block.fp_mrt_count = fp.mrt_buffers_count; return data_block; } diff --git a/rpcs3/util/fnv_hash.hpp b/rpcs3/util/fnv_hash.hpp index 42ff84abe2..dea3d8b480 100644 --- a/rpcs3/util/fnv_hash.hpp +++ b/rpcs3/util/fnv_hash.hpp @@ -61,4 +61,29 @@ namespace rpcs3 return hash_struct_base(value); } + + template + requires std::is_integral_v + static inline usz hash_array(const T(&arr)[N]) + { + usz hash = fnv_seed; + for (size_t i = 0; i < N; ++i) + { + hash = hash64(hash, arr[i]); + } + return hash; + } + + template + requires std::is_class_v + static inline usz hash_array(const T(&arr)[N]) + { + usz hash = fnv_seed; + for (size_t i = 0; i < N; ++i) + { + const u64 item_hash = hash_struct(arr[i]); + hash = hash64(hash, item_hash); + } + return hash; + } }