From 4322ebda2cb719e94a836fdd6eb8ecc42c472bc0 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Thu, 15 Jun 2023 22:45:13 +0200 Subject: [PATCH] [GPU] Start texture combiner implementation This commit first implements experimental and incomplete texture combiner support. Currently only the first texture combiner is implemented. Many sources and combine modes are not implemented yet. --- .gitignore | 1 + include/renderer_gl/renderer_gl.hpp | 9 +- src/core/renderer_gl/renderer_gl.cpp | 140 ++++++++++++++++++++++++--- 3 files changed, 135 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 65eb8d7a..de5ebd86 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ #Clion Files .idea/ +cmake-build-* Debug/ Release/ diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 2891db44..33599769 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -24,7 +24,14 @@ class Renderer { OpenGL::VertexBuffer vbo; GLint alphaControlLoc = -1; GLint texUnitConfigLoc = -1; - + + // TEV configuration uniform locations + GLint textureEnv0SourceLoc = -1; + GLint textureEnv0OperandLoc = -1; + GLint textureEnv0CombinerLoc = -1; + GLint textureEnv0ColorLoc = -1; + GLint textureEnv0ScaleLoc = -1; + // Depth configuration uniform locations GLint depthOffsetLoc = -1; GLint depthScaleLoc = -1; diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index a5e34923..23c27329 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -38,6 +38,14 @@ const char* fragmentShader = R"( uniform uint u_alphaControl; uniform uint u_textureConfig; + // TEV uniforms + // TODO: use arrays to handle TEVs 1-3. + uniform uint u_textureEnv0Source; + uniform uint u_textureEnv0Operand; + uniform uint u_textureEnv0Combiner; + uniform vec4 u_textureEnv0Color; // TODO: -> Colour + uniform uint u_textureEnv0Scale; + // Depth control uniforms uniform float u_depthScale; uniform float u_depthOffset; @@ -45,6 +53,88 @@ const char* fragmentShader = R"( uniform sampler2D u_tex0; + vec4 tev_evaluate_source(int src_id) { + vec4 source = vec4(1.0); + + uint rgb_source = (u_textureEnv0Source >> (src_id * 4)) & 15u; + uint alpha_source = (u_textureEnv0Source >> (16 + src_id * 4)) & 15u; + + // TODO: get rid of redundant texture fetches during TEV evaluation. + + switch (rgb_source) { + case 0u: source.rgb = colour.rgb; break; // Primary color, TODO: confirm that this is correct + case 3u: source.rgb = texture(u_tex0, tex0_UVs).rgb; break; // Texture 0 + case 14u: source.rgb = u_textureEnv0Color.rgb; break; // Constant (GPUREG_TEXENVi_COLOR) + default: break; // TODO: implement remaining sources + } + + switch (alpha_source) { + case 0u: source.a = colour.a; break; // Primary color, TODO: confirm that this is correct + case 3u: source.a = texture(u_tex0, tex0_UVs).a; break; // Texture 0 + case 14u: source.a = u_textureEnv0Color.a; break; // Constant (GPUREG_TEXENVi_COLOR) + default: break; // TODO: implement remaining sources + } + + uint rgb_operand = (u_textureEnv0Operand >> (src_id * 4)) & 15u; + uint alpha_operand = (u_textureEnv0Operand >> (12 + src_id * 4)) & 7u; + + vec4 final; + + switch (rgb_operand) { + case 0u: final.rgb = source.rgb; break; // Source color + case 1u: final.rgb = 1.0 - source.rgb; break; // One minus source color + case 2u: final.rgb = vec3(source.a); break; // Source alpha + case 3u: final.rgb = vec3(1.0 - source.a); break; // One minus source alpha + case 4u: final.rgb = vec3(source.r); break; // Source red + case 5u: final.rgb = vec3(1.0 - source.r); break; // One minus source red + case 8u: final.rgb = vec3(source.g); break; // Source green + case 9u: final.rgb = vec3(1.0 - source.g); break; // One minus source green + case 12u: final.rgb = vec3(source.b); break; // Source blue + case 13u: final.rgb = vec3(1.0 - source.b); break; // One minus source blue + default: break; // TODO: figure out what the undocumented values do + } + + switch (alpha_operand) { + case 0u: final.a = source.a; break; // Source alpha + case 1u: final.a = 1.0 - source.a; break; // One minus source alpha + case 2u: final.a = source.r; break; // Source red + case 3u: final.a = 1.0 - source.r; break; // One minus source red + case 4u: final.a = source.g; break; // Source green + case 5u: final.a = 1.0 - source.g; break; // One minus source green + case 6u: final.a = source.b; break; // Source blue + case 7u: final.a = 1.0 - source.b; break; // One minus source blue + } + + // TODO: respect GPUREG_TEXENVi_SCALE + + return final; + } + + vec4 tev_combine() { + vec4 source0 = tev_evaluate_source(0); + vec4 source1 = tev_evaluate_source(1); + vec4 source2 = tev_evaluate_source(2); + + uint rgb_combine = u_textureEnv0Combiner & 15u; + uint alpha_combine = (u_textureEnv0Combiner >> 16) & 15u; + + vec4 result = vec4(1.0); + + // TODO: figure out how most of the combine modes work. + + switch (rgb_combine) { + case 1u: result.rgb = source0.rgb * source1.rgb * source2.rgb; break; // Modulate + default: return vec4(1.0, 0.0, 0.0, 1.0); + } + + switch (alpha_combine) { + case 1u: result.a = source0.a * source1.a * source2.a; break; // Modulate + default: return vec4(1.0, 0.0, 0.0, 1.0); + } + + return result; + } + void main() { if ((u_textureConfig & 1u) != 0) { // Render texture 0 if enabled fragColour = texture(u_tex0, tex0_UVs); @@ -70,28 +160,28 @@ const char* fragmentShader = R"( switch (func) { case 0: discard; // Never pass alpha test - case 1: break; // Always pass alpha test - case 2: // Pass if equal + case 1: break; // Always pass alpha test + case 2: // Pass if equal if (alpha != reference) discard; break; - case 3: // Pass if not equal + case 3: // Pass if not equal if (alpha == reference) discard; break; - case 4: // Pass if less than + case 4: // Pass if less than if (alpha >= reference) discard; break; - case 5: // Pass if less than or equal + case 5: // Pass if less than or equal if (alpha > reference) discard; break; - case 6: // Pass if greater than + case 6: // Pass if greater than if (alpha <= reference) discard; break; - case 7: // Pass if greater than or equal + case 7: // Pass if greater than or equal if (alpha < reference) discard; break; @@ -106,11 +196,11 @@ const char* displayVertexShader = R"( void main() { const vec4 positions[4] = vec4[]( - vec4(-1.0, 1.0, 1.0, 1.0), // Top-left - vec4(1.0, 1.0, 1.0, 1.0), // Top-right - vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left - vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right - ); + vec4(-1.0, 1.0, 1.0, 1.0), // Top-left + vec4(1.0, 1.0, 1.0, 1.0), // Top-right + vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left + vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right + ); // The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise // So we adjust our texcoords accordingly @@ -119,10 +209,10 @@ const char* displayVertexShader = R"( vec2(1.0, 0.0), // Bottom-right vec2(0.0, 1.0), // Top-left vec2(0.0, 0.0) // Bottom-left - ); + ); gl_Position = positions[gl_VertexID]; - UV = texcoords[gl_VertexID]; + UV = texcoords[gl_VertexID]; } )"; @@ -179,6 +269,12 @@ void Renderer::initGraphicsContext() { alphaControlLoc = OpenGL::uniformLocation(triangleProgram, "u_alphaControl"); texUnitConfigLoc = OpenGL::uniformLocation(triangleProgram, "u_textureConfig"); + textureEnv0SourceLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnv0Source"); + textureEnv0OperandLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnv0Operand"); + textureEnv0CombinerLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnv0Combiner"); + textureEnv0ColorLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnv0Color"); + textureEnv0ScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnv0Scale"); + depthScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_depthScale"); depthOffsetLoc = OpenGL::uniformLocation(triangleProgram, "u_depthOffset"); depthmapEnableLoc = OpenGL::uniformLocation(triangleProgram, "u_depthmapEnable"); @@ -331,6 +427,22 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c glUniform1i(depthmapEnableLoc, depthMapEnable); } + // TODO: only update TEV uniforms when the configuration changes. + // TODO: define registers in the PICAInternalRegs enumeration + glUniform1ui(textureEnv0SourceLoc, regs[0xc0]); + glUniform1ui(textureEnv0OperandLoc, regs[0xc1]); + glUniform1ui(textureEnv0CombinerLoc, regs[0xc2]); + { + const u32 rgba = regs[0xc3]; + const float r = (float)(rgba & 0xff) / 255.0f; + const float g = (float)((rgba >> 8) & 0xff) / 255.0f; + const float b = (float)((rgba >> 16) & 0xff) / 255.0f; + const float a = (float)(rgba >> 24) / 255.0f; + + glUniform4f(textureEnv0ColorLoc, r, g, b, a); + } + glUniform1ui(textureEnv0ScaleLoc, regs[0xc4]); + // Hack for rendering texture 1 if (regs[0x80] & 1) { const u32 dim = regs[0x82];