mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Shader recompiler: Add UBO
This commit is contained in:
parent
fe4bbea2ef
commit
c8eb1c1128
6 changed files with 105 additions and 22 deletions
|
@ -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/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/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/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(
|
cmrc_add_resource_library(
|
||||||
|
|
|
@ -23,13 +23,11 @@ namespace PICA {
|
||||||
u32 texUnitConfig;
|
u32 texUnitConfig;
|
||||||
u32 texEnvUpdateBuffer;
|
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
|
// There's 6 TEV stages, and each one is configured via 5 word-sized registers
|
||||||
std::array<u32, 5 * 6> tevConfigs;
|
std::array<u32, 5 * 6> tevConfigs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Config used for identifying unique fragment pipeline configurations
|
||||||
struct FragmentConfig {
|
struct FragmentConfig {
|
||||||
OutputConfig outConfig;
|
OutputConfig outConfig;
|
||||||
TextureConfig texConfig;
|
TextureConfig texConfig;
|
||||||
|
|
18
include/PICA/pica_frag_uniforms.hpp
Normal file
18
include/PICA/pica_frag_uniforms.hpp
Normal file
|
@ -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
|
|
@ -67,7 +67,12 @@ class RendererGL final : public Renderer {
|
||||||
OpenGL::Framebuffer screenFramebuffer;
|
OpenGL::Framebuffer screenFramebuffer;
|
||||||
OpenGL::Texture blankTexture;
|
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::Framebuffer getColourFBO();
|
||||||
OpenGL::Texture getTexture(Texture& tex);
|
OpenGL::Texture getTexture(Texture& tex);
|
||||||
|
|
|
@ -38,8 +38,6 @@ std::string FragmentGenerator::getVertexShader(const PICARegs& regs) {
|
||||||
out vec2 v_texcoord1;
|
out vec2 v_texcoord1;
|
||||||
out vec3 v_view;
|
out vec3 v_view;
|
||||||
out vec2 v_texcoord2;
|
out vec2 v_texcoord2;
|
||||||
flat out vec4 v_textureEnvColor[6];
|
|
||||||
flat out vec4 v_textureEnvBufferColor;
|
|
||||||
|
|
||||||
//out float gl_ClipDistance[2];
|
//out float gl_ClipDistance[2];
|
||||||
|
|
||||||
|
@ -103,8 +101,6 @@ std::string FragmentGenerator::generate(const PICARegs& regs) {
|
||||||
in vec2 v_texcoord1;
|
in vec2 v_texcoord1;
|
||||||
in vec3 v_view;
|
in vec3 v_view;
|
||||||
in vec2 v_texcoord2;
|
in vec2 v_texcoord2;
|
||||||
flat in vec4 v_textureEnvColor[6];
|
|
||||||
flat in vec4 v_textureEnvBufferColor;
|
|
||||||
|
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
uniform sampler2D u_tex0;
|
uniform sampler2D u_tex0;
|
||||||
|
@ -115,18 +111,21 @@ std::string FragmentGenerator::generate(const PICARegs& regs) {
|
||||||
uniform sampler1DArray u_tex_lighting_lut;
|
uniform sampler1DArray u_tex_lighting_lut;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vec4 tevSources[16];
|
layout(std140) uniform FragmentUniforms {
|
||||||
vec4 tevNextPreviousBuffer;
|
int alphaReference;
|
||||||
|
|
||||||
|
vec4 constantColors[6];
|
||||||
|
vec4 tevBufferColor;
|
||||||
|
};
|
||||||
)";
|
)";
|
||||||
|
|
||||||
// Emit main function for fragment shader
|
// 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
|
// When not initialized, source 13 is set to vec4(0.0) and 15 is set to the vertex colour
|
||||||
ret += R"(
|
ret += R"(
|
||||||
void main() {
|
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 combinerOutput = v_colour; // Last TEV output
|
||||||
|
vec4 previousBuffer = vec4(0.0); // Previous buffer
|
||||||
|
vec4 tevNextPreviousBuffer = tevBufferColor;
|
||||||
)";
|
)";
|
||||||
|
|
||||||
ret += R"(
|
ret += R"(
|
||||||
|
@ -148,7 +147,7 @@ std::string FragmentGenerator::generate(const PICARegs& regs) {
|
||||||
|
|
||||||
ret += "fragColor = combinerOutput;\n";
|
ret += "fragColor = combinerOutput;\n";
|
||||||
ret += "}"; // End of main function
|
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;
|
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()) +
|
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, vec3(0.0), vec3(1.0)), clamp(outputAlpha" + std::to_string(stage) + " * " + std::to_string(tev.getAlphaScale()) +
|
||||||
".0, 0.0, 1.0));\n";
|
".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::Previous: shader += "combinerOutput"; break;
|
||||||
|
case TexEnvConfig::Source::Constant: shader += "constantColors[" + std::to_string(index) + "]"; break;
|
||||||
|
case TexEnvConfig::Source::PreviousBuffer: shader += "previousBuffer"; break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Helpers::warn("Unimplemented TEV source: %d", static_cast<int>(source));
|
Helpers::warn("Unimplemented TEV source: %d", static_cast<int>(source));
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cmrc/cmrc.hpp>
|
#include <cmrc/cmrc.hpp>
|
||||||
|
|
||||||
#include "PICA/float_types.hpp"
|
#include "PICA/float_types.hpp"
|
||||||
|
#include "PICA/pica_frag_uniforms.hpp"
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include "math_util.hpp"
|
#include "math_util.hpp"
|
||||||
|
@ -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 float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
||||||
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
|
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
|
||||||
|
|
||||||
// Update depth uniforms
|
// Update ubershader uniforms
|
||||||
if (usingUbershader) {
|
if (usingUbershader) {
|
||||||
if (oldDepthScale != depthScale) {
|
if (oldDepthScale != depthScale) {
|
||||||
oldDepthScale = depthScale;
|
oldDepthScale = depthScale;
|
||||||
|
@ -429,17 +430,15 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
|
||||||
oldDepthmapEnable = depthMapEnable;
|
oldDepthmapEnable = depthMapEnable;
|
||||||
glUniform1i(ubershaderData.depthmapEnableLoc, 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)
|
// 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
|
// 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]);
|
glUniform1uiv(ubershaderData.picaRegLoc, 0x200 - 0x48, ®s[0x48]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupTextureEnvState();
|
||||||
|
bindTexturesToSlots();
|
||||||
|
|
||||||
if (gpu.lightingLUTDirty) {
|
if (gpu.lightingLUTDirty) {
|
||||||
updateLightingLUT();
|
updateLightingLUT();
|
||||||
}
|
}
|
||||||
|
@ -778,6 +777,8 @@ std::optional<ColourBuffer> RendererGL::getColourBuffer(u32 addr, PICA::ColorFmt
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGL::Program& RendererGL::getSpecializedShader() {
|
OpenGL::Program& RendererGL::getSpecializedShader() {
|
||||||
|
constexpr uint uboBlockBinding = 2;
|
||||||
|
|
||||||
PICA::FragmentConfig fsConfig;
|
PICA::FragmentConfig fsConfig;
|
||||||
auto& outConfig = fsConfig.outConfig;
|
auto& outConfig = fsConfig.outConfig;
|
||||||
auto& texConfig = fsConfig.texConfig;
|
auto& texConfig = fsConfig.texConfig;
|
||||||
|
@ -788,7 +789,6 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
|
||||||
|
|
||||||
texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg];
|
texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg];
|
||||||
texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer];
|
texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer];
|
||||||
texConfig.texEnvBufferColor = 0;
|
|
||||||
|
|
||||||
// Set up TEV stages
|
// Set up TEV stages
|
||||||
std::memcpy(&texConfig.tevConfigs[0 * 5], ®s[InternalRegs::TexEnv0Source], 5 * sizeof(u32));
|
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[4 * 5], ®s[InternalRegs::TexEnv4Source], 5 * sizeof(u32));
|
||||||
std::memcpy(&texConfig.tevConfigs[5 * 5], ®s[InternalRegs::TexEnv5Source], 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()) {
|
if (!program.exists()) {
|
||||||
std::string vs = fragShaderGen.getVertexShader(regs);
|
std::string vs = fragShaderGen.getVertexShader(regs);
|
||||||
std::string fs = fragShaderGen.generate(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_tex1"), 1);
|
||||||
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
|
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
|
||||||
glUniform1i(OpenGL::uniformLocation(program, "u_tex_lighting_lut"), 3);
|
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;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue