From e3f4233cefff611e03a2031c6194a118d946a5d9 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 25 Jul 2015 20:13:11 -0500 Subject: [PATCH 01/19] Initial implementation of fragment shader generation with caching. --- src/video_core/pica.h | 4 + .../renderer_opengl/gl_rasterizer.cpp | 373 +++++++----------- .../renderer_opengl/gl_rasterizer.h | 110 ++++-- .../renderer_opengl/gl_shader_util.cpp | 349 ++++++++++++++++ .../renderer_opengl/gl_shader_util.h | 6 + src/video_core/renderer_opengl/gl_shaders.h | 8 +- src/video_core/renderer_opengl/gl_state.h | 1 + 7 files changed, 579 insertions(+), 272 deletions(-) diff --git a/src/video_core/pica.h b/src/video_core/pica.h index ff81b409d..18fdc8c85 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -317,6 +317,7 @@ struct Regs { }; union { + u32 source_raw; BitField< 0, 4, Source> color_source1; BitField< 4, 4, Source> color_source2; BitField< 8, 4, Source> color_source3; @@ -326,6 +327,7 @@ struct Regs { }; union { + u32 modifier_raw; BitField< 0, 4, ColorModifier> color_modifier1; BitField< 4, 4, ColorModifier> color_modifier2; BitField< 8, 4, ColorModifier> color_modifier3; @@ -335,6 +337,7 @@ struct Regs { }; union { + u32 op_raw; BitField< 0, 4, Operation> color_op; BitField<16, 4, Operation> alpha_op; }; @@ -348,6 +351,7 @@ struct Regs { }; union { + u32 scale_raw; BitField< 0, 2, u32> color_scale; BitField<16, 2, u32> alpha_scale; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a613fe136..45329d561 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -8,6 +8,7 @@ #include #include "common/color.h" +#include "common/file_util.h" #include "common/math_util.h" #include "common/microprofile.h" #include "common/profiler.h" @@ -38,36 +39,6 @@ RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr RasterizerOpenGL::~RasterizerOpenGL() { } void RasterizerOpenGL::InitObjects() { - // Create the hardware shader program and get attrib/uniform locations - shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw); - attrib_position = glGetAttribLocation(shader.handle, "vert_position"); - attrib_color = glGetAttribLocation(shader.handle, "vert_color"); - attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords"); - - uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled"); - uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func"); - uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref"); - - uniform_tex = glGetUniformLocation(shader.handle, "tex"); - - uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color"); - - const auto tev_stages = Pica::g_state.regs.GetTevStages(); - for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { - auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index]; - - std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]"; - uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str()); - uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str()); - uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str()); - uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str()); - uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str()); - uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str()); - uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str()); - uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str()); - uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str()); - } - // Create sampler objects for (size_t i = 0; i < texture_samplers.size(); ++i) { texture_samplers[i].Create(); @@ -78,29 +49,25 @@ void RasterizerOpenGL::InitObjects() { vertex_buffer.Create(); vertex_array.Create(); - // Update OpenGL state state.draw.vertex_array = vertex_array.handle; state.draw.vertex_buffer = vertex_buffer.handle; - state.draw.shader_program = shader.handle; - state.Apply(); - // Set the texture samplers to correspond to different texture units - glUniform1i(uniform_tex, 0); - glUniform1i(uniform_tex + 1, 1); - glUniform1i(uniform_tex + 2, 2); - // Set vertex attributes - glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); - glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); - glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); - glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); - glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); - glEnableVertexAttribArray(attrib_position); - glEnableVertexAttribArray(attrib_color); - glEnableVertexAttribArray(attrib_texcoords); - glEnableVertexAttribArray(attrib_texcoords + 1); - glEnableVertexAttribArray(attrib_texcoords + 2); + glVertexAttribPointer(ShaderUtil::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); + glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_POSITION); + + glVertexAttribPointer(ShaderUtil::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); + glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_COLOR); + + glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); + glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); + glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); + glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS); + glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1); + glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2); + + RegenerateShaders(); // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation fb_color_texture.texture.Create(); @@ -156,55 +123,11 @@ void RasterizerOpenGL::Reset() { SyncBlendEnabled(); SyncBlendFuncs(); SyncBlendColor(); - SyncAlphaTest(); SyncLogicOp(); SyncStencilTest(); SyncDepthTest(); - // TEV stage 0 - SyncTevSources(0, regs.tev_stage0); - SyncTevModifiers(0, regs.tev_stage0); - SyncTevOps(0, regs.tev_stage0); - SyncTevColor(0, regs.tev_stage0); - SyncTevMultipliers(0, regs.tev_stage0); - - // TEV stage 1 - SyncTevSources(1, regs.tev_stage1); - SyncTevModifiers(1, regs.tev_stage1); - SyncTevOps(1, regs.tev_stage1); - SyncTevColor(1, regs.tev_stage1); - SyncTevMultipliers(1, regs.tev_stage1); - - // TEV stage 2 - SyncTevSources(2, regs.tev_stage2); - SyncTevModifiers(2, regs.tev_stage2); - SyncTevOps(2, regs.tev_stage2); - SyncTevColor(2, regs.tev_stage2); - SyncTevMultipliers(2, regs.tev_stage2); - - // TEV stage 3 - SyncTevSources(3, regs.tev_stage3); - SyncTevModifiers(3, regs.tev_stage3); - SyncTevOps(3, regs.tev_stage3); - SyncTevColor(3, regs.tev_stage3); - SyncTevMultipliers(3, regs.tev_stage3); - - // TEV stage 4 - SyncTevSources(4, regs.tev_stage4); - SyncTevModifiers(4, regs.tev_stage4); - SyncTevOps(4, regs.tev_stage4); - SyncTevColor(4, regs.tev_stage4); - SyncTevMultipliers(4, regs.tev_stage4); - - // TEV stage 5 - SyncTevSources(5, regs.tev_stage5); - SyncTevModifiers(5, regs.tev_stage5); - SyncTevOps(5, regs.tev_stage5); - SyncTevColor(5, regs.tev_stage5); - SyncTevMultipliers(5, regs.tev_stage5); - - SyncCombinerColor(); - SyncCombinerWriteFlags(); + RegenerateShaders(); res_cache.FullFlush(); } @@ -217,10 +140,88 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, vertex_batch.emplace_back(v2); } +namespace ShaderCache { +extern std::string GenerateFragmentShader(const ShaderCacheKey& config); +} + +void RasterizerOpenGL::RegenerateShaders() { + const auto& regs = Pica::g_state.regs; + + ShaderCacheKey config; + config.alpha_test_func = regs.output_merger.alpha_test.enable ? + regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; + config.tev_stages = regs.GetTevStages(); + for (auto& tev : config.tev_stages) { + tev.const_r = 0; + tev.const_g = 0; + tev.const_b = 0; + tev.const_a = 0; + } + config.combiner_buffer_input = + regs.tev_combiner_buffer_input.update_mask_rgb.Value() | + regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; + + auto cached_shader = shader_cache.find(config); + if (cached_shader != shader_cache.end()) { + current_shader = &cached_shader->second; + state.draw.shader_program = current_shader->shader.handle; + state.Apply(); + } else { + LOG_CRITICAL(Render_OpenGL, "Creating new shader: %08X", hash(config)); + + TEVShader shader; + + std::string fragShader = ShaderCache::GenerateFragmentShader(config); + shader.shader.Create(GLShaders::g_vertex_shader_hw, fragShader.c_str()); + + shader.uniform_alphatest_ref = glGetUniformLocation(shader.shader.handle, "alphatest_ref"); + shader.uniform_tex = glGetUniformLocation(shader.shader.handle, "tex"); + shader.uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.shader.handle, "tev_combiner_buffer_color"); + shader.uniform_tev_const_colors = glGetUniformLocation(shader.shader.handle, "const_color"); + + current_shader = &shader_cache.emplace(config, std::move(shader)).first->second; + + state.draw.shader_program = current_shader->shader.handle; + state.Apply(); + + // Set the texture samplers to correspond to different texture units + if (shader.uniform_tex != -1) { + glUniform1i(shader.uniform_tex, 0); + glUniform1i(shader.uniform_tex + 1, 1); + glUniform1i(shader.uniform_tex + 2, 2); + } + } + + + // Sync alpha reference + if (current_shader->uniform_alphatest_ref != -1) + glUniform1f(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); + + // Sync combiner buffer color + if (current_shader->uniform_tev_combiner_buffer_color != -1) { + auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); + glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data()); + } + + // Sync TEV const colors + if (current_shader->uniform_tev_const_colors != -1) { + auto& tev_stages = Pica::g_state.regs.GetTevStages(); + for (int tev_index = 0; tev_index < tev_stages.size(); ++tev_index) { + auto const_color = PicaToGL::ColorRGBA8(tev_stages[tev_index].const_color); + glUniform4fv(current_shader->uniform_tev_const_colors + tev_index, 1, const_color.data()); + } + } +} + void RasterizerOpenGL::DrawTriangles() { SyncFramebuffer(); SyncDrawState(); + if (state.draw.shader_dirty) { + RegenerateShaders(); + state.draw.shader_dirty = false; + } + glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); @@ -272,6 +273,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { // Alpha test case PICA_REG_INDEX(output_merger.alpha_test): SyncAlphaTest(); + state.draw.shader_dirty = true; break; // Stencil test @@ -290,117 +292,57 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncLogicOp(); break; - // TEV stage 0 + // TEV stages case PICA_REG_INDEX(tev_stage0.color_source1): - SyncTevSources(0, regs.tev_stage0); - break; case PICA_REG_INDEX(tev_stage0.color_modifier1): - SyncTevModifiers(0, regs.tev_stage0); - break; case PICA_REG_INDEX(tev_stage0.color_op): - SyncTevOps(0, regs.tev_stage0); + case PICA_REG_INDEX(tev_stage0.color_scale): + case PICA_REG_INDEX(tev_stage1.color_source1): + case PICA_REG_INDEX(tev_stage1.color_modifier1): + case PICA_REG_INDEX(tev_stage1.color_op): + case PICA_REG_INDEX(tev_stage1.color_scale): + case PICA_REG_INDEX(tev_stage2.color_source1): + case PICA_REG_INDEX(tev_stage2.color_modifier1): + case PICA_REG_INDEX(tev_stage2.color_op): + case PICA_REG_INDEX(tev_stage2.color_scale): + case PICA_REG_INDEX(tev_stage3.color_source1): + case PICA_REG_INDEX(tev_stage3.color_modifier1): + case PICA_REG_INDEX(tev_stage3.color_op): + case PICA_REG_INDEX(tev_stage3.color_scale): + case PICA_REG_INDEX(tev_stage4.color_source1): + case PICA_REG_INDEX(tev_stage4.color_modifier1): + case PICA_REG_INDEX(tev_stage4.color_op): + case PICA_REG_INDEX(tev_stage4.color_scale): + case PICA_REG_INDEX(tev_stage5.color_source1): + case PICA_REG_INDEX(tev_stage5.color_modifier1): + case PICA_REG_INDEX(tev_stage5.color_op): + case PICA_REG_INDEX(tev_stage5.color_scale): + case PICA_REG_INDEX(tev_combiner_buffer_input): + state.draw.shader_dirty = true; break; case PICA_REG_INDEX(tev_stage0.const_r): - SyncTevColor(0, regs.tev_stage0); - break; - case PICA_REG_INDEX(tev_stage0.color_scale): - SyncTevMultipliers(0, regs.tev_stage0); - break; - - // TEV stage 1 - case PICA_REG_INDEX(tev_stage1.color_source1): - SyncTevSources(1, regs.tev_stage1); - break; - case PICA_REG_INDEX(tev_stage1.color_modifier1): - SyncTevModifiers(1, regs.tev_stage1); - break; - case PICA_REG_INDEX(tev_stage1.color_op): - SyncTevOps(1, regs.tev_stage1); + SyncTevConstColor(0, regs.tev_stage0); break; case PICA_REG_INDEX(tev_stage1.const_r): - SyncTevColor(1, regs.tev_stage1); - break; - case PICA_REG_INDEX(tev_stage1.color_scale): - SyncTevMultipliers(1, regs.tev_stage1); - break; - - // TEV stage 2 - case PICA_REG_INDEX(tev_stage2.color_source1): - SyncTevSources(2, regs.tev_stage2); - break; - case PICA_REG_INDEX(tev_stage2.color_modifier1): - SyncTevModifiers(2, regs.tev_stage2); - break; - case PICA_REG_INDEX(tev_stage2.color_op): - SyncTevOps(2, regs.tev_stage2); + SyncTevConstColor(1, regs.tev_stage0); break; case PICA_REG_INDEX(tev_stage2.const_r): - SyncTevColor(2, regs.tev_stage2); - break; - case PICA_REG_INDEX(tev_stage2.color_scale): - SyncTevMultipliers(2, regs.tev_stage2); - break; - - // TEV stage 3 - case PICA_REG_INDEX(tev_stage3.color_source1): - SyncTevSources(3, regs.tev_stage3); - break; - case PICA_REG_INDEX(tev_stage3.color_modifier1): - SyncTevModifiers(3, regs.tev_stage3); - break; - case PICA_REG_INDEX(tev_stage3.color_op): - SyncTevOps(3, regs.tev_stage3); + SyncTevConstColor(2, regs.tev_stage0); break; case PICA_REG_INDEX(tev_stage3.const_r): - SyncTevColor(3, regs.tev_stage3); - break; - case PICA_REG_INDEX(tev_stage3.color_scale): - SyncTevMultipliers(3, regs.tev_stage3); - break; - - // TEV stage 4 - case PICA_REG_INDEX(tev_stage4.color_source1): - SyncTevSources(4, regs.tev_stage4); - break; - case PICA_REG_INDEX(tev_stage4.color_modifier1): - SyncTevModifiers(4, regs.tev_stage4); - break; - case PICA_REG_INDEX(tev_stage4.color_op): - SyncTevOps(4, regs.tev_stage4); + SyncTevConstColor(3, regs.tev_stage0); break; case PICA_REG_INDEX(tev_stage4.const_r): - SyncTevColor(4, regs.tev_stage4); - break; - case PICA_REG_INDEX(tev_stage4.color_scale): - SyncTevMultipliers(4, regs.tev_stage4); - break; - - // TEV stage 5 - case PICA_REG_INDEX(tev_stage5.color_source1): - SyncTevSources(5, regs.tev_stage5); - break; - case PICA_REG_INDEX(tev_stage5.color_modifier1): - SyncTevModifiers(5, regs.tev_stage5); - break; - case PICA_REG_INDEX(tev_stage5.color_op): - SyncTevOps(5, regs.tev_stage5); + SyncTevConstColor(4, regs.tev_stage0); break; case PICA_REG_INDEX(tev_stage5.const_r): - SyncTevColor(5, regs.tev_stage5); - break; - case PICA_REG_INDEX(tev_stage5.color_scale): - SyncTevMultipliers(5, regs.tev_stage5); + SyncTevConstColor(5, regs.tev_stage0); break; // TEV combiner buffer color case PICA_REG_INDEX(tev_combiner_buffer_color): SyncCombinerColor(); break; - - // TEV combiner buffer write flags - case PICA_REG_INDEX(tev_combiner_buffer_input): - SyncCombinerWriteFlags(); - break; } } @@ -712,9 +654,8 @@ void RasterizerOpenGL::SyncBlendColor() { void RasterizerOpenGL::SyncAlphaTest() { const auto& regs = Pica::g_state.regs; - glUniform1i(uniform_alphatest_enabled, regs.output_merger.alpha_test.enable); - glUniform1i(uniform_alphatest_func, (GLint)regs.output_merger.alpha_test.func.Value()); - glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); + if (current_shader->uniform_alphatest_ref != -1) + glUniform1f(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); } void RasterizerOpenGL::SyncLogicOp() { @@ -744,55 +685,17 @@ void RasterizerOpenGL::SyncDepthTest() { state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; } -void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { - GLint color_srcs[3] = { (GLint)config.color_source1.Value(), - (GLint)config.color_source2.Value(), - (GLint)config.color_source3.Value() }; - GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(), - (GLint)config.alpha_source2.Value(), - (GLint)config.alpha_source3.Value() }; - - glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs); - glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs); -} - -void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { - GLint color_mods[3] = { (GLint)config.color_modifier1.Value(), - (GLint)config.color_modifier2.Value(), - (GLint)config.color_modifier3.Value() }; - GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(), - (GLint)config.alpha_modifier2.Value(), - (GLint)config.alpha_modifier3.Value() }; - - glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods); - glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods); -} - -void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { - glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value()); -} - -void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { - auto const_color = PicaToGL::ColorRGBA8(config.const_color); - glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data()); -} - -void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { - glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier()); -} - void RasterizerOpenGL::SyncCombinerColor() { - auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); - glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data()); + if (current_shader->uniform_tev_combiner_buffer_color != -1) { + auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); + glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data()); + } } -void RasterizerOpenGL::SyncCombinerWriteFlags() { - const auto& regs = Pica::g_state.regs; - const auto tev_stages = regs.GetTevStages(); - for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { - glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha, - regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index), - regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)); +void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) { + if (current_shader->uniform_tev_const_colors != -1) { + auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color); + glUniform4fv(current_shader->uniform_tev_const_colors + stage_index, 1, const_color.data()); } } @@ -824,12 +727,6 @@ void RasterizerOpenGL::SyncDrawState() { } } - // Skip processing TEV stages that simply pass the previous stage results through - const auto tev_stages = regs.GetTevStages(); - for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { - glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index])); - } - state.Apply(); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 1fe307846..19e8db69a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" @@ -13,6 +14,60 @@ #include "video_core/renderer_opengl/gl_state.h" #include "video_core/shader/shader_interpreter.h" +template +inline size_t hash(const T& o) { + return std::hash()(o); +} + +template +inline size_t combine_hash(const T& o) { + return hash(o); +} + +template +inline size_t combine_hash(const T& o, const Args&... args) { + return hash(o) * 3 + combine_hash(args...); +} + +struct ShaderCacheKey { + using Regs = Pica::Regs; + + bool operator ==(const ShaderCacheKey& o) const { + return hash(*this) == hash(o); + }; + + Regs::CompareFunc alpha_test_func; + std::array tev_stages; + u8 combiner_buffer_input; + + bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { + return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index)); + } + + bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { + return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); + } +}; + +namespace std { + +template<> struct hash<::Pica::Regs::TevStageConfig> { + 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> { + 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]); + } +}; +} + class RasterizerOpenGL : public HWRasterizer { public: @@ -33,6 +88,8 @@ public: /// Draw the current batch of triangles void DrawTriangles() override; + void RegenerateShaders(); + /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer void CommitFramebuffer() override; @@ -59,6 +116,22 @@ private: GLuint updates_combiner_buffer_color_alpha; }; + struct TEVShader { + OGLShader shader; + + // Hardware fragment shader + GLuint uniform_alphatest_ref; + GLuint uniform_tex; + GLuint uniform_tev_combiner_buffer_color; + GLuint uniform_tev_const_colors; + + TEVShader() = default; + TEVShader(TEVShader&& o) : shader(std::move(o.shader)), + uniform_alphatest_ref(o.uniform_alphatest_ref), uniform_tex(o.uniform_tex), + uniform_tev_combiner_buffer_color(o.uniform_tev_combiner_buffer_color), + uniform_tev_const_colors(o.uniform_tev_const_colors) {} + }; + /// Structure used for storing information about color textures struct TextureInfo { OGLTexture texture; @@ -156,27 +229,12 @@ private: /// Syncs the depth test states to match the PICA register void SyncDepthTest(); - /// Syncs the specified TEV stage's color and alpha sources to match the PICA register - void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config); - - /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register - void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config); - - /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register - void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config); - - /// Syncs the specified TEV stage's constant color to match the PICA register - void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config); - - /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register - void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config); + /// Syncs the TEV constant color to match the PICA register + void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage); /// Syncs the TEV combiner color buffer to match the PICA register void SyncCombinerColor(); - /// Syncs the TEV combiner write flags to match the PICA register - void SyncCombinerWriteFlags(); - /// Syncs the remaining OpenGL drawing state to match the current PICA state void SyncDrawState(); @@ -213,21 +271,11 @@ private: std::array texture_samplers; TextureInfo fb_color_texture; DepthTextureInfo fb_depth_texture; - OGLShader shader; + + std::unordered_map shader_cache; + TEVShader* current_shader = nullptr; + OGLVertexArray vertex_array; OGLBuffer vertex_buffer; OGLFramebuffer framebuffer; - - // Hardware vertex shader - GLuint attrib_position; - GLuint attrib_color; - GLuint attrib_texcoords; - - // Hardware fragment shader - GLuint uniform_alphatest_enabled; - GLuint uniform_alphatest_func; - GLuint uniform_alphatest_ref; - GLuint uniform_tex; - GLuint uniform_tev_combiner_buffer_color; - TEVConfigUniforms uniform_tev_cfgs[6]; }; diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 4cf246c06..ee32f6a31 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -2,6 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. + +#include "gl_shader_util.h" +#include "gl_rasterizer.h" +#include "common/logging/log.h" + +#include "video_core/pica.h" + #include #include @@ -65,6 +72,13 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { GLuint program_id = glCreateProgram(); glAttachShader(program_id, vertex_shader_id); glAttachShader(program_id, fragment_shader_id); + + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_POSITION, "vert_position"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_COLOR, "vert_color"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 0, "vert_texcoords0"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 1, "vert_texcoords1"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 2, "vert_texcoords2"); + glLinkProgram(program_id); // Check the program @@ -88,3 +102,338 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { } } + +namespace ShaderCache +{ + +static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { + return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && + stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && + stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous && + stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous && + stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor && + stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha && + stage.GetColorMultiplier() == 1 && + stage.GetAlphaMultiplier() == 1); +} + +void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { + using Source = Pica::Regs::TevStageConfig::Source; + switch (source) { + case Source::PrimaryColor: + shader += "o[2]"; + break; + case Source::PrimaryFragmentColor: + // HACK: Until we implement fragment lighting, use primary_color + shader += "o[2]"; + break; + case Source::SecondaryFragmentColor: + // HACK: Until we implement fragment lighting, use zero + shader += "vec4(0.0, 0.0, 0.0, 0.0)"; + break; + case Source::Texture0: + shader += "texture(tex[0], o[3].xy)"; + break; + case Source::Texture1: + shader += "texture(tex[1], o[3].zw)"; + break; + case Source::Texture2: // TODO: Unverified + shader += "texture(tex[2], o[5].zw)"; + break; + case Source::PreviousBuffer: + shader += "g_combiner_buffer"; + break; + case Source::Constant: + shader += "const_color[" + index_name + "]"; + break; + case Source::Previous: + shader += "g_last_tex_env_out"; + break; + default: + shader += "vec4(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); + break; + } +} + +void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { + using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; + switch (modifier) { + case ColorModifier::SourceColor: + AppendSource(shader, source, index_name); + shader += ".rgb"; + break; + case ColorModifier::OneMinusSourceColor: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".rgb"; + break; + case ColorModifier::SourceAlpha: + AppendSource(shader, source, index_name); + shader += ".aaa"; + break; + case ColorModifier::OneMinusSourceAlpha: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".aaa"; + break; + case ColorModifier::SourceRed: + AppendSource(shader, source, index_name); + shader += ".rrr"; + break; + case ColorModifier::OneMinusSourceRed: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".rrr"; + break; + case ColorModifier::SourceGreen: + AppendSource(shader, source, index_name); + shader += ".ggg"; + break; + case ColorModifier::OneMinusSourceGreen: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".ggg"; + break; + case ColorModifier::SourceBlue: + AppendSource(shader, source, index_name); + shader += ".bbb"; + break; + case ColorModifier::OneMinusSourceBlue: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".bbb"; + break; + default: + shader += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); + break; + } +} + +void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { + using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; + switch (modifier) { + case AlphaModifier::SourceAlpha: + AppendSource(shader, source, index_name); + shader += ".a"; + break; + case AlphaModifier::OneMinusSourceAlpha: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".a"; + break; + case AlphaModifier::SourceRed: + AppendSource(shader, source, index_name); + shader += ".r"; + break; + case AlphaModifier::OneMinusSourceRed: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".r"; + break; + case AlphaModifier::SourceGreen: + AppendSource(shader, source, index_name); + shader += ".g"; + break; + case AlphaModifier::OneMinusSourceGreen: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".g"; + break; + case AlphaModifier::SourceBlue: + AppendSource(shader, source, index_name); + shader += ".b"; + break; + case AlphaModifier::OneMinusSourceBlue: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".b"; + break; + default: + shader += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); + break; + } +} + +void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) { + using Operation = Pica::Regs::TevStageConfig::Operation; + + switch (operation) { + case Operation::Replace: + shader += variable_name + "[0]"; + break; + case Operation::Modulate: + shader += variable_name + "[0] * " + variable_name + "[1]"; + break; + case Operation::Add: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; + break; + case Operation::AddSigned: + shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), 0.0, 1.0)"; + break; + case Operation::Lerp: + shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; + break; + case Operation::Subtract: + shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; + break; + case Operation::MultiplyThenAdd: + shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; + break; + case Operation::AddThenMultiply: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; + break; + default: + shader += "0.0"; + LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); + break; + } +} + +void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) { + using Operation = Pica::Regs::TevStageConfig::Operation; + switch (operation) { + case Operation::Replace: + shader += variable_name + "[0]"; + break; + case Operation::Modulate: + shader += variable_name + "[0] * " + variable_name + "[1]"; + break; + case Operation::Add: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; + break; + case Operation::AddSigned: + shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; + break; + case Operation::Lerp: + shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; + break; + case Operation::Subtract: + shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; + break; + case Operation::MultiplyThenAdd: + shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; + break; + case Operation::AddThenMultiply: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; + break; + default: + shader += "0.0"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation); + break; + } +} + +void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) { + using CompareFunc = Pica::Regs::CompareFunc; + switch (func) { + case CompareFunc::Never: + shader += "true"; + break; + case CompareFunc::Always: + shader += "false"; + break; + case CompareFunc::Equal: + shader += "g_last_tex_env_out.a != alphatest_ref"; + break; + case CompareFunc::NotEqual: + shader += "g_last_tex_env_out.a == alphatest_ref"; + break; + case CompareFunc::LessThan: + shader += "g_last_tex_env_out.a >= alphatest_ref"; + break; + case CompareFunc::LessThanOrEqual: + shader += "g_last_tex_env_out.a > alphatest_ref"; + break; + case CompareFunc::GreaterThan: + shader += "g_last_tex_env_out.a <= alphatest_ref"; + break; + case CompareFunc::GreaterThanOrEqual: + shader += "g_last_tex_env_out.a < alphatest_ref"; + break; + default: + shader += "false"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); + break; + } +} + +std::string GenerateFragmentShader(const ShaderCacheKey& config) { + std::string shader = R"( +#version 150 core + +#define NUM_VTX_ATTR 7 +#define NUM_TEV_STAGES 6 + +in vec4 o[NUM_VTX_ATTR]; +out vec4 color; + +uniform float alphatest_ref; +uniform vec4 const_color[NUM_TEV_STAGES]; +uniform sampler2D tex[3]; + +uniform vec4 tev_combiner_buffer_color; + +void main(void) { + vec4 g_combiner_buffer = tev_combiner_buffer_color; + vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); +)"; + + // Do not do any sort of processing if it's obvious we're not going to pass the alpha test + if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) { + shader += "discard;"; + return shader; + } + + auto& tev_stages = config.tev_stages; + for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { + auto& tev_stage = tev_stages[tev_stage_index]; + if (!IsPassThroughTevStage(tev_stage)) { + std::string index_name = std::to_string(tev_stage_index); + + shader += "vec3 color_results_" + index_name + "[3] = vec3[3]("; + AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name); + shader += ", "; + AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name); + shader += ", "; + AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name); + shader += ");\n"; + + shader += "vec3 color_output_" + index_name + " = "; + AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name); + shader += ";\n"; + + shader += "float alpha_results_" + index_name + "[3] = float[3]("; + AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name); + shader += ", "; + AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name); + shader += ", "; + AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name); + shader += ");\n"; + + shader += "float alpha_output_" + index_name + " = "; + AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name); + shader += ";\n"; + + shader += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + std::to_string(tev_stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + std::to_string(tev_stage.GetAlphaMultiplier()) + ".0, 1.0));\n"; + } + + if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index)) + shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; + + if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) + shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; + } + + if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) { + shader += "if ("; + AppendAlphaTestCondition(shader, config.alpha_test_func); + shader += ") {\n discard;\n }\n"; + } + + shader += "color = g_last_tex_env_out;\n}"; + return shader; +} +} diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index c9d7cc380..ca62c83ba 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -8,6 +8,12 @@ namespace ShaderUtil { +enum Attributes { + ATTRIBUTE_POSITION = 0, + ATTRIBUTE_COLOR = 1, + ATTRIBUTE_TEXCOORDS = 2, +}; + GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); } diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h index a8cb2f595..2ba2c6b0f 100644 --- a/src/video_core/renderer_opengl/gl_shaders.h +++ b/src/video_core/renderer_opengl/gl_shaders.h @@ -49,14 +49,16 @@ const char g_vertex_shader_hw[] = R"( in vec4 vert_position; in vec4 vert_color; -in vec2 vert_texcoords[3]; +in vec2 vert_texcoords0; +in vec2 vert_texcoords1; +in vec2 vert_texcoords2; out vec4 o[NUM_VTX_ATTR]; void main() { o[2] = vert_color; - o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy); - o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy); + o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); + o[5] = vec4(0.0, 0.0, vert_texcoords2.xy); gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 6ecbedbb4..668b04259 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -65,6 +65,7 @@ public: GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING GLuint shader_program; // GL_CURRENT_PROGRAM + bool shader_dirty; } draw; OpenGLState(); From 82f3e6dc69d23c3e70c1ad2805e2e15397cd9156 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 1 Oct 2015 18:34:10 -0400 Subject: [PATCH 02/19] gl_shader_util: Fix precision bug with alpha testing. - Alpha testing is not done with float32 precision, this makes the HW renderer match the SW renderer. --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 ++-- src/video_core/renderer_opengl/gl_shader_util.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 45329d561..38d184ae2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -195,7 +195,7 @@ void RasterizerOpenGL::RegenerateShaders() { // Sync alpha reference if (current_shader->uniform_alphatest_ref != -1) - glUniform1f(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); + glUniform1i(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref); // Sync combiner buffer color if (current_shader->uniform_tev_combiner_buffer_color != -1) { @@ -655,7 +655,7 @@ void RasterizerOpenGL::SyncBlendColor() { void RasterizerOpenGL::SyncAlphaTest() { const auto& regs = Pica::g_state.regs; if (current_shader->uniform_alphatest_ref != -1) - glUniform1f(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); + glUniform1i(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref); } void RasterizerOpenGL::SyncLogicOp() { diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index ee32f6a31..2c049e940 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -336,22 +336,22 @@ void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) shader += "false"; break; case CompareFunc::Equal: - shader += "g_last_tex_env_out.a != alphatest_ref"; + shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; break; case CompareFunc::NotEqual: - shader += "g_last_tex_env_out.a == alphatest_ref"; + shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; break; case CompareFunc::LessThan: - shader += "g_last_tex_env_out.a >= alphatest_ref"; + shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; break; case CompareFunc::LessThanOrEqual: - shader += "g_last_tex_env_out.a > alphatest_ref"; + shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; break; case CompareFunc::GreaterThan: - shader += "g_last_tex_env_out.a <= alphatest_ref"; + shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; break; case CompareFunc::GreaterThanOrEqual: - shader += "g_last_tex_env_out.a < alphatest_ref"; + shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; break; default: shader += "false"; @@ -370,7 +370,7 @@ std::string GenerateFragmentShader(const ShaderCacheKey& config) { in vec4 o[NUM_VTX_ATTR]; out vec4 color; -uniform float alphatest_ref; +uniform int alphatest_ref; uniform vec4 const_color[NUM_TEV_STAGES]; uniform sampler2D tex[3]; From 37b0aa5af74dc01dadff5b173523580d03db2f02 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 1 Oct 2015 22:12:44 -0400 Subject: [PATCH 03/19] gl_rasterizer: Fix typo in uploading TEV const color uniforms. --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 38d184ae2..a69e09188 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -324,19 +324,19 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncTevConstColor(0, regs.tev_stage0); break; case PICA_REG_INDEX(tev_stage1.const_r): - SyncTevConstColor(1, regs.tev_stage0); + SyncTevConstColor(1, regs.tev_stage1); break; case PICA_REG_INDEX(tev_stage2.const_r): - SyncTevConstColor(2, regs.tev_stage0); + SyncTevConstColor(2, regs.tev_stage2); break; case PICA_REG_INDEX(tev_stage3.const_r): - SyncTevConstColor(3, regs.tev_stage0); + SyncTevConstColor(3, regs.tev_stage3); break; case PICA_REG_INDEX(tev_stage4.const_r): - SyncTevConstColor(4, regs.tev_stage0); + SyncTevConstColor(4, regs.tev_stage4); break; case PICA_REG_INDEX(tev_stage5.const_r): - SyncTevConstColor(5, regs.tev_stage0); + SyncTevConstColor(5, regs.tev_stage5); break; // TEV combiner buffer color From b02a533d946fbf98897ee42334a343d2aa4d7bf4 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 2 Oct 2015 00:12:50 -0400 Subject: [PATCH 04/19] gl_shader_util: Use vec3 constants for AppendColorCombiner. --- src/video_core/renderer_opengl/gl_shader_util.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 2c049e940..8a6a51ad4 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -268,25 +268,25 @@ void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operat shader += variable_name + "[0] * " + variable_name + "[1]"; break; case Operation::Add: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; break; case Operation::AddSigned: - shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), 0.0, 1.0)"; + shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; break; case Operation::Lerp: shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; break; case Operation::Subtract: - shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; + shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; break; case Operation::MultiplyThenAdd: - shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; + shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; break; case Operation::AddThenMultiply: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; break; default: - shader += "0.0"; + shader += "vec3(0.0)"; LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); break; } From 3c057bd3d80b049720b11d0b44391c18870c28e8 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Oct 2015 20:52:04 -0400 Subject: [PATCH 05/19] gl_rasterizer: Move logic for creating ShaderCacheKey to a static function. --- src/video_core/pica.h | 8 ++-- .../renderer_opengl/gl_rasterizer.cpp | 19 +------- .../renderer_opengl/gl_rasterizer.h | 45 ++++++++++++++++++- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 18fdc8c85..2f1b2dec4 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -317,7 +317,7 @@ struct Regs { }; union { - u32 source_raw; + u32 sources_raw; BitField< 0, 4, Source> color_source1; BitField< 4, 4, Source> color_source2; BitField< 8, 4, Source> color_source3; @@ -327,7 +327,7 @@ struct Regs { }; union { - u32 modifier_raw; + u32 modifiers_raw; BitField< 0, 4, ColorModifier> color_modifier1; BitField< 4, 4, ColorModifier> color_modifier2; BitField< 8, 4, ColorModifier> color_modifier3; @@ -337,7 +337,7 @@ struct Regs { }; union { - u32 op_raw; + u32 ops_raw; BitField< 0, 4, Operation> color_op; BitField<16, 4, Operation> alpha_op; }; @@ -351,7 +351,7 @@ struct Regs { }; union { - u32 scale_raw; + u32 scales_raw; BitField< 0, 2, u32> color_scale; BitField<16, 2, u32> alpha_scale; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a69e09188..01b9c91c6 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -145,21 +145,7 @@ extern std::string GenerateFragmentShader(const ShaderCacheKey& config); } void RasterizerOpenGL::RegenerateShaders() { - const auto& regs = Pica::g_state.regs; - - ShaderCacheKey config; - config.alpha_test_func = regs.output_merger.alpha_test.enable ? - regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; - config.tev_stages = regs.GetTevStages(); - for (auto& tev : config.tev_stages) { - tev.const_r = 0; - tev.const_g = 0; - tev.const_b = 0; - tev.const_a = 0; - } - config.combiner_buffer_input = - regs.tev_combiner_buffer_input.update_mask_rgb.Value() | - regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; + ShaderCacheKey config = ShaderCacheKey::CurrentShaderConfig(); auto cached_shader = shader_cache.find(config); if (cached_shader != shader_cache.end()) { @@ -192,10 +178,9 @@ void RasterizerOpenGL::RegenerateShaders() { } } - // Sync alpha reference if (current_shader->uniform_alphatest_ref != -1) - glUniform1i(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref); + glUniform1i(current_shader->uniform_alphatest_ref, Pica::g_state.regs.output_merger.alpha_test.ref); // Sync combiner buffer color if (current_shader->uniform_tev_combiner_buffer_color != -1) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 19e8db69a..5bc4a319f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -9,6 +9,7 @@ #include "common/common_types.h" +#include "video_core/pica.h" #include "video_core/hwrasterizer_base.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" @@ -37,7 +38,7 @@ struct ShaderCacheKey { }; Regs::CompareFunc alpha_test_func; - std::array tev_stages; + std::array tev_stages = {}; u8 combiner_buffer_input; bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { @@ -47,6 +48,48 @@ struct ShaderCacheKey { bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); } + + static ShaderCacheKey CurrentShaderConfig() { + const auto& regs = Pica::g_state.regs; + ShaderCacheKey config; + + config.alpha_test_func = regs.output_merger.alpha_test.enable ? + regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; + + 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 { From c86b9d42423b5a83ccba40f828b7ad9dafe3e317 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Oct 2015 22:33:47 -0400 Subject: [PATCH 06/19] renderer_opengl: Refactor shader generation/caching to be more organized + various cleanups. --- src/common/common_funcs.h | 18 + src/video_core/CMakeLists.txt | 3 +- .../renderer_opengl/gl_rasterizer.cpp | 131 +++---- .../renderer_opengl/gl_rasterizer.h | 41 +- .../renderer_opengl/gl_resource_manager.h | 2 +- .../renderer_opengl/gl_shader_gen.cpp | 371 ++++++++++++++++++ .../renderer_opengl/gl_shader_gen.h | 17 + .../renderer_opengl/gl_shader_util.cpp | 348 +--------------- .../renderer_opengl/gl_shader_util.h | 6 +- src/video_core/renderer_opengl/gl_shaders.h | 339 ---------------- .../renderer_opengl/renderer_opengl.cpp | 39 +- 11 files changed, 527 insertions(+), 788 deletions(-) create mode 100644 src/video_core/renderer_opengl/gl_shader_gen.cpp create mode 100644 src/video_core/renderer_opengl/gl_shader_gen.h delete mode 100644 src/video_core/renderer_opengl/gl_shaders.h diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index ed20c3629..7a8dd39a0 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -4,6 +4,9 @@ #pragma once +#include +#include + #include "common_types.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -95,3 +98,18 @@ 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/CMakeLists.txt b/src/video_core/CMakeLists.txt index 8c9d76ab4..2a924f4ad 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -1,6 +1,7 @@ set(SRCS renderer_opengl/gl_rasterizer.cpp renderer_opengl/gl_rasterizer_cache.cpp + renderer_opengl/gl_shader_gen.cpp renderer_opengl/gl_shader_util.cpp renderer_opengl/gl_state.cpp renderer_opengl/renderer_opengl.cpp @@ -21,8 +22,8 @@ set(HEADERS renderer_opengl/gl_rasterizer.h renderer_opengl/gl_rasterizer_cache.h renderer_opengl/gl_resource_manager.h + renderer_opengl/gl_shader_gen.h renderer_opengl/gl_shader_util.h - renderer_opengl/gl_shaders.h renderer_opengl/gl_state.h renderer_opengl/pica_to_gl.h renderer_opengl/renderer_opengl.h diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 01b9c91c6..4f9865230 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -9,6 +9,7 @@ #include "common/color.h" #include "common/file_util.h" +#include "common/make_unique.h" #include "common/math_util.h" #include "common/microprofile.h" #include "common/profiler.h" @@ -20,7 +21,7 @@ #include "video_core/pica.h" #include "video_core/utils.h" #include "video_core/renderer_opengl/gl_rasterizer.h" -#include "video_core/renderer_opengl/gl_shaders.h" +#include "video_core/renderer_opengl/gl_shader_gen.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/pica_to_gl.h" @@ -54,20 +55,20 @@ void RasterizerOpenGL::InitObjects() { state.Apply(); // Set vertex attributes - glVertexAttribPointer(ShaderUtil::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); - glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_POSITION); + glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); - glVertexAttribPointer(ShaderUtil::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); - glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_COLOR); + glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR); - glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); - glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); - glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); - glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS); - glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1); - glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2); + glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); + glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); + glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 0); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 1); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 2); - RegenerateShaders(); + SetShader(); // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation fb_color_texture.texture.Create(); @@ -117,8 +118,6 @@ void RasterizerOpenGL::InitObjects() { } void RasterizerOpenGL::Reset() { - const auto& regs = Pica::g_state.regs; - SyncCullMode(); SyncBlendEnabled(); SyncBlendFuncs(); @@ -127,7 +126,7 @@ void RasterizerOpenGL::Reset() { SyncStencilTest(); SyncDepthTest(); - RegenerateShaders(); + SetShader(); res_cache.FullFlush(); } @@ -140,70 +139,12 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, vertex_batch.emplace_back(v2); } -namespace ShaderCache { -extern std::string GenerateFragmentShader(const ShaderCacheKey& config); -} - -void RasterizerOpenGL::RegenerateShaders() { - ShaderCacheKey config = ShaderCacheKey::CurrentShaderConfig(); - - auto cached_shader = shader_cache.find(config); - if (cached_shader != shader_cache.end()) { - current_shader = &cached_shader->second; - state.draw.shader_program = current_shader->shader.handle; - state.Apply(); - } else { - LOG_CRITICAL(Render_OpenGL, "Creating new shader: %08X", hash(config)); - - TEVShader shader; - - std::string fragShader = ShaderCache::GenerateFragmentShader(config); - shader.shader.Create(GLShaders::g_vertex_shader_hw, fragShader.c_str()); - - shader.uniform_alphatest_ref = glGetUniformLocation(shader.shader.handle, "alphatest_ref"); - shader.uniform_tex = glGetUniformLocation(shader.shader.handle, "tex"); - shader.uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.shader.handle, "tev_combiner_buffer_color"); - shader.uniform_tev_const_colors = glGetUniformLocation(shader.shader.handle, "const_color"); - - current_shader = &shader_cache.emplace(config, std::move(shader)).first->second; - - state.draw.shader_program = current_shader->shader.handle; - state.Apply(); - - // Set the texture samplers to correspond to different texture units - if (shader.uniform_tex != -1) { - glUniform1i(shader.uniform_tex, 0); - glUniform1i(shader.uniform_tex + 1, 1); - glUniform1i(shader.uniform_tex + 2, 2); - } - } - - // Sync alpha reference - if (current_shader->uniform_alphatest_ref != -1) - glUniform1i(current_shader->uniform_alphatest_ref, Pica::g_state.regs.output_merger.alpha_test.ref); - - // Sync combiner buffer color - if (current_shader->uniform_tev_combiner_buffer_color != -1) { - auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); - glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data()); - } - - // Sync TEV const colors - if (current_shader->uniform_tev_const_colors != -1) { - auto& tev_stages = Pica::g_state.regs.GetTevStages(); - for (int tev_index = 0; tev_index < tev_stages.size(); ++tev_index) { - auto const_color = PicaToGL::ColorRGBA8(tev_stages[tev_index].const_color); - glUniform4fv(current_shader->uniform_tev_const_colors + tev_index, 1, const_color.data()); - } - } -} - void RasterizerOpenGL::DrawTriangles() { SyncFramebuffer(); SyncDrawState(); if (state.draw.shader_dirty) { - RegenerateShaders(); + SetShader(); state.draw.shader_dirty = false; } @@ -519,6 +460,48 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: state.Apply(); } +void RasterizerOpenGL::SetShader() { + ShaderCacheKey config = ShaderCacheKey::CurrentConfig(); + + // Find (or generate) the GLSL shader for the current TEV state + auto cached_shader = shader_cache.find(config); + if (cached_shader != shader_cache.end()) { + current_shader = cached_shader->second.get(); + + 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(); + + shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); + shader->uniform_alphatest_ref = glGetUniformLocation(shader->shader.handle, "alphatest_ref"); + shader->uniform_tex = glGetUniformLocation(shader->shader.handle, "tex"); + shader->uniform_tev_combiner_buffer_color = glGetUniformLocation(shader->shader.handle, "tev_combiner_buffer_color"); + shader->uniform_tev_const_colors = glGetUniformLocation(shader->shader.handle, "const_color"); + + state.draw.shader_program = shader->shader.handle; + state.Apply(); + + // Set the texture samplers to correspond to different texture units + if (shader->uniform_tex != -1) { + glUniform1i(shader->uniform_tex, 0); + glUniform1i(shader->uniform_tex + 1, 1); + glUniform1i(shader->uniform_tex + 2, 2); + } + + current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); + } + + // Update uniforms + SyncAlphaTest(); + SyncCombinerColor(); + auto& tev_stages = Pica::g_state.regs.GetTevStages(); + for (int index = 0; index < tev_stages.size(); ++index) + SyncTevConstColor(index, tev_stages[index]); +} + void RasterizerOpenGL::SyncFramebuffer() { const auto& regs = Pica::g_state.regs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 5bc4a319f..de9e4d22e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include #include @@ -15,21 +17,6 @@ #include "video_core/renderer_opengl/gl_state.h" #include "video_core/shader/shader_interpreter.h" -template -inline size_t hash(const T& o) { - return std::hash()(o); -} - -template -inline size_t combine_hash(const T& o) { - return hash(o); -} - -template -inline size_t combine_hash(const T& o, const Args&... args) { - return hash(o) * 3 + combine_hash(args...); -} - struct ShaderCacheKey { using Regs = Pica::Regs; @@ -49,7 +36,7 @@ struct ShaderCacheKey { return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); } - static ShaderCacheKey CurrentShaderConfig() { + static ShaderCacheKey CurrentConfig() { const auto& regs = Pica::g_state.regs; ShaderCacheKey config; @@ -94,8 +81,14 @@ struct ShaderCacheKey { 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> { - size_t operator()(const ::Pica::Regs::TevStageConfig& o) { + 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)); @@ -103,13 +96,14 @@ template<> struct hash<::Pica::Regs::TevStageConfig> { }; template<> struct hash<::ShaderCacheKey> { - size_t operator()(const ::ShaderCacheKey& o) const { + 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]); } }; -} + +} // namespace std class RasterizerOpenGL : public HWRasterizer { public: @@ -131,8 +125,6 @@ public: /// Draw the current batch of triangles void DrawTriangles() override; - void RegenerateShaders(); - /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer void CommitFramebuffer() override; @@ -245,6 +237,9 @@ private: /// Reconfigure the OpenGL depth texture to use the given format and dimensions void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); + /// Sets the OpenGL shader in accordance with the current PICA register state + void SetShader(); + /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer void SyncFramebuffer(); @@ -315,8 +310,8 @@ private: TextureInfo fb_color_texture; DepthTextureInfo fb_depth_texture; - std::unordered_map shader_cache; - TEVShader* current_shader = nullptr; + std::unordered_map> shader_cache; + const TEVShader* current_shader = nullptr; OGLVertexArray vertex_array; OGLBuffer vertex_buffer; diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 65034d40d..eb128966c 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -71,7 +71,7 @@ public: /// Creates a new internal OpenGL resource and stores the handle void Create(const char* vert_shader, const char* frag_shader) { if (handle != 0) return; - handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); + handle = GLShader::LoadProgram(vert_shader, frag_shader); } /// Deletes the internal OpenGL resource diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp new file mode 100644 index 000000000..059f127af --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -0,0 +1,371 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/pica.h" +#include "video_core/renderer_opengl/gl_rasterizer.h" +#include "video_core/renderer_opengl/gl_shader_gen.h" + +namespace GLShader { + +static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { + return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && + stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && + stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous && + stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous && + stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor && + stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha && + stage.GetColorMultiplier() == 1 && + stage.GetAlphaMultiplier() == 1); +} + +static void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { + using Source = Pica::Regs::TevStageConfig::Source; + switch (source) { + case Source::PrimaryColor: + shader += "o[2]"; + break; + case Source::PrimaryFragmentColor: + // HACK: Until we implement fragment lighting, use primary_color + shader += "o[2]"; + break; + case Source::SecondaryFragmentColor: + // HACK: Until we implement fragment lighting, use zero + shader += "vec4(0.0, 0.0, 0.0, 0.0)"; + break; + case Source::Texture0: + shader += "texture(tex[0], o[3].xy)"; + break; + case Source::Texture1: + shader += "texture(tex[1], o[3].zw)"; + break; + case Source::Texture2: // TODO: Unverified + shader += "texture(tex[2], o[5].zw)"; + break; + case Source::PreviousBuffer: + shader += "g_combiner_buffer"; + break; + case Source::Constant: + shader += "const_color[" + index_name + "]"; + break; + case Source::Previous: + shader += "g_last_tex_env_out"; + break; + default: + shader += "vec4(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); + break; + } +} + +static void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, + Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { + using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; + switch (modifier) { + case ColorModifier::SourceColor: + AppendSource(shader, source, index_name); + shader += ".rgb"; + break; + case ColorModifier::OneMinusSourceColor: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".rgb"; + break; + case ColorModifier::SourceAlpha: + AppendSource(shader, source, index_name); + shader += ".aaa"; + break; + case ColorModifier::OneMinusSourceAlpha: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".aaa"; + break; + case ColorModifier::SourceRed: + AppendSource(shader, source, index_name); + shader += ".rrr"; + break; + case ColorModifier::OneMinusSourceRed: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".rrr"; + break; + case ColorModifier::SourceGreen: + AppendSource(shader, source, index_name); + shader += ".ggg"; + break; + case ColorModifier::OneMinusSourceGreen: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".ggg"; + break; + case ColorModifier::SourceBlue: + AppendSource(shader, source, index_name); + shader += ".bbb"; + break; + case ColorModifier::OneMinusSourceBlue: + shader += "vec3(1.0) - "; + AppendSource(shader, source, index_name); + shader += ".bbb"; + break; + default: + shader += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); + break; + } +} + +static void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, + Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { + using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; + switch (modifier) { + case AlphaModifier::SourceAlpha: + AppendSource(shader, source, index_name); + shader += ".a"; + break; + case AlphaModifier::OneMinusSourceAlpha: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".a"; + break; + case AlphaModifier::SourceRed: + AppendSource(shader, source, index_name); + shader += ".r"; + break; + case AlphaModifier::OneMinusSourceRed: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".r"; + break; + case AlphaModifier::SourceGreen: + AppendSource(shader, source, index_name); + shader += ".g"; + break; + case AlphaModifier::OneMinusSourceGreen: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".g"; + break; + case AlphaModifier::SourceBlue: + AppendSource(shader, source, index_name); + shader += ".b"; + break; + case AlphaModifier::OneMinusSourceBlue: + shader += "1.0 - "; + AppendSource(shader, source, index_name); + shader += ".b"; + break; + default: + shader += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); + break; + } +} + +static void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, + const std::string& variable_name) { + using Operation = Pica::Regs::TevStageConfig::Operation; + + switch (operation) { + case Operation::Replace: + shader += variable_name + "[0]"; + break; + case Operation::Modulate: + shader += variable_name + "[0] * " + variable_name + "[1]"; + break; + case Operation::Add: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; + break; + case Operation::AddSigned: + shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; + break; + case Operation::Lerp: + shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; + break; + case Operation::Subtract: + shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; + break; + case Operation::MultiplyThenAdd: + shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; + break; + case Operation::AddThenMultiply: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; + break; + default: + shader += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); + break; + } +} + +static void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, + const std::string& variable_name) { + using Operation = Pica::Regs::TevStageConfig::Operation; + switch (operation) { + case Operation::Replace: + shader += variable_name + "[0]"; + break; + case Operation::Modulate: + shader += variable_name + "[0] * " + variable_name + "[1]"; + break; + case Operation::Add: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; + break; + case Operation::AddSigned: + shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; + break; + case Operation::Lerp: + shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; + break; + case Operation::Subtract: + shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; + break; + case Operation::MultiplyThenAdd: + shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; + break; + case Operation::AddThenMultiply: + shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; + break; + default: + shader += "0.0"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation); + break; + } +} + +static void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) { + using CompareFunc = Pica::Regs::CompareFunc; + switch (func) { + case CompareFunc::Never: + shader += "true"; + break; + case CompareFunc::Always: + shader += "false"; + break; + case CompareFunc::Equal: + shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; + break; + case CompareFunc::NotEqual: + shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; + break; + case CompareFunc::LessThan: + shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; + break; + case CompareFunc::LessThanOrEqual: + shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; + break; + case CompareFunc::GreaterThan: + shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; + break; + case CompareFunc::GreaterThanOrEqual: + shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; + break; + default: + shader += "false"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); + break; + } +} + +std::string GenerateFragmentShader(const ShaderCacheKey& config) { + std::string shader = R"( +#version 150 core + +#define NUM_VTX_ATTR 7 +#define NUM_TEV_STAGES 6 + +in vec4 o[NUM_VTX_ATTR]; +out vec4 color; + +uniform int alphatest_ref; +uniform vec4 const_color[NUM_TEV_STAGES]; +uniform sampler2D tex[3]; + +uniform vec4 tev_combiner_buffer_color; + +void main(void) { +vec4 g_combiner_buffer = tev_combiner_buffer_color; +vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); +)"; + + // Do not do any sort of processing if it's obvious we're not going to pass the alpha test + if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) { + shader += "discard;"; + return shader; + } + + auto& tev_stages = config.tev_stages; + for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { + auto& tev_stage = tev_stages[tev_stage_index]; + if (!IsPassThroughTevStage(tev_stage)) { + std::string index_name = std::to_string(tev_stage_index); + + shader += "vec3 color_results_" + index_name + "[3] = vec3[3]("; + AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name); + shader += ", "; + AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name); + shader += ", "; + AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name); + shader += ");\n"; + + shader += "vec3 color_output_" + index_name + " = "; + AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name); + shader += ";\n"; + + shader += "float alpha_results_" + index_name + "[3] = float[3]("; + AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name); + shader += ", "; + AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name); + shader += ", "; + AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name); + shader += ");\n"; + + shader += "float alpha_output_" + index_name + " = "; + AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name); + shader += ";\n"; + + shader += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + std::to_string(tev_stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + std::to_string(tev_stage.GetAlphaMultiplier()) + ".0, 1.0));\n"; + } + + if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index)) + shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; + + if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) + shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; + } + + if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) { + shader += "if ("; + AppendAlphaTestCondition(shader, config.alpha_test_func); + shader += ") {\n discard;\n }\n"; + } + + shader += "color = g_last_tex_env_out;\n}"; + return shader; +} + +std::string GenerateVertexShader() { + static const std::string shader_str = R"( +#version 150 core + +#define NUM_VTX_ATTR 7 + +in vec4 vert_position; +in vec4 vert_color; +in vec2 vert_texcoords0; +in vec2 vert_texcoords1; +in vec2 vert_texcoords2; + +out vec4 o[NUM_VTX_ATTR]; + +void main() { + o[2] = vert_color; + o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); + o[5] = vec4(0.0, 0.0, vert_texcoords2.xy); + + gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); +} +)"; + return shader_str; +} + +} // namespace GLShaderGen diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h new file mode 100644 index 000000000..7fd18de6d --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -0,0 +1,17 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "video_core/renderer_opengl/gl_rasterizer.h" + +namespace GLShader { + +std::string GenerateVertexShader(); + +std::string GenerateFragmentShader(const ShaderCacheKey& config); + +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 8a6a51ad4..ce218b857 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -2,22 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - -#include "gl_shader_util.h" -#include "gl_rasterizer.h" -#include "common/logging/log.h" - -#include "video_core/pica.h" - #include #include #include "common/logging/log.h" #include "video_core/renderer_opengl/gl_shader_util.h" -namespace ShaderUtil { +namespace GLShader { -GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { +GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { // Create the shaders GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); @@ -101,339 +94,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { return program_id; } -} - -namespace ShaderCache -{ - -static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { - return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && - stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && - stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous && - stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous && - stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor && - stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha && - stage.GetColorMultiplier() == 1 && - stage.GetAlphaMultiplier() == 1); -} - -void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { - using Source = Pica::Regs::TevStageConfig::Source; - switch (source) { - case Source::PrimaryColor: - shader += "o[2]"; - break; - case Source::PrimaryFragmentColor: - // HACK: Until we implement fragment lighting, use primary_color - shader += "o[2]"; - break; - case Source::SecondaryFragmentColor: - // HACK: Until we implement fragment lighting, use zero - shader += "vec4(0.0, 0.0, 0.0, 0.0)"; - break; - case Source::Texture0: - shader += "texture(tex[0], o[3].xy)"; - break; - case Source::Texture1: - shader += "texture(tex[1], o[3].zw)"; - break; - case Source::Texture2: // TODO: Unverified - shader += "texture(tex[2], o[5].zw)"; - break; - case Source::PreviousBuffer: - shader += "g_combiner_buffer"; - break; - case Source::Constant: - shader += "const_color[" + index_name + "]"; - break; - case Source::Previous: - shader += "g_last_tex_env_out"; - break; - default: - shader += "vec4(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); - break; - } -} - -void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { - using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; - switch (modifier) { - case ColorModifier::SourceColor: - AppendSource(shader, source, index_name); - shader += ".rgb"; - break; - case ColorModifier::OneMinusSourceColor: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".rgb"; - break; - case ColorModifier::SourceAlpha: - AppendSource(shader, source, index_name); - shader += ".aaa"; - break; - case ColorModifier::OneMinusSourceAlpha: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".aaa"; - break; - case ColorModifier::SourceRed: - AppendSource(shader, source, index_name); - shader += ".rrr"; - break; - case ColorModifier::OneMinusSourceRed: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".rrr"; - break; - case ColorModifier::SourceGreen: - AppendSource(shader, source, index_name); - shader += ".ggg"; - break; - case ColorModifier::OneMinusSourceGreen: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".ggg"; - break; - case ColorModifier::SourceBlue: - AppendSource(shader, source, index_name); - shader += ".bbb"; - break; - case ColorModifier::OneMinusSourceBlue: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".bbb"; - break; - default: - shader += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); - break; - } -} - -void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { - using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; - switch (modifier) { - case AlphaModifier::SourceAlpha: - AppendSource(shader, source, index_name); - shader += ".a"; - break; - case AlphaModifier::OneMinusSourceAlpha: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".a"; - break; - case AlphaModifier::SourceRed: - AppendSource(shader, source, index_name); - shader += ".r"; - break; - case AlphaModifier::OneMinusSourceRed: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".r"; - break; - case AlphaModifier::SourceGreen: - AppendSource(shader, source, index_name); - shader += ".g"; - break; - case AlphaModifier::OneMinusSourceGreen: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".g"; - break; - case AlphaModifier::SourceBlue: - AppendSource(shader, source, index_name); - shader += ".b"; - break; - case AlphaModifier::OneMinusSourceBlue: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".b"; - break; - default: - shader += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); - break; - } -} - -void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) { - using Operation = Pica::Regs::TevStageConfig::Operation; - - switch (operation) { - case Operation::Replace: - shader += variable_name + "[0]"; - break; - case Operation::Modulate: - shader += variable_name + "[0] * " + variable_name + "[1]"; - break; - case Operation::Add: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; - break; - case Operation::AddSigned: - shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; - break; - case Operation::Lerp: - shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; - break; - case Operation::Subtract: - shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; - break; - case Operation::MultiplyThenAdd: - shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; - break; - case Operation::AddThenMultiply: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; - break; - default: - shader += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); - break; - } -} - -void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) { - using Operation = Pica::Regs::TevStageConfig::Operation; - switch (operation) { - case Operation::Replace: - shader += variable_name + "[0]"; - break; - case Operation::Modulate: - shader += variable_name + "[0] * " + variable_name + "[1]"; - break; - case Operation::Add: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; - break; - case Operation::AddSigned: - shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; - break; - case Operation::Lerp: - shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; - break; - case Operation::Subtract: - shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; - break; - case Operation::MultiplyThenAdd: - shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; - break; - case Operation::AddThenMultiply: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; - break; - default: - shader += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation); - break; - } -} - -void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) { - using CompareFunc = Pica::Regs::CompareFunc; - switch (func) { - case CompareFunc::Never: - shader += "true"; - break; - case CompareFunc::Always: - shader += "false"; - break; - case CompareFunc::Equal: - shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; - break; - case CompareFunc::NotEqual: - shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; - break; - case CompareFunc::LessThan: - shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; - break; - case CompareFunc::LessThanOrEqual: - shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; - break; - case CompareFunc::GreaterThan: - shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; - break; - case CompareFunc::GreaterThanOrEqual: - shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; - break; - default: - shader += "false"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); - break; - } -} - -std::string GenerateFragmentShader(const ShaderCacheKey& config) { - std::string shader = R"( -#version 150 core - -#define NUM_VTX_ATTR 7 -#define NUM_TEV_STAGES 6 - -in vec4 o[NUM_VTX_ATTR]; -out vec4 color; - -uniform int alphatest_ref; -uniform vec4 const_color[NUM_TEV_STAGES]; -uniform sampler2D tex[3]; - -uniform vec4 tev_combiner_buffer_color; - -void main(void) { - vec4 g_combiner_buffer = tev_combiner_buffer_color; - vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); -)"; - - // Do not do any sort of processing if it's obvious we're not going to pass the alpha test - if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) { - shader += "discard;"; - return shader; - } - - auto& tev_stages = config.tev_stages; - for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { - auto& tev_stage = tev_stages[tev_stage_index]; - if (!IsPassThroughTevStage(tev_stage)) { - std::string index_name = std::to_string(tev_stage_index); - - shader += "vec3 color_results_" + index_name + "[3] = vec3[3]("; - AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name); - shader += ", "; - AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name); - shader += ", "; - AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name); - shader += ");\n"; - - shader += "vec3 color_output_" + index_name + " = "; - AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name); - shader += ";\n"; - - shader += "float alpha_results_" + index_name + "[3] = float[3]("; - AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name); - shader += ", "; - AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name); - shader += ", "; - AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name); - shader += ");\n"; - - shader += "float alpha_output_" + index_name + " = "; - AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name); - shader += ";\n"; - - shader += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + std::to_string(tev_stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + std::to_string(tev_stage.GetAlphaMultiplier()) + ".0, 1.0));\n"; - } - - if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index)) - shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; - - if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) - shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; - } - - if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) { - shader += "if ("; - AppendAlphaTestCondition(shader, config.alpha_test_func); - shader += ") {\n discard;\n }\n"; - } - - shader += "color = g_last_tex_env_out;\n}"; - return shader; -} -} +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index ca62c83ba..6e2d007f8 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -6,7 +6,7 @@ #include -namespace ShaderUtil { +namespace GLShader { enum Attributes { ATTRIBUTE_POSITION = 0, @@ -14,6 +14,6 @@ enum Attributes { ATTRIBUTE_TEXCOORDS = 2, }; -GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); +GLuint LoadProgram(const char* vertex_file_path, const char* fragment_file_path); -} +} // namespace diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h deleted file mode 100644 index 2ba2c6b0f..000000000 --- a/src/video_core/renderer_opengl/gl_shaders.h +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -namespace GLShaders { - -const char g_vertex_shader[] = R"( -#version 150 core - -in vec2 vert_position; -in vec2 vert_tex_coord; -out vec2 frag_tex_coord; - -// This is a truncated 3x3 matrix for 2D transformations: -// The upper-left 2x2 submatrix performs scaling/rotation/mirroring. -// The third column performs translation. -// The third row could be used for projection, which we don't need in 2D. It hence is assumed to -// implicitly be [0, 0, 1] -uniform mat3x2 modelview_matrix; - -void main() { - // Multiply input position by the rotscale part of the matrix and then manually translate by - // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector - // to `vec3(vert_position.xy, 1.0)` - gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); - frag_tex_coord = vert_tex_coord; -} -)"; - -const char g_fragment_shader[] = R"( -#version 150 core - -in vec2 frag_tex_coord; -out vec4 color; - -uniform sampler2D color_texture; - -void main() { - color = texture(color_texture, frag_tex_coord); -} -)"; - -const char g_vertex_shader_hw[] = R"( -#version 150 core - -#define NUM_VTX_ATTR 7 - -in vec4 vert_position; -in vec4 vert_color; -in vec2 vert_texcoords0; -in vec2 vert_texcoords1; -in vec2 vert_texcoords2; - -out vec4 o[NUM_VTX_ATTR]; - -void main() { - o[2] = vert_color; - o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); - o[5] = vec4(0.0, 0.0, vert_texcoords2.xy); - - gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); -} -)"; - -// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms -const char g_fragment_shader_hw[] = R"( -#version 150 core - -#define NUM_VTX_ATTR 7 -#define NUM_TEV_STAGES 6 - -#define SOURCE_PRIMARYCOLOR 0x0 -#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1 -#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2 -#define SOURCE_TEXTURE0 0x3 -#define SOURCE_TEXTURE1 0x4 -#define SOURCE_TEXTURE2 0x5 -#define SOURCE_TEXTURE3 0x6 -#define SOURCE_PREVIOUSBUFFER 0xd -#define SOURCE_CONSTANT 0xe -#define SOURCE_PREVIOUS 0xf - -#define COLORMODIFIER_SOURCECOLOR 0x0 -#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1 -#define COLORMODIFIER_SOURCEALPHA 0x2 -#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3 -#define COLORMODIFIER_SOURCERED 0x4 -#define COLORMODIFIER_ONEMINUSSOURCERED 0x5 -#define COLORMODIFIER_SOURCEGREEN 0x8 -#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9 -#define COLORMODIFIER_SOURCEBLUE 0xc -#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd - -#define ALPHAMODIFIER_SOURCEALPHA 0x0 -#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1 -#define ALPHAMODIFIER_SOURCERED 0x2 -#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3 -#define ALPHAMODIFIER_SOURCEGREEN 0x4 -#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5 -#define ALPHAMODIFIER_SOURCEBLUE 0x6 -#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7 - -#define OPERATION_REPLACE 0 -#define OPERATION_MODULATE 1 -#define OPERATION_ADD 2 -#define OPERATION_ADDSIGNED 3 -#define OPERATION_LERP 4 -#define OPERATION_SUBTRACT 5 -#define OPERATION_MULTIPLYTHENADD 8 -#define OPERATION_ADDTHENMULTIPLY 9 - -#define COMPAREFUNC_NEVER 0 -#define COMPAREFUNC_ALWAYS 1 -#define COMPAREFUNC_EQUAL 2 -#define COMPAREFUNC_NOTEQUAL 3 -#define COMPAREFUNC_LESSTHAN 4 -#define COMPAREFUNC_LESSTHANOREQUAL 5 -#define COMPAREFUNC_GREATERTHAN 6 -#define COMPAREFUNC_GREATERTHANOREQUAL 7 - -in vec4 o[NUM_VTX_ATTR]; -out vec4 color; - -uniform bool alphatest_enabled; -uniform int alphatest_func; -uniform float alphatest_ref; - -uniform sampler2D tex[3]; - -uniform vec4 tev_combiner_buffer_color; - -struct TEVConfig -{ - bool enabled; - ivec3 color_sources; - ivec3 alpha_sources; - ivec3 color_modifiers; - ivec3 alpha_modifiers; - ivec2 color_alpha_op; - ivec2 color_alpha_multiplier; - vec4 const_color; - bvec2 updates_combiner_buffer_color_alpha; -}; - -uniform TEVConfig tev_cfgs[NUM_TEV_STAGES]; - -vec4 g_combiner_buffer; -vec4 g_last_tex_env_out; -vec4 g_const_color; - -vec4 GetSource(int source) { - if (source == SOURCE_PRIMARYCOLOR) { - return o[2]; - } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) { - // HACK: Until we implement fragment lighting, use primary_color - return o[2]; - } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) { - // HACK: Until we implement fragment lighting, use zero - return vec4(0.0, 0.0, 0.0, 0.0); - } else if (source == SOURCE_TEXTURE0) { - return texture(tex[0], o[3].xy); - } else if (source == SOURCE_TEXTURE1) { - return texture(tex[1], o[3].zw); - } else if (source == SOURCE_TEXTURE2) { - // TODO: Unverified - return texture(tex[2], o[5].zw); - } else if (source == SOURCE_TEXTURE3) { - // TODO: no 4th texture? - } else if (source == SOURCE_PREVIOUSBUFFER) { - return g_combiner_buffer; - } else if (source == SOURCE_CONSTANT) { - return g_const_color; - } else if (source == SOURCE_PREVIOUS) { - return g_last_tex_env_out; - } - - return vec4(0.0); -} - -vec3 GetColorModifier(int factor, vec4 color) { - if (factor == COLORMODIFIER_SOURCECOLOR) { - return color.rgb; - } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) { - return vec3(1.0) - color.rgb; - } else if (factor == COLORMODIFIER_SOURCEALPHA) { - return color.aaa; - } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) { - return vec3(1.0) - color.aaa; - } else if (factor == COLORMODIFIER_SOURCERED) { - return color.rrr; - } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) { - return vec3(1.0) - color.rrr; - } else if (factor == COLORMODIFIER_SOURCEGREEN) { - return color.ggg; - } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) { - return vec3(1.0) - color.ggg; - } else if (factor == COLORMODIFIER_SOURCEBLUE) { - return color.bbb; - } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) { - return vec3(1.0) - color.bbb; - } - - return vec3(0.0); -} - -float GetAlphaModifier(int factor, vec4 color) { - if (factor == ALPHAMODIFIER_SOURCEALPHA) { - return color.a; - } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) { - return 1.0 - color.a; - } else if (factor == ALPHAMODIFIER_SOURCERED) { - return color.r; - } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) { - return 1.0 - color.r; - } else if (factor == ALPHAMODIFIER_SOURCEGREEN) { - return color.g; - } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) { - return 1.0 - color.g; - } else if (factor == ALPHAMODIFIER_SOURCEBLUE) { - return color.b; - } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) { - return 1.0 - color.b; - } - - return 0.0; -} - -vec3 ColorCombine(int op, vec3 color[3]) { - if (op == OPERATION_REPLACE) { - return color[0]; - } else if (op == OPERATION_MODULATE) { - return color[0] * color[1]; - } else if (op == OPERATION_ADD) { - return min(color[0] + color[1], 1.0); - } else if (op == OPERATION_ADDSIGNED) { - return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0); - } else if (op == OPERATION_LERP) { - return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]); - } else if (op == OPERATION_SUBTRACT) { - return max(color[0] - color[1], 0.0); - } else if (op == OPERATION_MULTIPLYTHENADD) { - return min(color[0] * color[1] + color[2], 1.0); - } else if (op == OPERATION_ADDTHENMULTIPLY) { - return min(color[0] + color[1], 1.0) * color[2]; - } - - return vec3(0.0); -} - -float AlphaCombine(int op, float alpha[3]) { - if (op == OPERATION_REPLACE) { - return alpha[0]; - } else if (op == OPERATION_MODULATE) { - return alpha[0] * alpha[1]; - } else if (op == OPERATION_ADD) { - return min(alpha[0] + alpha[1], 1.0); - } else if (op == OPERATION_ADDSIGNED) { - return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0); - } else if (op == OPERATION_LERP) { - return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]); - } else if (op == OPERATION_SUBTRACT) { - return max(alpha[0] - alpha[1], 0.0); - } else if (op == OPERATION_MULTIPLYTHENADD) { - return min(alpha[0] * alpha[1] + alpha[2], 1.0); - } else if (op == OPERATION_ADDTHENMULTIPLY) { - return min(alpha[0] + alpha[1], 1.0) * alpha[2]; - } - - return 0.0; -} - -void main(void) { - g_combiner_buffer = tev_combiner_buffer_color; - - for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) { - if (tev_cfgs[tex_env_idx].enabled) { - g_const_color = tev_cfgs[tex_env_idx].const_color; - - vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)), - GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)), - GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z))); - vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results); - - float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)), - GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)), - GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z))); - float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results); - - g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0)); - } - - if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) { - g_combiner_buffer.rgb = g_last_tex_env_out.rgb; - } - - if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) { - g_combiner_buffer.a = g_last_tex_env_out.a; - } - } - - if (alphatest_enabled) { - if (alphatest_func == COMPAREFUNC_NEVER) { - discard; - } else if (alphatest_func == COMPAREFUNC_ALWAYS) { - - } else if (alphatest_func == COMPAREFUNC_EQUAL) { - if (g_last_tex_env_out.a != alphatest_ref) { - discard; - } - } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) { - if (g_last_tex_env_out.a == alphatest_ref) { - discard; - } - } else if (alphatest_func == COMPAREFUNC_LESSTHAN) { - if (g_last_tex_env_out.a >= alphatest_ref) { - discard; - } - } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) { - if (g_last_tex_env_out.a > alphatest_ref) { - discard; - } - } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) { - if (g_last_tex_env_out.a <= alphatest_ref) { - discard; - } - } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) { - if (g_last_tex_env_out.a < alphatest_ref) { - discard; - } - } - } - - color = g_last_tex_env_out; -} -)"; - -} diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f1313b54f..ac0a058db 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -21,9 +21,44 @@ #include "video_core/debug_utils/debug_utils.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_util.h" -#include "video_core/renderer_opengl/gl_shaders.h" #include "video_core/renderer_opengl/renderer_opengl.h" +static const char vertex_shader[] = R"( +#version 150 core + +in vec2 vert_position; +in vec2 vert_tex_coord; +out vec2 frag_tex_coord; + +// This is a truncated 3x3 matrix for 2D transformations: +// The upper-left 2x2 submatrix performs scaling/rotation/mirroring. +// The third column performs translation. +// The third row could be used for projection, which we don't need in 2D. It hence is assumed to +// implicitly be [0, 0, 1] +uniform mat3x2 modelview_matrix; + +void main() { + // Multiply input position by the rotscale part of the matrix and then manually translate by + // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector + // to `vec3(vert_position.xy, 1.0)` + gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); + frag_tex_coord = vert_tex_coord; +} +)"; + +static const char fragment_shader[] = R"( +#version 150 core + +in vec2 frag_tex_coord; +out vec4 color; + +uniform sampler2D color_texture; + +void main() { + color = texture(color_texture, frag_tex_coord); +} +)"; + /** * Vertex structure that the drawn screen rectangles are composed of. */ @@ -207,7 +242,7 @@ void RendererOpenGL::InitOpenGLObjects() { glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); // Link shaders and get variable locations - program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); + program_id = GLShader::LoadProgram(vertex_shader, fragment_shader); uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix"); uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); attrib_position = glGetAttribLocation(program_id, "vert_position"); From a74774257eafd37f2efae5b745930c3950a3aa04 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 6 Oct 2015 18:01:53 -0400 Subject: [PATCH 07/19] gl_shader_gen: Various cleanups + moved TEV stage generation to its own function. --- .../renderer_opengl/gl_shader_gen.cpp | 329 +++++++++--------- 1 file changed, 169 insertions(+), 160 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 059f127af..4854c347e 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -6,268 +6,313 @@ #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_gen.h" +using Pica::Regs; +using TevStageConfig = Regs::TevStageConfig; + namespace GLShader { -static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { - return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && - stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && - stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous && - stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous && - stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor && - stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha && - stage.GetColorMultiplier() == 1 && - stage.GetAlphaMultiplier() == 1); +static bool IsPassThroughTevStage(const TevStageConfig& stage) { + return (stage.color_op == TevStageConfig::Operation::Replace && + stage.alpha_op == TevStageConfig::Operation::Replace && + stage.color_source1 == TevStageConfig::Source::Previous && + stage.alpha_source1 == TevStageConfig::Source::Previous && + stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor && + stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha && + stage.GetColorMultiplier() == 1 && + stage.GetAlphaMultiplier() == 1); } -static void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { - using Source = Pica::Regs::TevStageConfig::Source; +static void AppendSource(std::string& out, TevStageConfig::Source source, + const std::string& index_name) { + using Source = TevStageConfig::Source; switch (source) { case Source::PrimaryColor: - shader += "o[2]"; + out += "o[2]"; break; case Source::PrimaryFragmentColor: // HACK: Until we implement fragment lighting, use primary_color - shader += "o[2]"; + out += "o[2]"; break; case Source::SecondaryFragmentColor: // HACK: Until we implement fragment lighting, use zero - shader += "vec4(0.0, 0.0, 0.0, 0.0)"; + out += "vec4(0.0, 0.0, 0.0, 0.0)"; break; case Source::Texture0: - shader += "texture(tex[0], o[3].xy)"; + out += "texture(tex[0], o[3].xy)"; break; case Source::Texture1: - shader += "texture(tex[1], o[3].zw)"; + out += "texture(tex[1], o[3].zw)"; break; case Source::Texture2: // TODO: Unverified - shader += "texture(tex[2], o[5].zw)"; + out += "texture(tex[2], o[5].zw)"; break; case Source::PreviousBuffer: - shader += "g_combiner_buffer"; + out += "g_combiner_buffer"; break; case Source::Constant: - shader += "const_color[" + index_name + "]"; + out += "const_color[" + index_name + "]"; break; case Source::Previous: - shader += "g_last_tex_env_out"; + out += "g_last_tex_env_out"; break; default: - shader += "vec4(0.0)"; + out += "vec4(0.0)"; LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); break; } } -static void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, - Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { - using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; +static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier, + TevStageConfig::Source source, const std::string& index_name) { + using ColorModifier = TevStageConfig::ColorModifier; switch (modifier) { case ColorModifier::SourceColor: - AppendSource(shader, source, index_name); - shader += ".rgb"; + AppendSource(out, source, index_name); + out += ".rgb"; break; case ColorModifier::OneMinusSourceColor: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".rgb"; + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".rgb"; break; case ColorModifier::SourceAlpha: - AppendSource(shader, source, index_name); - shader += ".aaa"; + AppendSource(out, source, index_name); + out += ".aaa"; break; case ColorModifier::OneMinusSourceAlpha: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".aaa"; + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".aaa"; break; case ColorModifier::SourceRed: - AppendSource(shader, source, index_name); - shader += ".rrr"; + AppendSource(out, source, index_name); + out += ".rrr"; break; case ColorModifier::OneMinusSourceRed: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".rrr"; + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".rrr"; break; case ColorModifier::SourceGreen: - AppendSource(shader, source, index_name); - shader += ".ggg"; + AppendSource(out, source, index_name); + out += ".ggg"; break; case ColorModifier::OneMinusSourceGreen: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".ggg"; + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".ggg"; break; case ColorModifier::SourceBlue: - AppendSource(shader, source, index_name); - shader += ".bbb"; + AppendSource(out, source, index_name); + out += ".bbb"; break; case ColorModifier::OneMinusSourceBlue: - shader += "vec3(1.0) - "; - AppendSource(shader, source, index_name); - shader += ".bbb"; + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".bbb"; break; default: - shader += "vec3(0.0)"; + out += "vec3(0.0)"; LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); break; } } -static void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, - Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { - using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; +static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier, + TevStageConfig::Source source, const std::string& index_name) { + using AlphaModifier = TevStageConfig::AlphaModifier; switch (modifier) { case AlphaModifier::SourceAlpha: - AppendSource(shader, source, index_name); - shader += ".a"; + AppendSource(out, source, index_name); + out += ".a"; break; case AlphaModifier::OneMinusSourceAlpha: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".a"; + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".a"; break; case AlphaModifier::SourceRed: - AppendSource(shader, source, index_name); - shader += ".r"; + AppendSource(out, source, index_name); + out += ".r"; break; case AlphaModifier::OneMinusSourceRed: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".r"; + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".r"; break; case AlphaModifier::SourceGreen: - AppendSource(shader, source, index_name); - shader += ".g"; + AppendSource(out, source, index_name); + out += ".g"; break; case AlphaModifier::OneMinusSourceGreen: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".g"; + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".g"; break; case AlphaModifier::SourceBlue: - AppendSource(shader, source, index_name); - shader += ".b"; + AppendSource(out, source, index_name); + out += ".b"; break; case AlphaModifier::OneMinusSourceBlue: - shader += "1.0 - "; - AppendSource(shader, source, index_name); - shader += ".b"; + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".b"; break; default: - shader += "vec3(0.0)"; + out += "vec3(0.0)"; LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); break; } } -static void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, +static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation, const std::string& variable_name) { - using Operation = Pica::Regs::TevStageConfig::Operation; + using Operation = TevStageConfig::Operation; switch (operation) { case Operation::Replace: - shader += variable_name + "[0]"; + out += variable_name + "[0]"; break; case Operation::Modulate: - shader += variable_name + "[0] * " + variable_name + "[1]"; + out += variable_name + "[0] * " + variable_name + "[1]"; break; case Operation::Add: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; + out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; break; case Operation::AddSigned: - shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; + out += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; break; case Operation::Lerp: - shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; + out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; break; case Operation::Subtract: - shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; + out += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; break; case Operation::MultiplyThenAdd: - shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; + out += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; break; case Operation::AddThenMultiply: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; + out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; break; default: - shader += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); + out += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); break; } } -static void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, +static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation, const std::string& variable_name) { - using Operation = Pica::Regs::TevStageConfig::Operation; + using Operation = TevStageConfig::Operation; switch (operation) { case Operation::Replace: - shader += variable_name + "[0]"; + out += variable_name + "[0]"; break; case Operation::Modulate: - shader += variable_name + "[0] * " + variable_name + "[1]"; + out += variable_name + "[0] * " + variable_name + "[1]"; break; case Operation::Add: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; + out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; break; case Operation::AddSigned: - shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; + out += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; break; case Operation::Lerp: - shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; + out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; break; case Operation::Subtract: - shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; + out += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; break; case Operation::MultiplyThenAdd: - shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; + out += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; break; case Operation::AddThenMultiply: - shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; + out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; break; default: - shader += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation); + out += "0.0"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); break; } } -static void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) { - using CompareFunc = Pica::Regs::CompareFunc; +static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { + using CompareFunc = Regs::CompareFunc; switch (func) { case CompareFunc::Never: - shader += "true"; + out += "true"; break; case CompareFunc::Always: - shader += "false"; + out += "false"; break; case CompareFunc::Equal: - shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; + out += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; break; case CompareFunc::NotEqual: - shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; + out += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; break; case CompareFunc::LessThan: - shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; + out += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; break; case CompareFunc::LessThanOrEqual: - shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; + out += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; break; case CompareFunc::GreaterThan: - shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; + out += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; break; case CompareFunc::GreaterThanOrEqual: - shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; + out += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; break; default: - shader += "false"; + out += "false"; LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); break; } } +static void WriteTevStage(std::string& out, const ShaderCacheKey& config, unsigned index) { + auto& stage = config.tev_stages[index]; + if (!IsPassThroughTevStage(stage)) { + std::string index_name = std::to_string(index); + + out += "vec3 color_results_" + index_name + "[3] = vec3[3]("; + AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name); + out += ", "; + AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name); + out += ", "; + AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name); + out += ");\n"; + + out += "vec3 color_output_" + index_name + " = "; + AppendColorCombiner(out, stage.color_op, "color_results_" + index_name); + out += ";\n"; + + out += "float alpha_results_" + index_name + "[3] = float[3]("; + AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name); + out += ", "; + AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name); + out += ", "; + AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name); + out += ");\n"; + + out += "float alpha_output_" + index_name + " = "; + AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name); + out += ";\n"; + + out += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + + std::to_string(stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + + std::to_string(stage.GetAlphaMultiplier()) + ".0, 1.0));\n"; + } + + if (config.TevStageUpdatesCombinerBufferColor(index)) + out += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; + + if (config.TevStageUpdatesCombinerBufferAlpha(index)) + out += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; +} + std::string GenerateFragmentShader(const ShaderCacheKey& config) { - std::string shader = R"( + std::string out = R"( #version 150 core #define NUM_VTX_ATTR 7 @@ -288,63 +333,27 @@ vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); )"; // Do not do any sort of processing if it's obvious we're not going to pass the alpha test - if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) { - shader += "discard;"; - return shader; + if (config.alpha_test_func == Regs::CompareFunc::Never) { + out += "discard;"; + return out; } - auto& tev_stages = config.tev_stages; - for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { - auto& tev_stage = tev_stages[tev_stage_index]; - if (!IsPassThroughTevStage(tev_stage)) { - std::string index_name = std::to_string(tev_stage_index); + for (std::size_t index = 0; index < config.tev_stages.size(); ++index) + WriteTevStage(out, config, (unsigned)index); - shader += "vec3 color_results_" + index_name + "[3] = vec3[3]("; - AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name); - shader += ", "; - AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name); - shader += ", "; - AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name); - shader += ");\n"; - - shader += "vec3 color_output_" + index_name + " = "; - AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name); - shader += ";\n"; - - shader += "float alpha_results_" + index_name + "[3] = float[3]("; - AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name); - shader += ", "; - AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name); - shader += ", "; - AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name); - shader += ");\n"; - - shader += "float alpha_output_" + index_name + " = "; - AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name); - shader += ";\n"; - - shader += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + std::to_string(tev_stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + std::to_string(tev_stage.GetAlphaMultiplier()) + ".0, 1.0));\n"; - } - - if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index)) - shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; - - if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) - shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; + if (config.alpha_test_func != Regs::CompareFunc::Always) { + out += "if ("; + AppendAlphaTestCondition(out, config.alpha_test_func); + out += ") {\n discard;\n }\n"; } - if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) { - shader += "if ("; - AppendAlphaTestCondition(shader, config.alpha_test_func); - shader += ") {\n discard;\n }\n"; - } + out += "color = g_last_tex_env_out;\n}"; - shader += "color = g_last_tex_env_out;\n}"; - return shader; + return out; } std::string GenerateVertexShader() { - static const std::string shader_str = R"( + static const std::string out = R"( #version 150 core #define NUM_VTX_ATTR 7 @@ -365,7 +374,7 @@ void main() { gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); } )"; - return shader_str; + return out; } -} // namespace GLShaderGen +} // namespace GLShader From 2a0a86f62998fa2a7a76ea3862b1de245132f85a Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 6 Oct 2015 18:10:32 -0400 Subject: [PATCH 08/19] gl_shader_util: Cleanup header file + add docstring. --- src/video_core/renderer_opengl/gl_shader_util.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 6e2d007f8..4d3791d50 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -14,6 +14,12 @@ enum Attributes { ATTRIBUTE_TEXCOORDS = 2, }; -GLuint LoadProgram(const char* vertex_file_path, const char* fragment_file_path); +/** + * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) + * @param vertex_shader String of the GLSL vertex shader program + * @param fragment_shader String of the GLSL fragment shader program + * @returns Handle of the newly created OpenGL shader object + */ +GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader); } // namespace From 4b5141954ebb2416c9de1996f155641cea46f60a Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 6 Oct 2015 18:21:28 -0400 Subject: [PATCH 09/19] gl_shader_gen: Add additional function documentation. --- src/video_core/renderer_opengl/gl_shader_gen.cpp | 8 ++++++++ src/video_core/renderer_opengl/gl_shader_gen.h | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 4854c347e..5093710d4 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -11,6 +11,7 @@ using TevStageConfig = Regs::TevStageConfig; namespace GLShader { +/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code) static bool IsPassThroughTevStage(const TevStageConfig& stage) { return (stage.color_op == TevStageConfig::Operation::Replace && stage.alpha_op == TevStageConfig::Operation::Replace && @@ -22,6 +23,7 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) { stage.GetAlphaMultiplier() == 1); } +/// Writes the specified TEV stage source component(s) static void AppendSource(std::string& out, TevStageConfig::Source source, const std::string& index_name) { using Source = TevStageConfig::Source; @@ -62,6 +64,7 @@ static void AppendSource(std::string& out, TevStageConfig::Source source, } } +/// Writes the color components to use for the specified TEV stage color modifier static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier, TevStageConfig::Source source, const std::string& index_name) { using ColorModifier = TevStageConfig::ColorModifier; @@ -118,6 +121,7 @@ static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier } } +/// Writes the alpha component to use for the specified TEV stage alpha modifier static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier, TevStageConfig::Source source, const std::string& index_name) { using AlphaModifier = TevStageConfig::AlphaModifier; @@ -165,6 +169,7 @@ static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier } } +/// Writes the combiner function for the color components for the specified TEV stage operation static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation, const std::string& variable_name) { using Operation = TevStageConfig::Operation; @@ -201,6 +206,7 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper } } +/// Writes the combiner function for the alpha component for the specified TEV stage operation static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation, const std::string& variable_name) { using Operation = TevStageConfig::Operation; @@ -236,6 +242,7 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper } } +/// Writes the if-statement condition used to evaluate alpha testing static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { using CompareFunc = Regs::CompareFunc; switch (func) { @@ -270,6 +277,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) { auto& stage = config.tev_stages[index]; if (!IsPassThroughTevStage(stage)) { diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 7fd18de6d..c8295e9e0 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -10,8 +10,18 @@ namespace GLShader { +/** + * Generates the GLSL vertex shader program source code for the current Pica state + * @returns String of the shader source code + */ std::string GenerateVertexShader(); +/** + * Generates the GLSL fragment shader program source code for the current Pica state + * @param config ShaderCacheKey object generated for the current Pica state, used for the shader + * 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); } // namespace GLShader From f2e7f7e1019eeffc0aee438f788449fc4e0c118f Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 6 Oct 2015 18:34:50 -0400 Subject: [PATCH 10/19] gl_rasterizer: Add documentation to ShaderCacheKey. --- src/video_core/renderer_opengl/gl_rasterizer.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index de9e4d22e..484579d82 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -17,6 +17,14 @@ #include "video_core/renderer_opengl/gl_state.h" #include "video_core/shader/shader_interpreter.h" +/** + * This struct contains all state used to generate the GLSL shader program that emulates the current + * Pica register configuration. This struct is used as a cache key for generated GLSL shader + * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by + * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where + * 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; @@ -36,6 +44,11 @@ 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; @@ -43,6 +56,9 @@ struct ShaderCacheKey { 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; From bd833b8dd8b872833a6867266da4d61f058e340f Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 7 Oct 2015 21:01:28 -0400 Subject: [PATCH 11/19] gl_shader_gen: Fix bug where TEV stage outputs should be clamped. --- src/video_core/renderer_opengl/gl_shader_gen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 5093710d4..74f0d4805 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -307,9 +307,9 @@ static void WriteTevStage(std::string& out, const ShaderCacheKey& config, unsign AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name); out += ";\n"; - out += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + - std::to_string(stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + - std::to_string(stage.GetAlphaMultiplier()) + ".0, 1.0));\n"; + out += "g_last_tex_env_out = vec4(" + "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, 0.0, 1.0)," + "clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n"; } if (config.TevStageUpdatesCombinerBufferColor(index)) From c2c4faef4c03d0c7b026672f2862b0022e5dc9cd Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 7 Oct 2015 21:02:50 -0400 Subject: [PATCH 12/19] gl_shader_gen: AppendAlphaModifier default should be 0.0, not vec4(0.0). --- src/video_core/renderer_opengl/gl_shader_gen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 74f0d4805..b24c763f2 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -163,7 +163,7 @@ static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier out += ".b"; break; default: - out += "vec3(0.0)"; + out += "0.0"; LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); break; } From 5ef2df056d0c765093b00932ae240093543708fc Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 7 Oct 2015 21:11:32 -0400 Subject: [PATCH 13/19] gl_shader_gen: Rename 'o' to 'attr' in vertex/fragment shaders. --- .../renderer_opengl/gl_shader_gen.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index b24c763f2..79c690e76 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -29,24 +29,24 @@ static void AppendSource(std::string& out, TevStageConfig::Source source, using Source = TevStageConfig::Source; switch (source) { case Source::PrimaryColor: - out += "o[2]"; + out += "attr[2]"; break; case Source::PrimaryFragmentColor: // HACK: Until we implement fragment lighting, use primary_color - out += "o[2]"; + out += "attr[2]"; break; case Source::SecondaryFragmentColor: // HACK: Until we implement fragment lighting, use zero out += "vec4(0.0, 0.0, 0.0, 0.0)"; break; case Source::Texture0: - out += "texture(tex[0], o[3].xy)"; + out += "texture(tex[0], attr[3].xy)"; break; case Source::Texture1: - out += "texture(tex[1], o[3].zw)"; + out += "texture(tex[1], attr[3].zw)"; break; case Source::Texture2: // TODO: Unverified - out += "texture(tex[2], o[5].zw)"; + out += "texture(tex[2], attr[5].zw)"; break; case Source::PreviousBuffer: out += "g_combiner_buffer"; @@ -326,7 +326,7 @@ std::string GenerateFragmentShader(const ShaderCacheKey& config) { #define NUM_VTX_ATTR 7 #define NUM_TEV_STAGES 6 -in vec4 o[NUM_VTX_ATTR]; +in vec4 attr[NUM_VTX_ATTR]; out vec4 color; uniform int alphatest_ref; @@ -342,7 +342,7 @@ vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); // Do not do any sort of processing if it's obvious we're not going to pass the alpha test if (config.alpha_test_func == Regs::CompareFunc::Never) { - out += "discard;"; + out += "discard; }"; return out; } @@ -372,12 +372,12 @@ in vec2 vert_texcoords0; in vec2 vert_texcoords1; in vec2 vert_texcoords2; -out vec4 o[NUM_VTX_ATTR]; +out vec4 attr[NUM_VTX_ATTR]; void main() { - o[2] = vert_color; - o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); - o[5] = vec4(0.0, 0.0, vert_texcoords2.xy); + attr[2] = vert_color; + attr[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); + attr[5] = vec4(0.0, 0.0, vert_texcoords2.xy); gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); } From 71edb55114af129d6f580e271ad5196043342abe Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 Oct 2015 19:32:38 -0400 Subject: [PATCH 14/19] gl_shader_gen: Require explicit uniform locations. - Fixes uniform issue on AMD. --- .../renderer_opengl/gl_rasterizer.cpp | 27 ++++--------- .../renderer_opengl/gl_rasterizer.h | 40 +++++++------------ .../renderer_opengl/gl_shader_gen.cpp | 25 ++++++------ 3 files changed, 35 insertions(+), 57 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4f9865230..64639ed26 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -476,20 +476,14 @@ void RasterizerOpenGL::SetShader() { std::unique_ptr shader = Common::make_unique(); shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); - shader->uniform_alphatest_ref = glGetUniformLocation(shader->shader.handle, "alphatest_ref"); - shader->uniform_tex = glGetUniformLocation(shader->shader.handle, "tex"); - shader->uniform_tev_combiner_buffer_color = glGetUniformLocation(shader->shader.handle, "tev_combiner_buffer_color"); - shader->uniform_tev_const_colors = glGetUniformLocation(shader->shader.handle, "const_color"); state.draw.shader_program = shader->shader.handle; state.Apply(); // Set the texture samplers to correspond to different texture units - if (shader->uniform_tex != -1) { - glUniform1i(shader->uniform_tex, 0); - glUniform1i(shader->uniform_tex + 1, 1); - glUniform1i(shader->uniform_tex + 2, 2); - } + glUniform1i(PicaShader::Uniform::Texture0, 0); + glUniform1i(PicaShader::Uniform::Texture1, 1); + glUniform1i(PicaShader::Uniform::Texture2, 2); current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); } @@ -622,8 +616,7 @@ void RasterizerOpenGL::SyncBlendColor() { void RasterizerOpenGL::SyncAlphaTest() { const auto& regs = Pica::g_state.regs; - if (current_shader->uniform_alphatest_ref != -1) - glUniform1i(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref); + glUniform1i(PicaShader::Uniform::AlphaTestRef, regs.output_merger.alpha_test.ref); } void RasterizerOpenGL::SyncLogicOp() { @@ -654,17 +647,13 @@ void RasterizerOpenGL::SyncDepthTest() { } void RasterizerOpenGL::SyncCombinerColor() { - if (current_shader->uniform_tev_combiner_buffer_color != -1) { - auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); - glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data()); - } + auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); + glUniform4fv(PicaShader::Uniform::TevCombinerBufferColor, 1, combiner_color.data()); } void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) { - if (current_shader->uniform_tev_const_colors != -1) { - auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color); - glUniform4fv(current_shader->uniform_tev_const_colors + stage_index, 1, const_color.data()); - } + auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color); + glUniform4fv(PicaShader::Uniform::TevConstColors + stage_index, 1, const_color.data()); } void RasterizerOpenGL::SyncDrawState() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 484579d82..79c34944a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -153,36 +153,24 @@ public: /// Notify rasterizer that a 3DS memory region has been changed void NotifyFlush(PAddr addr, u32 size) override; -private: - /// Structure used for managing texture environment states - struct TEVConfigUniforms { - GLuint enabled; - GLuint color_sources; - GLuint alpha_sources; - GLuint color_modifiers; - GLuint alpha_modifiers; - GLuint color_alpha_op; - GLuint color_alpha_multiplier; - GLuint const_color; - GLuint updates_combiner_buffer_color_alpha; - }; - - struct TEVShader { + /// OpenGL shader generated for a given Pica register state + struct PicaShader { + /// OpenGL shader resource OGLShader shader; - // Hardware fragment shader - GLuint uniform_alphatest_ref; - GLuint uniform_tex; - GLuint uniform_tev_combiner_buffer_color; - GLuint uniform_tev_const_colors; - - TEVShader() = default; - TEVShader(TEVShader&& o) : shader(std::move(o.shader)), - uniform_alphatest_ref(o.uniform_alphatest_ref), uniform_tex(o.uniform_tex), - uniform_tev_combiner_buffer_color(o.uniform_tev_combiner_buffer_color), - uniform_tev_const_colors(o.uniform_tev_const_colors) {} + /// Fragment shader uniforms + enum Uniform : GLuint { + AlphaTestRef = 0, + TevConstColors = 1, + Texture0 = 7, + Texture1 = 8, + Texture2 = 9, + TevCombinerBufferColor = 10, + }; }; +private: + /// Structure used for storing information about color textures struct TextureInfo { OGLTexture texture; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 79c690e76..50bb2e3cc 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -321,25 +321,26 @@ static void WriteTevStage(std::string& out, const ShaderCacheKey& config, unsign std::string GenerateFragmentShader(const ShaderCacheKey& config) { std::string out = R"( -#version 150 core +#version 330 +#extension GL_ARB_explicit_uniform_location : require #define NUM_VTX_ATTR 7 #define NUM_TEV_STAGES 6 in vec4 attr[NUM_VTX_ATTR]; out vec4 color; - -uniform int alphatest_ref; -uniform vec4 const_color[NUM_TEV_STAGES]; -uniform sampler2D tex[3]; - -uniform vec4 tev_combiner_buffer_color; - -void main(void) { -vec4 g_combiner_buffer = tev_combiner_buffer_color; -vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); )"; + using Uniform = RasterizerOpenGL::PicaShader::Uniform; + out += "layout(location = " + std::to_string(Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n"; + out += "layout(location = " + std::to_string(Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n"; + out += "layout(location = " + std::to_string(Uniform::Texture0) + ") uniform sampler2D tex[3];\n"; + out += "layout(location = " + std::to_string(Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n"; + + out += "void main() {\n"; + out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n"; + out += "vec4 last_tex_env_out = vec4(0.0);\n"; + // Do not do any sort of processing if it's obvious we're not going to pass the alpha test if (config.alpha_test_func == Regs::CompareFunc::Never) { out += "discard; }"; @@ -362,7 +363,7 @@ vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); std::string GenerateVertexShader() { static const std::string out = R"( -#version 150 core +#version 330 #define NUM_VTX_ATTR 7 From 240a3b80d970b56b4ed3671536489eb0e32532ae Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 Oct 2015 22:46:47 -0400 Subject: [PATCH 15/19] 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 From 0ebcff710e6b5d266158ec364cf08bd8f22ae74a Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 12 Oct 2015 22:23:26 -0400 Subject: [PATCH 16/19] gl_shader_gen: Various cleanups to shader generation. --- .../renderer_opengl/gl_rasterizer.h | 2 +- .../renderer_opengl/gl_shader_gen.cpp | 92 ++++++++++--------- .../renderer_opengl/gl_shader_util.cpp | 6 +- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index cf8df8d9c..872cae7da 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -95,7 +95,7 @@ namespace std { template <> struct hash { - std::size_t operator()(const PicaShaderConfig& k) const { + size_t operator()(const PicaShaderConfig& k) const { return Common::ComputeHash64(&k, sizeof(PicaShaderConfig)); } }; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 84883b483..7506cdc08 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -29,33 +29,33 @@ static void AppendSource(std::string& out, TevStageConfig::Source source, using Source = TevStageConfig::Source; switch (source) { case Source::PrimaryColor: - out += "attr[2]"; + out += "primary_color"; break; case Source::PrimaryFragmentColor: // HACK: Until we implement fragment lighting, use primary_color - out += "attr[2]"; + out += "primary_color"; break; case Source::SecondaryFragmentColor: // HACK: Until we implement fragment lighting, use zero - out += "vec4(0.0, 0.0, 0.0, 0.0)"; + out += "vec4(0.0)"; break; case Source::Texture0: - out += "texture(tex[0], attr[3].xy)"; + out += "texture(tex[0], texcoord[0])"; break; case Source::Texture1: - out += "texture(tex[1], attr[3].zw)"; + out += "texture(tex[1], texcoord[1])"; break; - case Source::Texture2: // TODO: Unverified - out += "texture(tex[2], attr[5].zw)"; + case Source::Texture2: + out += "texture(tex[2], texcoord[2])"; break; case Source::PreviousBuffer: - out += "g_combiner_buffer"; + out += "combiner_buffer"; break; case Source::Constant: - out += "const_color[" + index_name + "]"; + ((out += "const_color[") += index_name) += ']'; break; case Source::Previous: - out += "g_last_tex_env_out"; + out += "last_tex_env_out"; break; default: out += "vec4(0.0)"; @@ -172,8 +172,8 @@ static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier /// Writes the combiner function for the color components for the specified TEV stage operation static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation, const std::string& variable_name) { + out += "clamp("; using Operation = TevStageConfig::Operation; - switch (operation) { case Operation::Replace: out += variable_name + "[0]"; @@ -182,19 +182,20 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper out += variable_name + "[0] * " + variable_name + "[1]"; break; case Operation::Add: - out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; + out += variable_name + "[0] + " + variable_name + "[1]"; break; case Operation::AddSigned: - out += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; + out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)"; break; case Operation::Lerp: + // TODO(bunnei): Verify if HW actually does this per-component, otherwise we can just use builtin lerp out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; break; case Operation::Subtract: - out += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; + out += variable_name + "[0] - " + variable_name + "[1]"; break; case Operation::MultiplyThenAdd: - out += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; + out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; break; case Operation::AddThenMultiply: out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; @@ -204,11 +205,13 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); break; } + out += ", vec3(0.0), vec3(1.0))"; } /// Writes the combiner function for the alpha component for the specified TEV stage operation static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation, const std::string& variable_name) { + out += "clamp("; using Operation = TevStageConfig::Operation; switch (operation) { case Operation::Replace: @@ -218,19 +221,19 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper out += variable_name + "[0] * " + variable_name + "[1]"; break; case Operation::Add: - out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; + out += variable_name + "[0] + " + variable_name + "[1]"; break; case Operation::AddSigned: - out += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; + out += variable_name + "[0] + " + variable_name + "[1] - 0.5"; break; case Operation::Lerp: out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; break; case Operation::Subtract: - out += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; + out += variable_name + "[0] - " + variable_name + "[1]"; break; case Operation::MultiplyThenAdd: - out += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; + out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; break; case Operation::AddThenMultiply: out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; @@ -240,6 +243,7 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); break; } + out += ", 0.0, 1.0)"; } /// Writes the if-statement condition used to evaluate alpha testing @@ -253,22 +257,22 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { out += "false"; break; case CompareFunc::Equal: - out += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0f) != alphatest_ref"; break; case CompareFunc::NotEqual: - out += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0f) == alphatest_ref"; break; case CompareFunc::LessThan: - out += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0f) >= alphatest_ref"; break; case CompareFunc::LessThanOrEqual: - out += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0f) > alphatest_ref"; break; case CompareFunc::GreaterThan: - out += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0f) <= alphatest_ref"; break; case CompareFunc::GreaterThanOrEqual: - out += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0f) < alphatest_ref"; break; default: out += "false"; @@ -307,16 +311,16 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name); out += ";\n"; - out += "g_last_tex_env_out = vec4(" - "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, 0.0, 1.0)," + out += "last_tex_env_out = vec4(" + "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, vec3(0.0), vec3(1.0))," "clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n"; } if (config.TevStageUpdatesCombinerBufferColor(index)) - out += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; + out += "combiner_buffer.rgb = last_tex_env_out.rgb;\n"; if (config.TevStageUpdatesCombinerBufferAlpha(index)) - out += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; + out += "combiner_buffer.a = last_tex_env_out.a;\n"; } std::string GenerateFragmentShader(const PicaShaderConfig& config) { @@ -324,10 +328,11 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { #version 330 #extension GL_ARB_explicit_uniform_location : require -#define NUM_VTX_ATTR 7 #define NUM_TEV_STAGES 6 -in vec4 attr[NUM_VTX_ATTR]; +in vec4 primary_color; +in vec2 texcoord[3]; + out vec4 color; )"; @@ -347,16 +352,16 @@ out vec4 color; return out; } - for (std::size_t index = 0; index < config.tev_stages.size(); ++index) + for (size_t index = 0; index < config.tev_stages.size(); ++index) WriteTevStage(out, config, (unsigned)index); if (config.alpha_test_func != Regs::CompareFunc::Always) { out += "if ("; AppendAlphaTestCondition(out, config.alpha_test_func); - out += ") {\n discard;\n }\n"; + out += ") discard;\n"; } - out += "color = g_last_tex_env_out;\n}"; + out += "color = last_tex_env_out;\n}"; return out; } @@ -365,21 +370,20 @@ std::string GenerateVertexShader() { static const std::string out = R"( #version 330 -#define NUM_VTX_ATTR 7 - in vec4 vert_position; in vec4 vert_color; -in vec2 vert_texcoords0; -in vec2 vert_texcoords1; -in vec2 vert_texcoords2; +in vec2 vert_texcoord0; +in vec2 vert_texcoord1; +in vec2 vert_texcoord2; -out vec4 attr[NUM_VTX_ATTR]; +out vec4 primary_color; +out vec2 texcoord[3]; void main() { - attr[2] = vert_color; - attr[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); - attr[5] = vec4(0.0, 0.0, vert_texcoords2.xy); - + primary_color = vert_color; + texcoord[0] = vert_texcoord0; + texcoord[1] = vert_texcoord1; + texcoord[2] = vert_texcoord2; gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); } )"; diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index ce218b857..2fa0ceb3e 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -68,9 +68,9 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { glBindAttribLocation(program_id, Attributes::ATTRIBUTE_POSITION, "vert_position"); glBindAttribLocation(program_id, Attributes::ATTRIBUTE_COLOR, "vert_color"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 0, "vert_texcoords0"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 1, "vert_texcoords1"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 2, "vert_texcoords2"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 0, "vert_texcoord0"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 1, "vert_texcoord1"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 2, "vert_texcoord2"); glLinkProgram(program_id); From e7b1f2ae0af304abea3fb9a5b658abb92737caaa Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 Oct 2015 00:03:22 -0400 Subject: [PATCH 17/19] gl_rasterizer: Define enum types for each vertex texcoord attribute. --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 12 ++++++------ src/video_core/renderer_opengl/gl_shader_util.cpp | 6 +++--- src/video_core/renderer_opengl/gl_shader_util.h | 8 +++++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4ae42f226..d1def2f3b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -61,12 +61,12 @@ void RasterizerOpenGL::InitObjects() { glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR); - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 0); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 1); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 2); + glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); + glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); + glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2); SetShader(); diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 2fa0ceb3e..735c86d22 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -68,9 +68,9 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { glBindAttribLocation(program_id, Attributes::ATTRIBUTE_POSITION, "vert_position"); glBindAttribLocation(program_id, Attributes::ATTRIBUTE_COLOR, "vert_color"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 0, "vert_texcoord0"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 1, "vert_texcoord1"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORDS + 2, "vert_texcoord2"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORD0, "vert_texcoord0"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORD1, "vert_texcoord1"); + glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORD2, "vert_texcoord2"); glLinkProgram(program_id); diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 4d3791d50..046aae14f 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -9,9 +9,11 @@ namespace GLShader { enum Attributes { - ATTRIBUTE_POSITION = 0, - ATTRIBUTE_COLOR = 1, - ATTRIBUTE_TEXCOORDS = 2, + ATTRIBUTE_POSITION, + ATTRIBUTE_COLOR, + ATTRIBUTE_TEXCOORD0, + ATTRIBUTE_TEXCOORD1, + ATTRIBUTE_TEXCOORD2, }; /** From e663f5c91450ac5b36358195718edddbde25cd75 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 Oct 2015 00:04:02 -0400 Subject: [PATCH 18/19] gl_shader_gen: Optimize code for AppendAlphaTestCondition. - Also add a comment to AppendColorCombiner. --- .../renderer_opengl/gl_shader_gen.cpp | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 7506cdc08..e456f5847 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -205,7 +205,7 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); break; } - out += ", vec3(0.0), vec3(1.0))"; + out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0 } /// Writes the combiner function for the alpha component for the specified TEV stage operation @@ -257,23 +257,18 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { out += "false"; break; case CompareFunc::Equal: - out += "int(last_tex_env_out.a * 255.0f) != alphatest_ref"; - break; case CompareFunc::NotEqual: - out += "int(last_tex_env_out.a * 255.0f) == alphatest_ref"; - break; case CompareFunc::LessThan: - out += "int(last_tex_env_out.a * 255.0f) >= alphatest_ref"; - break; case CompareFunc::LessThanOrEqual: - out += "int(last_tex_env_out.a * 255.0f) > alphatest_ref"; - break; case CompareFunc::GreaterThan: - out += "int(last_tex_env_out.a * 255.0f) <= alphatest_ref"; - break; case CompareFunc::GreaterThanOrEqual: - out += "int(last_tex_env_out.a * 255.0f) < alphatest_ref"; + { + static const char* op[] = { "!=", "==", ">=", ">", "<=", "<", }; + unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal; + out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref"; break; + } + default: out += "false"; LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); @@ -337,10 +332,10 @@ out vec4 color; )"; using Uniform = RasterizerOpenGL::PicaShader::Uniform; - out += "layout(location = " + std::to_string(Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n"; - out += "layout(location = " + std::to_string(Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n"; - out += "layout(location = " + std::to_string(Uniform::Texture0) + ") uniform sampler2D tex[3];\n"; - out += "layout(location = " + std::to_string(Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n"; + out += "layout(location = " + std::to_string((int)Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n"; + out += "layout(location = " + std::to_string((int)Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n"; + out += "layout(location = " + std::to_string((int)Uniform::Texture0) + ") uniform sampler2D tex[3];\n"; + out += "layout(location = " + std::to_string((int)Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n"; out += "void main() {\n"; out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n"; From 74186a5f016a72f28f2d227bbe524787cd5b685f Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 Oct 2015 21:50:55 -0400 Subject: [PATCH 19/19] gl_shader_gen: Use explicit locations for vertex shader attributes. --- src/video_core/renderer_opengl/gl_shader_gen.cpp | 16 ++++++++-------- .../renderer_opengl/gl_shader_util.cpp | 6 ------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index e456f5847..d19d15e75 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -362,15 +362,14 @@ out vec4 color; } std::string GenerateVertexShader() { - static const std::string out = R"( -#version 330 - -in vec4 vert_position; -in vec4 vert_color; -in vec2 vert_texcoord0; -in vec2 vert_texcoord1; -in vec2 vert_texcoord2; + std::string out = "#version 330\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n"; + out += R"( out vec4 primary_color; out vec2 texcoord[3]; @@ -382,6 +381,7 @@ void main() { gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); } )"; + return out; } diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 735c86d22..e3f7a5868 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -66,12 +66,6 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { glAttachShader(program_id, vertex_shader_id); glAttachShader(program_id, fragment_shader_id); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_POSITION, "vert_position"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_COLOR, "vert_color"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORD0, "vert_texcoord0"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORD1, "vert_texcoord1"); - glBindAttribLocation(program_id, Attributes::ATTRIBUTE_TEXCOORD2, "vert_texcoord2"); - glLinkProgram(program_id); // Check the program