mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #1187 from bunnei/shader-gen
GLSL Fragment Shader Generation
This commit is contained in:
		
						commit
						4195d9b3f8
					
				
					 12 changed files with 689 additions and 631 deletions
				
			
		|  | @ -1,6 +1,7 @@ | ||||||
| set(SRCS | set(SRCS | ||||||
|             renderer_opengl/gl_rasterizer.cpp |             renderer_opengl/gl_rasterizer.cpp | ||||||
|             renderer_opengl/gl_rasterizer_cache.cpp |             renderer_opengl/gl_rasterizer_cache.cpp | ||||||
|  |             renderer_opengl/gl_shader_gen.cpp | ||||||
|             renderer_opengl/gl_shader_util.cpp |             renderer_opengl/gl_shader_util.cpp | ||||||
|             renderer_opengl/gl_state.cpp |             renderer_opengl/gl_state.cpp | ||||||
|             renderer_opengl/renderer_opengl.cpp |             renderer_opengl/renderer_opengl.cpp | ||||||
|  | @ -21,8 +22,8 @@ set(HEADERS | ||||||
|             renderer_opengl/gl_rasterizer.h |             renderer_opengl/gl_rasterizer.h | ||||||
|             renderer_opengl/gl_rasterizer_cache.h |             renderer_opengl/gl_rasterizer_cache.h | ||||||
|             renderer_opengl/gl_resource_manager.h |             renderer_opengl/gl_resource_manager.h | ||||||
|  |             renderer_opengl/gl_shader_gen.h | ||||||
|             renderer_opengl/gl_shader_util.h |             renderer_opengl/gl_shader_util.h | ||||||
|             renderer_opengl/gl_shaders.h |  | ||||||
|             renderer_opengl/gl_state.h |             renderer_opengl/gl_state.h | ||||||
|             renderer_opengl/pica_to_gl.h |             renderer_opengl/pica_to_gl.h | ||||||
|             renderer_opengl/renderer_opengl.h |             renderer_opengl/renderer_opengl.h | ||||||
|  |  | ||||||
|  | @ -317,6 +317,7 @@ struct Regs { | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|  |             u32 sources_raw; | ||||||
|             BitField< 0, 4, Source> color_source1; |             BitField< 0, 4, Source> color_source1; | ||||||
|             BitField< 4, 4, Source> color_source2; |             BitField< 4, 4, Source> color_source2; | ||||||
|             BitField< 8, 4, Source> color_source3; |             BitField< 8, 4, Source> color_source3; | ||||||
|  | @ -326,6 +327,7 @@ struct Regs { | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|  |             u32 modifiers_raw; | ||||||
|             BitField< 0, 4, ColorModifier> color_modifier1; |             BitField< 0, 4, ColorModifier> color_modifier1; | ||||||
|             BitField< 4, 4, ColorModifier> color_modifier2; |             BitField< 4, 4, ColorModifier> color_modifier2; | ||||||
|             BitField< 8, 4, ColorModifier> color_modifier3; |             BitField< 8, 4, ColorModifier> color_modifier3; | ||||||
|  | @ -335,6 +337,7 @@ struct Regs { | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|  |             u32 ops_raw; | ||||||
|             BitField< 0, 4, Operation> color_op; |             BitField< 0, 4, Operation> color_op; | ||||||
|             BitField<16, 4, Operation> alpha_op; |             BitField<16, 4, Operation> alpha_op; | ||||||
|         }; |         }; | ||||||
|  | @ -348,6 +351,7 @@ struct Regs { | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|  |             u32 scales_raw; | ||||||
|             BitField< 0, 2, u32> color_scale; |             BitField< 0, 2, u32> color_scale; | ||||||
|             BitField<16, 2, u32> alpha_scale; |             BitField<16, 2, u32> alpha_scale; | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,8 @@ | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| 
 | 
 | ||||||
| #include "common/color.h" | #include "common/color.h" | ||||||
|  | #include "common/file_util.h" | ||||||
|  | #include "common/make_unique.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "common/profiler.h" | #include "common/profiler.h" | ||||||
|  | @ -19,7 +21,7 @@ | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer.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/gl_shader_util.h" | ||||||
| #include "video_core/renderer_opengl/pica_to_gl.h" | #include "video_core/renderer_opengl/pica_to_gl.h" | ||||||
| 
 | 
 | ||||||
|  | @ -38,36 +40,6 @@ RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr | ||||||
| RasterizerOpenGL::~RasterizerOpenGL() { } | RasterizerOpenGL::~RasterizerOpenGL() { } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::InitObjects() { | void RasterizerOpenGL::InitObjects() { | ||||||
|     // Create the hardware shader program and get attrib/uniform locations
 |  | ||||||
|     shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw); |  | ||||||
|     attrib_position = glGetAttribLocation(shader.handle, "vert_position"); |  | ||||||
|     attrib_color = glGetAttribLocation(shader.handle, "vert_color"); |  | ||||||
|     attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords"); |  | ||||||
| 
 |  | ||||||
|     uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled"); |  | ||||||
|     uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func"); |  | ||||||
|     uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref"); |  | ||||||
| 
 |  | ||||||
|     uniform_tex = glGetUniformLocation(shader.handle, "tex"); |  | ||||||
| 
 |  | ||||||
|     uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color"); |  | ||||||
| 
 |  | ||||||
|     const auto tev_stages = Pica::g_state.regs.GetTevStages(); |  | ||||||
|     for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { |  | ||||||
|         auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index]; |  | ||||||
| 
 |  | ||||||
|         std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]"; |  | ||||||
|         uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str()); |  | ||||||
|         uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str()); |  | ||||||
|         uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str()); |  | ||||||
|         uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str()); |  | ||||||
|         uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str()); |  | ||||||
|         uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str()); |  | ||||||
|         uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str()); |  | ||||||
|         uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str()); |  | ||||||
|         uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Create sampler objects
 |     // Create sampler objects
 | ||||||
|     for (size_t i = 0; i < texture_samplers.size(); ++i) { |     for (size_t i = 0; i < texture_samplers.size(); ++i) { | ||||||
|         texture_samplers[i].Create(); |         texture_samplers[i].Create(); | ||||||
|  | @ -78,29 +50,25 @@ void RasterizerOpenGL::InitObjects() { | ||||||
|     vertex_buffer.Create(); |     vertex_buffer.Create(); | ||||||
|     vertex_array.Create(); |     vertex_array.Create(); | ||||||
| 
 | 
 | ||||||
|     // Update OpenGL state
 |  | ||||||
|     state.draw.vertex_array = vertex_array.handle; |     state.draw.vertex_array = vertex_array.handle; | ||||||
|     state.draw.vertex_buffer = vertex_buffer.handle; |     state.draw.vertex_buffer = vertex_buffer.handle; | ||||||
|     state.draw.shader_program = shader.handle; |  | ||||||
| 
 |  | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     // Set the texture samplers to correspond to different texture units
 |  | ||||||
|     glUniform1i(uniform_tex, 0); |  | ||||||
|     glUniform1i(uniform_tex + 1, 1); |  | ||||||
|     glUniform1i(uniform_tex + 2, 2); |  | ||||||
| 
 |  | ||||||
|     // Set vertex attributes
 |     // Set vertex attributes
 | ||||||
|     glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); |     glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); | ||||||
|     glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); |     glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); | ||||||
|     glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); | 
 | ||||||
|     glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); |     glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); | ||||||
|     glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); |     glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR); | ||||||
|     glEnableVertexAttribArray(attrib_position); | 
 | ||||||
|     glEnableVertexAttribArray(attrib_color); |     glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); | ||||||
|     glEnableVertexAttribArray(attrib_texcoords); |     glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); | ||||||
|     glEnableVertexAttribArray(attrib_texcoords + 1); |     glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); | ||||||
|     glEnableVertexAttribArray(attrib_texcoords + 2); |     glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0); | ||||||
|  |     glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1); | ||||||
|  |     glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2); | ||||||
|  | 
 | ||||||
|  |     SetShader(); | ||||||
| 
 | 
 | ||||||
|     // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
 |     // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
 | ||||||
|     fb_color_texture.texture.Create(); |     fb_color_texture.texture.Create(); | ||||||
|  | @ -150,61 +118,15 @@ void RasterizerOpenGL::InitObjects() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::Reset() { | void RasterizerOpenGL::Reset() { | ||||||
|     const auto& regs = Pica::g_state.regs; |  | ||||||
| 
 |  | ||||||
|     SyncCullMode(); |     SyncCullMode(); | ||||||
|     SyncBlendEnabled(); |     SyncBlendEnabled(); | ||||||
|     SyncBlendFuncs(); |     SyncBlendFuncs(); | ||||||
|     SyncBlendColor(); |     SyncBlendColor(); | ||||||
|     SyncAlphaTest(); |  | ||||||
|     SyncLogicOp(); |     SyncLogicOp(); | ||||||
|     SyncStencilTest(); |     SyncStencilTest(); | ||||||
|     SyncDepthTest(); |     SyncDepthTest(); | ||||||
| 
 | 
 | ||||||
|     // TEV stage 0
 |     SetShader(); | ||||||
|     SyncTevSources(0, regs.tev_stage0); |  | ||||||
|     SyncTevModifiers(0, regs.tev_stage0); |  | ||||||
|     SyncTevOps(0, regs.tev_stage0); |  | ||||||
|     SyncTevColor(0, regs.tev_stage0); |  | ||||||
|     SyncTevMultipliers(0, regs.tev_stage0); |  | ||||||
| 
 |  | ||||||
|     // TEV stage 1
 |  | ||||||
|     SyncTevSources(1, regs.tev_stage1); |  | ||||||
|     SyncTevModifiers(1, regs.tev_stage1); |  | ||||||
|     SyncTevOps(1, regs.tev_stage1); |  | ||||||
|     SyncTevColor(1, regs.tev_stage1); |  | ||||||
|     SyncTevMultipliers(1, regs.tev_stage1); |  | ||||||
| 
 |  | ||||||
|     // TEV stage 2
 |  | ||||||
|     SyncTevSources(2, regs.tev_stage2); |  | ||||||
|     SyncTevModifiers(2, regs.tev_stage2); |  | ||||||
|     SyncTevOps(2, regs.tev_stage2); |  | ||||||
|     SyncTevColor(2, regs.tev_stage2); |  | ||||||
|     SyncTevMultipliers(2, regs.tev_stage2); |  | ||||||
| 
 |  | ||||||
|     // TEV stage 3
 |  | ||||||
|     SyncTevSources(3, regs.tev_stage3); |  | ||||||
|     SyncTevModifiers(3, regs.tev_stage3); |  | ||||||
|     SyncTevOps(3, regs.tev_stage3); |  | ||||||
|     SyncTevColor(3, regs.tev_stage3); |  | ||||||
|     SyncTevMultipliers(3, regs.tev_stage3); |  | ||||||
| 
 |  | ||||||
|     // TEV stage 4
 |  | ||||||
|     SyncTevSources(4, regs.tev_stage4); |  | ||||||
|     SyncTevModifiers(4, regs.tev_stage4); |  | ||||||
|     SyncTevOps(4, regs.tev_stage4); |  | ||||||
|     SyncTevColor(4, regs.tev_stage4); |  | ||||||
|     SyncTevMultipliers(4, regs.tev_stage4); |  | ||||||
| 
 |  | ||||||
|     // TEV stage 5
 |  | ||||||
|     SyncTevSources(5, regs.tev_stage5); |  | ||||||
|     SyncTevModifiers(5, regs.tev_stage5); |  | ||||||
|     SyncTevOps(5, regs.tev_stage5); |  | ||||||
|     SyncTevColor(5, regs.tev_stage5); |  | ||||||
|     SyncTevMultipliers(5, regs.tev_stage5); |  | ||||||
| 
 |  | ||||||
|     SyncCombinerColor(); |  | ||||||
|     SyncCombinerWriteFlags(); |  | ||||||
| 
 | 
 | ||||||
|     res_cache.FullFlush(); |     res_cache.FullFlush(); | ||||||
| } | } | ||||||
|  | @ -221,6 +143,11 @@ void RasterizerOpenGL::DrawTriangles() { | ||||||
|     SyncFramebuffer(); |     SyncFramebuffer(); | ||||||
|     SyncDrawState(); |     SyncDrawState(); | ||||||
| 
 | 
 | ||||||
|  |     if (state.draw.shader_dirty) { | ||||||
|  |         SetShader(); | ||||||
|  |         state.draw.shader_dirty = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); |     glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); | ||||||
|     glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); |     glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -272,6 +199,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | ||||||
|     // Alpha test
 |     // Alpha test
 | ||||||
|     case PICA_REG_INDEX(output_merger.alpha_test): |     case PICA_REG_INDEX(output_merger.alpha_test): | ||||||
|         SyncAlphaTest(); |         SyncAlphaTest(); | ||||||
|  |         state.draw.shader_dirty = true; | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // Stencil test
 |     // Stencil test
 | ||||||
|  | @ -290,117 +218,57 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | ||||||
|         SyncLogicOp(); |         SyncLogicOp(); | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // TEV stage 0
 |     // TEV stages
 | ||||||
|     case PICA_REG_INDEX(tev_stage0.color_source1): |     case PICA_REG_INDEX(tev_stage0.color_source1): | ||||||
|         SyncTevSources(0, regs.tev_stage0); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage0.color_modifier1): |     case PICA_REG_INDEX(tev_stage0.color_modifier1): | ||||||
|         SyncTevModifiers(0, regs.tev_stage0); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage0.color_op): |     case PICA_REG_INDEX(tev_stage0.color_op): | ||||||
|         SyncTevOps(0, regs.tev_stage0); |     case PICA_REG_INDEX(tev_stage0.color_scale): | ||||||
|  |     case PICA_REG_INDEX(tev_stage1.color_source1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage1.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage1.color_op): | ||||||
|  |     case PICA_REG_INDEX(tev_stage1.color_scale): | ||||||
|  |     case PICA_REG_INDEX(tev_stage2.color_source1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage2.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage2.color_op): | ||||||
|  |     case PICA_REG_INDEX(tev_stage2.color_scale): | ||||||
|  |     case PICA_REG_INDEX(tev_stage3.color_source1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage3.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage3.color_op): | ||||||
|  |     case PICA_REG_INDEX(tev_stage3.color_scale): | ||||||
|  |     case PICA_REG_INDEX(tev_stage4.color_source1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage4.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage4.color_op): | ||||||
|  |     case PICA_REG_INDEX(tev_stage4.color_scale): | ||||||
|  |     case PICA_REG_INDEX(tev_stage5.color_source1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage5.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(tev_stage5.color_op): | ||||||
|  |     case PICA_REG_INDEX(tev_stage5.color_scale): | ||||||
|  |     case PICA_REG_INDEX(tev_combiner_buffer_input): | ||||||
|  |         state.draw.shader_dirty = true; | ||||||
|         break; |         break; | ||||||
|     case PICA_REG_INDEX(tev_stage0.const_r): |     case PICA_REG_INDEX(tev_stage0.const_r): | ||||||
|         SyncTevColor(0, regs.tev_stage0); |         SyncTevConstColor(0, regs.tev_stage0); | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage0.color_scale): |  | ||||||
|         SyncTevMultipliers(0, regs.tev_stage0); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     // TEV stage 1
 |  | ||||||
|     case PICA_REG_INDEX(tev_stage1.color_source1): |  | ||||||
|         SyncTevSources(1, regs.tev_stage1); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage1.color_modifier1): |  | ||||||
|         SyncTevModifiers(1, regs.tev_stage1); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage1.color_op): |  | ||||||
|         SyncTevOps(1, regs.tev_stage1); |  | ||||||
|         break; |         break; | ||||||
|     case PICA_REG_INDEX(tev_stage1.const_r): |     case PICA_REG_INDEX(tev_stage1.const_r): | ||||||
|         SyncTevColor(1, regs.tev_stage1); |         SyncTevConstColor(1, regs.tev_stage1); | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage1.color_scale): |  | ||||||
|         SyncTevMultipliers(1, regs.tev_stage1); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     // TEV stage 2
 |  | ||||||
|     case PICA_REG_INDEX(tev_stage2.color_source1): |  | ||||||
|         SyncTevSources(2, regs.tev_stage2); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage2.color_modifier1): |  | ||||||
|         SyncTevModifiers(2, regs.tev_stage2); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage2.color_op): |  | ||||||
|         SyncTevOps(2, regs.tev_stage2); |  | ||||||
|         break; |         break; | ||||||
|     case PICA_REG_INDEX(tev_stage2.const_r): |     case PICA_REG_INDEX(tev_stage2.const_r): | ||||||
|         SyncTevColor(2, regs.tev_stage2); |         SyncTevConstColor(2, regs.tev_stage2); | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage2.color_scale): |  | ||||||
|         SyncTevMultipliers(2, regs.tev_stage2); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     // TEV stage 3
 |  | ||||||
|     case PICA_REG_INDEX(tev_stage3.color_source1): |  | ||||||
|         SyncTevSources(3, regs.tev_stage3); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage3.color_modifier1): |  | ||||||
|         SyncTevModifiers(3, regs.tev_stage3); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage3.color_op): |  | ||||||
|         SyncTevOps(3, regs.tev_stage3); |  | ||||||
|         break; |         break; | ||||||
|     case PICA_REG_INDEX(tev_stage3.const_r): |     case PICA_REG_INDEX(tev_stage3.const_r): | ||||||
|         SyncTevColor(3, regs.tev_stage3); |         SyncTevConstColor(3, regs.tev_stage3); | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage3.color_scale): |  | ||||||
|         SyncTevMultipliers(3, regs.tev_stage3); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     // TEV stage 4
 |  | ||||||
|     case PICA_REG_INDEX(tev_stage4.color_source1): |  | ||||||
|         SyncTevSources(4, regs.tev_stage4); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage4.color_modifier1): |  | ||||||
|         SyncTevModifiers(4, regs.tev_stage4); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage4.color_op): |  | ||||||
|         SyncTevOps(4, regs.tev_stage4); |  | ||||||
|         break; |         break; | ||||||
|     case PICA_REG_INDEX(tev_stage4.const_r): |     case PICA_REG_INDEX(tev_stage4.const_r): | ||||||
|         SyncTevColor(4, regs.tev_stage4); |         SyncTevConstColor(4, regs.tev_stage4); | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage4.color_scale): |  | ||||||
|         SyncTevMultipliers(4, regs.tev_stage4); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     // TEV stage 5
 |  | ||||||
|     case PICA_REG_INDEX(tev_stage5.color_source1): |  | ||||||
|         SyncTevSources(5, regs.tev_stage5); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage5.color_modifier1): |  | ||||||
|         SyncTevModifiers(5, regs.tev_stage5); |  | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage5.color_op): |  | ||||||
|         SyncTevOps(5, regs.tev_stage5); |  | ||||||
|         break; |         break; | ||||||
|     case PICA_REG_INDEX(tev_stage5.const_r): |     case PICA_REG_INDEX(tev_stage5.const_r): | ||||||
|         SyncTevColor(5, regs.tev_stage5); |         SyncTevConstColor(5, regs.tev_stage5); | ||||||
|         break; |  | ||||||
|     case PICA_REG_INDEX(tev_stage5.color_scale): |  | ||||||
|         SyncTevMultipliers(5, regs.tev_stage5); |  | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // TEV combiner buffer color
 |     // TEV combiner buffer color
 | ||||||
|     case PICA_REG_INDEX(tev_combiner_buffer_color): |     case PICA_REG_INDEX(tev_combiner_buffer_color): | ||||||
|         SyncCombinerColor(); |         SyncCombinerColor(); | ||||||
|         break; |         break; | ||||||
| 
 |  | ||||||
|     // TEV combiner buffer write flags
 |  | ||||||
|     case PICA_REG_INDEX(tev_combiner_buffer_input): |  | ||||||
|         SyncCombinerWriteFlags(); |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -592,6 +460,41 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void RasterizerOpenGL::SetShader() { | ||||||
|  |     PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); | ||||||
|  |     std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>(); | ||||||
|  | 
 | ||||||
|  |     // Find (or generate) the GLSL shader for the current TEV state
 | ||||||
|  |     auto cached_shader = shader_cache.find(config); | ||||||
|  |     if (cached_shader != shader_cache.end()) { | ||||||
|  |         current_shader = cached_shader->second.get(); | ||||||
|  | 
 | ||||||
|  |         state.draw.shader_program = current_shader->shader.handle; | ||||||
|  |         state.Apply(); | ||||||
|  |     } else { | ||||||
|  |         LOG_DEBUG(Render_OpenGL, "Creating new shader"); | ||||||
|  | 
 | ||||||
|  |         shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); | ||||||
|  | 
 | ||||||
|  |         state.draw.shader_program = shader->shader.handle; | ||||||
|  |         state.Apply(); | ||||||
|  | 
 | ||||||
|  |         // Set the texture samplers to correspond to different texture units
 | ||||||
|  |         glUniform1i(PicaShader::Uniform::Texture0, 0); | ||||||
|  |         glUniform1i(PicaShader::Uniform::Texture1, 1); | ||||||
|  |         glUniform1i(PicaShader::Uniform::Texture2, 2); | ||||||
|  | 
 | ||||||
|  |         current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Update uniforms
 | ||||||
|  |     SyncAlphaTest(); | ||||||
|  |     SyncCombinerColor(); | ||||||
|  |     auto& tev_stages = Pica::g_state.regs.GetTevStages(); | ||||||
|  |     for (int index = 0; index < tev_stages.size(); ++index) | ||||||
|  |         SyncTevConstColor(index, tev_stages[index]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void RasterizerOpenGL::SyncFramebuffer() { | void RasterizerOpenGL::SyncFramebuffer() { | ||||||
|     const auto& regs = Pica::g_state.regs; |     const auto& regs = Pica::g_state.regs; | ||||||
| 
 | 
 | ||||||
|  | @ -712,9 +615,7 @@ void RasterizerOpenGL::SyncBlendColor() { | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SyncAlphaTest() { | void RasterizerOpenGL::SyncAlphaTest() { | ||||||
|     const auto& regs = Pica::g_state.regs; |     const auto& regs = Pica::g_state.regs; | ||||||
|     glUniform1i(uniform_alphatest_enabled, regs.output_merger.alpha_test.enable); |     glUniform1i(PicaShader::Uniform::AlphaTestRef, regs.output_merger.alpha_test.ref); | ||||||
|     glUniform1i(uniform_alphatest_func, (GLint)regs.output_merger.alpha_test.func.Value()); |  | ||||||
|     glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SyncLogicOp() { | void RasterizerOpenGL::SyncLogicOp() { | ||||||
|  | @ -744,56 +645,14 @@ void RasterizerOpenGL::SyncDepthTest() { | ||||||
|     state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; |     state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { |  | ||||||
|     GLint color_srcs[3] = { (GLint)config.color_source1.Value(), |  | ||||||
|                             (GLint)config.color_source2.Value(), |  | ||||||
|                             (GLint)config.color_source3.Value() }; |  | ||||||
|     GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(), |  | ||||||
|                             (GLint)config.alpha_source2.Value(), |  | ||||||
|                             (GLint)config.alpha_source3.Value() }; |  | ||||||
| 
 |  | ||||||
|     glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs); |  | ||||||
|     glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { |  | ||||||
|     GLint color_mods[3] = { (GLint)config.color_modifier1.Value(), |  | ||||||
|                             (GLint)config.color_modifier2.Value(), |  | ||||||
|                             (GLint)config.color_modifier3.Value() }; |  | ||||||
|     GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(), |  | ||||||
|                             (GLint)config.alpha_modifier2.Value(), |  | ||||||
|                             (GLint)config.alpha_modifier3.Value() }; |  | ||||||
| 
 |  | ||||||
|     glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods); |  | ||||||
|     glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { |  | ||||||
|     glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { |  | ||||||
|     auto const_color = PicaToGL::ColorRGBA8(config.const_color); |  | ||||||
|     glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { |  | ||||||
|     glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RasterizerOpenGL::SyncCombinerColor() { | void RasterizerOpenGL::SyncCombinerColor() { | ||||||
|     auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); |     auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); | ||||||
|     glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data()); |     glUniform4fv(PicaShader::Uniform::TevCombinerBufferColor, 1, combiner_color.data()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SyncCombinerWriteFlags() { | void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) { | ||||||
|     const auto& regs = Pica::g_state.regs; |     auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color); | ||||||
|     const auto tev_stages = regs.GetTevStages(); |     glUniform4fv(PicaShader::Uniform::TevConstColors + stage_index, 1, const_color.data()); | ||||||
|     for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { |  | ||||||
|         glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha, |  | ||||||
|                     regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index), |  | ||||||
|                     regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SyncDrawState() { | void RasterizerOpenGL::SyncDrawState() { | ||||||
|  | @ -824,12 +683,6 @@ void RasterizerOpenGL::SyncDrawState() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Skip processing TEV stages that simply pass the previous stage results through
 |  | ||||||
|     const auto tev_stages = regs.GetTevStages(); |  | ||||||
|     for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { |  | ||||||
|         glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index])); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,15 +4,104 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <cstddef> | ||||||
|  | #include <cstring> | ||||||
|  | #include <memory> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <unordered_map> | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "common/hash.h" | ||||||
| 
 | 
 | ||||||
|  | #include "video_core/pica.h" | ||||||
| #include "video_core/hwrasterizer_base.h" | #include "video_core/hwrasterizer_base.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
| #include "video_core/shader/shader_interpreter.h" | #include "video_core/shader/shader_interpreter.h" | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * This struct contains all state used to generate the GLSL shader program that emulates the current | ||||||
|  |  * Pica register configuration. This struct is used as a cache key for generated GLSL shader | ||||||
|  |  * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by | ||||||
|  |  * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where | ||||||
|  |  * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) | ||||||
|  |  * two separate shaders sharing the same key. | ||||||
|  |  */ | ||||||
|  | struct PicaShaderConfig { | ||||||
|  |     /// Construct a PicaShaderConfig with the current Pica register configuration.
 | ||||||
|  |     static PicaShaderConfig CurrentConfig() { | ||||||
|  |         PicaShaderConfig res; | ||||||
|  |         const auto& regs = Pica::g_state.regs; | ||||||
|  | 
 | ||||||
|  |         res.alpha_test_func = regs.output_merger.alpha_test.enable ? | ||||||
|  |             regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; | ||||||
|  | 
 | ||||||
|  |         // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
 | ||||||
|  |         // the GetTevStages() function) because BitField explicitly disables copies.
 | ||||||
|  | 
 | ||||||
|  |         res.tev_stages[0].sources_raw = regs.tev_stage0.sources_raw; | ||||||
|  |         res.tev_stages[1].sources_raw = regs.tev_stage1.sources_raw; | ||||||
|  |         res.tev_stages[2].sources_raw = regs.tev_stage2.sources_raw; | ||||||
|  |         res.tev_stages[3].sources_raw = regs.tev_stage3.sources_raw; | ||||||
|  |         res.tev_stages[4].sources_raw = regs.tev_stage4.sources_raw; | ||||||
|  |         res.tev_stages[5].sources_raw = regs.tev_stage5.sources_raw; | ||||||
|  | 
 | ||||||
|  |         res.tev_stages[0].modifiers_raw = regs.tev_stage0.modifiers_raw; | ||||||
|  |         res.tev_stages[1].modifiers_raw = regs.tev_stage1.modifiers_raw; | ||||||
|  |         res.tev_stages[2].modifiers_raw = regs.tev_stage2.modifiers_raw; | ||||||
|  |         res.tev_stages[3].modifiers_raw = regs.tev_stage3.modifiers_raw; | ||||||
|  |         res.tev_stages[4].modifiers_raw = regs.tev_stage4.modifiers_raw; | ||||||
|  |         res.tev_stages[5].modifiers_raw = regs.tev_stage5.modifiers_raw; | ||||||
|  | 
 | ||||||
|  |         res.tev_stages[0].ops_raw = regs.tev_stage0.ops_raw; | ||||||
|  |         res.tev_stages[1].ops_raw = regs.tev_stage1.ops_raw; | ||||||
|  |         res.tev_stages[2].ops_raw = regs.tev_stage2.ops_raw; | ||||||
|  |         res.tev_stages[3].ops_raw = regs.tev_stage3.ops_raw; | ||||||
|  |         res.tev_stages[4].ops_raw = regs.tev_stage4.ops_raw; | ||||||
|  |         res.tev_stages[5].ops_raw = regs.tev_stage5.ops_raw; | ||||||
|  | 
 | ||||||
|  |         res.tev_stages[0].scales_raw = regs.tev_stage0.scales_raw; | ||||||
|  |         res.tev_stages[1].scales_raw = regs.tev_stage1.scales_raw; | ||||||
|  |         res.tev_stages[2].scales_raw = regs.tev_stage2.scales_raw; | ||||||
|  |         res.tev_stages[3].scales_raw = regs.tev_stage3.scales_raw; | ||||||
|  |         res.tev_stages[4].scales_raw = regs.tev_stage4.scales_raw; | ||||||
|  |         res.tev_stages[5].scales_raw = regs.tev_stage5.scales_raw; | ||||||
|  | 
 | ||||||
|  |         res.combiner_buffer_input = | ||||||
|  |             regs.tev_combiner_buffer_input.update_mask_rgb.Value() | | ||||||
|  |             regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; | ||||||
|  | 
 | ||||||
|  |         return res; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { | ||||||
|  |         return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { | ||||||
|  |         return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool operator ==(const PicaShaderConfig& o) const { | ||||||
|  |         return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Pica::Regs::CompareFunc alpha_test_func; | ||||||
|  |     std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {}; | ||||||
|  |     u8 combiner_buffer_input; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | namespace std { | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | struct hash<PicaShaderConfig> { | ||||||
|  |     size_t operator()(const PicaShaderConfig& k) const { | ||||||
|  |         return Common::ComputeHash64(&k, sizeof(PicaShaderConfig)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace std
 | ||||||
|  | 
 | ||||||
| class RasterizerOpenGL : public HWRasterizer { | class RasterizerOpenGL : public HWRasterizer { | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|  | @ -45,19 +134,23 @@ public: | ||||||
|     /// Notify rasterizer that a 3DS memory region has been changed
 |     /// Notify rasterizer that a 3DS memory region has been changed
 | ||||||
|     void NotifyFlush(PAddr addr, u32 size) override; |     void NotifyFlush(PAddr addr, u32 size) override; | ||||||
| 
 | 
 | ||||||
| private: |     /// OpenGL shader generated for a given Pica register state
 | ||||||
|     /// Structure used for managing texture environment states
 |     struct PicaShader { | ||||||
|     struct TEVConfigUniforms { |         /// OpenGL shader resource
 | ||||||
|         GLuint enabled; |         OGLShader shader; | ||||||
|         GLuint color_sources; | 
 | ||||||
|         GLuint alpha_sources; |         /// Fragment shader uniforms
 | ||||||
|         GLuint color_modifiers; |         enum Uniform : GLuint { | ||||||
|         GLuint alpha_modifiers; |             AlphaTestRef = 0, | ||||||
|         GLuint color_alpha_op; |             TevConstColors = 1, | ||||||
|         GLuint color_alpha_multiplier; |             Texture0 = 7, | ||||||
|         GLuint const_color; |             Texture1 = 8, | ||||||
|         GLuint updates_combiner_buffer_color_alpha; |             Texture2 = 9, | ||||||
|  |             TevCombinerBufferColor = 10, | ||||||
|         }; |         }; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
| 
 | 
 | ||||||
|     /// Structure used for storing information about color textures
 |     /// Structure used for storing information about color textures
 | ||||||
|     struct TextureInfo { |     struct TextureInfo { | ||||||
|  | @ -129,6 +222,9 @@ private: | ||||||
|     /// Reconfigure the OpenGL depth texture to use the given format and dimensions
 |     /// Reconfigure the OpenGL depth texture to use the given format and dimensions
 | ||||||
|     void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); |     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
 |     /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
 | ||||||
|     void SyncFramebuffer(); |     void SyncFramebuffer(); | ||||||
| 
 | 
 | ||||||
|  | @ -156,27 +252,12 @@ private: | ||||||
|     /// Syncs the depth test states to match the PICA register
 |     /// Syncs the depth test states to match the PICA register
 | ||||||
|     void SyncDepthTest(); |     void SyncDepthTest(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the specified TEV stage's color and alpha sources to match the PICA register
 |     /// Syncs the TEV constant color to match the PICA register
 | ||||||
|     void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config); |     void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage); | ||||||
| 
 |  | ||||||
|     /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register
 |  | ||||||
|     void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register
 |  | ||||||
|     void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified TEV stage's constant color to match the PICA register
 |  | ||||||
|     void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register
 |  | ||||||
|     void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config); |  | ||||||
| 
 | 
 | ||||||
|     /// Syncs the TEV combiner color buffer to match the PICA register
 |     /// Syncs the TEV combiner color buffer to match the PICA register
 | ||||||
|     void SyncCombinerColor(); |     void SyncCombinerColor(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the TEV combiner write flags to match the PICA register
 |  | ||||||
|     void SyncCombinerWriteFlags(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the remaining OpenGL drawing state to match the current PICA state
 |     /// Syncs the remaining OpenGL drawing state to match the current PICA state
 | ||||||
|     void SyncDrawState(); |     void SyncDrawState(); | ||||||
| 
 | 
 | ||||||
|  | @ -213,21 +294,11 @@ private: | ||||||
|     std::array<SamplerInfo, 3> texture_samplers; |     std::array<SamplerInfo, 3> texture_samplers; | ||||||
|     TextureInfo fb_color_texture; |     TextureInfo fb_color_texture; | ||||||
|     DepthTextureInfo fb_depth_texture; |     DepthTextureInfo fb_depth_texture; | ||||||
|     OGLShader shader; | 
 | ||||||
|  |     std::unordered_map<PicaShaderConfig, std::unique_ptr<PicaShader>> shader_cache; | ||||||
|  |     const PicaShader* current_shader = nullptr; | ||||||
|  | 
 | ||||||
|     OGLVertexArray vertex_array; |     OGLVertexArray vertex_array; | ||||||
|     OGLBuffer vertex_buffer; |     OGLBuffer vertex_buffer; | ||||||
|     OGLFramebuffer framebuffer; |     OGLFramebuffer framebuffer; | ||||||
| 
 |  | ||||||
|     // Hardware vertex shader
 |  | ||||||
|     GLuint attrib_position; |  | ||||||
|     GLuint attrib_color; |  | ||||||
|     GLuint attrib_texcoords; |  | ||||||
| 
 |  | ||||||
|     // Hardware fragment shader
 |  | ||||||
|     GLuint uniform_alphatest_enabled; |  | ||||||
|     GLuint uniform_alphatest_func; |  | ||||||
|     GLuint uniform_alphatest_ref; |  | ||||||
|     GLuint uniform_tex; |  | ||||||
|     GLuint uniform_tev_combiner_buffer_color; |  | ||||||
|     TEVConfigUniforms uniform_tev_cfgs[6]; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ public: | ||||||
|     /// Creates a new internal OpenGL resource and stores the handle
 |     /// Creates a new internal OpenGL resource and stores the handle
 | ||||||
|     void Create(const char* vert_shader, const char* frag_shader) { |     void Create(const char* vert_shader, const char* frag_shader) { | ||||||
|         if (handle != 0) return; |         if (handle != 0) return; | ||||||
|         handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); |         handle = GLShader::LoadProgram(vert_shader, frag_shader); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Deletes the internal OpenGL resource
 |     /// Deletes the internal OpenGL resource
 | ||||||
|  |  | ||||||
							
								
								
									
										388
									
								
								src/video_core/renderer_opengl/gl_shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								src/video_core/renderer_opengl/gl_shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,388 @@ | ||||||
|  | // Copyright 2015 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "video_core/pica.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_shader_gen.h" | ||||||
|  | 
 | ||||||
|  | using Pica::Regs; | ||||||
|  | using TevStageConfig = Regs::TevStageConfig; | ||||||
|  | 
 | ||||||
|  | namespace GLShader { | ||||||
|  | 
 | ||||||
|  | /// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
 | ||||||
|  | static bool IsPassThroughTevStage(const TevStageConfig& stage) { | ||||||
|  |     return (stage.color_op             == TevStageConfig::Operation::Replace && | ||||||
|  |             stage.alpha_op             == TevStageConfig::Operation::Replace && | ||||||
|  |             stage.color_source1        == TevStageConfig::Source::Previous && | ||||||
|  |             stage.alpha_source1        == TevStageConfig::Source::Previous && | ||||||
|  |             stage.color_modifier1      == TevStageConfig::ColorModifier::SourceColor && | ||||||
|  |             stage.alpha_modifier1      == TevStageConfig::AlphaModifier::SourceAlpha && | ||||||
|  |             stage.GetColorMultiplier() == 1 && | ||||||
|  |             stage.GetAlphaMultiplier() == 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the specified TEV stage source component(s)
 | ||||||
|  | static void AppendSource(std::string& out, TevStageConfig::Source source, | ||||||
|  |         const std::string& index_name) { | ||||||
|  |     using Source = TevStageConfig::Source; | ||||||
|  |     switch (source) { | ||||||
|  |     case Source::PrimaryColor: | ||||||
|  |         out += "primary_color"; | ||||||
|  |         break; | ||||||
|  |     case Source::PrimaryFragmentColor: | ||||||
|  |         // HACK: Until we implement fragment lighting, use primary_color
 | ||||||
|  |         out += "primary_color"; | ||||||
|  |         break; | ||||||
|  |     case Source::SecondaryFragmentColor: | ||||||
|  |         // HACK: Until we implement fragment lighting, use zero
 | ||||||
|  |         out += "vec4(0.0)"; | ||||||
|  |         break; | ||||||
|  |     case Source::Texture0: | ||||||
|  |         out += "texture(tex[0], texcoord[0])"; | ||||||
|  |         break; | ||||||
|  |     case Source::Texture1: | ||||||
|  |         out += "texture(tex[1], texcoord[1])"; | ||||||
|  |         break; | ||||||
|  |     case Source::Texture2: | ||||||
|  |         out += "texture(tex[2], texcoord[2])"; | ||||||
|  |         break; | ||||||
|  |     case Source::PreviousBuffer: | ||||||
|  |         out += "combiner_buffer"; | ||||||
|  |         break; | ||||||
|  |     case Source::Constant: | ||||||
|  |         ((out += "const_color[") += index_name) += ']'; | ||||||
|  |         break; | ||||||
|  |     case Source::Previous: | ||||||
|  |         out += "last_tex_env_out"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         out += "vec4(0.0)"; | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the color components to use for the specified TEV stage color modifier
 | ||||||
|  | static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier, | ||||||
|  |         TevStageConfig::Source source, const std::string& index_name) { | ||||||
|  |     using ColorModifier = TevStageConfig::ColorModifier; | ||||||
|  |     switch (modifier) { | ||||||
|  |     case ColorModifier::SourceColor: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".rgb"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::OneMinusSourceColor: | ||||||
|  |         out += "vec3(1.0) - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".rgb"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::SourceAlpha: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".aaa"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::OneMinusSourceAlpha: | ||||||
|  |         out += "vec3(1.0) - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".aaa"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::SourceRed: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".rrr"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::OneMinusSourceRed: | ||||||
|  |         out += "vec3(1.0) - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".rrr"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::SourceGreen: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".ggg"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::OneMinusSourceGreen: | ||||||
|  |         out += "vec3(1.0) - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".ggg"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::SourceBlue: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".bbb"; | ||||||
|  |         break; | ||||||
|  |     case ColorModifier::OneMinusSourceBlue: | ||||||
|  |         out += "vec3(1.0) - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".bbb"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         out += "vec3(0.0)"; | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the alpha component to use for the specified TEV stage alpha modifier
 | ||||||
|  | static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier, | ||||||
|  |         TevStageConfig::Source source, const std::string& index_name) { | ||||||
|  |     using AlphaModifier = TevStageConfig::AlphaModifier; | ||||||
|  |     switch (modifier) { | ||||||
|  |     case AlphaModifier::SourceAlpha: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".a"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::OneMinusSourceAlpha: | ||||||
|  |         out += "1.0 - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".a"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::SourceRed: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".r"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::OneMinusSourceRed: | ||||||
|  |         out += "1.0 - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".r"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::SourceGreen: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".g"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::OneMinusSourceGreen: | ||||||
|  |         out += "1.0 - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".g"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::SourceBlue: | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".b"; | ||||||
|  |         break; | ||||||
|  |     case AlphaModifier::OneMinusSourceBlue: | ||||||
|  |         out += "1.0 - "; | ||||||
|  |         AppendSource(out, source, index_name); | ||||||
|  |         out += ".b"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         out += "0.0"; | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the combiner function for the color components for the specified TEV stage operation
 | ||||||
|  | static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation, | ||||||
|  |         const std::string& variable_name) { | ||||||
|  |     out += "clamp("; | ||||||
|  |     using Operation = TevStageConfig::Operation; | ||||||
|  |     switch (operation) { | ||||||
|  |     case Operation::Replace: | ||||||
|  |         out += variable_name + "[0]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Modulate: | ||||||
|  |         out += variable_name + "[0] * " + variable_name + "[1]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Add: | ||||||
|  |         out += variable_name + "[0] + " + variable_name + "[1]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::AddSigned: | ||||||
|  |         out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Lerp: | ||||||
|  |         // TODO(bunnei): Verify if HW actually does this per-component, otherwise we can just use builtin lerp
 | ||||||
|  |         out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Subtract: | ||||||
|  |         out += variable_name + "[0] - " + variable_name + "[1]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::MultiplyThenAdd: | ||||||
|  |         out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::AddThenMultiply: | ||||||
|  |         out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         out += "vec3(0.0)"; | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the combiner function for the alpha component for the specified TEV stage operation
 | ||||||
|  | static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation, | ||||||
|  |         const std::string& variable_name) { | ||||||
|  |     out += "clamp("; | ||||||
|  |     using Operation = TevStageConfig::Operation; | ||||||
|  |     switch (operation) { | ||||||
|  |     case Operation::Replace: | ||||||
|  |         out += variable_name + "[0]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Modulate: | ||||||
|  |         out += variable_name + "[0] * " + variable_name + "[1]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Add: | ||||||
|  |         out += variable_name + "[0] + " + variable_name + "[1]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::AddSigned: | ||||||
|  |         out += variable_name + "[0] + " + variable_name + "[1] - 0.5"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Lerp: | ||||||
|  |         out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; | ||||||
|  |         break; | ||||||
|  |     case Operation::Subtract: | ||||||
|  |         out += variable_name + "[0] - " + variable_name + "[1]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::MultiplyThenAdd: | ||||||
|  |         out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; | ||||||
|  |         break; | ||||||
|  |     case Operation::AddThenMultiply: | ||||||
|  |         out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         out += "0.0"; | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     out += ", 0.0, 1.0)"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the if-statement condition used to evaluate alpha testing
 | ||||||
|  | static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { | ||||||
|  |     using CompareFunc = Regs::CompareFunc; | ||||||
|  |     switch (func) { | ||||||
|  |     case CompareFunc::Never: | ||||||
|  |         out += "true"; | ||||||
|  |         break; | ||||||
|  |     case CompareFunc::Always: | ||||||
|  |         out += "false"; | ||||||
|  |         break; | ||||||
|  |     case CompareFunc::Equal: | ||||||
|  |     case CompareFunc::NotEqual: | ||||||
|  |     case CompareFunc::LessThan: | ||||||
|  |     case CompareFunc::LessThanOrEqual: | ||||||
|  |     case CompareFunc::GreaterThan: | ||||||
|  |     case CompareFunc::GreaterThanOrEqual: | ||||||
|  |     { | ||||||
|  |         static const char* op[] = { "!=", "==", ">=", ">", "<=", "<", }; | ||||||
|  |         unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal; | ||||||
|  |         out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref"; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         out += "false"; | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Writes the code to emulate the specified TEV stage
 | ||||||
|  | static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) { | ||||||
|  |     auto& stage = config.tev_stages[index]; | ||||||
|  |     if (!IsPassThroughTevStage(stage)) { | ||||||
|  |         std::string index_name = std::to_string(index); | ||||||
|  | 
 | ||||||
|  |         out += "vec3 color_results_" + index_name + "[3] = vec3[3]("; | ||||||
|  |         AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name); | ||||||
|  |         out += ", "; | ||||||
|  |         AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name); | ||||||
|  |         out += ", "; | ||||||
|  |         AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name); | ||||||
|  |         out += ");\n"; | ||||||
|  | 
 | ||||||
|  |         out += "vec3 color_output_" + index_name + " = "; | ||||||
|  |         AppendColorCombiner(out, stage.color_op, "color_results_" + index_name); | ||||||
|  |         out += ";\n"; | ||||||
|  | 
 | ||||||
|  |         out += "float alpha_results_" + index_name + "[3] = float[3]("; | ||||||
|  |         AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name); | ||||||
|  |         out += ", "; | ||||||
|  |         AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name); | ||||||
|  |         out += ", "; | ||||||
|  |         AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name); | ||||||
|  |         out += ");\n"; | ||||||
|  | 
 | ||||||
|  |         out += "float alpha_output_" + index_name + " = "; | ||||||
|  |         AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name); | ||||||
|  |         out += ";\n"; | ||||||
|  | 
 | ||||||
|  |         out += "last_tex_env_out = vec4(" | ||||||
|  |             "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, vec3(0.0), vec3(1.0))," | ||||||
|  |             "clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (config.TevStageUpdatesCombinerBufferColor(index)) | ||||||
|  |         out += "combiner_buffer.rgb = last_tex_env_out.rgb;\n"; | ||||||
|  | 
 | ||||||
|  |     if (config.TevStageUpdatesCombinerBufferAlpha(index)) | ||||||
|  |         out += "combiner_buffer.a = last_tex_env_out.a;\n"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string GenerateFragmentShader(const PicaShaderConfig& config) { | ||||||
|  |     std::string out = R"( | ||||||
|  | #version 330 | ||||||
|  | #extension GL_ARB_explicit_uniform_location : require | ||||||
|  | 
 | ||||||
|  | #define NUM_TEV_STAGES 6 | ||||||
|  | 
 | ||||||
|  | in vec4 primary_color; | ||||||
|  | in vec2 texcoord[3]; | ||||||
|  | 
 | ||||||
|  | out vec4 color; | ||||||
|  | )"; | ||||||
|  | 
 | ||||||
|  |     using Uniform = RasterizerOpenGL::PicaShader::Uniform; | ||||||
|  |     out += "layout(location = " + std::to_string((int)Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)Uniform::Texture0) + ") uniform sampler2D tex[3];\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n"; | ||||||
|  | 
 | ||||||
|  |     out += "void main() {\n"; | ||||||
|  |     out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n"; | ||||||
|  |     out += "vec4 last_tex_env_out = vec4(0.0);\n"; | ||||||
|  | 
 | ||||||
|  |     // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
 | ||||||
|  |     if (config.alpha_test_func == Regs::CompareFunc::Never) { | ||||||
|  |         out += "discard; }"; | ||||||
|  |         return out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (size_t index = 0; index < config.tev_stages.size(); ++index) | ||||||
|  |         WriteTevStage(out, config, (unsigned)index); | ||||||
|  | 
 | ||||||
|  |     if (config.alpha_test_func != Regs::CompareFunc::Always) { | ||||||
|  |         out += "if ("; | ||||||
|  |         AppendAlphaTestCondition(out, config.alpha_test_func); | ||||||
|  |         out += ") discard;\n"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     out += "color = last_tex_env_out;\n}"; | ||||||
|  | 
 | ||||||
|  |     return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string GenerateVertexShader() { | ||||||
|  |     std::string out = "#version 330\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION)  + ") in vec4 vert_position;\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR)     + ") in vec4 vert_color;\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n"; | ||||||
|  |     out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n"; | ||||||
|  | 
 | ||||||
|  |     out += R"( | ||||||
|  | out vec4 primary_color; | ||||||
|  | out vec2 texcoord[3]; | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |     primary_color = vert_color; | ||||||
|  |     texcoord[0] = vert_texcoord0; | ||||||
|  |     texcoord[1] = vert_texcoord1; | ||||||
|  |     texcoord[2] = vert_texcoord2; | ||||||
|  |     gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); | ||||||
|  | } | ||||||
|  | )"; | ||||||
|  | 
 | ||||||
|  |     return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace GLShader
 | ||||||
							
								
								
									
										27
									
								
								src/video_core/renderer_opengl/gl_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/video_core/renderer_opengl/gl_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | // Copyright 2015 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
|  | 
 | ||||||
|  | namespace GLShader { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL vertex shader program source code for the current Pica state | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateVertexShader(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL fragment shader program source code for the current Pica state | ||||||
|  |  * @param config ShaderCacheKey object generated for the current Pica state, used for the shader | ||||||
|  |  *               configuration (NOTE: Use state in this struct only, not the Pica registers!) | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateFragmentShader(const PicaShaderConfig& config); | ||||||
|  | 
 | ||||||
|  | } // namespace GLShader
 | ||||||
|  | @ -8,9 +8,9 @@ | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_util.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
 |     // Create the shaders
 | ||||||
|     GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); |     GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); | ||||||
|  | @ -65,6 +65,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | ||||||
|     GLuint program_id = glCreateProgram(); |     GLuint program_id = glCreateProgram(); | ||||||
|     glAttachShader(program_id, vertex_shader_id); |     glAttachShader(program_id, vertex_shader_id); | ||||||
|     glAttachShader(program_id, fragment_shader_id); |     glAttachShader(program_id, fragment_shader_id); | ||||||
|  | 
 | ||||||
|     glLinkProgram(program_id); |     glLinkProgram(program_id); | ||||||
| 
 | 
 | ||||||
|     // Check the program
 |     // Check the program
 | ||||||
|  | @ -87,4 +88,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | ||||||
|     return program_id; |     return program_id; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } // namespace GLShader
 | ||||||
|  |  | ||||||
|  | @ -6,8 +6,22 @@ | ||||||
| 
 | 
 | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| 
 | 
 | ||||||
| namespace ShaderUtil { | namespace GLShader { | ||||||
| 
 | 
 | ||||||
| GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); | enum Attributes { | ||||||
|  |     ATTRIBUTE_POSITION, | ||||||
|  |     ATTRIBUTE_COLOR, | ||||||
|  |     ATTRIBUTE_TEXCOORD0, | ||||||
|  |     ATTRIBUTE_TEXCOORD1, | ||||||
|  |     ATTRIBUTE_TEXCOORD2, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| } | /**
 | ||||||
|  |  * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) | ||||||
|  |  * @param vertex_shader String of the GLSL vertex shader program | ||||||
|  |  * @param fragment_shader String of the GLSL fragment shader program | ||||||
|  |  * @returns Handle of the newly created OpenGL shader object | ||||||
|  |  */ | ||||||
|  | GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader); | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -1,337 +0,0 @@ | ||||||
| // Copyright 2014 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| namespace GLShaders { |  | ||||||
| 
 |  | ||||||
| const char g_vertex_shader[] = R"( |  | ||||||
| #version 150 core |  | ||||||
| 
 |  | ||||||
| in vec2 vert_position; |  | ||||||
| in vec2 vert_tex_coord; |  | ||||||
| out vec2 frag_tex_coord; |  | ||||||
| 
 |  | ||||||
| // This is a truncated 3x3 matrix for 2D transformations:
 |  | ||||||
| // The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
 |  | ||||||
| // The third column performs translation.
 |  | ||||||
| // The third row could be used for projection, which we don't need in 2D. It hence is assumed to
 |  | ||||||
| // implicitly be [0, 0, 1]
 |  | ||||||
| uniform mat3x2 modelview_matrix; |  | ||||||
| 
 |  | ||||||
| void main() { |  | ||||||
|     // Multiply input position by the rotscale part of the matrix and then manually translate by
 |  | ||||||
|     // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
 |  | ||||||
|     // to `vec3(vert_position.xy, 1.0)`
 |  | ||||||
|     gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); |  | ||||||
|     frag_tex_coord = vert_tex_coord; |  | ||||||
| } |  | ||||||
| )"; |  | ||||||
| 
 |  | ||||||
| const char g_fragment_shader[] = R"( |  | ||||||
| #version 150 core |  | ||||||
| 
 |  | ||||||
| in vec2 frag_tex_coord; |  | ||||||
| out vec4 color; |  | ||||||
| 
 |  | ||||||
| uniform sampler2D color_texture; |  | ||||||
| 
 |  | ||||||
| void main() { |  | ||||||
|     color = texture(color_texture, frag_tex_coord); |  | ||||||
| } |  | ||||||
| )"; |  | ||||||
| 
 |  | ||||||
| const char g_vertex_shader_hw[] = R"( |  | ||||||
| #version 150 core |  | ||||||
| 
 |  | ||||||
| #define NUM_VTX_ATTR 7 |  | ||||||
| 
 |  | ||||||
| in vec4 vert_position; |  | ||||||
| in vec4 vert_color; |  | ||||||
| in vec2 vert_texcoords[3]; |  | ||||||
| 
 |  | ||||||
| out vec4 o[NUM_VTX_ATTR]; |  | ||||||
| 
 |  | ||||||
| void main() { |  | ||||||
|     o[2] = vert_color; |  | ||||||
|     o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy); |  | ||||||
|     o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy); |  | ||||||
| 
 |  | ||||||
|     gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); |  | ||||||
| } |  | ||||||
| )"; |  | ||||||
| 
 |  | ||||||
| // TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
 |  | ||||||
| const char g_fragment_shader_hw[] = R"( |  | ||||||
| #version 150 core |  | ||||||
| 
 |  | ||||||
| #define NUM_VTX_ATTR 7 |  | ||||||
| #define NUM_TEV_STAGES 6 |  | ||||||
| 
 |  | ||||||
| #define SOURCE_PRIMARYCOLOR           0x0 |  | ||||||
| #define SOURCE_PRIMARYFRAGMENTCOLOR   0x1 |  | ||||||
| #define SOURCE_SECONDARYFRAGMENTCOLOR 0x2 |  | ||||||
| #define SOURCE_TEXTURE0               0x3 |  | ||||||
| #define SOURCE_TEXTURE1               0x4 |  | ||||||
| #define SOURCE_TEXTURE2               0x5 |  | ||||||
| #define SOURCE_TEXTURE3               0x6 |  | ||||||
| #define SOURCE_PREVIOUSBUFFER         0xd |  | ||||||
| #define SOURCE_CONSTANT               0xe |  | ||||||
| #define SOURCE_PREVIOUS               0xf |  | ||||||
| 
 |  | ||||||
| #define COLORMODIFIER_SOURCECOLOR         0x0 |  | ||||||
| #define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1 |  | ||||||
| #define COLORMODIFIER_SOURCEALPHA         0x2 |  | ||||||
| #define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3 |  | ||||||
| #define COLORMODIFIER_SOURCERED           0x4 |  | ||||||
| #define COLORMODIFIER_ONEMINUSSOURCERED   0x5 |  | ||||||
| #define COLORMODIFIER_SOURCEGREEN         0x8 |  | ||||||
| #define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9 |  | ||||||
| #define COLORMODIFIER_SOURCEBLUE          0xc |  | ||||||
| #define COLORMODIFIER_ONEMINUSSOURCEBLUE  0xd |  | ||||||
| 
 |  | ||||||
| #define ALPHAMODIFIER_SOURCEALPHA         0x0 |  | ||||||
| #define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1 |  | ||||||
| #define ALPHAMODIFIER_SOURCERED           0x2 |  | ||||||
| #define ALPHAMODIFIER_ONEMINUSSOURCERED   0x3 |  | ||||||
| #define ALPHAMODIFIER_SOURCEGREEN         0x4 |  | ||||||
| #define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5 |  | ||||||
| #define ALPHAMODIFIER_SOURCEBLUE          0x6 |  | ||||||
| #define ALPHAMODIFIER_ONEMINUSSOURCEBLUE  0x7 |  | ||||||
| 
 |  | ||||||
| #define OPERATION_REPLACE         0 |  | ||||||
| #define OPERATION_MODULATE        1 |  | ||||||
| #define OPERATION_ADD             2 |  | ||||||
| #define OPERATION_ADDSIGNED       3 |  | ||||||
| #define OPERATION_LERP            4 |  | ||||||
| #define OPERATION_SUBTRACT        5 |  | ||||||
| #define OPERATION_MULTIPLYTHENADD 8 |  | ||||||
| #define OPERATION_ADDTHENMULTIPLY 9 |  | ||||||
| 
 |  | ||||||
| #define COMPAREFUNC_NEVER              0 |  | ||||||
| #define COMPAREFUNC_ALWAYS             1 |  | ||||||
| #define COMPAREFUNC_EQUAL              2 |  | ||||||
| #define COMPAREFUNC_NOTEQUAL           3 |  | ||||||
| #define COMPAREFUNC_LESSTHAN           4 |  | ||||||
| #define COMPAREFUNC_LESSTHANOREQUAL    5 |  | ||||||
| #define COMPAREFUNC_GREATERTHAN        6 |  | ||||||
| #define COMPAREFUNC_GREATERTHANOREQUAL 7 |  | ||||||
| 
 |  | ||||||
| in vec4 o[NUM_VTX_ATTR]; |  | ||||||
| out vec4 color; |  | ||||||
| 
 |  | ||||||
| uniform bool alphatest_enabled; |  | ||||||
| uniform int alphatest_func; |  | ||||||
| uniform float alphatest_ref; |  | ||||||
| 
 |  | ||||||
| uniform sampler2D tex[3]; |  | ||||||
| 
 |  | ||||||
| uniform vec4 tev_combiner_buffer_color; |  | ||||||
| 
 |  | ||||||
| struct TEVConfig |  | ||||||
| { |  | ||||||
|     bool enabled; |  | ||||||
|     ivec3 color_sources; |  | ||||||
|     ivec3 alpha_sources; |  | ||||||
|     ivec3 color_modifiers; |  | ||||||
|     ivec3 alpha_modifiers; |  | ||||||
|     ivec2 color_alpha_op; |  | ||||||
|     ivec2 color_alpha_multiplier; |  | ||||||
|     vec4 const_color; |  | ||||||
|     bvec2 updates_combiner_buffer_color_alpha; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| uniform TEVConfig tev_cfgs[NUM_TEV_STAGES]; |  | ||||||
| 
 |  | ||||||
| vec4 g_combiner_buffer; |  | ||||||
| vec4 g_last_tex_env_out; |  | ||||||
| vec4 g_const_color; |  | ||||||
| 
 |  | ||||||
| vec4 GetSource(int source) { |  | ||||||
|     if (source == SOURCE_PRIMARYCOLOR) { |  | ||||||
|         return o[2]; |  | ||||||
|     } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) { |  | ||||||
|         // HACK: Until we implement fragment lighting, use primary_color
 |  | ||||||
|         return o[2]; |  | ||||||
|     } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) { |  | ||||||
|         // HACK: Until we implement fragment lighting, use zero
 |  | ||||||
|         return vec4(0.0, 0.0, 0.0, 0.0); |  | ||||||
|     } else if (source == SOURCE_TEXTURE0) { |  | ||||||
|         return texture(tex[0], o[3].xy); |  | ||||||
|     } else if (source == SOURCE_TEXTURE1) { |  | ||||||
|         return texture(tex[1], o[3].zw); |  | ||||||
|     } else if (source == SOURCE_TEXTURE2) { |  | ||||||
|         // TODO: Unverified
 |  | ||||||
|         return texture(tex[2], o[5].zw); |  | ||||||
|     } else if (source == SOURCE_TEXTURE3) { |  | ||||||
|         // TODO: no 4th texture?
 |  | ||||||
|     } else if (source == SOURCE_PREVIOUSBUFFER) { |  | ||||||
|         return g_combiner_buffer; |  | ||||||
|     } else if (source == SOURCE_CONSTANT) { |  | ||||||
|         return g_const_color; |  | ||||||
|     } else if (source == SOURCE_PREVIOUS) { |  | ||||||
|         return g_last_tex_env_out; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return vec4(0.0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| vec3 GetColorModifier(int factor, vec4 color) { |  | ||||||
|     if (factor == COLORMODIFIER_SOURCECOLOR) { |  | ||||||
|         return color.rgb; |  | ||||||
|     } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) { |  | ||||||
|         return vec3(1.0) - color.rgb; |  | ||||||
|     } else if (factor == COLORMODIFIER_SOURCEALPHA) { |  | ||||||
|         return color.aaa; |  | ||||||
|     } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) { |  | ||||||
|         return vec3(1.0) - color.aaa; |  | ||||||
|     } else if (factor == COLORMODIFIER_SOURCERED) { |  | ||||||
|         return color.rrr; |  | ||||||
|     } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) { |  | ||||||
|         return vec3(1.0) - color.rrr; |  | ||||||
|     } else if (factor == COLORMODIFIER_SOURCEGREEN) { |  | ||||||
|         return color.ggg; |  | ||||||
|     } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) { |  | ||||||
|         return vec3(1.0) - color.ggg; |  | ||||||
|     } else if (factor == COLORMODIFIER_SOURCEBLUE) { |  | ||||||
|         return color.bbb; |  | ||||||
|     } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) { |  | ||||||
|         return vec3(1.0) - color.bbb; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return vec3(0.0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| float GetAlphaModifier(int factor, vec4 color) { |  | ||||||
|     if (factor == ALPHAMODIFIER_SOURCEALPHA) { |  | ||||||
|         return color.a; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) { |  | ||||||
|         return 1.0 - color.a; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_SOURCERED) { |  | ||||||
|         return color.r; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) { |  | ||||||
|         return 1.0 - color.r; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_SOURCEGREEN) { |  | ||||||
|         return color.g; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) { |  | ||||||
|         return 1.0 - color.g; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_SOURCEBLUE) { |  | ||||||
|         return color.b; |  | ||||||
|     } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) { |  | ||||||
|         return 1.0 - color.b; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return 0.0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| vec3 ColorCombine(int op, vec3 color[3]) { |  | ||||||
|     if (op == OPERATION_REPLACE) { |  | ||||||
|         return color[0]; |  | ||||||
|     } else if (op == OPERATION_MODULATE) { |  | ||||||
|         return color[0] * color[1]; |  | ||||||
|     } else if (op == OPERATION_ADD) { |  | ||||||
|         return min(color[0] + color[1], 1.0); |  | ||||||
|     } else if (op == OPERATION_ADDSIGNED) { |  | ||||||
|         return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0); |  | ||||||
|     } else if (op == OPERATION_LERP) { |  | ||||||
|         return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]); |  | ||||||
|     } else if (op == OPERATION_SUBTRACT) { |  | ||||||
|         return max(color[0] - color[1], 0.0); |  | ||||||
|     } else if (op == OPERATION_MULTIPLYTHENADD) { |  | ||||||
|         return min(color[0] * color[1] + color[2], 1.0); |  | ||||||
|     } else if (op == OPERATION_ADDTHENMULTIPLY) { |  | ||||||
|         return min(color[0] + color[1], 1.0) * color[2]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return vec3(0.0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| float AlphaCombine(int op, float alpha[3]) { |  | ||||||
|     if (op == OPERATION_REPLACE) { |  | ||||||
|         return alpha[0]; |  | ||||||
|     } else if (op == OPERATION_MODULATE) { |  | ||||||
|         return alpha[0] * alpha[1]; |  | ||||||
|     } else if (op == OPERATION_ADD) { |  | ||||||
|         return min(alpha[0] + alpha[1], 1.0); |  | ||||||
|     } else if (op == OPERATION_ADDSIGNED) { |  | ||||||
|         return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0); |  | ||||||
|     } else if (op == OPERATION_LERP) { |  | ||||||
|         return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]); |  | ||||||
|     } else if (op == OPERATION_SUBTRACT) { |  | ||||||
|         return max(alpha[0] - alpha[1], 0.0); |  | ||||||
|     } else if (op == OPERATION_MULTIPLYTHENADD) { |  | ||||||
|         return min(alpha[0] * alpha[1] + alpha[2], 1.0); |  | ||||||
|     } else if (op == OPERATION_ADDTHENMULTIPLY) { |  | ||||||
|         return min(alpha[0] + alpha[1], 1.0) * alpha[2]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return 0.0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void main(void) { |  | ||||||
|     g_combiner_buffer = tev_combiner_buffer_color; |  | ||||||
| 
 |  | ||||||
|     for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) { |  | ||||||
|         if (tev_cfgs[tex_env_idx].enabled) { |  | ||||||
|             g_const_color = tev_cfgs[tex_env_idx].const_color; |  | ||||||
| 
 |  | ||||||
|             vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)), |  | ||||||
|                                             GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)), |  | ||||||
|                                             GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z))); |  | ||||||
|             vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results); |  | ||||||
| 
 |  | ||||||
|             float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)), |  | ||||||
|                                               GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)), |  | ||||||
|                                               GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z))); |  | ||||||
|             float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results); |  | ||||||
| 
 |  | ||||||
|             g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) { |  | ||||||
|             g_combiner_buffer.rgb = g_last_tex_env_out.rgb; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) { |  | ||||||
|             g_combiner_buffer.a = g_last_tex_env_out.a; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (alphatest_enabled) { |  | ||||||
|         if (alphatest_func == COMPAREFUNC_NEVER) { |  | ||||||
|             discard; |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_ALWAYS) { |  | ||||||
| 
 |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_EQUAL) { |  | ||||||
|             if (g_last_tex_env_out.a != alphatest_ref) { |  | ||||||
|                 discard; |  | ||||||
|             } |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) { |  | ||||||
|             if (g_last_tex_env_out.a == alphatest_ref) { |  | ||||||
|                 discard; |  | ||||||
|             } |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_LESSTHAN) { |  | ||||||
|             if (g_last_tex_env_out.a >= alphatest_ref) { |  | ||||||
|                 discard; |  | ||||||
|             } |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) { |  | ||||||
|             if (g_last_tex_env_out.a > alphatest_ref) { |  | ||||||
|                 discard; |  | ||||||
|             } |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) { |  | ||||||
|             if (g_last_tex_env_out.a <= alphatest_ref) { |  | ||||||
|                 discard; |  | ||||||
|             } |  | ||||||
|         } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) { |  | ||||||
|             if (g_last_tex_env_out.a < alphatest_ref) { |  | ||||||
|                 discard; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     color = g_last_tex_env_out; |  | ||||||
| } |  | ||||||
| )"; |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -65,6 +65,7 @@ public: | ||||||
|         GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
 |         GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
 | ||||||
|         GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
 |         GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
 | ||||||
|         GLuint shader_program; // GL_CURRENT_PROGRAM
 |         GLuint shader_program; // GL_CURRENT_PROGRAM
 | ||||||
|  |         bool shader_dirty; | ||||||
|     } draw; |     } draw; | ||||||
| 
 | 
 | ||||||
|     OpenGLState(); |     OpenGLState(); | ||||||
|  |  | ||||||
|  | @ -21,9 +21,44 @@ | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer.h" | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_util.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" | #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. |  * 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); |     glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); | ||||||
| 
 | 
 | ||||||
|     // Link shaders and get variable locations
 |     // 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_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix"); | ||||||
|     uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); |     uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); | ||||||
|     attrib_position = glGetAttribLocation(program_id, "vert_position"); |     attrib_position = glGetAttribLocation(program_id, "vert_position"); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue