This commit is contained in:
offtkp 2024-07-31 03:16:38 +03:00
parent 78ac8d2c0d
commit ab4c9b2ae5
7 changed files with 100 additions and 65 deletions

View file

@ -14,7 +14,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION
endif() endif()
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release) set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif() endif()
project(Alber) project(Alber)

View file

@ -40,7 +40,8 @@ namespace PICA
struct AsyncCompilerState struct AsyncCompilerState
{ {
// The constructor will start the thread that will compile the shaders and create an OpenGL context // The constructor will start the thread that will compile the shaders and create an OpenGL context
explicit AsyncCompilerState(PICA::ShaderGen::FragmentGenerator& fragShaderGen); // contextCreationUserdata is frontend specific, for example for SDL it's an SDL_Window*
explicit AsyncCompilerState(PICA::ShaderGen::FragmentGenerator& fragShaderGen, void* contextCreationUserdata);
// The destructor will first set the stop flag and join the thread (which will wait until it exits) // The destructor will first set the stop flag and join the thread (which will wait until it exits)
// and then clean up the queues // and then clean up the queues
@ -60,9 +61,6 @@ struct AsyncCompilerState
// Manually stops the thread // Manually stops the thread
void Stop(); void Stop();
private: private:
void createGLContext();
void destroyGLContext();
PICA::ShaderGen::FragmentGenerator& fragShaderGen; PICA::ShaderGen::FragmentGenerator& fragShaderGen;
OpenGL::Shader defaultShadergenVs; OpenGL::Shader defaultShadergenVs;
@ -72,4 +70,6 @@ private:
lockfree::spsc::Queue<CompiledProgram*, maxInFlightShaderCount> compiledQueue; lockfree::spsc::Queue<CompiledProgram*, maxInFlightShaderCount> compiledQueue;
std::atomic_bool running = false; std::atomic_bool running = false;
std::thread shaderCompilationThread; std::thread shaderCompilationThread;
void* contextCreationUserdata;
}; };

View file

@ -26,7 +26,7 @@ class GPU;
// Cached recompiled fragment shader // Cached recompiled fragment shader
struct CachedProgram { struct CachedProgram {
OpenGL::Program program; OpenGL::Program program;
uint uboBinding; GLuint uboBinding = 0;
bool ready = false; bool ready = false;
}; };
@ -79,10 +79,7 @@ class RendererGL final : public Renderer {
OpenGL::Shader defaultShadergenVs; OpenGL::Shader defaultShadergenVs;
GLuint shadergenFragmentUBO; GLuint shadergenFragmentUBO;
// Cached recompiled fragment shader void* contextCreationUserdata = nullptr;
struct CachedProgram {
OpenGL::Program program;
};
std::unordered_map<PICA::FragmentConfig, CachedProgram> shaderCache; std::unordered_map<PICA::FragmentConfig, CachedProgram> shaderCache;
std::unique_ptr<AsyncCompilerState> asyncCompiler; std::unique_ptr<AsyncCompilerState> asyncCompiler;

View file

@ -4,8 +4,14 @@
#include "glad/gl.h" #include "glad/gl.h"
#include "opengl.hpp" #include "opengl.hpp"
AsyncCompilerState::AsyncCompilerState(PICA::ShaderGen::FragmentGenerator& fragShaderGenRef) namespace Frontend::AsyncCompiler {
: fragShaderGen(fragShaderGenRef) void* createContext(void* userdata);
void makeCurrent(void* userdata, void* context);
void destroyContext(void* userdata, void* context);
}
AsyncCompilerState::AsyncCompilerState(PICA::ShaderGen::FragmentGenerator& fragShaderGenRef, void* contextCreationUserdata)
: fragShaderGen(fragShaderGenRef), contextCreationUserdata(contextCreationUserdata)
{ {
Start(); Start();
} }
@ -33,14 +39,11 @@ bool AsyncCompilerState::PopCompiledProgram(CompiledProgram*& program)
return hasItem; return hasItem;
} }
void AsyncCompilerState::createGLContext() {
// TODO: do me
}
void AsyncCompilerState::Start() { void AsyncCompilerState::Start() {
shaderCompilationThread = std::thread([this]() { void* context = Frontend::AsyncCompiler::createContext(contextCreationUserdata);
createGLContext(); shaderCompilationThread = std::thread([this, context]() {
Frontend::AsyncCompiler::makeCurrent(contextCreationUserdata, context);
printf("Async compiler started, version: %s\n", glGetString(GL_VERSION));
std::string defaultShadergenVSSource = fragShaderGen.getDefaultVertexShader(); std::string defaultShadergenVSSource = fragShaderGen.getDefaultVertexShader();
defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex); defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex);
@ -83,7 +86,7 @@ void AsyncCompilerState::Start() {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
destroyGLContext(); Frontend::AsyncCompiler::destroyContext(contextCreationUserdata, context);
}); });
} }

View file

@ -202,14 +202,19 @@ void RendererGL::initGraphicsContextInternal() {
if (shaderMode == ShaderMode::Hybrid && !asyncCompiler) if (shaderMode == ShaderMode::Hybrid && !asyncCompiler)
{ {
// This will create and start the async compiler thread if (contextCreationUserdata == nullptr) {
asyncCompiler = std::make_unique<AsyncCompilerState>(fragShaderGen); Helpers::warn("No context creation userdata provided for some reason");
shaderMode = defaultShaderMode;
} else {
// This will create and start the async compiler thread
asyncCompiler = std::make_unique<AsyncCompilerState>(fragShaderGen, contextCreationUserdata);
}
} }
} }
// The OpenGL renderer doesn't need to do anything with the GL context (For Qt frontend) or the SDL window (For SDL frontend) // The OpenGL renderer doesn't need to do anything with the GL context (For Qt frontend) or the SDL window (For SDL frontend)
// So we just call initGraphicsContextInternal for both // So we just call initGraphicsContextInternal for both
void RendererGL::initGraphicsContext([[maybe_unused]] SDL_Window* window) { initGraphicsContextInternal(); } void RendererGL::initGraphicsContext([[maybe_unused]] SDL_Window* window) { contextCreationUserdata = window; initGraphicsContextInternal(); }
// Set up the OpenGL blending context to match the emulated PICA // Set up the OpenGL blending context to match the emulated PICA
void RendererGL::setupBlending() { void RendererGL::setupBlending() {
@ -449,6 +454,8 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
OpenGL::Triangle, OpenGL::Triangle,
}; };
PICA::FragmentConfig fsConfig(regs);
bool usingUbershader = false; bool usingUbershader = false;
if (shaderMode == ShaderMode::Ubershader) { if (shaderMode == ShaderMode::Ubershader) {
usingUbershader = true; usingUbershader = true;
@ -461,52 +468,36 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
if (emulatorConfig->forceShadergenForLights && lightsEnabled && lightCount >= emulatorConfig->lightShadergenThreshold) { if (emulatorConfig->forceShadergenForLights && lightsEnabled && lightCount >= emulatorConfig->lightShadergenThreshold) {
usingUbershader = false; usingUbershader = false;
} }
} else { } else if (shaderMode == ShaderMode::Hybrid) {
PICA::FragmentConfig fsConfig(regs); CompiledProgram* compiledProgram;
// If shaderMode is Specialized, shaderCompiled is set to true which means getSpecializedShader will // Pop all the queued compiled programs so they can be added to the shader cache
// compile the shader for us if it's not compiled yet while (asyncCompiler->PopCompiledProgram(compiledProgram)) {
bool shaderCompiled = true; CachedProgram& programEntry = shaderCache[compiledProgram->fsConfig];
programEntry.ready = true;
if (shaderMode == ShaderMode::Hybrid) {
CompiledProgram* compiledProgram;
// Pop all the queued compiled programs so they can be added to the shader cache programEntry.program.createFromBinary(compiledProgram->binary, compiledProgram->binaryFormat);
while (asyncCompiler->PopCompiledProgram(compiledProgram)) { initializeProgramEntry(gl, programEntry);
CachedProgram& programEntry = shaderCache[compiledProgram->fsConfig];
programEntry.ready = true;
glProgramBinary(programEntry.program.handle(), compiledProgram->binaryFormat, compiledProgram->binary.data(), compiledProgram->binary.size()); delete compiledProgram;
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) { bool contains = shaderCache.contains(fsConfig);
OpenGL::Program& program = getSpecializedShader(fsConfig);
gl.useProgram(program); if (!contains) {
} else { asyncCompiler->PushFragmentConfig(fsConfig);
useUbershader = true; shaderCache[fsConfig] = {};
usingUbershader = true;
} else if (!shaderCache[fsConfig].ready) {
usingUbershader = true;
} }
} }
if (useUbershader) { if (usingUbershader) {
gl.useProgram(triangleProgram); gl.useProgram(triangleProgram);
} else {
OpenGL::Program& program = getSpecializedShader(fsConfig);
gl.useProgram(program);
} }
const auto primitiveTopology = primTypes[static_cast<usize>(primType)]; const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
@ -534,7 +525,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}; 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 // Update ubershader uniforms
if (useUbershader) { if (usingUbershader) {
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32(); const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
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;
@ -1090,10 +1081,6 @@ void RendererGL::setUbershader(const std::string& shader) {
glUniform1i(ubershaderData.depthmapEnableLoc, oldDepthmapEnable); glUniform1i(ubershaderData.depthmapEnableLoc, oldDepthmapEnable);
} }
void RendererGL::setShaderMode(ShaderMode mode) {
shaderMode = mode;
}
void RendererGL::initUbershader(OpenGL::Program& program) { void RendererGL::initUbershader(OpenGL::Program& program) {
gl.useProgram(program); gl.useProgram(program);

View file

@ -1,6 +1,7 @@
#include "panda_sdl/frontend_sdl.hpp" #include "panda_sdl/frontend_sdl.hpp"
#include <glad/gl.h> #include <glad/gl.h>
#include "SDL_video.h"
FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMappings()) { FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMappings()) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
@ -342,3 +343,31 @@ void FrontendSDL::run() {
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
} }
} }
namespace Frontend::AsyncCompiler {
void* createContext(void* userdata) {
SDL_Window* window = static_cast<SDL_Window*>(userdata);
SDL_GLContext previousContext = SDL_GL_GetCurrentContext();
SDL_GLContext context = SDL_GL_CreateContext(window); // this sets it as current :(
SDL_GL_MakeCurrent(window, previousContext);
if (context == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}
return context;
}
void makeCurrent(void* userdata, void* context) {
SDL_Window* window = static_cast<SDL_Window*>(userdata);
int result = SDL_GL_MakeCurrent(window, context);
if (result < 0) {
Helpers::panic("OpenGL context make current failed: %s", SDL_GetError());
}
}
void destroyContext(void* userdata, void* context) {
SDL_GL_DeleteContext(static_cast<SDL_GLContext>(context));
}
}

View file

@ -29,6 +29,7 @@
#include <string_view> #include <string_view>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "helpers.hpp"
#include <glad/gl.h> #include <glad/gl.h>
@ -432,6 +433,24 @@ namespace OpenGL {
return m_handle != 0; return m_handle != 0;
} }
bool createFromBinary(const std::vector<u8>& binary, GLenum format) {
m_handle = glCreateProgram();
glProgramBinary(m_handle, format, binary.data(), binary.size());
GLint success;
glGetProgramiv(m_handle, GL_LINK_STATUS, &success);
if (!success) {
char buf[4096];
glGetProgramInfoLog(m_handle, 4096, nullptr, buf);
fprintf(stderr, "Failed to link program\nError: %s\n", buf);
glDeleteProgram(m_handle);
m_handle = 0;
}
return m_handle != 0;
}
GLuint handle() const { return m_handle; } GLuint handle() const { return m_handle; }
bool exists() const { return m_handle != 0; } bool exists() const { return m_handle != 0; }
void use() const { glUseProgram(m_handle); } void use() const { glUseProgram(m_handle); }