mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-12 09:09:47 +12:00
Boops
This commit is contained in:
parent
78ac8d2c0d
commit
ab4c9b2ae5
7 changed files with 100 additions and 65 deletions
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
19
third_party/opengl/opengl.hpp
vendored
19
third_party/opengl/opengl.hpp
vendored
|
@ -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); }
|
||||||
|
|
Loading…
Add table
Reference in a new issue