Skip to content

Commit

Permalink
gles: create angle context in webgl mode on windows
Browse files Browse the repository at this point in the history
Cubemaps are broken in ANGLE's D3D11 backend, except in WebGL mode for
some reason. This is a terrible workaround, but it beats fucked up
rendering in stage backgrounds.
  • Loading branch information
Akaricchi committed Aug 28, 2023
1 parent 97e99df commit cab432f
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 19 deletions.
5 changes: 4 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,12 @@ dep_gamemode = dependency('gamemode', required : f
dep_m = cc.find_library('m', required : false)

dep_basisu_transcoder = subproject('basis_universal').get_variable('basisu_transcoder_dep')
dep_glad = subproject('glad').get_variable('glad_dep')
dep_koishi = subproject('koishi').get_variable('koishi_dep')

sub_glad = subproject('glad')
dep_glad_gl = sub_glad.get_variable('glad_gl_dep')
dep_glad_egl = sub_glad.get_variable('glad_egl_dep')

taisei_deps = [
dep_basisu_transcoder,
dep_cglm,
Expand Down
17 changes: 12 additions & 5 deletions src/renderer/gl33/gl33.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,23 +282,29 @@ static void gl41_set_viewport(const FloatRect *vp) {
}
#endif

static void gl33_init_context(SDL_Window *window) {
R.gl_context = SDL_GL_CreateContext(window);
static SDL_GLContext gl33_create_context(SDL_Window *window) {
SDL_GLContext ctx = SDL_GL_CreateContext(window);

int gl_profile;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &gl_profile);

if(!R.gl_context && gl_profile != SDL_GL_CONTEXT_PROFILE_ES) {
if(!ctx && gl_profile != SDL_GL_CONTEXT_PROFILE_ES) {
log_error("Failed to create OpenGL context: %s", SDL_GetError());
log_warn("Attempting to create a fallback context");
SDL_GL_ResetAttributes();
R.gl_context = SDL_GL_CreateContext(window);
ctx = SDL_GL_CreateContext(window);
}

if(!R.gl_context) {
if(!ctx) {
log_fatal("Failed to create OpenGL context: %s", SDL_GetError());
}

return ctx;
}

static void gl33_init_context(SDL_Window *window) {
R.gl_context = GLVT.create_context(window);

glcommon_load_functions();
glcommon_check_capabilities();

Expand Down Expand Up @@ -1537,6 +1543,7 @@ RendererBackend _r_backend_gl33 = {
},
.custom = &(GLBackendData) {
.vtable = {
.create_context = gl33_create_context,
.init_context = gl33_init_context,
}
},
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/glcommon/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ r_glcommon_src = files(
r_glcommon_libdeps = []

if not static_gles30
r_glcommon_libdeps += dep_glad
r_glcommon_libdeps += dep_glad_gl
endif
3 changes: 2 additions & 1 deletion src/renderer/glcommon/vtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
typedef struct GLVTable {
// GLTextureTypeInfo* (*texture_type_info)(TextureType type);
// GLTexFormatCapabilities (*texture_format_caps)(GLenum internal_fmt);
SDL_GLContext (*create_context)(SDL_Window *window);
void (*init_context)(SDL_Window *window);
void (*get_viewport)(FloatRect *vp);
void (*set_viewport)(const FloatRect *vp);
Expand All @@ -25,5 +26,5 @@ typedef struct GLBackendData {
GLVTable vtable;
} GLBackendData;

#define GLVT_OF(backend) (((GLBackendData*)backend.custom)->vtable)
#define GLVT_OF(backend) (((GLBackendData*)(backend).custom)->vtable)
#define GLVT GLVT_OF(_r_backend)
189 changes: 189 additions & 0 deletions src/renderer/glescommon/angle_egl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/

#include "taisei.h"

#include "angle_egl.h"
#include "../glcommon/debug.h"
#include "glad/egl.h"
#include "util.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;
}

12 changes: 12 additions & 0 deletions src/renderer/glescommon/angle_egl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/

#pragma once
#include "taisei.h"

SDL_GLContext gles_create_context_angle(SDL_Window *window, int major, int minor, bool webgl);
16 changes: 16 additions & 0 deletions src/renderer/glescommon/angle_egl_stub.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/

#include "taisei.h"

#include "angle_egl.h"
#include "util.h"

SDL_GLContext gles_create_context_angle(SDL_Window *window, int major, int minor, bool webgl) {
log_fatal("Built without custom ANGLE EGL code path");
}
23 changes: 23 additions & 0 deletions src/renderer/glescommon/gles.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,27 @@
#include "gles.h"
#include "../common/backend.h"
#include "../gl33/gl33.h"
#include "angle_egl.h"

#ifdef _WIN32
// Enable WebGL compatibility mode on Windows, because cubemaps are broken in the D3D11 backend
// except in WebGL mode for some reason.
#define ANGLE_WEBGL_DEFAULT true
#else
#define ANGLE_WEBGL_DEFAULT false
#endif

attr_unused
static SDL_GLContext gles_create_context_webgl(SDL_Window *window) {
int major, minor;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
return gles_create_context_angle(window, major, minor, true);
}

void gles_init(RendererBackend *gles_backend, int major, int minor) {
GLVT_OF(*gles_backend).create_context = GLVT_OF(_r_backend_gl33).create_context;

#ifdef TAISEI_BUILDCONF_HAVE_ANGLE
// Load ANGLE by default by setting up some SDL-specific environment vars.
// These are not overwritten if they are already set in the environment, so
Expand All @@ -37,6 +56,10 @@ void gles_init(RendererBackend *gles_backend, int major, int minor) {
#endif

env_set("SDL_OPENGL_ES_DRIVER", 1, false);

if(env_get("TAISEI_ANGLE_WEBGL", ANGLE_WEBGL_DEFAULT)) {
GLVT_OF(*gles_backend).create_context = gles_create_context_webgl;
}
#endif // TAISEI_BUILDCONF_HAVE_ANGLE

_r_backend_inherit(gles_backend, &_r_backend_gl33);
Expand Down
11 changes: 9 additions & 2 deletions src/renderer/glescommon/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@

r_glescommon_deps = ['gl33'] + r_gl33_deps
r_glescommon_libdeps = r_gl33_libdeps

r_glescommon_src = files(
'gles.c',
)

r_glescommon_deps = ['gl33'] + r_gl33_deps
r_glescommon_libdeps = r_gl33_libdeps
if config.get('TAISEI_BUILDCONF_HAVE_ANGLE', false)
r_glescommon_src += files('angle_egl.c')
r_glescommon_libdeps += dep_glad_egl
else
r_glescommon_src += files('angle_egl_stub.c')
endif
17 changes: 13 additions & 4 deletions subprojects/glad/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,26 @@ glad_args = [
glad_sources = []
glad_includes = include_directories('include')

subdir('src')
glad_gl_lib = static_library('gladGL', files('src/gl.c'),
install : not meson.is_subproject(),
build_by_default : not meson.is_subproject(),
include_directories : glad_includes,
)

glad_lib = static_library('glad', glad_sources,
glad_egl_lib = static_library('gladEGL', files('src/egl.c'),
install : not meson.is_subproject(),
build_by_default : not meson.is_subproject(),
include_directories : glad_includes,
)

glad_dep = declare_dependency(
glad_gl_dep = declare_dependency(
include_directories : glad_includes,
link_with : glad_gl_lib
)

glad_egl_dep = declare_dependency(
include_directories : glad_includes,
link_with : glad_lib
link_with : glad_egl_lib
)

if glad_program.found()
Expand Down
5 changes: 0 additions & 5 deletions subprojects/glad/src/meson.build

This file was deleted.

0 comments on commit cab432f

Please sign in to comment.