gles: create angle context in webgl mode on windows

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.
This commit is contained in:
Andrei Alexeyev 2023-08-28 15:58:28 +02:00
parent 97e99df9fc
commit cab432f843
No known key found for this signature in database
GPG key ID: 72D26128040B9690
11 changed files with 282 additions and 20 deletions

View file

@ -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,

View file

@ -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();
@ -1537,6 +1543,7 @@ RendererBackend _r_backend_gl33 = {
},
.custom = &(GLBackendData) {
.vtable = {
.create_context = gl33_create_context,
.init_context = gl33_init_context,
}
},

View file

@ -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

View file

@ -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);
@ -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)

View file

@ -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;
}

View file

@ -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);

View file

@ -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");
}

View file

@ -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
@ -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);

View file

@ -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

View file

@ -77,17 +77,26 @@ glad_args = [
glad_sources = []
glad_includes = include_directories('include')
subdir('src')
glad_lib = static_library('glad', glad_sources,
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_dep = declare_dependency(
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_gl_dep = declare_dependency(
include_directories : glad_includes,
link_with : glad_lib
link_with : glad_gl_lib
)
glad_egl_dep = declare_dependency(
include_directories : glad_includes,
link_with : glad_egl_lib
)
if glad_program.found()

View file

@ -1,5 +0,0 @@
glad_sources += files(
'gl.c',
'egl.c',
)