mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-15 22:21:25 +00:00
rsx/util: Add simple consistent hash function
rsx/vk/shaders_cache: Move vp control mask to dynamic state rsx/vk/gl: adds a shader cache for GL. Also Separates pipeline storage for each backend rsx: Add more texture state variables to the cache
This commit is contained in:
parent
c7dca1dbef
commit
00c6a589a5
27
Utilities/hash.h
Normal file
27
Utilities/hash.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
template<typename T>
|
||||
static size_t hash_base(T value)
|
||||
{
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
{
|
||||
// FNV 64-bit
|
||||
size_t result = 14695981039346656037ull;
|
||||
const unsigned char *bytes = reinterpret_cast<const unsigned char*>(&value);
|
||||
|
||||
for (size_t n = 0; n < sizeof(T); ++n)
|
||||
{
|
||||
result ^= bytes[n];
|
||||
result *= 1099511628211ull;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -109,10 +109,19 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con
|
||||
if (binary1.texture_dimensions != binary2.texture_dimensions || binary1.unnormalized_coords != binary2.unnormalized_coords ||
|
||||
binary1.height != binary2.height || binary1.origin_mode != binary2.origin_mode || binary1.pixel_center_mode != binary2.pixel_center_mode ||
|
||||
binary1.back_color_diffuse_output != binary2.back_color_diffuse_output || binary1.back_color_specular_output != binary2.back_color_specular_output ||
|
||||
binary1.front_back_color_enabled != binary2.front_back_color_enabled || binary1.alpha_func != binary2.alpha_func ||
|
||||
binary1.front_back_color_enabled != binary2.front_back_color_enabled || binary1.alpha_func != binary2.alpha_func || binary1.fog_equation != binary2.fog_equation ||
|
||||
binary1.shadow_textures != binary2.shadow_textures || binary1.redirected_textures != binary2.redirected_textures)
|
||||
return false;
|
||||
|
||||
for (u8 index = 0; index < 16; ++index)
|
||||
{
|
||||
if (binary1.textures_alpha_kill[index] != binary2.textures_alpha_kill[index])
|
||||
return false;
|
||||
|
||||
if (binary1.textures_zfunc[index] != binary2.textures_zfunc[index])
|
||||
return false;
|
||||
}
|
||||
|
||||
const qword *instBuffer1 = (const qword*)binary1.addr;
|
||||
const qword *instBuffer2 = (const qword*)binary2.addr;
|
||||
size_t instIndex = 0;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Emu/Memory/vm.h"
|
||||
|
||||
#include "Utilities/GSL.h"
|
||||
#include "Utilities/hash.h"
|
||||
|
||||
enum class SHADER_TYPE
|
||||
{
|
||||
@ -93,9 +94,9 @@ class program_state_cache
|
||||
size_t operator()(const pipeline_key &key) const
|
||||
{
|
||||
size_t hashValue = 0;
|
||||
hashValue ^= std::hash<unsigned>()(key.vertex_program_id);
|
||||
hashValue ^= std::hash<unsigned>()(key.fragment_program_id);
|
||||
hashValue ^= std::hash<pipeline_properties>()(key.properties);
|
||||
hashValue ^= rpcs3::hash_base<unsigned>(key.vertex_program_id);
|
||||
hashValue ^= rpcs3::hash_base<unsigned>(key.fragment_program_id);
|
||||
hashValue ^= rpcs3::hash_struct<pipeline_properties>(key.properties);
|
||||
return hashValue;
|
||||
}
|
||||
};
|
||||
|
@ -24,8 +24,7 @@ namespace
|
||||
|
||||
GLGSRender::GLGSRender() : GSRender()
|
||||
{
|
||||
//TODO
|
||||
//shaders_cache.load(rsx::old_shaders_cache::shader_language::glsl);
|
||||
m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1"));
|
||||
|
||||
if (g_cfg.video.disable_vertex_cache)
|
||||
m_vertex_cache.reset(new gl::null_vertex_cache());
|
||||
@ -699,6 +698,8 @@ void GLGSRender::on_init_thread()
|
||||
glEnable(GL_CLIP_DISTANCE0 + 5);
|
||||
|
||||
m_gl_texture_cache.initialize(this);
|
||||
|
||||
m_shaders_cache->load();
|
||||
}
|
||||
|
||||
void GLGSRender::on_exit()
|
||||
@ -911,10 +912,14 @@ void GLGSRender::load_program(u32 vertex_base, u32 vertex_count)
|
||||
}
|
||||
|
||||
vertex_program.skip_vertex_input_check = true; //not needed for us since decoding is done server side
|
||||
auto old_program = m_program;
|
||||
m_program = &m_prog_buffer.getGraphicPipelineState(vertex_program, fragment_program, nullptr);
|
||||
void* pipeline_properties = nullptr;
|
||||
|
||||
m_program = &m_prog_buffer.getGraphicPipelineState(vertex_program, fragment_program, pipeline_properties);
|
||||
m_program->use();
|
||||
|
||||
if (m_prog_buffer.check_cache_missed())
|
||||
m_shaders_cache->store(pipeline_properties, vertex_program, fragment_program);
|
||||
|
||||
u8 *buf;
|
||||
u32 vertex_state_offset;
|
||||
u32 vertex_constants_offset;
|
||||
@ -1361,4 +1366,4 @@ u32 GLGSRender::synchronize_zcull_stats(bool hard_sync)
|
||||
void GLGSRender::notify_zcull_info_changed()
|
||||
{
|
||||
check_zcull_status(false, false);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ namespace gl
|
||||
using vertex_cache = rsx::vertex_cache::default_vertex_cache<rsx::vertex_cache::uploaded_range<GLenum>, GLenum>;
|
||||
using weak_vertex_cache = rsx::vertex_cache::weak_vertex_cache<GLenum>;
|
||||
using null_vertex_cache = vertex_cache;
|
||||
|
||||
using shader_cache = rsx::shaders_cache<void*, GLProgramBuffer>;
|
||||
}
|
||||
|
||||
struct work_item
|
||||
@ -66,6 +68,7 @@ private:
|
||||
s64 m_textures_upload_time = 0;
|
||||
|
||||
std::unique_ptr<gl::vertex_cache> m_vertex_cache;
|
||||
std::unique_ptr<gl::shader_cache> m_shaders_cache;
|
||||
|
||||
GLint m_min_texbuffer_alignment = 256;
|
||||
GLint m_uniform_buffer_offset_align = 256;
|
||||
|
@ -73,4 +73,32 @@ struct GLTraits
|
||||
|
||||
class GLProgramBuffer : public program_state_cache<GLTraits>
|
||||
{
|
||||
public:
|
||||
|
||||
u64 get_hash(void*&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 get_hash(RSXVertexProgram &prog)
|
||||
{
|
||||
return program_hash_util::vertex_program_hash()(prog);
|
||||
}
|
||||
|
||||
u64 get_hash(RSXFragmentProgram &prog)
|
||||
{
|
||||
return program_hash_util::fragment_program_hash()(prog);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void add_pipeline_entry(RSXVertexProgram &vp, RSXFragmentProgram &fp, void* &props, Args&& ...args)
|
||||
{
|
||||
vp.skip_vertex_input_check = true;
|
||||
getGraphicPipelineState(vp, fp, props, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
bool check_cache_missed() const
|
||||
{
|
||||
return m_cache_miss_flag;
|
||||
}
|
||||
};
|
||||
|
@ -495,7 +495,7 @@ void VKFragmentProgram::Compile()
|
||||
VkDevice dev = (VkDevice)*vk::get_current_renderer();
|
||||
vkCreateShaderModule(dev, &fs_info, nullptr, &handle);
|
||||
|
||||
id = (u32)((u64)handle);
|
||||
id = UINT32_MAX;
|
||||
}
|
||||
|
||||
void VKFragmentProgram::Delete()
|
||||
|
@ -621,7 +621,7 @@ VKGSRender::VKGSRender() : GSRender()
|
||||
else
|
||||
m_vertex_cache.reset(new vk::weak_vertex_cache());
|
||||
|
||||
m_shaders_cache.reset(new vk::shader_cache(*m_prog_buffer.get(), "v1"));
|
||||
m_shaders_cache.reset(new vk::shader_cache(*m_prog_buffer.get(), "vulkan", "v1"));
|
||||
|
||||
open_command_buffer();
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "VKVertexProgram.h"
|
||||
#include "VKFragmentProgram.h"
|
||||
#include "../Common/ProgramStateCache.h"
|
||||
|
||||
#include "Utilities/hash.h"
|
||||
|
||||
namespace vk
|
||||
{
|
||||
@ -52,38 +52,24 @@ namespace vk
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
size_t hash_struct(const T& structure)
|
||||
{
|
||||
char *data = (char*)(&structure);
|
||||
size_t result = 0;
|
||||
for (unsigned i = 0; i < sizeof(T); i++)
|
||||
result ^= std::hash<char>()(data[i]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
namespace rpcs3
|
||||
{
|
||||
template <>
|
||||
struct hash<vk::pipeline_props> {
|
||||
size_t operator()(const vk::pipeline_props &pipelineProperties) const {
|
||||
size_t seed = hash<unsigned>()(pipelineProperties.num_targets);
|
||||
seed ^= hash_struct(pipelineProperties.ia);
|
||||
seed ^= hash_struct(pipelineProperties.ds);
|
||||
seed ^= hash_struct(pipelineProperties.rs);
|
||||
size_t hash_struct<vk::pipeline_props>(const vk::pipeline_props &pipelineProperties)
|
||||
{
|
||||
size_t seed = hash_base<int>(pipelineProperties.num_targets);
|
||||
seed ^= hash_struct(pipelineProperties.ia);
|
||||
seed ^= hash_struct(pipelineProperties.ds);
|
||||
seed ^= hash_struct(pipelineProperties.rs);
|
||||
|
||||
//Do not compare pointers to memory!
|
||||
auto tmp = pipelineProperties.cs;
|
||||
tmp.pAttachments = nullptr;
|
||||
seed ^= hash_struct(tmp);
|
||||
//Do not compare pointers to memory!
|
||||
auto tmp = pipelineProperties.cs;
|
||||
tmp.pAttachments = nullptr;
|
||||
seed ^= hash_struct(tmp);
|
||||
|
||||
seed ^= hash_struct(pipelineProperties.att_state[0]);
|
||||
return hash<size_t>()(seed);
|
||||
}
|
||||
};
|
||||
seed ^= hash_struct(pipelineProperties.att_state[0]);
|
||||
return hash_base<size_t>(seed);
|
||||
}
|
||||
}
|
||||
|
||||
struct VKTraits
|
||||
@ -94,17 +80,19 @@ struct VKTraits
|
||||
using pipeline_properties = vk::pipeline_props;
|
||||
|
||||
static
|
||||
void recompile_fragment_program(const RSXFragmentProgram &RSXFP, fragment_program_type& fragmentProgramData, size_t /*ID*/)
|
||||
void recompile_fragment_program(const RSXFragmentProgram &RSXFP, fragment_program_type& fragmentProgramData, size_t ID)
|
||||
{
|
||||
fragmentProgramData.Decompile(RSXFP);
|
||||
fragmentProgramData.Compile();
|
||||
fragmentProgramData.id = static_cast<u32>(ID);
|
||||
}
|
||||
|
||||
static
|
||||
void recompile_vertex_program(const RSXVertexProgram &RSXVP, vertex_program_type& vertexProgramData, size_t /*ID*/)
|
||||
void recompile_vertex_program(const RSXVertexProgram &RSXVP, vertex_program_type& vertexProgramData, size_t ID)
|
||||
{
|
||||
vertexProgramData.Decompile(RSXVP);
|
||||
vertexProgramData.Compile();
|
||||
vertexProgramData.id = static_cast<u32>(ID);
|
||||
}
|
||||
|
||||
static
|
||||
@ -191,7 +179,7 @@ public:
|
||||
|
||||
u64 get_hash(vk::pipeline_props &props)
|
||||
{
|
||||
return std::hash<vk::pipeline_props>()(props);
|
||||
return rpcs3::hash_struct<vk::pipeline_props>(props);
|
||||
}
|
||||
|
||||
u64 get_hash(RSXVertexProgram &prog)
|
||||
|
@ -355,7 +355,7 @@ void VKVertexProgram::Compile()
|
||||
VkDevice dev = (VkDevice)*vk::get_current_renderer();
|
||||
vkCreateShaderModule(dev, &vs_info, nullptr, &handle);
|
||||
|
||||
id = (u32)((u64)handle);
|
||||
id = UINT32_MAX;
|
||||
}
|
||||
|
||||
void VKVertexProgram::Delete()
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "Utilities/VirtualMemory.h"
|
||||
#include "Utilities/hash.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "gcm_enums.h"
|
||||
#include "Common/ProgramStateCache.h"
|
||||
@ -214,6 +215,8 @@ namespace rsx
|
||||
u64 fragment_program_hash;
|
||||
u64 pipeline_storage_hash;
|
||||
|
||||
u32 vp_ctrl;
|
||||
|
||||
u32 fp_ctrl;
|
||||
u32 fp_texture_dimensions;
|
||||
u16 fp_unnormalized_coords;
|
||||
@ -222,20 +225,24 @@ namespace rsx
|
||||
u16 fp_lighting_flags;
|
||||
u16 fp_shadow_textures;
|
||||
u16 fp_redirected_textures;
|
||||
u16 fp_alphakill_mask;
|
||||
u64 fp_zfunc_mask;
|
||||
|
||||
pipeline_storage_type pipeline_properties;
|
||||
};
|
||||
|
||||
std::string version_prefix;
|
||||
std::string root_path;
|
||||
std::string pipeline_class_name;
|
||||
std::unordered_map<u64, std::vector<u8>> fragment_program_data;
|
||||
|
||||
backend_storage& m_storage;
|
||||
|
||||
public:
|
||||
|
||||
shaders_cache(backend_storage& storage, std::string version_prefix_str = "v1")
|
||||
shaders_cache(backend_storage& storage, std::string pipeline_class, std::string version_prefix_str = "v1")
|
||||
: version_prefix(version_prefix_str)
|
||||
, pipeline_class_name(pipeline_class)
|
||||
, m_storage(storage)
|
||||
{
|
||||
root_path = Emu.GetCachePath() + "/shaders_cache";
|
||||
@ -244,7 +251,7 @@ namespace rsx
|
||||
template <typename... Args>
|
||||
void load(Args&& ...args)
|
||||
{
|
||||
std::string directory_path = root_path + "/pipelines";
|
||||
std::string directory_path = root_path + "/pipelines/" + pipeline_class_name;
|
||||
|
||||
if (!fs::is_dir(directory_path))
|
||||
{
|
||||
@ -340,26 +347,24 @@ namespace rsx
|
||||
|
||||
if (!fs::is_file(vp_name))
|
||||
{
|
||||
std::vector<u32> output;
|
||||
output.resize(vp.data.size() + 1);
|
||||
output[0] = vp.output_mask;
|
||||
std::copy(vp.data.begin(), vp.data.end(), output.begin() + 1);
|
||||
|
||||
fs::file(vp_name, fs::rewrite).write<u32>(output);
|
||||
fs::file(vp_name, fs::rewrite).write<u32>(vp.data);
|
||||
}
|
||||
|
||||
u64 state_hash = 0;
|
||||
state_hash ^= std::hash<u32>()(data.fp_ctrl);
|
||||
state_hash ^= std::hash<u32>()(data.fp_texture_dimensions);
|
||||
state_hash ^= std::hash<u16>()(data.fp_unnormalized_coords);
|
||||
state_hash ^= std::hash<u16>()(data.fp_height);
|
||||
state_hash ^= std::hash<u16>()(data.fp_pixel_layout);
|
||||
state_hash ^= std::hash<u16>()(data.fp_lighting_flags);
|
||||
state_hash ^= std::hash<u16>()(data.fp_shadow_textures);
|
||||
state_hash ^= std::hash<u16>()(data.fp_redirected_textures);
|
||||
state_hash ^= rpcs3::hash_base<u32>(data.vp_ctrl);
|
||||
state_hash ^= rpcs3::hash_base<u32>(data.fp_ctrl);
|
||||
state_hash ^= rpcs3::hash_base<u32>(data.fp_texture_dimensions);
|
||||
state_hash ^= rpcs3::hash_base<u16>(data.fp_unnormalized_coords);
|
||||
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.fp_alphakill_mask);
|
||||
state_hash ^= rpcs3::hash_base<u64>(data.fp_zfunc_mask);
|
||||
|
||||
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);
|
||||
std::string pipeline_path = root_path + "/pipelines/" + version_prefix + "-" + pipeline_file_name;
|
||||
std::string pipeline_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix + "-" + pipeline_file_name;
|
||||
fs::file(pipeline_path, fs::rewrite).write(&data, sizeof(pipeline_data));
|
||||
}
|
||||
|
||||
@ -372,11 +377,8 @@ namespace rsx
|
||||
f.read<u32>(data, f.size() / sizeof(u32));
|
||||
|
||||
RSXVertexProgram vp = {};
|
||||
vp.data.resize(data.size() - 1);
|
||||
|
||||
vp.output_mask = data[0];
|
||||
vp.data = data;
|
||||
vp.skip_vertex_input_check = true;
|
||||
std::copy(data.begin() + 1, data.end(), vp.data.begin());
|
||||
|
||||
return vp;
|
||||
}
|
||||
@ -402,6 +404,8 @@ namespace rsx
|
||||
RSXFragmentProgram fp = load_fp_raw(data.fragment_program_hash);
|
||||
pipeline_storage_type pipeline = data.pipeline_properties;
|
||||
|
||||
vp.output_mask = data.vp_ctrl;
|
||||
|
||||
fp.ctrl = data.fp_ctrl;
|
||||
fp.texture_dimensions = data.fp_texture_dimensions;
|
||||
fp.unnormalized_coords = data.fp_unnormalized_coords;
|
||||
@ -409,6 +413,7 @@ namespace rsx
|
||||
fp.pixel_center_mode = (rsx::window_pixel_center)(data.fp_pixel_layout & 0x3);
|
||||
fp.origin_mode = (rsx::window_origin)((data.fp_pixel_layout >> 2) & 0x1);
|
||||
fp.alpha_func = (rsx::comparison_function)((data.fp_pixel_layout >> 3) & 0xF);
|
||||
fp.fog_equation = (rsx::fog_mode)((data.fp_pixel_layout >> 7) & 0xF);
|
||||
fp.front_back_color_enabled = (data.fp_lighting_flags & 0x1) != 0;
|
||||
fp.back_color_diffuse_output = ((data.fp_lighting_flags >> 1) & 0x1) != 0;
|
||||
fp.back_color_specular_output = ((data.fp_lighting_flags >> 2) & 0x1) != 0;
|
||||
@ -417,17 +422,25 @@ namespace rsx
|
||||
fp.shadow_textures = data.fp_shadow_textures;
|
||||
fp.redirected_textures = data.fp_redirected_textures;
|
||||
|
||||
for (u8 index = 0; index < 16; ++index)
|
||||
{
|
||||
fp.textures_alpha_kill[index] = (data.fp_alphakill_mask & (1 << index))? 1: 0;
|
||||
fp.textures_zfunc[index] = (data.fp_zfunc_mask >> (index << 2)) & 0xF;
|
||||
}
|
||||
|
||||
return std::make_tuple(pipeline, vp, fp);
|
||||
}
|
||||
|
||||
pipeline_data pack(pipeline_storage_type &pipeline, RSXVertexProgram &vp, RSXFragmentProgram &fp)
|
||||
{
|
||||
pipeline_data data_block;
|
||||
pipeline_data data_block = {};
|
||||
data_block.pipeline_properties = pipeline;
|
||||
data_block.vertex_program_hash = m_storage.get_hash(vp);
|
||||
data_block.fragment_program_hash = m_storage.get_hash(fp);
|
||||
data_block.pipeline_storage_hash = m_storage.get_hash(pipeline);
|
||||
|
||||
data_block.vp_ctrl = vp.output_mask;
|
||||
|
||||
data_block.fp_ctrl = fp.ctrl;
|
||||
data_block.fp_texture_dimensions = fp.texture_dimensions;
|
||||
data_block.fp_unnormalized_coords = fp.unnormalized_coords;
|
||||
@ -438,6 +451,12 @@ namespace rsx
|
||||
data_block.fp_shadow_textures = fp.shadow_textures;
|
||||
data_block.fp_redirected_textures = fp.redirected_textures;
|
||||
|
||||
for (u8 index = 0; index < 16; ++index)
|
||||
{
|
||||
data_block.fp_alphakill_mask |= (fp.textures_alpha_kill[index] & 0x1) << index;
|
||||
data_block.fp_zfunc_mask |= (fp.textures_zfunc[index] & 0xF) << (index << 2);
|
||||
}
|
||||
|
||||
return data_block;
|
||||
}
|
||||
};
|
||||
|
@ -422,6 +422,7 @@
|
||||
<ClInclude Include="..\Utilities\GDBDebugServer.h" />
|
||||
<ClInclude Include="..\Utilities\geometry.h" />
|
||||
<ClInclude Include="..\Utilities\GSL.h" />
|
||||
<ClInclude Include="..\Utilities\hash.h" />
|
||||
<ClInclude Include="..\Utilities\JIT.h" />
|
||||
<ClInclude Include="..\Utilities\lockless.h" />
|
||||
<ClInclude Include="..\Utilities\mutex.h" />
|
||||
|
@ -1786,5 +1786,8 @@
|
||||
<ClInclude Include="Emu\RSX\Common\GLSLCommon.h">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\hash.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user