diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 8c9d76ab42..2a924f4ad0 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/pica.h b/src/video_core/pica.h index ff81b409db..2f1b2dec4f 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -317,6 +317,7 @@ struct Regs { }; union { + u32 sources_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 modifiers_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 ops_raw; BitField< 0, 4, Operation> color_op; BitField<16, 4, Operation> alpha_op; }; @@ -348,6 +351,7 @@ struct Regs { }; union { + 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 a613fe1366..d1def2f3b3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -8,6 +8,8 @@ #include #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" @@ -19,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" @@ -38,36 +40,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 +50,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(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); + + glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR); + + 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(); // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation fb_color_texture.texture.Create(); @@ -150,61 +118,15 @@ void RasterizerOpenGL::InitObjects() { } void RasterizerOpenGL::Reset() { - const auto& regs = Pica::g_state.regs; - SyncCullMode(); 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(); + SetShader(); res_cache.FullFlush(); } @@ -221,6 +143,11 @@ void RasterizerOpenGL::DrawTriangles() { SyncFramebuffer(); SyncDrawState(); + if (state.draw.shader_dirty) { + SetShader(); + 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 +199,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 +218,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_stage1); 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_stage2); 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_stage3); 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_stage4); 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_stage5); 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; } } @@ -592,6 +460,41 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: state.Apply(); } +void RasterizerOpenGL::SetShader() { + 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); + 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"); + + shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); + + state.draw.shader_program = shader->shader.handle; + state.Apply(); + + // Set the texture samplers to correspond to different texture units + 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(); + } + + // 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; @@ -712,9 +615,7 @@ 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); + glUniform1i(PicaShader::Uniform::AlphaTestRef, regs.output_merger.alpha_test.ref); } void RasterizerOpenGL::SyncLogicOp() { @@ -744,56 +645,14 @@ 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()); + glUniform4fv(PicaShader::Uniform::TevCombinerBufferColor, 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) { + auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color); + glUniform4fv(PicaShader::Uniform::TevConstColors + stage_index, 1, const_color.data()); } void RasterizerOpenGL::SyncDrawState() { @@ -824,12 +683,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 1fe3078468..872cae7dae 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -4,15 +4,104 @@ #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" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #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 PicaShaderConfig { + /// Construct a PicaShaderConfig with the current Pica register configuration. + static PicaShaderConfig CurrentConfig() { + PicaShaderConfig res; + const auto& regs = Pica::g_state.regs; + + res.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. + + 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)); + } + + bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { + return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); + } + + bool operator ==(const PicaShaderConfig& o) const { + return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; + }; + + Pica::Regs::CompareFunc alpha_test_func; + std::array tev_stages = {}; + u8 combiner_buffer_input; +}; + +namespace std { + +template <> +struct hash { + size_t operator()(const PicaShaderConfig& k) const { + return Common::ComputeHash64(&k, sizeof(PicaShaderConfig)); + } +}; + +} // namespace std + class RasterizerOpenGL : public HWRasterizer { public: @@ -45,20 +134,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; + /// OpenGL shader generated for a given Pica register state + struct PicaShader { + /// OpenGL shader resource + OGLShader shader; + + /// 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; @@ -129,6 +222,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(); @@ -156,27 +252,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 +294,11 @@ private: std::array texture_samplers; TextureInfo fb_color_texture; DepthTextureInfo fb_depth_texture; - OGLShader shader; + + std::unordered_map> shader_cache; + const PicaShader* 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_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 65034d40de..eb128966c8 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 0000000000..d19d15e759 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -0,0 +1,388 @@ +// 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" + +using Pica::Regs; +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 && + 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); +} + +/// 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; + switch (source) { + case Source::PrimaryColor: + out += "primary_color"; + break; + case Source::PrimaryFragmentColor: + // HACK: Until we implement fragment lighting, use primary_color + out += "primary_color"; + break; + case Source::SecondaryFragmentColor: + // HACK: Until we implement fragment lighting, use zero + out += "vec4(0.0)"; + break; + case Source::Texture0: + out += "texture(tex[0], texcoord[0])"; + break; + case Source::Texture1: + out += "texture(tex[1], texcoord[1])"; + break; + case Source::Texture2: + out += "texture(tex[2], texcoord[2])"; + break; + case Source::PreviousBuffer: + out += "combiner_buffer"; + break; + case Source::Constant: + ((out += "const_color[") += index_name) += ']'; + break; + case Source::Previous: + out += "last_tex_env_out"; + break; + default: + out += "vec4(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); + break; + } +} + +/// 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; + switch (modifier) { + case ColorModifier::SourceColor: + AppendSource(out, source, index_name); + out += ".rgb"; + break; + case ColorModifier::OneMinusSourceColor: + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".rgb"; + break; + case ColorModifier::SourceAlpha: + AppendSource(out, source, index_name); + out += ".aaa"; + break; + case ColorModifier::OneMinusSourceAlpha: + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".aaa"; + break; + case ColorModifier::SourceRed: + AppendSource(out, source, index_name); + out += ".rrr"; + break; + case ColorModifier::OneMinusSourceRed: + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".rrr"; + break; + case ColorModifier::SourceGreen: + AppendSource(out, source, index_name); + out += ".ggg"; + break; + case ColorModifier::OneMinusSourceGreen: + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".ggg"; + break; + case ColorModifier::SourceBlue: + AppendSource(out, source, index_name); + out += ".bbb"; + break; + case ColorModifier::OneMinusSourceBlue: + out += "vec3(1.0) - "; + AppendSource(out, source, index_name); + out += ".bbb"; + break; + default: + out += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); + break; + } +} + +/// 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; + switch (modifier) { + case AlphaModifier::SourceAlpha: + AppendSource(out, source, index_name); + out += ".a"; + break; + case AlphaModifier::OneMinusSourceAlpha: + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".a"; + break; + case AlphaModifier::SourceRed: + AppendSource(out, source, index_name); + out += ".r"; + break; + case AlphaModifier::OneMinusSourceRed: + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".r"; + break; + case AlphaModifier::SourceGreen: + AppendSource(out, source, index_name); + out += ".g"; + break; + case AlphaModifier::OneMinusSourceGreen: + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".g"; + break; + case AlphaModifier::SourceBlue: + AppendSource(out, source, index_name); + out += ".b"; + break; + case AlphaModifier::OneMinusSourceBlue: + out += "1.0 - "; + AppendSource(out, source, index_name); + out += ".b"; + break; + default: + out += "0.0"; + LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); + break; + } +} + +/// 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]"; + break; + case Operation::Modulate: + out += variable_name + "[0] * " + variable_name + "[1]"; + break; + case Operation::Add: + out += variable_name + "[0] + " + variable_name + "[1]"; + break; + case Operation::AddSigned: + 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 += variable_name + "[0] - " + variable_name + "[1]"; + break; + case Operation::MultiplyThenAdd: + 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]"; + break; + default: + out += "vec3(0.0)"; + LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); + break; + } + 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 +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: + out += variable_name + "[0]"; + break; + case Operation::Modulate: + out += variable_name + "[0] * " + variable_name + "[1]"; + break; + case Operation::Add: + out += variable_name + "[0] + " + variable_name + "[1]"; + break; + case Operation::AddSigned: + 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 += variable_name + "[0] - " + variable_name + "[1]"; + break; + case Operation::MultiplyThenAdd: + 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]"; + break; + default: + out += "0.0"; + 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 +static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { + using CompareFunc = Regs::CompareFunc; + switch (func) { + case CompareFunc::Never: + out += "true"; + break; + case CompareFunc::Always: + out += "false"; + break; + case CompareFunc::Equal: + case CompareFunc::NotEqual: + case CompareFunc::LessThan: + case CompareFunc::LessThanOrEqual: + case CompareFunc::GreaterThan: + case CompareFunc::GreaterThanOrEqual: + { + 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); + break; + } +} + +/// Writes the code to emulate the specified TEV stage +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); + + 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 += "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 += "combiner_buffer.rgb = last_tex_env_out.rgb;\n"; + + if (config.TevStageUpdatesCombinerBufferAlpha(index)) + out += "combiner_buffer.a = last_tex_env_out.a;\n"; +} + +std::string GenerateFragmentShader(const PicaShaderConfig& config) { + std::string out = R"( +#version 330 +#extension GL_ARB_explicit_uniform_location : require + +#define NUM_TEV_STAGES 6 + +in vec4 primary_color; +in vec2 texcoord[3]; + +out vec4 color; +)"; + + using Uniform = RasterizerOpenGL::PicaShader::Uniform; + 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"; + 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; }"; + return out; + } + + 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 += ") discard;\n"; + } + + out += "color = last_tex_env_out;\n}"; + + return out; +} + +std::string GenerateVertexShader() { + 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]; + +void main() { + 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); +} +)"; + + return out; +} + +} // namespace GLShader 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 0000000000..0ca9d2879d --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -0,0 +1,27 @@ +// 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 { + +/** + * 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 PicaShaderConfig& 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 4cf246c061..e3f7a5868c 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -8,9 +8,9 @@ #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); @@ -65,6 +65,7 @@ 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); + glLinkProgram(program_id); // Check the program @@ -87,4 +88,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { return program_id; } -} +} // 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 c9d7cc380c..046aae14f6 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -6,8 +6,22 @@ #include -namespace ShaderUtil { +namespace GLShader { -GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); +enum Attributes { + ATTRIBUTE_POSITION, + ATTRIBUTE_COLOR, + ATTRIBUTE_TEXCOORD0, + ATTRIBUTE_TEXCOORD1, + ATTRIBUTE_TEXCOORD2, +}; -} +/** + * 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 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 a8cb2f5950..0000000000 --- a/src/video_core/renderer_opengl/gl_shaders.h +++ /dev/null @@ -1,337 +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_texcoords[3]; - -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); - - 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/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 6ecbedbb45..668b042593 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(); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f1313b54f5..ac0a058db9 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");