diff --git a/meson.build b/meson.build index 7fc2c5d8..17584f92 100644 --- a/meson.build +++ b/meson.build @@ -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, diff --git a/src/renderer/gl33/gl33.c b/src/renderer/gl33/gl33.c index c811f6a8..5d754798 100644 --- a/src/renderer/gl33/gl33.c +++ b/src/renderer/gl33/gl33.c @@ -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, } }, diff --git a/src/renderer/glcommon/meson.build b/src/renderer/glcommon/meson.build index 2f6b5c05..7fe9ebc6 100644 --- a/src/renderer/glcommon/meson.build +++ b/src/renderer/glcommon/meson.build @@ -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 diff --git a/src/renderer/glcommon/vtable.h b/src/renderer/glcommon/vtable.h index d87224d7..fe6949bd 100644 --- a/src/renderer/glcommon/vtable.h +++ b/src/renderer/glcommon/vtable.h @@ -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) diff --git a/src/renderer/glescommon/angle_egl.c b/src/renderer/glescommon/angle_egl.c new file mode 100644 index 00000000..9c88fc3f --- /dev/null +++ b/src/renderer/glescommon/angle_egl.c @@ -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 . + * Copyright (c) 2012-2019, Andrei Alexeyev . +*/ + +#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; +} + diff --git a/src/renderer/glescommon/angle_egl.h b/src/renderer/glescommon/angle_egl.h new file mode 100644 index 00000000..c00bd3af --- /dev/null +++ b/src/renderer/glescommon/angle_egl.h @@ -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 . + * Copyright (c) 2012-2019, Andrei Alexeyev . +*/ + +#pragma once +#include "taisei.h" + +SDL_GLContext gles_create_context_angle(SDL_Window *window, int major, int minor, bool webgl); diff --git a/src/renderer/glescommon/angle_egl_stub.c b/src/renderer/glescommon/angle_egl_stub.c new file mode 100644 index 00000000..20aa4b7b --- /dev/null +++ b/src/renderer/glescommon/angle_egl_stub.c @@ -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 . + * Copyright (c) 2012-2019, Andrei Alexeyev . + */ + +#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"); +} diff --git a/src/renderer/glescommon/gles.c b/src/renderer/glescommon/gles.c index d7b7fcd8..b5e76b37 100644 --- a/src/renderer/glescommon/gles.c +++ b/src/renderer/glescommon/gles.c @@ -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); diff --git a/src/renderer/glescommon/meson.build b/src/renderer/glescommon/meson.build index 1779d353..071ce699 100644 --- a/src/renderer/glescommon/meson.build +++ b/src/renderer/glescommon/meson.build @@ -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 diff --git a/subprojects/glad/meson.build b/subprojects/glad/meson.build index dfd8f7af..440e1729 100644 --- a/subprojects/glad/meson.build +++ b/subprojects/glad/meson.build @@ -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() diff --git a/subprojects/glad/src/meson.build b/subprojects/glad/src/meson.build deleted file mode 100644 index 00fb1e7d..00000000 --- a/subprojects/glad/src/meson.build +++ /dev/null @@ -1,5 +0,0 @@ - -glad_sources += files( - 'gl.c', - 'egl.c', -)