190 lines
4.8 KiB
C
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;
|
|
}
|
|
|