diff --git a/CMakeLists.txt b/CMakeLists.txt index c9795d86..d34200f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION endif() if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) + set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() project(Alber) diff --git a/include/renderer_gl/async_compiler.hpp b/include/renderer_gl/async_compiler.hpp index 8786b0c7..0f3bd055 100644 --- a/include/renderer_gl/async_compiler.hpp +++ b/include/renderer_gl/async_compiler.hpp @@ -40,7 +40,8 @@ namespace PICA struct AsyncCompilerState { // 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) // and then clean up the queues @@ -60,9 +61,6 @@ struct AsyncCompilerState // Manually stops the thread void Stop(); private: - void createGLContext(); - void destroyGLContext(); - PICA::ShaderGen::FragmentGenerator& fragShaderGen; OpenGL::Shader defaultShadergenVs; @@ -72,4 +70,6 @@ private: lockfree::spsc::Queue compiledQueue; std::atomic_bool running = false; std::thread shaderCompilationThread; + + void* contextCreationUserdata; }; \ No newline at end of file diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 0a60a834..9395648c 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -26,7 +26,7 @@ class GPU; // Cached recompiled fragment shader struct CachedProgram { OpenGL::Program program; - uint uboBinding; + GLuint uboBinding = 0; bool ready = false; }; @@ -79,10 +79,7 @@ class RendererGL final : public Renderer { OpenGL::Shader defaultShadergenVs; GLuint shadergenFragmentUBO; - // Cached recompiled fragment shader - struct CachedProgram { - OpenGL::Program program; - }; + void* contextCreationUserdata = nullptr; std::unordered_map shaderCache; std::unique_ptr asyncCompiler; diff --git a/src/core/renderer_gl/async_compiler.cpp b/src/core/renderer_gl/async_compiler.cpp index cc3dbb36..41e311fe 100644 --- a/src/core/renderer_gl/async_compiler.cpp +++ b/src/core/renderer_gl/async_compiler.cpp @@ -4,8 +4,14 @@ #include "glad/gl.h" #include "opengl.hpp" -AsyncCompilerState::AsyncCompilerState(PICA::ShaderGen::FragmentGenerator& fragShaderGenRef) - : fragShaderGen(fragShaderGenRef) +namespace Frontend::AsyncCompiler { + 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(); } @@ -33,14 +39,11 @@ bool AsyncCompilerState::PopCompiledProgram(CompiledProgram*& program) return hasItem; } -void AsyncCompilerState::createGLContext() { - // TODO: do me -} - void AsyncCompilerState::Start() { - shaderCompilationThread = std::thread([this]() { - createGLContext(); - + void* context = Frontend::AsyncCompiler::createContext(contextCreationUserdata); + 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(); 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)); } - destroyGLContext(); + Frontend::AsyncCompiler::destroyContext(contextCreationUserdata, context); }); } diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 4efab639..4fc2ed7a 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -202,14 +202,19 @@ void RendererGL::initGraphicsContextInternal() { if (shaderMode == ShaderMode::Hybrid && !asyncCompiler) { - // This will create and start the async compiler thread - asyncCompiler = std::make_unique(fragShaderGen); + if (contextCreationUserdata == nullptr) { + 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(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) // 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 void RendererGL::setupBlending() { @@ -449,6 +454,8 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v OpenGL::Triangle, }; + PICA::FragmentConfig fsConfig(regs); + bool usingUbershader = false; if (shaderMode == ShaderMode::Ubershader) { usingUbershader = true; @@ -461,52 +468,36 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v if (emulatorConfig->forceShadergenForLights && lightsEnabled && lightCount >= emulatorConfig->lightShadergenThreshold) { usingUbershader = false; } - } else { - PICA::FragmentConfig fsConfig(regs); + } else if (shaderMode == ShaderMode::Hybrid) { + CompiledProgram* compiledProgram; - // 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; - // 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; + programEntry.program.createFromBinary(compiledProgram->binary, compiledProgram->binaryFormat); + initializeProgramEntry(gl, programEntry); - 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); - } - } + delete compiledProgram; } - if (shaderCompiled) { - OpenGL::Program& program = getSpecializedShader(fsConfig); - gl.useProgram(program); - } else { - useUbershader = true; + bool contains = shaderCache.contains(fsConfig); + + if (!contains) { + asyncCompiler->PushFragmentConfig(fsConfig); + shaderCache[fsConfig] = {}; + usingUbershader = true; + } else if (!shaderCache[fsConfig].ready) { + usingUbershader = true; } } - if (useUbershader) { + if (usingUbershader) { gl.useProgram(triangleProgram); + } else { + OpenGL::Program& program = getSpecializedShader(fsConfig); + gl.useProgram(program); } const auto primitiveTopology = primTypes[static_cast(primType)]; @@ -534,7 +525,7 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v static constexpr std::array depthModes = {GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL}; // Update ubershader uniforms - if (useUbershader) { + if (usingUbershader) { 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; @@ -1090,10 +1081,6 @@ 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); diff --git a/src/panda_sdl/frontend_sdl.cpp b/src/panda_sdl/frontend_sdl.cpp index 77b1f55f..4c1df3d8 100644 --- a/src/panda_sdl/frontend_sdl.cpp +++ b/src/panda_sdl/frontend_sdl.cpp @@ -1,6 +1,7 @@ #include "panda_sdl/frontend_sdl.hpp" #include +#include "SDL_video.h" FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMappings()) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { @@ -342,3 +343,31 @@ void FrontendSDL::run() { SDL_GL_SwapWindow(window); } } + +namespace Frontend::AsyncCompiler { + void* createContext(void* userdata) { + SDL_Window* window = static_cast(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(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(context)); + } +} \ No newline at end of file diff --git a/third_party/opengl/opengl.hpp b/third_party/opengl/opengl.hpp index 4a08650a..756921a0 100644 --- a/third_party/opengl/opengl.hpp +++ b/third_party/opengl/opengl.hpp @@ -29,6 +29,7 @@ #include #include #include +#include "helpers.hpp" #include @@ -432,6 +433,24 @@ namespace OpenGL { return m_handle != 0; } + bool createFromBinary(const std::vector& 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; } bool exists() const { return m_handle != 0; } void use() const { glUseProgram(m_handle); }