From 240a3b80d970b56b4ed3671536489eb0e32532ae Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 Oct 2015 22:46:47 -0400 Subject: [PATCH] gl_rasterizer: Use MMH3 hash for shader cache hey. - Includes a check to confirm no hash collisions. --- src/common/common_funcs.h | 18 --- .../renderer_opengl/gl_rasterizer.cpp | 7 +- .../renderer_opengl/gl_rasterizer.h | 135 ++++++++---------- .../renderer_opengl/gl_shader_gen.cpp | 4 +- .../renderer_opengl/gl_shader_gen.h | 2 +- 5 files changed, 64 insertions(+), 102 deletions(-) diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 7a8dd39a0..ed20c3629 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -4,9 +4,6 @@ #pragma once -#include -#include - #include "common_types.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -98,18 +95,3 @@ inline u64 _rotr64(u64 x, unsigned int shift){ // This function might change the error code. // Defined in Misc.cpp. const char* GetLastErrorMsg(); - -template -inline std::size_t hash(const T& o) { - return std::hash()(o); -} - -template -inline std::size_t combine_hash(const T& o) { - return hash(o); -} - -template -inline std::size_t combine_hash(const T& o, const Args&... args) { - return hash(o) * 3 + combine_hash(args...); -} diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 64639ed26..4ae42f226 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -461,7 +461,8 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: } void RasterizerOpenGL::SetShader() { - ShaderCacheKey config = ShaderCacheKey::CurrentConfig(); + PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); + std::unique_ptr shader = Common::make_unique(); // Find (or generate) the GLSL shader for the current TEV state auto cached_shader = shader_cache.find(config); @@ -471,9 +472,7 @@ void RasterizerOpenGL::SetShader() { state.draw.shader_program = current_shader->shader.handle; state.Apply(); } else { - LOG_DEBUG(Render_OpenGL, "Creating new shader: %08X", hash(config)); - - std::unique_ptr shader = Common::make_unique(); + LOG_DEBUG(Render_OpenGL, "Creating new shader"); shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 79c34944a..cf8df8d9c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -5,11 +5,13 @@ #pragma once #include +#include #include #include #include #include "common/common_types.h" +#include "common/hash.h" #include "video_core/pica.h" #include "video_core/hwrasterizer_base.h" @@ -25,16 +27,52 @@ * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) * two separate shaders sharing the same key. */ -struct ShaderCacheKey { - using Regs = Pica::Regs; +struct PicaShaderConfig { + /// Construct a PicaShaderConfig with the current Pica register configuration. + static PicaShaderConfig CurrentConfig() { + PicaShaderConfig res; + const auto& regs = Pica::g_state.regs; - bool operator ==(const ShaderCacheKey& o) const { - return hash(*this) == hash(o); - }; + res.alpha_test_func = regs.output_merger.alpha_test.enable ? + regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; - Regs::CompareFunc alpha_test_func; - std::array tev_stages = {}; - u8 combiner_buffer_input; + // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling + // the GetTevStages() function) because BitField explicitly disables copies. + + res.tev_stages[0].sources_raw = regs.tev_stage0.sources_raw; + res.tev_stages[1].sources_raw = regs.tev_stage1.sources_raw; + res.tev_stages[2].sources_raw = regs.tev_stage2.sources_raw; + res.tev_stages[3].sources_raw = regs.tev_stage3.sources_raw; + res.tev_stages[4].sources_raw = regs.tev_stage4.sources_raw; + res.tev_stages[5].sources_raw = regs.tev_stage5.sources_raw; + + res.tev_stages[0].modifiers_raw = regs.tev_stage0.modifiers_raw; + res.tev_stages[1].modifiers_raw = regs.tev_stage1.modifiers_raw; + res.tev_stages[2].modifiers_raw = regs.tev_stage2.modifiers_raw; + res.tev_stages[3].modifiers_raw = regs.tev_stage3.modifiers_raw; + res.tev_stages[4].modifiers_raw = regs.tev_stage4.modifiers_raw; + res.tev_stages[5].modifiers_raw = regs.tev_stage5.modifiers_raw; + + res.tev_stages[0].ops_raw = regs.tev_stage0.ops_raw; + res.tev_stages[1].ops_raw = regs.tev_stage1.ops_raw; + res.tev_stages[2].ops_raw = regs.tev_stage2.ops_raw; + res.tev_stages[3].ops_raw = regs.tev_stage3.ops_raw; + res.tev_stages[4].ops_raw = regs.tev_stage4.ops_raw; + res.tev_stages[5].ops_raw = regs.tev_stage5.ops_raw; + + res.tev_stages[0].scales_raw = regs.tev_stage0.scales_raw; + res.tev_stages[1].scales_raw = regs.tev_stage1.scales_raw; + res.tev_stages[2].scales_raw = regs.tev_stage2.scales_raw; + res.tev_stages[3].scales_raw = regs.tev_stage3.scales_raw; + res.tev_stages[4].scales_raw = regs.tev_stage4.scales_raw; + res.tev_stages[5].scales_raw = regs.tev_stage5.scales_raw; + + res.combiner_buffer_input = + regs.tev_combiner_buffer_input.update_mask_rgb.Value() | + regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; + + return res; + } bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index)); @@ -44,78 +82,21 @@ struct ShaderCacheKey { return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); } - /** - * This function is used to construct a ShaderCacheKey with the current Pica register - * configuration. Don't construct a ShaderCacheKey manually, instead call this function (and - * extend it as additional state needs to be captured to generate shaders). - */ - static ShaderCacheKey CurrentConfig() { - const auto& regs = Pica::g_state.regs; - ShaderCacheKey config; + bool operator ==(const PicaShaderConfig& o) const { + return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; + }; - config.alpha_test_func = regs.output_merger.alpha_test.enable ? - regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; - - // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling - // the GetTevStages() function) because BitField explicitly disables copies. - - config.tev_stages[0].source_raw = regs.tev_stage0.source_raw; - config.tev_stages[1].source_raw = regs.tev_stage1.source_raw; - config.tev_stages[2].source_raw = regs.tev_stage2.source_raw; - config.tev_stages[3].source_raw = regs.tev_stage3.source_raw; - config.tev_stages[4].source_raw = regs.tev_stage4.source_raw; - config.tev_stages[5].source_raw = regs.tev_stage5.source_raw; - - config.tev_stages[0].modifier_raw = regs.tev_stage0.modifier_raw; - config.tev_stages[1].modifier_raw = regs.tev_stage1.modifier_raw; - config.tev_stages[2].modifier_raw = regs.tev_stage2.modifier_raw; - config.tev_stages[3].modifier_raw = regs.tev_stage3.modifier_raw; - config.tev_stages[4].modifier_raw = regs.tev_stage4.modifier_raw; - config.tev_stages[5].modifier_raw = regs.tev_stage5.modifier_raw; - - config.tev_stages[0].op_raw = regs.tev_stage0.op_raw; - config.tev_stages[1].op_raw = regs.tev_stage1.op_raw; - config.tev_stages[2].op_raw = regs.tev_stage2.op_raw; - config.tev_stages[3].op_raw = regs.tev_stage3.op_raw; - config.tev_stages[4].op_raw = regs.tev_stage4.op_raw; - config.tev_stages[5].op_raw = regs.tev_stage5.op_raw; - - config.tev_stages[0].scale_raw = regs.tev_stage0.scale_raw; - config.tev_stages[1].scale_raw = regs.tev_stage1.scale_raw; - config.tev_stages[2].scale_raw = regs.tev_stage2.scale_raw; - config.tev_stages[3].scale_raw = regs.tev_stage3.scale_raw; - config.tev_stages[4].scale_raw = regs.tev_stage4.scale_raw; - config.tev_stages[5].scale_raw = regs.tev_stage5.scale_raw; - - config.combiner_buffer_input = - regs.tev_combiner_buffer_input.update_mask_rgb.Value() | - regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; - - return config; - } + Pica::Regs::CompareFunc alpha_test_func; + std::array tev_stages = {}; + u8 combiner_buffer_input; }; namespace std { -template<> struct hash<::Pica::Regs::CompareFunc> { - std::size_t operator()(const ::Pica::Regs::CompareFunc& o) { - return ::hash((unsigned)o); - } -}; - -template<> struct hash<::Pica::Regs::TevStageConfig> { - std::size_t operator()(const ::Pica::Regs::TevStageConfig& o) { - return ::combine_hash( - ::hash(o.source_raw), ::hash(o.modifier_raw), - ::hash(o.op_raw), ::hash(o.scale_raw)); - } -}; - -template<> struct hash<::ShaderCacheKey> { - std::size_t operator()(const ::ShaderCacheKey& o) const { - return ::combine_hash(o.alpha_test_func, o.combiner_buffer_input, - o.tev_stages[0], o.tev_stages[1], o.tev_stages[2], - o.tev_stages[3], o.tev_stages[4], o.tev_stages[5]); +template <> +struct hash { + std::size_t operator()(const PicaShaderConfig& k) const { + return Common::ComputeHash64(&k, sizeof(PicaShaderConfig)); } }; @@ -314,8 +295,8 @@ private: TextureInfo fb_color_texture; DepthTextureInfo fb_depth_texture; - std::unordered_map> shader_cache; - const TEVShader* current_shader = nullptr; + std::unordered_map> shader_cache; + const PicaShader* current_shader = nullptr; OGLVertexArray vertex_array; OGLBuffer vertex_buffer; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 50bb2e3cc..84883b483 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -278,7 +278,7 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { } /// Writes the code to emulate the specified TEV stage -static void WriteTevStage(std::string& out, const ShaderCacheKey& config, unsigned index) { +static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) { auto& stage = config.tev_stages[index]; if (!IsPassThroughTevStage(stage)) { std::string index_name = std::to_string(index); @@ -319,7 +319,7 @@ static void WriteTevStage(std::string& out, const ShaderCacheKey& config, unsign out += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; } -std::string GenerateFragmentShader(const ShaderCacheKey& config) { +std::string GenerateFragmentShader(const PicaShaderConfig& config) { std::string out = R"( #version 330 #extension GL_ARB_explicit_uniform_location : require diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index c8295e9e0..0ca9d2879 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -22,6 +22,6 @@ std::string GenerateVertexShader(); * configuration (NOTE: Use state in this struct only, not the Pica registers!) * @returns String of the shader source code */ -std::string GenerateFragmentShader(const ShaderCacheKey& config); +std::string GenerateFragmentShader(const PicaShaderConfig& config); } // namespace GLShader