From c86b9d42423b5a83ccba40f828b7ad9dafe3e317 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Oct 2015 22:33:47 -0400 Subject: [PATCH] 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");