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:
kd-11 2017-08-10 22:40:20 +03:00
parent c7dca1dbef
commit 00c6a589a5
13 changed files with 150 additions and 66 deletions

27
Utilities/hash.h Normal file
View 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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" />

View File

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