rsx/shaders: Track active MRT count per shader

- Also use more robust hashing to avoid collisions
This commit is contained in:
kd-11 2025-01-09 03:22:12 +03:00 committed by kd-11
parent a635e24fc2
commit 27c56cde22
15 changed files with 135 additions and 43 deletions

View File

@ -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<int>(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<int>(color_target));
}
usz get_aligned_pitch(surface_color_format format, u32 width)

View File

@ -17,6 +17,7 @@ namespace rsx
namespace utility
{
std::vector<u8> 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);
}

View File

@ -39,7 +39,7 @@ u64 GLGSRender::get_cycles()
GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar)
{
m_shaders_cache = std::make_unique<gl::shader_cache>(m_prog_buffer, "opengl", "v1.94");
m_shaders_cache = std::make_unique<gl::shader_cache>(m_prog_buffer, "opengl", "v1.95");
if (g_cfg.video.disable_vertex_cache)
m_vertex_cache = std::make_unique<gl::null_vertex_cache>();
@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -84,6 +84,6 @@ namespace gl
void update_fragment_textures(const std::array<std::unique_ptr<rsx::sampled_image_descriptor_base>, 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;
};
}

View File

@ -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];

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -730,7 +730,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
else
m_vertex_cache = std::make_unique<vk::weak_vertex_cache>();
m_shaders_cache = std::make_unique<vk::shader_cache>(*m_prog_buffer, "vulkan", "v1.94");
m_shaders_cache = std::make_unique<vk::shader_cache>(*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;

View File

@ -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;

View File

@ -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<u32>(data.vp_ctrl0);
state_hash ^= rpcs3::hash_base<u32>(data.vp_ctrl1);
state_hash ^= rpcs3::hash_base<u32>(data.fp_ctrl);
state_hash ^= rpcs3::hash_base<u32>(data.vp_texture_dimensions);
state_hash ^= rpcs3::hash_base<u32>(data.fp_texture_dimensions);
state_hash ^= rpcs3::hash_base<u32>(data.fp_texcoord_control);
state_hash ^= rpcs3::hash_base<u16>(data.fp_height);
state_hash ^= rpcs3::hash_base<u16>(data.fp_pixel_layout);
state_hash ^= rpcs3::hash_base<u16>(data.fp_lighting_flags);
state_hash ^= rpcs3::hash_base<u16>(data.fp_shadow_textures);
state_hash ^= rpcs3::hash_base<u16>(data.fp_redirected_textures);
state_hash ^= rpcs3::hash_base<u16>(data.vp_multisampled_textures);
state_hash ^= rpcs3::hash_base<u16>(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;
}

View File

@ -61,4 +61,29 @@ namespace rpcs3
return hash_struct_base<T, u8>(value);
}
template <typename T, size_t N>
requires std::is_integral_v<T>
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 <typename T, size_t N>
requires std::is_class_v<T>
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;
}
}