diff --git a/Utilities/hash.h b/Utilities/hash.h new file mode 100644 index 0000000000..bc161ff96f --- /dev/null +++ b/Utilities/hash.h @@ -0,0 +1,27 @@ +#pragma once +#include + +namespace rpcs3 +{ + template + static size_t hash_base(T value) + { + return static_cast(value); + } + + template + static size_t hash_struct(const T& value) + { + // FNV 64-bit + size_t result = 14695981039346656037ull; + const unsigned char *bytes = reinterpret_cast(&value); + + for (size_t n = 0; n < sizeof(T); ++n) + { + result ^= bytes[n]; + result *= 1099511628211ull; + } + + return result; + } +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Common/ProgramStateCache.cpp index 7f73832364..62c85832a8 100644 --- a/rpcs3/Emu/RSX/Common/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Common/ProgramStateCache.cpp @@ -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; diff --git a/rpcs3/Emu/RSX/Common/ProgramStateCache.h b/rpcs3/Emu/RSX/Common/ProgramStateCache.h index 620255e3d9..60f754b7d9 100644 --- a/rpcs3/Emu/RSX/Common/ProgramStateCache.h +++ b/rpcs3/Emu/RSX/Common/ProgramStateCache.h @@ -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()(key.vertex_program_id); - hashValue ^= std::hash()(key.fragment_program_id); - hashValue ^= std::hash()(key.properties); + hashValue ^= rpcs3::hash_base(key.vertex_program_id); + hashValue ^= rpcs3::hash_base(key.fragment_program_id); + hashValue ^= rpcs3::hash_struct(key.properties); return hashValue; } }; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index bb2dc3c7a9..9296da2378 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -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); -} \ No newline at end of file +} diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index fdbb45aa60..259783bebe 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -19,6 +19,8 @@ namespace gl using vertex_cache = rsx::vertex_cache::default_vertex_cache, GLenum>; using weak_vertex_cache = rsx::vertex_cache::weak_vertex_cache; using null_vertex_cache = vertex_cache; + + using shader_cache = rsx::shaders_cache; } struct work_item @@ -66,6 +68,7 @@ private: s64 m_textures_upload_time = 0; std::unique_ptr m_vertex_cache; + std::unique_ptr m_shaders_cache; GLint m_min_texbuffer_alignment = 256; GLint m_uniform_buffer_offset_align = 256; diff --git a/rpcs3/Emu/RSX/GL/GLProgramBuffer.h b/rpcs3/Emu/RSX/GL/GLProgramBuffer.h index b9560a0952..7ea8c85bfc 100644 --- a/rpcs3/Emu/RSX/GL/GLProgramBuffer.h +++ b/rpcs3/Emu/RSX/GL/GLProgramBuffer.h @@ -73,4 +73,32 @@ struct GLTraits class GLProgramBuffer : public program_state_cache { +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 + 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)...); + } + + bool check_cache_missed() const + { + return m_cache_miss_flag; + } }; diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index f194115076..74055eba5c 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -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() diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 7fb313183e..21302f49c4 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -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(); diff --git a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h index 685a21268c..53cab010de 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h +++ b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h @@ -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 - 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()(data[i]); - return result; - } -} - -namespace std +namespace rpcs3 { template <> - struct hash { - size_t operator()(const vk::pipeline_props &pipelineProperties) const { - size_t seed = hash()(pipelineProperties.num_targets); - seed ^= hash_struct(pipelineProperties.ia); - seed ^= hash_struct(pipelineProperties.ds); - seed ^= hash_struct(pipelineProperties.rs); + size_t hash_struct(const vk::pipeline_props &pipelineProperties) + { + size_t seed = hash_base(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()(seed); - } - }; + seed ^= hash_struct(pipelineProperties.att_state[0]); + return hash_base(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(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(ID); } static @@ -191,7 +179,7 @@ public: u64 get_hash(vk::pipeline_props &props) { - return std::hash()(props); + return rpcs3::hash_struct(props); } u64 get_hash(RSXVertexProgram &prog) diff --git a/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp b/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp index 633b4cf2b0..c63ea7e447 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp @@ -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() diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index d49756872f..3ef9e67158 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -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> 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 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 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(output); + fs::file(vp_name, fs::rewrite).write(vp.data); } u64 state_hash = 0; - state_hash ^= std::hash()(data.fp_ctrl); - state_hash ^= std::hash()(data.fp_texture_dimensions); - state_hash ^= std::hash()(data.fp_unnormalized_coords); - state_hash ^= std::hash()(data.fp_height); - state_hash ^= std::hash()(data.fp_pixel_layout); - state_hash ^= std::hash()(data.fp_lighting_flags); - state_hash ^= std::hash()(data.fp_shadow_textures); - state_hash ^= std::hash()(data.fp_redirected_textures); + state_hash ^= rpcs3::hash_base(data.vp_ctrl); + state_hash ^= rpcs3::hash_base(data.fp_ctrl); + state_hash ^= rpcs3::hash_base(data.fp_texture_dimensions); + state_hash ^= rpcs3::hash_base(data.fp_unnormalized_coords); + state_hash ^= rpcs3::hash_base(data.fp_height); + state_hash ^= rpcs3::hash_base(data.fp_pixel_layout); + state_hash ^= rpcs3::hash_base(data.fp_lighting_flags); + state_hash ^= rpcs3::hash_base(data.fp_shadow_textures); + state_hash ^= rpcs3::hash_base(data.fp_redirected_textures); + state_hash ^= rpcs3::hash_base(data.fp_alphakill_mask); + state_hash ^= rpcs3::hash_base(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(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; } }; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 8ec112c7c4..c17cdc946a 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -422,6 +422,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index abc827a06b..0a2f2d33f1 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1786,5 +1786,8 @@ Emu\GPU\RSX\Common + + Utilities + \ No newline at end of file