mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-30 10:42:47 +01:00
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…
Reference in a new issue