mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	renderer_opengl: Refactor shader generation/caching to be more organized + various cleanups.
This commit is contained in:
		
							parent
							
								
									3c057bd3d8
								
							
						
					
					
						commit
						c86b9d4242
					
				
					 11 changed files with 527 additions and 788 deletions
				
			
		|  | @ -4,6 +4,9 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <functional> | ||||
| 
 | ||||
| #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 <typename T> | ||||
| inline std::size_t hash(const T& o) { | ||||
|     return std::hash<T>()(o); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| inline std::size_t combine_hash(const T& o) { | ||||
|     return hash(o); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename... Args> | ||||
| inline std::size_t combine_hash(const T& o, const Args&... args) { | ||||
|     return hash(o) * 3 + combine_hash(args...); | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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<TEVShader> shader = Common::make_unique<TEVShader>(); | ||||
| 
 | ||||
|         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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
|  | @ -15,21 +17,6 @@ | |||
| #include "video_core/renderer_opengl/gl_state.h" | ||||
| #include "video_core/shader/shader_interpreter.h" | ||||
| 
 | ||||
| template <typename T> | ||||
| inline size_t hash(const T& o) { | ||||
|     return std::hash<T>()(o); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| inline size_t combine_hash(const T& o) { | ||||
|     return hash(o); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename... Args> | ||||
| 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<ShaderCacheKey, TEVShader> shader_cache; | ||||
|     TEVShader* current_shader = nullptr; | ||||
|     std::unordered_map<ShaderCacheKey, std::unique_ptr<TEVShader>> shader_cache; | ||||
|     const TEVShader* current_shader = nullptr; | ||||
| 
 | ||||
|     OGLVertexArray vertex_array; | ||||
|     OGLBuffer vertex_buffer; | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
							
								
								
									
										371
									
								
								src/video_core/renderer_opengl/gl_shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								src/video_core/renderer_opengl/gl_shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||
							
								
								
									
										17
									
								
								src/video_core/renderer_opengl/gl_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/video_core/renderer_opengl/gl_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 <string> | ||||
| 
 | ||||
| #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||
| 
 | ||||
| namespace GLShader { | ||||
| 
 | ||||
| std::string GenerateVertexShader(); | ||||
| 
 | ||||
| std::string GenerateFragmentShader(const ShaderCacheKey& config); | ||||
| 
 | ||||
| } // namespace GLShader
 | ||||
|  | @ -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 <algorithm> | ||||
| #include <vector> | ||||
| 
 | ||||
| #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
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 
 | ||||
| #include <glad/glad.h> | ||||
| 
 | ||||
| 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
 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
| } | ||||
|  | @ -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"); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue