mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
rsx/prog: Use a proper cache hint key instead of disjointed counters
This commit is contained in:
parent
26495a8455
commit
a1c8f3a528
9 changed files with 110 additions and 101 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue