Browse Source

Merge pull request #6142 from Daft-Freak/em-webgl-selector-hint

emscripten: use webgl APIs directly and add canvas selector hint
Ryan C. Gordon 2 years ago
parent
commit
a5e9d9bd7e

+ 9 - 0
include/SDL_hints.h

@@ -363,6 +363,15 @@ extern "C" {
  */
 #define SDL_HINT_EMSCRIPTEN_ASYNCIFY   "SDL_EMSCRIPTEN_ASYNCIFY"
 
+/**
+ *  \brief Specify the CSS selector used for the "default" window/canvas
+ *
+ * This hint only applies to the emscripten platform
+ *
+ * The default value is "#canvas"
+ */
+#define SDL_HINT_EMSCRIPTEN_CANVAS_SELECTOR "SDL_EMSCRIPTEN_CANVAS_SELECTOR"
+
 /**
  *  \brief override the binding element for keyboard inputs for Emscripten builds
  *

+ 7 - 4
src/video/emscripten/SDL_emscriptenframebuffer.c

@@ -75,12 +75,15 @@ int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rec
         var w = $0;
         var h = $1;
         var pixels = $2;
+        var canvasId = UTF8ToString($3);
+        var canvas = document.querySelector(canvasId);
 
+        //TODO: this should store a context per canvas
         if (!Module['SDL3']) Module['SDL3'] = {};
         var SDL3 = Module['SDL3'];
-        if (SDL3.ctxCanvas !== Module['canvas']) {
-            SDL3.ctx = Module['createContext'](Module['canvas'], false, true);
-            SDL3.ctxCanvas = Module['canvas'];
+        if (SDL3.ctxCanvas !== canvas) {
+            SDL3.ctx = Module['createContext'](canvas, false, true);
+            SDL3.ctxCanvas = canvas;
         }
         if (SDL3.w !== w || SDL3.h !== h || SDL3.imageCtx !== SDL3.ctx) {
             SDL3.image = SDL3.ctx.createImageData(w, h);
@@ -156,7 +159,7 @@ int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rec
         }
 
         SDL3.ctx.putImageData(SDL3.image, 0, 0);
-    }, surface->w, surface->h, surface->pixels);
+    }, surface->w, surface->h, surface->pixels, data->canvas_id);
 
     if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, SDL_TRUE)) {
         /* give back control to browser for screen refresh */

+ 116 - 48
src/video/emscripten/SDL_emscriptenopengles.c

@@ -20,83 +20,151 @@
 */
 #include "../../SDL_internal.h"
 
-#if SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
 
 #include <emscripten/emscripten.h>
+#include <emscripten/html5_webgl.h>
 #include <GLES2/gl2.h>
 
 #include "SDL_emscriptenvideo.h"
 #include "SDL_emscriptenopengles.h"
 #include "SDL_hints.h"
 
-#define LOAD_FUNC(NAME) _this->egl_data->NAME = NAME;
 
-/* EGL implementation of SDL OpenGL support */
+int
+Emscripten_GLES_LoadLibrary(_THIS, const char *path)
+{
+    return 0;
+}
+
+void
+Emscripten_GLES_UnloadLibrary(_THIS)
+{
+}
+
+void *
+Emscripten_GLES_GetProcAddress(_THIS, const char *proc)
+{
+    return emscripten_webgl_get_proc_address(proc);
+}
 
 int
-Emscripten_GLES_LoadLibrary(_THIS, const char *path) {
-    /*we can't load EGL dynamically*/
-    _this->egl_data = (struct SDL_EGL_VideoData *) SDL_calloc(1, sizeof(SDL_EGL_VideoData));
-    if (!_this->egl_data) {
-        return SDL_OutOfMemory();
+Emscripten_GLES_SetSwapInterval(_THIS, int interval)
+{
+    if (interval < 0) {
+        return SDL_SetError("Late swap tearing currently unsupported");
+    } else if(interval == 0) {
+        emscripten_set_main_loop_timing(EM_TIMING_SETTIMEOUT, 0);
+    } else {
+        emscripten_set_main_loop_timing(EM_TIMING_RAF, interval);
     }
 
-    /* Emscripten forces you to manually cast eglGetProcAddress to the real
-       function type; grep for "__eglMustCastToProperFunctionPointerType" in
-       Emscripten's egl.h for details. */
-    _this->egl_data->eglGetProcAddress = (void *(EGLAPIENTRY *)(const char *)) eglGetProcAddress;
-
-    LOAD_FUNC(eglGetDisplay);
-    LOAD_FUNC(eglInitialize);
-    LOAD_FUNC(eglTerminate);
-    LOAD_FUNC(eglChooseConfig);
-    LOAD_FUNC(eglGetConfigAttrib);
-    LOAD_FUNC(eglCreateContext);
-    LOAD_FUNC(eglDestroyContext);
-    LOAD_FUNC(eglCreateWindowSurface);
-    LOAD_FUNC(eglDestroySurface);
-    LOAD_FUNC(eglMakeCurrent);
-    LOAD_FUNC(eglSwapBuffers);
-    LOAD_FUNC(eglSwapInterval);
-    LOAD_FUNC(eglWaitNative);
-    LOAD_FUNC(eglWaitGL);
-    LOAD_FUNC(eglBindAPI);
-    LOAD_FUNC(eglQueryString);
-    LOAD_FUNC(eglGetError);
-
-    _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (!_this->egl_data->egl_display) {
-        return SDL_SetError("Could not get EGL display");
+    return 0;
+}
+
+int
+Emscripten_GLES_GetSwapInterval(_THIS)
+{
+    int mode, value;
+
+    emscripten_get_main_loop_timing(&mode, &value);
+
+    if(mode == EM_TIMING_RAF)
+        return value;
+
+    return 0;
+}
+
+SDL_GLContext
+Emscripten_GLES_CreateContext(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *window_data;
+
+    EmscriptenWebGLContextAttributes attribs;
+    EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
+
+    emscripten_webgl_init_context_attributes(&attribs);
+
+    attribs.alpha = _this->gl_config.alpha_size > 0;
+    attribs.depth = _this->gl_config.depth_size > 0;
+    attribs.stencil = _this->gl_config.stencil_size > 0;
+    attribs.antialias = _this->gl_config.multisamplebuffers == 1;
+
+    if(_this->gl_config.major_version == 3)
+        attribs.majorVersion = 2; /* WebGL 2.0 ~= GLES 3.0 */
+
+    window_data = (SDL_WindowData *) window->driverdata;
+
+    if (window_data->gl_context) {
+        SDL_SetError("Cannot create multiple webgl contexts per window");
+        return NULL;
     }
-    
-    if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
-        return SDL_SetError("Could not initialize EGL");
+
+    context = emscripten_webgl_create_context(window_data->canvas_id, &attribs);
+
+    if (context < 0) {
+        SDL_SetError("Could not create webgl context");
+        return NULL;
     }
 
-    if (path) {
-        SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
-    } else {
-        *_this->gl_config.driver_path = '\0';
+    if (emscripten_webgl_make_context_current(context) != EMSCRIPTEN_RESULT_SUCCESS) {
+        emscripten_webgl_destroy_context(context);
+        return NULL;
     }
-    
-    return 0;
+
+    window_data->gl_context = (SDL_GLContext)context;
+
+    return (SDL_GLContext)context;
 }
 
-SDL_EGL_CreateContext_impl(Emscripten)
-SDL_EGL_MakeCurrent_impl(Emscripten)
+void
+Emscripten_GLES_DeleteContext(_THIS, SDL_GLContext context)
+{
+    SDL_Window *window;
+
+    /* remove the context from its window */
+    for (window = _this->windows; window != NULL; window = window->next) {
+        SDL_WindowData *window_data;
+        window_data = (SDL_WindowData *) window->driverdata;
+
+        if (window_data->gl_context == context) {
+            window_data->gl_context = NULL;
+        }
+    }
+
+    emscripten_webgl_destroy_context((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)context);
+}
 
 int
 Emscripten_GLES_SwapWindow(_THIS, SDL_Window * window)
 {
-    EGLBoolean ret = SDL_EGL_SwapBuffers(_this, ((SDL_WindowData *) window->driverdata)->egl_surface);
     if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, SDL_TRUE)) {
         /* give back control to browser for screen refresh */
         emscripten_sleep(0);
     }
-    return ret;
+    return 0;
+}
+
+int
+Emscripten_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
+{
+    /* it isn't possible to reuse contexts across canvases */
+    if (window && context) {
+        SDL_WindowData *window_data;
+        window_data = (SDL_WindowData *) window->driverdata;
+
+        if (context != window_data->gl_context) {
+            return SDL_SetError("Cannot make context current to another window");
+        }
+    }
+
+    if (emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)context) != EMSCRIPTEN_RESULT_SUCCESS) {
+        return SDL_SetError("Unable to make context current");
+    }
+    return 0;
 }
 
-#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL */
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
 
 /* vi: set ts=4 sw=4 expandtab: */
 

+ 7 - 9
src/video/emscripten/SDL_emscriptenopengles.h

@@ -23,25 +23,23 @@
 #ifndef SDL_emscriptenopengles_h_
 #define SDL_emscriptenopengles_h_
 
-#if SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
 
 #include "../SDL_sysvideo.h"
-#include "../SDL_egl_c.h"
 
 /* OpenGLES functions */
-#define Emscripten_GLES_GetAttribute SDL_EGL_GetAttribute
-#define Emscripten_GLES_GetProcAddress SDL_EGL_GetProcAddress
-#define Emscripten_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
-#define Emscripten_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
-#define Emscripten_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
-#define Emscripten_GLES_DeleteContext SDL_EGL_DeleteContext
 
 extern int Emscripten_GLES_LoadLibrary(_THIS, const char *path);
+extern void Emscripten_GLES_UnloadLibrary(_THIS);
+extern void * Emscripten_GLES_GetProcAddress(_THIS, const char *proc);
+extern int Emscripten_GLES_SetSwapInterval(_THIS, int interval);
+extern int Emscripten_GLES_GetSwapInterval(_THIS);
 extern SDL_GLContext Emscripten_GLES_CreateContext(_THIS, SDL_Window * window);
+extern void Emscripten_GLES_DeleteContext(_THIS, SDL_GLContext context);
 extern int Emscripten_GLES_SwapWindow(_THIS, SDL_Window * window);
 extern int Emscripten_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context);
 
-#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL */
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
 
 #endif /* SDL_emscriptenopengles_h_ */
 

+ 7 - 25
src/video/emscripten/SDL_emscriptenvideo.c

@@ -27,7 +27,6 @@
 #include "SDL_hints.h"
 #include "../SDL_sysvideo.h"
 #include "../SDL_pixels_c.h"
-#include "../SDL_egl_c.h"
 #include "../../events/SDL_events_c.h"
 
 #include "SDL_emscriptenvideo.h"
@@ -110,7 +109,6 @@ Emscripten_CreateDevice(void)
     device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
     device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
 
-#if SDL_VIDEO_OPENGL_EGL
     device->GL_LoadLibrary = Emscripten_GLES_LoadLibrary;
     device->GL_GetProcAddress = Emscripten_GLES_GetProcAddress;
     device->GL_UnloadLibrary = Emscripten_GLES_UnloadLibrary;
@@ -120,7 +118,6 @@ Emscripten_CreateDevice(void)
     device->GL_GetSwapInterval = Emscripten_GLES_GetSwapInterval;
     device->GL_SwapWindow = Emscripten_GLES_SwapWindow;
     device->GL_DeleteContext = Emscripten_GLES_DeleteContext;
-#endif
 
     device->free = Emscripten_DeleteDevice;
 
@@ -218,6 +215,7 @@ Emscripten_CreateWindow(_THIS, SDL_Window * window)
     SDL_WindowData *wdata;
     double scaled_w, scaled_h;
     double css_w, css_h;
+    const char *selector;
 
     /* Allocate window internal data */
     wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
@@ -225,7 +223,12 @@ Emscripten_CreateWindow(_THIS, SDL_Window * window)
         return SDL_OutOfMemory();
     }
 
-    wdata->canvas_id = SDL_strdup("#canvas");
+    selector = SDL_GetHint(SDL_HINT_EMSCRIPTEN_CANVAS_SELECTOR);
+    if (!selector) {
+        selector = "#canvas";
+    }
+
+    wdata->canvas_id = SDL_strdup(selector);
 
     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
         wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
@@ -259,21 +262,6 @@ Emscripten_CreateWindow(_THIS, SDL_Window * window)
         }
     }
 
-#if SDL_VIDEO_OPENGL_EGL
-    if (window->flags & SDL_WINDOW_OPENGL) {
-        if (!_this->egl_data) {
-            if (SDL_GL_LoadLibrary(NULL) < 0) {
-                return -1;
-            }
-        }
-        wdata->egl_surface = SDL_EGL_CreateSurface(_this, 0);
-
-        if (wdata->egl_surface == EGL_NO_SURFACE) {
-            return SDL_SetError("Could not create GLES window surface");
-        }
-    }
-#endif
-
     wdata->window = window;
 
     /* Setup driver data for this window */
@@ -329,12 +317,6 @@ Emscripten_DestroyWindow(_THIS, SDL_Window * window)
         data = (SDL_WindowData *) window->driverdata;
 
         Emscripten_UnregisterEventHandlers(data);
-#if SDL_VIDEO_OPENGL_EGL
-        if (data->egl_surface != EGL_NO_SURFACE) {
-            SDL_EGL_DestroySurface(_this, data->egl_surface);
-            data->egl_surface = EGL_NO_SURFACE;
-        }
-#endif
 
         /* We can't destroy the canvas, so resize it to zero instead */
         emscripten_set_canvas_element_size(data->canvas_id, 0, 0);

+ 2 - 7
src/video/emscripten/SDL_emscriptenvideo.h

@@ -28,18 +28,13 @@
 #include <emscripten/emscripten.h>
 #include <emscripten/html5.h>
 
-#if SDL_VIDEO_OPENGL_EGL
-#include <EGL/egl.h>
-#endif
-
 typedef struct SDL_WindowData
 {
-#if SDL_VIDEO_OPENGL_EGL
-    EGLSurface egl_surface;
-#endif
     SDL_Window *window;
     SDL_Surface *surface;
 
+    SDL_GLContext gl_context;
+
     char *canvas_id;
 
     float pixel_ratio;