forked from eden-emu/eden
		
	gl_rasterizer: Use MMH3 hash for shader cache hey.
- Includes a check to confirm no hash collisions.
This commit is contained in:
		
							parent
							
								
									71edb55114
								
							
						
					
					
						commit
						240a3b80d9
					
				
					 5 changed files with 64 additions and 102 deletions
				
			
		|  | @ -4,9 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> |  | ||||||
| #include <functional> |  | ||||||
| 
 |  | ||||||
| #include "common_types.h" | #include "common_types.h" | ||||||
| 
 | 
 | ||||||
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | #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.
 | // This function might change the error code.
 | ||||||
| // Defined in Misc.cpp.
 | // Defined in Misc.cpp.
 | ||||||
| const char* GetLastErrorMsg(); | const char* GetLastErrorMsg(); | ||||||
| 
 |  | ||||||
| template <typename T> |  | ||||||
| inline std::size_t hash(const T& o) { |  | ||||||
|     return std::hash<T>()(o); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <typename T> |  | ||||||
| inline std::size_t combine_hash(const T& o) { |  | ||||||
|     return hash(o); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <typename T, typename... Args> |  | ||||||
| inline std::size_t combine_hash(const T& o, const Args&... args) { |  | ||||||
|     return hash(o) * 3 + combine_hash(args...); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -461,7 +461,8 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SetShader() { | void RasterizerOpenGL::SetShader() { | ||||||
|     ShaderCacheKey config = ShaderCacheKey::CurrentConfig(); |     PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); | ||||||
|  |     std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>(); | ||||||
| 
 | 
 | ||||||
|     // Find (or generate) the GLSL shader for the current TEV state
 |     // Find (or generate) the GLSL shader for the current TEV state
 | ||||||
|     auto cached_shader = shader_cache.find(config); |     auto cached_shader = shader_cache.find(config); | ||||||
|  | @ -471,9 +472,7 @@ void RasterizerOpenGL::SetShader() { | ||||||
|         state.draw.shader_program = current_shader->shader.handle; |         state.draw.shader_program = current_shader->shader.handle; | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
|     } else { |     } else { | ||||||
|         LOG_DEBUG(Render_OpenGL, "Creating new shader: %08X", hash(config)); |         LOG_DEBUG(Render_OpenGL, "Creating new shader"); | ||||||
| 
 |  | ||||||
|         std::unique_ptr<TEVShader> shader = Common::make_unique<TEVShader>(); |  | ||||||
| 
 | 
 | ||||||
|         shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); |         shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,11 +5,13 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
|  | #include <cstring> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "common/hash.h" | ||||||
| 
 | 
 | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/hwrasterizer_base.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) |  * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) | ||||||
|  * two separate shaders sharing the same key. |  * two separate shaders sharing the same key. | ||||||
|  */ |  */ | ||||||
| struct ShaderCacheKey { | struct PicaShaderConfig { | ||||||
|     using Regs = Pica::Regs; |     /// 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 { |         res.alpha_test_func = regs.output_merger.alpha_test.enable ? | ||||||
|         return hash(*this) == hash(o); |             regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     Regs::CompareFunc alpha_test_func; |         // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
 | ||||||
|     std::array<Regs::TevStageConfig, 6> tev_stages = {}; |         // the GetTevStages() function) because BitField explicitly disables copies.
 | ||||||
|     u8 combiner_buffer_input; | 
 | ||||||
|  |         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 { |     bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { | ||||||
|         return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index)); |         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)); |         return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /**
 |     bool operator ==(const PicaShaderConfig& o) const { | ||||||
|      * This function is used to construct a ShaderCacheKey with the current Pica register |         return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; | ||||||
|      * 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; |  | ||||||
| 
 | 
 | ||||||
|         config.alpha_test_func = regs.output_merger.alpha_test.enable ? |     Pica::Regs::CompareFunc alpha_test_func; | ||||||
|             regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; |     std::array<Pica::Regs::TevStageConfig, 6> 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.
 |  | ||||||
| 
 |  | ||||||
|         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; |  | ||||||
|     } |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| namespace std { | namespace std { | ||||||
| 
 | 
 | ||||||
| template<> struct hash<::Pica::Regs::CompareFunc> { | template <> | ||||||
|     std::size_t operator()(const ::Pica::Regs::CompareFunc& o) { | struct hash<PicaShaderConfig> { | ||||||
|         return ::hash((unsigned)o); |     std::size_t operator()(const PicaShaderConfig& k) const { | ||||||
|     } |         return Common::ComputeHash64(&k, sizeof(PicaShaderConfig)); | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 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]); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -314,8 +295,8 @@ private: | ||||||
|     TextureInfo fb_color_texture; |     TextureInfo fb_color_texture; | ||||||
|     DepthTextureInfo fb_depth_texture; |     DepthTextureInfo fb_depth_texture; | ||||||
| 
 | 
 | ||||||
|     std::unordered_map<ShaderCacheKey, std::unique_ptr<TEVShader>> shader_cache; |     std::unordered_map<PicaShaderConfig, std::unique_ptr<PicaShader>> shader_cache; | ||||||
|     const TEVShader* current_shader = nullptr; |     const PicaShader* current_shader = nullptr; | ||||||
| 
 | 
 | ||||||
|     OGLVertexArray vertex_array; |     OGLVertexArray vertex_array; | ||||||
|     OGLBuffer vertex_buffer; |     OGLBuffer vertex_buffer; | ||||||
|  |  | ||||||
|  | @ -278,7 +278,7 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Writes the code to emulate the specified TEV stage
 | /// 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]; |     auto& stage = config.tev_stages[index]; | ||||||
|     if (!IsPassThroughTevStage(stage)) { |     if (!IsPassThroughTevStage(stage)) { | ||||||
|         std::string index_name = std::to_string(index); |         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"; |         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"( |     std::string out = R"( | ||||||
| #version 330 | #version 330 | ||||||
| #extension GL_ARB_explicit_uniform_location : require | #extension GL_ARB_explicit_uniform_location : require | ||||||
|  |  | ||||||
|  | @ -22,6 +22,6 @@ std::string GenerateVertexShader(); | ||||||
|  *               configuration (NOTE: Use state in this struct only, not the Pica registers!) |  *               configuration (NOTE: Use state in this struct only, not the Pica registers!) | ||||||
|  * @returns String of the shader source code |  * @returns String of the shader source code | ||||||
|  */ |  */ | ||||||
| std::string GenerateFragmentShader(const ShaderCacheKey& config); | std::string GenerateFragmentShader(const PicaShaderConfig& config); | ||||||
| 
 | 
 | ||||||
| } // namespace GLShader
 | } // namespace GLShader
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bunnei
						bunnei