Skip to content

Commit

Permalink
Shader recompiler: Add UBO
Browse files Browse the repository at this point in the history
  • Loading branch information
wheremyfoodat committed Jul 15, 2024
1 parent fe4bbea commit c8eb1c1
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 22 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 1 addition & 3 deletions include/PICA/pica_frag_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32, 5 * 6> tevConfigs;
};

// Config used for identifying unique fragment pipeline configurations
struct FragmentConfig {
OutputConfig outConfig;
TextureConfig texConfig;
Expand Down
18 changes: 18 additions & 0 deletions include/PICA/pica_frag_uniforms.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#include <array>
#include <type_traits>

#include "helpers.hpp"

namespace PICA {
struct FragmentUniforms {
using vec3 = std::array<float, 3>;
using vec4 = std::array<float, 4>;
static constexpr usize tevStageCount = 6;

s32 alphaReference;

alignas(16) vec4 constantColors[tevStageCount];
alignas(16) vec4 tevBufferColor;
};
} // namespace PICA
7 changes: 6 additions & 1 deletion include/renderer_gl/renderer_gl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ class RendererGL final : public Renderer {
OpenGL::Framebuffer screenFramebuffer;
OpenGL::Texture blankTexture;

std::unordered_map<PICA::FragmentConfig, OpenGL::Program> shaderCache;
// Cached recompiled fragment shader
struct CachedProgram {
OpenGL::Program program;
uint uboBinding;
};
std::unordered_map<PICA::FragmentConfig, CachedProgram> shaderCache;

OpenGL::Framebuffer getColourFBO();
OpenGL::Texture getTexture(Texture& tex);
Expand Down
37 changes: 27 additions & 10 deletions src/core/PICA/shader_gen_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand All @@ -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"(
Expand All @@ -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;
}
Expand Down Expand Up @@ -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";
}
}
}
}

Expand Down Expand Up @@ -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<int>(source));
Expand Down
60 changes: 52 additions & 8 deletions src/core/renderer_gl/renderer_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cmrc/cmrc.hpp>

#include "PICA/float_types.hpp"
#include "PICA/pica_frag_uniforms.hpp"
#include "PICA/gpu.hpp"
#include "PICA/regs.hpp"
#include "math_util.hpp"
Expand Down Expand Up @@ -413,7 +414,7 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> 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;
Expand All @@ -429,17 +430,15 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> 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, &regs[0x48]);
}

setupTextureEnvState();
bindTexturesToSlots();

if (gpu.lightingLUTDirty) {
updateLightingLUT();
}
Expand Down Expand Up @@ -778,6 +777,8 @@ std::optional<ColourBuffer> 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;
Expand All @@ -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], &regs[InternalRegs::TexEnv0Source], 5 * sizeof(u32));
Expand All @@ -798,7 +798,9 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
std::memcpy(&texConfig.tevConfigs[4 * 5], &regs[InternalRegs::TexEnv4Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[5 * 5], &regs[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);
Expand All @@ -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<u32, 6> 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;
}

Expand Down

0 comments on commit c8eb1c1

Please sign in to comment.