A kissable commit

Minor fixes
This commit is contained in:
offtkp 2024-07-19 18:49:21 +03:00
parent 1b779cafa1
commit 78ac8d2c0d
10 changed files with 649 additions and 27 deletions

View file

@ -0,0 +1,114 @@
#include "renderer_gl/async_compiler.hpp"
#include "PICA/pica_frag_config.hpp"
#include "PICA/shader_gen.hpp"
#include "glad/gl.h"
#include "opengl.hpp"
AsyncCompilerState::AsyncCompilerState(PICA::ShaderGen::FragmentGenerator& fragShaderGenRef)
: fragShaderGen(fragShaderGenRef)
{
Start();
}
AsyncCompilerState::~AsyncCompilerState()
{
Stop();
}
bool AsyncCompilerState::PushFragmentConfig(const PICA::FragmentConfig& config)
{
PICA::FragmentConfig* newConfig = new PICA::FragmentConfig(config);
bool pushedSuccessfully = configQueue.Push(newConfig);
if (!pushedSuccessfully) {
Helpers::panic("Hlep we managed to fill the shader queue");
}
return pushedSuccessfully;
}
bool AsyncCompilerState::PopCompiledProgram(CompiledProgram*& program)
{
bool hasItem = compiledQueue.Pop(program);
return hasItem;
}
void AsyncCompilerState::createGLContext() {
// TODO: do me
}
void AsyncCompilerState::Start() {
shaderCompilationThread = std::thread([this]() {
createGLContext();
std::string defaultShadergenVSSource = fragShaderGen.getDefaultVertexShader();
defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex);
running = true;
while (running.load(std::memory_order_relaxed)) {
PICA::FragmentConfig* fsConfig;
if (configQueue.Pop(fsConfig)) {
OpenGL::Program glProgram;
std::string fs = fragShaderGen.generate(*fsConfig);
OpenGL::Shader fragShader({fs.c_str(), fs.size()}, OpenGL::Fragment);
glProgram.create({defaultShadergenVs, fragShader});
CompiledProgram* program = new CompiledProgram(*fsConfig);
GLint size;
glGetProgramiv(glProgram.handle(), GL_PROGRAM_BINARY_LENGTH, &size);
if (size == 0) {
Helpers::panic("Failed to get program binary size");
}
program->binary.resize(size);
GLint bytesWritten;
glGetProgramBinary(glProgram.handle(), size, &bytesWritten, &program->binaryFormat, program->binary.data());
if (bytesWritten != size || bytesWritten == 0) {
Helpers::panic("Failed to get program binary");
}
delete fsConfig;
bool pushedSuccessfully = compiledQueue.Push(program);
if (!pushedSuccessfully) {
Helpers::panic("Hlep we managed to fill the shader queue");
}
}
// Sleep for a bit to avoid excessive CPU usage
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
destroyGLContext();
});
}
void AsyncCompilerState::Stop() {
running = false;
shaderCompilationThread.join();
bool hasItem = false;
do
{
CompiledProgram* program;
hasItem = compiledQueue.Pop(program);
if (hasItem)
{
delete program;
}
} while (hasItem);
do
{
PICA::FragmentConfig* config;
hasItem = configQueue.Pop(config);
if (hasItem)
{
delete config;
}
} while (hasItem);
}

View file

@ -6,17 +6,46 @@
#include "config.hpp"
#include "PICA/float_types.hpp"
#include "PICA/pica_frag_config.hpp"
#include "PICA/pica_frag_uniforms.hpp"
#include "PICA/gpu.hpp"
#include "PICA/regs.hpp"
#include "math_util.hpp"
#include "opengl.hpp"
#include "renderer_gl/async_compiler.hpp"
#include "renderer_gl/gl_state.hpp"
CMRC_DECLARE(RendererGL);
using namespace Floats;
using namespace Helpers;
using namespace PICA;
namespace {
constexpr uint uboBlockBinding = 2;
void initializeProgramEntry(GLStateManager& gl, CachedProgram& programEntry) {
OpenGL::Program& program = programEntry.program;
// Init sampler objects. Texture 0 goes in texture unit 0, texture 1 in TU 1, texture 2 in TU 2, and the light maps go in TU 3
glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0);
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);
gl.bindUBO(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);
}
}
RendererGL::~RendererGL() {}
void RendererGL::reset() {
@ -170,6 +199,12 @@ void RendererGL::initGraphicsContextInternal() {
// Initialize the default vertex shader used with shadergen
std::string defaultShadergenVSSource = fragShaderGen.getDefaultVertexShader();
defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex);
if (shaderMode == ShaderMode::Hybrid && !asyncCompiler)
{
// This will create and start the async compiler thread
asyncCompiler = std::make_unique<AsyncCompilerState>(fragShaderGen);
}
}
// The OpenGL renderer doesn't need to do anything with the GL context (For Qt frontend) or the SDL window (For SDL frontend)
@ -426,13 +461,52 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
if (emulatorConfig->forceShadergenForLights && lightsEnabled && lightCount >= emulatorConfig->lightShadergenThreshold) {
usingUbershader = false;
}
}
if (usingUbershader) {
gl.useProgram(triangleProgram);
} else {
OpenGL::Program& program = getSpecializedShader();
gl.useProgram(program);
PICA::FragmentConfig fsConfig(regs);
// If shaderMode is Specialized, shaderCompiled is set to true which means getSpecializedShader will
// compile the shader for us if it's not compiled yet
bool shaderCompiled = true;
if (shaderMode == ShaderMode::Hybrid) {
CompiledProgram* compiledProgram;
// Pop all the queued compiled programs so they can be added to the shader cache
while (asyncCompiler->PopCompiledProgram(compiledProgram)) {
CachedProgram& programEntry = shaderCache[compiledProgram->fsConfig];
programEntry.ready = true;
glProgramBinary(programEntry.program.handle(), compiledProgram->binaryFormat, compiledProgram->binary.data(), compiledProgram->binary.size());
initializeProgramEntry(gl, programEntry);
delete compiledProgram;
}
bool contains = shaderCache.contains(fsConfig);
shaderCompiled = contains && shaderCache[fsConfig].ready;
if (!shaderCompiled) {
gl.useProgram(triangleProgram);
if (!contains) {
// Adds an empty shader to the shader cache and sets it to false
// This will prevent queueing the same shader multiple times
shaderCache[fsConfig].ready = false;
asyncCompiler->PushFragmentConfig(fsConfig);
}
}
}
if (shaderCompiled) {
OpenGL::Program& program = getSpecializedShader(fsConfig);
gl.useProgram(program);
} else {
useUbershader = true;
}
}
if (useUbershader) {
gl.useProgram(triangleProgram);
}
const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
@ -460,7 +534,7 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
static constexpr std::array<GLenum, 8> depthModes = {GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL};
// Update ubershader uniforms
if (usingUbershader) {
if (useUbershader) {
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
@ -837,15 +911,16 @@ std::optional<ColourBuffer> RendererGL::getColourBuffer(u32 addr, PICA::ColorFmt
return colourBufferCache.add(sampleBuffer);
}
OpenGL::Program& RendererGL::getSpecializedShader() {
constexpr uint uboBlockBinding = 2;
PICA::FragmentConfig fsConfig(regs);
OpenGL::Program& RendererGL::getSpecializedShader(const PICA::FragmentConfig& fsConfig) {
CachedProgram& programEntry = shaderCache[fsConfig];
OpenGL::Program& program = programEntry.program;
if (!program.exists()) {
if (shaderMode == ShaderMode::Hybrid) {
// If the shader mode is hybrid, we shouldn't reach this point
Helpers::panic("Trying to compile specialized shader from main thread in hybrid mode");
}
std::string fs = fragShaderGen.generate(fsConfig);
OpenGL::Shader fragShader({fs.c_str(), fs.size()}, OpenGL::Fragment);
@ -854,16 +929,7 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
fragShader.free();
// Init sampler objects. Texture 0 goes in texture unit 0, texture 1 in TU 1, texture 2 in TU 2, and the light maps go in TU 3
glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0);
glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1);
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
// 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);
initializeProgramEntry(gl, programEntry);
}
glBindBufferBase(GL_UNIFORM_BUFFER, uboBlockBinding, shadergenFragmentUBO);
@ -995,6 +1061,7 @@ void RendererGL::deinitGraphicsContext() {
depthBufferCache.reset();
colourBufferCache.reset();
clearShaderCache();
if (asyncCompiler) asyncCompiler->Stop();
// All other GL objects should be invalidated automatically and be recreated by the next call to initGraphicsContext
// TODO: Make it so that depth and colour buffers get written back to 3DS memory
@ -1023,6 +1090,10 @@ void RendererGL::setUbershader(const std::string& shader) {
glUniform1i(ubershaderData.depthmapEnableLoc, oldDepthmapEnable);
}
void RendererGL::setShaderMode(ShaderMode mode) {
shaderMode = mode;
}
void RendererGL::initUbershader(OpenGL::Program& program) {
gl.useProgram(program);