From c8eb1c1128581d7409464e98c2a672a394737da9 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 15 Jul 2024 04:10:47 +0300 Subject: [PATCH] Shader recompiler: Add UBO --- CMakeLists.txt | 1 + include/PICA/pica_frag_config.hpp | 4 +- include/PICA/pica_frag_uniforms.hpp | 18 +++++++++ include/renderer_gl/renderer_gl.hpp | 7 +++- src/core/PICA/shader_gen_glsl.cpp | 37 ++++++++++++----- src/core/renderer_gl/renderer_gl.cpp | 60 ++++++++++++++++++++++++---- 6 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 include/PICA/pica_frag_uniforms.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 194200f0d..c52ccd511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp + include/PICA/pica_frag_uniforms.hpp ) cmrc_add_resource_library( diff --git a/include/PICA/pica_frag_config.hpp b/include/PICA/pica_frag_config.hpp index c4d46b11a..8352cba20 100644 --- a/include/PICA/pica_frag_config.hpp +++ b/include/PICA/pica_frag_config.hpp @@ -23,13 +23,11 @@ namespace PICA { u32 texUnitConfig; u32 texEnvUpdateBuffer; - // TODO: This should probably be a uniform - u32 texEnvBufferColor; - // There's 6 TEV stages, and each one is configured via 5 word-sized registers std::array tevConfigs; }; + // Config used for identifying unique fragment pipeline configurations struct FragmentConfig { OutputConfig outConfig; TextureConfig texConfig; diff --git a/include/PICA/pica_frag_uniforms.hpp b/include/PICA/pica_frag_uniforms.hpp new file mode 100644 index 000000000..b151ed42f --- /dev/null +++ b/include/PICA/pica_frag_uniforms.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +#include "helpers.hpp" + +namespace PICA { + struct FragmentUniforms { + using vec3 = std::array; + using vec4 = std::array; + static constexpr usize tevStageCount = 6; + + s32 alphaReference; + + alignas(16) vec4 constantColors[tevStageCount]; + alignas(16) vec4 tevBufferColor; + }; +} // namespace PICA \ No newline at end of file diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index b4cf9c6f7..a028bdd32 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -67,7 +67,12 @@ class RendererGL final : public Renderer { OpenGL::Framebuffer screenFramebuffer; OpenGL::Texture blankTexture; - std::unordered_map shaderCache; + // Cached recompiled fragment shader + struct CachedProgram { + OpenGL::Program program; + uint uboBinding; + }; + std::unordered_map shaderCache; OpenGL::Framebuffer getColourFBO(); OpenGL::Texture getTexture(Texture& tex); diff --git a/src/core/PICA/shader_gen_glsl.cpp b/src/core/PICA/shader_gen_glsl.cpp index 0e51ad936..50e9c3def 100644 --- a/src/core/PICA/shader_gen_glsl.cpp +++ b/src/core/PICA/shader_gen_glsl.cpp @@ -38,8 +38,6 @@ std::string FragmentGenerator::getVertexShader(const PICARegs& regs) { out vec2 v_texcoord1; out vec3 v_view; out vec2 v_texcoord2; - flat out vec4 v_textureEnvColor[6]; - flat out vec4 v_textureEnvBufferColor; //out float gl_ClipDistance[2]; @@ -103,8 +101,6 @@ std::string FragmentGenerator::generate(const PICARegs& regs) { in vec2 v_texcoord1; in vec3 v_view; in vec2 v_texcoord2; - flat in vec4 v_textureEnvColor[6]; - flat in vec4 v_textureEnvBufferColor; out vec4 fragColor; uniform sampler2D u_tex0; @@ -115,18 +111,21 @@ std::string FragmentGenerator::generate(const PICARegs& regs) { uniform sampler1DArray u_tex_lighting_lut; #endif - vec4 tevSources[16]; - vec4 tevNextPreviousBuffer; + layout(std140) uniform FragmentUniforms { + int alphaReference; + + vec4 constantColors[6]; + vec4 tevBufferColor; + }; )"; // Emit main function for fragment shader // When not initialized, source 13 is set to vec4(0.0) and 15 is set to the vertex colour ret += R"( void main() { - tevSources[0] = v_colour; - tevSources[13] = vec4(0.0); // Previous buffer colour - tevSources[15] = v_colour; // Previous combiner vec4 combinerOutput = v_colour; // Last TEV output + vec4 previousBuffer = vec4(0.0); // Previous buffer + vec4 tevNextPreviousBuffer = tevBufferColor; )"; ret += R"( @@ -148,7 +147,7 @@ std::string FragmentGenerator::generate(const PICARegs& regs) { ret += "fragColor = combinerOutput;\n"; ret += "}"; // End of main function - ret += "\n\n\n\n\n\n\n\n\n\n\n\n\n"; + ret += "\n\n\n\n\n\n\n\n\n\n"; return ret; } @@ -201,6 +200,22 @@ void FragmentGenerator::compileTEV(std::string& shader, int stage, const PICAReg shader += "combinerOutput = vec4(clamp(outputColor" + std::to_string(stage) + " * " + std::to_string(tev.getColorScale()) + ".0, vec3(0.0), vec3(1.0)), clamp(outputAlpha" + std::to_string(stage) + " * " + std::to_string(tev.getAlphaScale()) + ".0, 0.0, 1.0));\n"; + + shader += "previousBuffer = tevNextPreviousBuffer;\n"; + + // Update the "next previous buffer" if necessary + const u32 textureEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer]; + if (stage < 4) { + // Check whether to update rgb + if ((textureEnvUpdateBuffer & (0x100 << stage))) { + shader += "tevNextPreviousBuffer.rgb = combinerOutput.rgb;\n"; + } + + // And whether to update alpha + if ((textureEnvUpdateBuffer & (0x1000u << stage))) { + shader += "tevNextPreviousBuffer.a = combinerOutput.a;\n"; + } + } } } @@ -308,6 +323,8 @@ void FragmentGenerator::getSource(std::string& shader, TexEnvConfig::Source sour } case TexEnvConfig::Source::Previous: shader += "combinerOutput"; break; + case TexEnvConfig::Source::Constant: shader += "constantColors[" + std::to_string(index) + "]"; break; + case TexEnvConfig::Source::PreviousBuffer: shader += "previousBuffer"; break; default: Helpers::warn("Unimplemented TEV source: %d", static_cast(source)); diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 0b26f0043..aa3bb61ba 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -5,6 +5,7 @@ #include #include "PICA/float_types.hpp" +#include "PICA/pica_frag_uniforms.hpp" #include "PICA/gpu.hpp" #include "PICA/regs.hpp" #include "math_util.hpp" @@ -413,7 +414,7 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32(); const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1; - // Update depth uniforms + // Update ubershader uniforms if (usingUbershader) { if (oldDepthScale != depthScale) { oldDepthScale = depthScale; @@ -429,17 +430,15 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v oldDepthmapEnable = depthMapEnable; glUniform1i(ubershaderData.depthmapEnableLoc, depthMapEnable); } - } - setupTextureEnvState(); - bindTexturesToSlots(); - - if (usingUbershader) { // Upload PICA Registers as a single uniform. The shader needs access to the rasterizer registers (for depth, starting from index 0x48) // The texturing and the fragment lighting registers. Therefore we upload them all in one go to avoid multiple slow uniform updates glUniform1uiv(ubershaderData.picaRegLoc, 0x200 - 0x48, ®s[0x48]); } + setupTextureEnvState(); + bindTexturesToSlots(); + if (gpu.lightingLUTDirty) { updateLightingLUT(); } @@ -778,6 +777,8 @@ std::optional RendererGL::getColourBuffer(u32 addr, PICA::ColorFmt } OpenGL::Program& RendererGL::getSpecializedShader() { + constexpr uint uboBlockBinding = 2; + PICA::FragmentConfig fsConfig; auto& outConfig = fsConfig.outConfig; auto& texConfig = fsConfig.texConfig; @@ -788,7 +789,6 @@ OpenGL::Program& RendererGL::getSpecializedShader() { texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg]; texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer]; - texConfig.texEnvBufferColor = 0; // Set up TEV stages std::memcpy(&texConfig.tevConfigs[0 * 5], ®s[InternalRegs::TexEnv0Source], 5 * sizeof(u32)); @@ -798,7 +798,9 @@ OpenGL::Program& RendererGL::getSpecializedShader() { std::memcpy(&texConfig.tevConfigs[4 * 5], ®s[InternalRegs::TexEnv4Source], 5 * sizeof(u32)); std::memcpy(&texConfig.tevConfigs[5 * 5], ®s[InternalRegs::TexEnv5Source], 5 * sizeof(u32)); - OpenGL::Program& program = shaderCache[fsConfig]; + CachedProgram& programEntry = shaderCache[fsConfig]; + OpenGL::Program& program = programEntry.program; + if (!program.exists()) { std::string vs = fragShaderGen.getVertexShader(regs); std::string fs = fragShaderGen.generate(regs); @@ -814,8 +816,50 @@ OpenGL::Program& RendererGL::getSpecializedShader() { glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1); glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2); glUniform1i(OpenGL::uniformLocation(program, "u_tex_lighting_lut"), 3); + + // Allocate memory for the program UBO + glGenBuffers(1, &programEntry.uboBinding); + glBindBuffer(GL_UNIFORM_BUFFER, programEntry.uboBinding); + glBufferData(GL_UNIFORM_BUFFER, sizeof(PICA::FragmentUniforms), nullptr, GL_DYNAMIC_DRAW); + + // Set up the binding for our UBO. Sadly we can't specify it in the shader like normal people, + // As it's an OpenGL 4.2 feature that MacOS doesn't support... + uint uboIndex = glGetUniformBlockIndex(program.handle(), "FragmentUniforms"); + glUniformBlockBinding(program.handle(), uboIndex, uboBlockBinding); + glBindBufferBase(GL_UNIFORM_BUFFER, uboBlockBinding, programEntry.uboBinding); + } + + // Upload uniform data to our shader's UBO + PICA::FragmentUniforms uniforms; + uniforms.alphaReference = Helpers::getBits<8, 8>(regs[InternalRegs::AlphaTestConfig]); + + // Set up the texenv buffer color + const u32 texEnvBufferColor = regs[InternalRegs::TexEnvBufferColor]; + uniforms.tevBufferColor[0] = float(texEnvBufferColor & 0xFF) / 255.0f; + uniforms.tevBufferColor[1] = float((texEnvBufferColor >> 8) & 0xFF) / 255.0f; + uniforms.tevBufferColor[2] = float((texEnvBufferColor >> 16) & 0xFF) / 255.0f; + uniforms.tevBufferColor[3] = float((texEnvBufferColor >> 24) & 0xFF) / 255.0f; + + // Set up the constant color for the 6 TEV stages + for (int i = 0; i < 6; i++) { + static constexpr std::array ioBases = { + PICA::InternalRegs::TexEnv0Source, PICA::InternalRegs::TexEnv1Source, PICA::InternalRegs::TexEnv2Source, + PICA::InternalRegs::TexEnv3Source, PICA::InternalRegs::TexEnv4Source, PICA::InternalRegs::TexEnv5Source, + }; + + auto& vec = uniforms.constantColors[i]; + u32 base = ioBases[i]; + u32 color = regs[base + 3]; + + vec[0] = float(color & 0xFF) / 255.0f; + vec[1] = float((color >> 8) & 0xFF) / 255.0f; + vec[2] = float((color >> 16) & 0xFF) / 255.0f; + vec[3] = float((color >> 24) & 0xFF) / 255.0f; } + glBindBuffer(GL_UNIFORM_BUFFER, programEntry.uboBinding); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(PICA::FragmentUniforms), &uniforms); + return program; }