taisei/src/renderer/glescommon/angle_egl.c
2024-05-17 14:11:48 +02:00

190 lines
4.8 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "angle_egl.h"
#include "../glcommon/debug.h"
#include "glad/egl.h"
#include "log.h"
#include "util.h"
#include "util/env.h"
/*
* All this garbage here serves one purpose: create a WebGL-compatible ANGLE context.
*
* To do this we should only need to pass one additional attribute to eglCreateContext.
*
* Unfortunately, SDL2's EGL code is totally opaque to the application, so that can't be done in a
* sane way. So we have to sidestep SDL a bit and create a context manually, relying on some
* implementation details.
*
* SDL3 seems to have fixed this problem with some new EGL-related APIs, thankfully.
*
* This code is a bad hack; please don't copy this if you're looking for a proper EGL example.
*/
// EGL_ANGLE_create_context_webgl_compatibility
#define EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE 0x33AC
// EGL_ANGLE_create_context_extensions_enabled
#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
static GLADapiproc egl_load_function(void *libegl, const char *name) {
return SDL_LoadFunction(libegl, name);
}
static const char *egl_error_string(EGLint error) {
#define E(e) \
case e: return #e;
switch(error) {
E(EGL_SUCCESS)
E(EGL_NOT_INITIALIZED)
E(EGL_BAD_ACCESS)
E(EGL_BAD_ALLOC)
E(EGL_BAD_ATTRIBUTE)
E(EGL_BAD_CONTEXT)
E(EGL_BAD_CONFIG)
E(EGL_BAD_CURRENT_SURFACE)
E(EGL_BAD_DISPLAY)
E(EGL_BAD_SURFACE)
E(EGL_BAD_MATCH)
E(EGL_BAD_PARAMETER)
E(EGL_BAD_NATIVE_PIXMAP)
E(EGL_BAD_NATIVE_WINDOW)
E(EGL_CONTEXT_LOST)
}
#undef E
UNREACHABLE;
}
static const char *egl_get_error_string(void) {
return egl_error_string(eglGetError());
}
static EGLConfig get_egl_config(EGLDisplay display, int major_version) {
EGLint attribs[64];
EGLConfig configs[128];
int i = 0;
int colorbits = 8;
attribs[i++] = EGL_RED_SIZE;
attribs[i++] = colorbits;
attribs[i++] = EGL_GREEN_SIZE;
attribs[i++] = colorbits;
attribs[i++] = EGL_BLUE_SIZE;
attribs[i++] = colorbits;
attribs[i++] = EGL_ALPHA_SIZE;
attribs[i++] = 0;
attribs[i++] = EGL_DEPTH_SIZE;
attribs[i++] = 0;
attribs[i++] = EGL_CONFIG_CAVEAT;
attribs[i++] = EGL_NONE;
attribs[i++] = EGL_RENDERABLE_TYPE;
attribs[i++] = (major_version > 2) ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
attribs[i++] = EGL_NONE;
EGLint found_configs;
if(!eglChooseConfig(display, attribs, configs, ARRAY_SIZE(configs), &found_configs)) {
log_fatal("eglChooseConfig failed: %s", egl_get_error_string());
}
if(!found_configs) {
log_fatal("No EGL configs found");
}
log_debug("Found %i configs", found_configs);
int choosen_config = 0;
for(int i = 0; i < found_configs; ++i) {
EGLint r, g, b;
eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &r);
eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &g);
eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &b);
if(r == colorbits && g == colorbits && b == colorbits) {
// just grab the first one that looks like it'll work
choosen_config = i;
break;
}
}
return configs[choosen_config];
}
static EGLDisplay egl_display_from_sdl(SDL_Window *window) {
// this is dumb as hell, but it's the only way
EGLContext ctx = SDL_GL_CreateContext(window);
if(!ctx) {
log_sdl_error(LOG_FATAL, "SDL_GL_CreateContext");
}
EGLDisplay display = eglGetCurrentDisplay();
assert(display != EGL_NO_DISPLAY);
SDL_GL_DeleteContext(ctx);
return display;
}
SDL_GLContext gles_create_context_angle(SDL_Window *window, int major, int minor, bool webgl) {
void *libegl = SDL_LoadObject(NOT_NULL(env_get("SDL_VIDEO_EGL_DRIVER", NULL)));
if(!libegl) {
log_sdl_error(LOG_FATAL, "SDL_LoadObject");
}
gladLoadEGLUserPtr(EGL_NO_DISPLAY, egl_load_function, libegl);
EGLDisplay display = egl_display_from_sdl(window);
gladLoadEGLUserPtr(display, egl_load_function, libegl);
eglBindAPI(EGL_OPENGL_ES_API);
EGLConfig config = get_egl_config(display, major);
EGLint attribs[64];
int i = 0;
attribs[i++] = EGL_CONTEXT_MAJOR_VERSION;
attribs[i++] = major;
attribs[i++] = EGL_CONTEXT_MINOR_VERSION;
attribs[i++] = minor;
attribs[i++] = EGL_CONTEXT_OPENGL_DEBUG;
attribs[i++] = glcommon_debug_requested();
attribs[i++] = EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE;
attribs[i++] = webgl;
attribs[i++] = EGL_EXTENSIONS_ENABLED_ANGLE;
attribs[i++] = EGL_TRUE;
attribs[i++] = EGL_NONE;
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, attribs);
if(context == EGL_NO_CONTEXT) {
log_fatal("eglCreateContext failed: %s", egl_get_error_string());
}
if(SDL_GL_MakeCurrent(window, context)) {
log_sdl_error(LOG_FATAL, "SDL_GL_MakeCurrent");
}
assert(SDL_GL_GetCurrentContext() == context);
SDL_UnloadObject(libegl);
return context;
}