Skip to content

Commit

Permalink
Support GLES on desktop
Browse files Browse the repository at this point in the history
  • Loading branch information
wheremyfoodat committed Dec 28, 2024
1 parent 80ccede commit cb8b13e
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 59 deletions.
22 changes: 0 additions & 22 deletions .github/gles.patch
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
diff --git a/src/host_shaders/opengl_display.frag b/src/host_shaders/opengl_display.frag
index 612671c8..1937f711 100644
--- a/src/host_shaders/opengl_display.frag
+++ b/src/host_shaders/opengl_display.frag
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
in vec2 UV;
out vec4 FragColor;

diff --git a/src/host_shaders/opengl_display.vert b/src/host_shaders/opengl_display.vert
index 990e2f80..2e7842ac 100644
--- a/src/host_shaders/opengl_display.vert
+++ b/src/host_shaders/opengl_display.vert
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
out vec2 UV;

void main() {
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
index 9f07df0b..96a35afa 100644
--- a/src/host_shaders/opengl_fragment_shader.frag
Expand Down
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,9 @@ if(ENABLE_OPENGL)

set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp
src/core/renderer_gl/textures.cpp src/core/renderer_gl/etc1.cpp
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag
src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.vert
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert
src/host_shaders/opengl_display.frag src/host_shaders/opengl_es_display.vert
src/host_shaders/opengl_es_display.frag src/host_shaders/opengl_vertex_shader.vert
src/host_shaders/opengl_fragment_shader.frag
)

Expand All @@ -479,8 +480,10 @@ if(ENABLE_OPENGL)
resources_renderer_gl
NAMESPACE RendererGL
WHENCE "src/host_shaders/"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_display.vert"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_es_display.vert"
"src/host_shaders/opengl_es_display.frag"
"src/host_shaders/opengl_vertex_shader.vert"
"src/host_shaders/opengl_fragment_shader.frag"
)
Expand Down
1 change: 0 additions & 1 deletion include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ class Emulator {
~Emulator();

void step();
void render();
void reset(ReloadOption reload);
void runFrame();
// Poll the scheduler for events
Expand Down
4 changes: 4 additions & 0 deletions include/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class Renderer {
virtual std::string getUbershader() { return ""; }
virtual void setUbershader(const std::string& shader) {}

// Only relevant for OpenGL renderer and other OpenGL-based backends (eg software)
// Called to notify the core to use OpenGL ES and not desktop GL
virtual void setupGLES() {}

// This function is called on every draw call before parsing vertex data.
// It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between
// ubershaders and shadergen, and so on.
Expand Down
8 changes: 5 additions & 3 deletions include/renderer_gl/renderer_gl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexArray hwShaderVAO;
OpenGL::VertexBuffer vbo;

// Data
// Data
struct {
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
Expand Down Expand Up @@ -157,6 +157,7 @@ class RendererGL final : public Renderer {
void initGraphicsContextInternal();

void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
void compileDisplayShader();

public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
Expand All @@ -169,14 +170,15 @@ class RendererGL final : public Renderer {
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void deinitGraphicsContext() override;

virtual bool supportsShaderReload() override { return true; }
virtual std::string getUbershader() override;
virtual void setUbershader(const std::string& shader) override;
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) override;

virtual void setupGLES() override;

std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);

// Note: The caller is responsible for deleting the currently bound FBO before calling this
Expand Down
42 changes: 28 additions & 14 deletions src/core/renderer_gl/renderer_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ void RendererGL::reset() {
void RendererGL::initGraphicsContextInternal() {
gl.reset();

#if defined(USING_GLES) || defined(__ANDROID__)
driverInfo.usingGLES = true;
#endif

auto gl_resources = cmrc::RendererGL::get_filesystem();
auto vertexShaderSource = gl_resources.open("opengl_vertex_shader.vert");
auto fragmentShaderSource = gl_resources.open("opengl_fragment_shader.frag");
Expand All @@ -69,16 +65,7 @@ void RendererGL::initGraphicsContextInternal() {
triangleProgram.create({vert, frag});
initUbershader(triangleProgram);

auto displayVertexShaderSource = gl_resources.open("opengl_display.vert");
auto displayFragmentShaderSource = gl_resources.open("opengl_display.frag");

OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex);
OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment);
displayProgram.create({vertDisplay, fragDisplay});

gl.useProgram(displayProgram);
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object

compileDisplayShader();
// Create stream buffers for vertex, index and uniform buffers
static constexpr usize hwIndexBufferSize = 2_MB;
static constexpr usize hwVertexBufferSize = 16_MB;
Expand Down Expand Up @@ -1156,6 +1143,19 @@ void RendererGL::initUbershader(OpenGL::Program& program) {
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
}

void RendererGL::compileDisplayShader() {
auto gl_resources = cmrc::RendererGL::get_filesystem();
auto displayVertexShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.vert") : gl_resources.open("opengl_display.vert");
auto displayFragmentShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.frag") : gl_resources.open("opengl_display.frag");

OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex);
OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment);
displayProgram.create({vertDisplay, fragDisplay});

gl.useProgram(displayProgram);
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
}

void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) {
u32 buffer = 0; // Vertex buffer index for non-fixed attributes
u32 attrCount = 0;
Expand Down Expand Up @@ -1250,4 +1250,18 @@ void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAccele
);
}
}
}

void RendererGL::setupGLES() {
driverInfo.usingGLES = true;

// OpenGL ES hardware is typically way too slow to use the ubershader (eg RPi, mobile phones, handhelds) or has other issues with it.
// So, display a warning and turn them off on OpenGL ES.
if (emulatorConfig->useUbershaders) {
emulatorConfig->useUbershaders = false;
Helpers::warn("Ubershaders enabled on OpenGL ES. This usually results in a worse experience, turning it off...");
}

// Stub out logic operations so that calling them doesn't crash the emulator
glLogicOp = [](GLenum) {};
}
1 change: 0 additions & 1 deletion src/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ std::filesystem::path Emulator::getConfigPath() {
#endif

void Emulator::step() {}
void Emulator::render() {}

// Only resume if a ROM is properly loaded
void Emulator::resume() {
Expand Down
10 changes: 10 additions & 0 deletions src/host_shaders/opengl_es_display.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#version 310 es
precision mediump float;

in vec2 UV;
out vec4 FragColor;

uniform sampler2D u_texture;
void main() {
FragColor = texture(u_texture, UV);
}
25 changes: 25 additions & 0 deletions src/host_shaders/opengl_es_display.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#version 310 es
precision mediump float;

out vec2 UV;

void main() {
const vec4 positions[4] = vec4[](
vec4(-1.0, 1.0, 1.0, 1.0), // Top-left
vec4(1.0, 1.0, 1.0, 1.0), // Top-right
vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left
vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right
);

// The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise
// So we adjust our texcoords accordingly
const vec2 texcoords[4] = vec2[](
vec2(1.0, 1.0), // Top-right
vec2(1.0, 0.0), // Bottom-right
vec2(0.0, 1.0), // Top-left
vec2(0.0, 0.0) // Bottom-left
);

gl_Position = positions[gl_VertexID];
UV = texcoords[gl_VertexID];
}
1 change: 1 addition & 0 deletions src/hydra_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ void HydraCore::resetContext() {
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}
emulator->getRenderer()->setupGLES();
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL init failed");
Expand Down
2 changes: 1 addition & 1 deletion src/jni_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) {
}

__android_log_print(ANDROID_LOG_INFO, "AlberDriver", "OpenGL ES %d.%d", GLVersion.major, GLVersion.minor);
emulator->getRenderer()->setupGLES();
emulator->initGraphicsContext(nullptr);
}

Expand Down Expand Up @@ -153,7 +154,6 @@ int AndroidUtils::openDocument(const char* path, const char* perms) {

jstring uri = env->NewStringUTF(path);
jstring jmode = env->NewStringUTF(perms);

jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode);

env->DeleteLocalRef(uri);
Expand Down
22 changes: 14 additions & 8 deletions src/libretro_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ static retro_input_state_t inputStateCallback;
static retro_hw_render_callback hwRender;
static std::filesystem::path savePath;

static bool screenTouched;
static bool screenTouched = false;
static bool usingGLES = false;

std::unique_ptr<Emulator> emulator;
RendererGL* renderer;
Expand All @@ -35,15 +36,19 @@ static void* getGLProcAddress(const char* name) {
}

static void videoResetContext() {
#ifdef USING_GLES
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL ES init failed");
if (usingGLES) {
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}

emulator->getRenderer()->setupGLES();
}
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL init failed");

else {
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL init failed");
}
}
#endif

emulator->initGraphicsContext(nullptr);
}
Expand Down Expand Up @@ -73,6 +78,7 @@ static bool setHWRender(retro_hw_context_type type) {
hwRender.version_minor = 1;

if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
usingGLES = true;
return true;
}
break;
Expand Down
4 changes: 4 additions & 0 deletions src/panda_qt/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
glContext->MakeCurrent();
glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);

if (glContext->IsGLES()) {
emu->getRenderer()->setupGLES();
}

emu->initGraphicsContext(glContext);
} else if (usingVk) {
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
Expand Down
9 changes: 7 additions & 2 deletions src/panda_qt/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,23 @@ void ScreenWidget::resizeSurface(u32 width, u32 height) {
}

bool ScreenWidget::createGLContext() {
// List of GL context versions we will try. Anything 4.1+ is good
static constexpr std::array<GL::Context::Version, 6> versionsToTry = {
// List of GL context versions we will try. Anything 4.1+ is good for desktop OpenGL, and 3.1+ for OpenGL ES
static constexpr std::array<GL::Context::Version, 8> versionsToTry = {
GL::Context::Version{GL::Context::Profile::Core, 4, 6}, GL::Context::Version{GL::Context::Profile::Core, 4, 5},
GL::Context::Version{GL::Context::Profile::Core, 4, 4}, GL::Context::Version{GL::Context::Profile::Core, 4, 3},
GL::Context::Version{GL::Context::Profile::Core, 4, 2}, GL::Context::Version{GL::Context::Profile::Core, 4, 1},
GL::Context::Version{GL::Context::Profile::ES, 3, 2}, GL::Context::Version{GL::Context::Profile::ES, 3, 1},
};

std::optional<WindowInfo> windowInfo = getWindowInfo();
if (windowInfo.has_value()) {
this->windowInfo = *windowInfo;

glContext = GL::Context::Create(*getWindowInfo(), versionsToTry);
if (glContext == nullptr) {
return false;
}

glContext->DoneCurrent();
}

Expand Down
24 changes: 20 additions & 4 deletions src/panda_sdl/frontend_sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,27 @@ FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMapp

glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}
Helpers::warn("OpenGL context creation failed: %s\nTrying again with OpenGL ES.", SDL_GetError());

// Some low end devices (eg RPi, emulation handhelds) don't support desktop GL, but only OpenGL ES, so fall back to that if GL context
// creation failed
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}

if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed");
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed");
}

emu.getRenderer()->setupGLES();
} else {
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed");
}
}

SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0);
Expand Down

0 comments on commit cb8b13e

Please sign in to comment.