rsx/prog: Use a proper cache hint key instead of disjointed counters

This commit is contained in:
kd-11 2025-03-08 19:03:05 +03:00 committed by kd-11
parent 26495a8455
commit a1c8f3a528
9 changed files with 110 additions and 101 deletions

View file

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

View file

@ -135,13 +135,13 @@ struct GLProgramBuffer : public program_state_cache<GLTraits>
template <typename... Args>
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>(args)...);
get_graphics_pipeline(nullptr, vp, fp, props, false, false, std::forward<Args>(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

View file

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

View file

@ -7,6 +7,7 @@
#include "util/logs.hpp"
#include "util/fnv_hash.hpp"
#include "util/v128.hpp"
#include <util/bless.hpp>
#include <span>
#include <unordered_map>
@ -86,6 +87,47 @@ namespace program_hash_util
namespace rsx
{
struct program_cache_hint_t
{
template <typename T>
T* get_fragment_program() const
{
return utils::bless<T>(cached_fragment_program);
}
template <typename T>
T* get_vertex_program() const
{
return utils::bless<T>(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<f32>& buffer, const RSXFragmentProgram& rsx_prog, const std::vector<usz>& 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<typename backend_traits>
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<const vertex_program_type&, bool> search_vertex_program(const RSXVertexProgram& rsx_vp, usz rsx_vp_invalidation_count)
std::tuple<const vertex_program_type&, bool> 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<vertex_program_type>(), true);
}
bool recompile = false;
vertex_program_type* new_shader;
{
thread_local const std::pair<const RSXVertexProgram, vertex_program_type>* prev_vp = nullptr;
thread_local usz prev_map_count = umax, prev_rsx_count = umax;
static atomic_t<usz> 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<const fragment_program_type&, bool> search_fragment_program(const RSXFragmentProgram& rsx_fp, usz rsx_fp_invalidation_count)
std::tuple<const fragment_program_type&, bool> 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<fragment_program_type>(), true);
}
bool recompile = false;
typename binary_to_fragment_program::iterator it;
fragment_program_type* new_shader;
{
thread_local const std::pair<const RSXFragmentProgram, fragment_program_type>* prev_fp = nullptr;
thread_local usz prev_map_count = umax, prev_rsx_count = umax;
static atomic_t<usz> 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<typename... Args>
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>(args)...); // Other arguments

View file

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

View file

@ -242,8 +242,7 @@ namespace rsx
rsx::atomic_bitmask_t<rsx::eng_interrupt_reason> m_eng_interrupt_mask;
rsx::bitmask_t<rsx::pipeline_state> 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();

View file

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

View file

@ -88,13 +88,13 @@ namespace vk
template <typename... Args>
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>(args)...);
get_graphics_pipeline(nullptr, vp, fp, props, false, false, std::forward<Args>(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

View file

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