From ee5f6324b2d5f67de8a63eaed4a613616227e4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 16 Feb 2018 19:17:28 +0200 Subject: [PATCH] OffscreenCanvas + proxied Canvas support This PR improves supports for OffscreenCanvas, allowing pthreads to resize canvases from threads, and also creating proxied canvases on the main thread when OffscreenCanvas is not present. Bump version to 1.38.19. --- emscripten-version.txt | 2 +- site/source/docs/api_reference/html5.h.rst | 11 + src/deps_info.json | 4 +- src/library_browser.js | 4 +- src/library_gl.js | 9 +- src/library_html5.js | 431 ++++++++++-- src/library_pthread.js | 81 ++- src/struct_info.json | 7 + system/include/emscripten/html5.h | 8 + system/include/emscripten/threading.h | 19 +- system/lib/gl.symbols | 328 ++++++++- system/lib/{ => gl}/gl.c | 0 system/lib/gl/webgl1.c | 627 ++++++++++++++++++ system/lib/gl/webgl1.h | 194 ++++++ system/lib/gl/webgl1_ext.h | 229 +++++++ system/lib/gl/webgl2.c | 136 ++++ system/lib/gl/webgl2.h | 108 +++ system/lib/gl/webgl2_ext.h | 10 + system/lib/pthread/library_pthread.c | 2 + tests/canvas_animate_resize.cpp | 157 +++++ tests/canvas_animate_resize_shell.html | 56 ++ tests/gl_in_proxy_pthread.cpp | 60 ++ tests/gl_in_pthread.cpp | 47 +- tests/gl_in_two_pthreads.cpp | 109 +++ ...esize_offscreencanvas_from_main_thread.cpp | 103 +++ tests/test_browser.py | 46 +- tests/test_interactive.py | 16 +- tests/test_other.py | 2 +- tools/system_libs.py | 34 +- 29 files changed, 2702 insertions(+), 138 deletions(-) rename system/lib/{ => gl}/gl.c (100%) create mode 100644 system/lib/gl/webgl1.c create mode 100644 system/lib/gl/webgl1.h create mode 100644 system/lib/gl/webgl1_ext.h create mode 100644 system/lib/gl/webgl2.c create mode 100644 system/lib/gl/webgl2.h create mode 100644 system/lib/gl/webgl2_ext.h create mode 100644 tests/canvas_animate_resize.cpp create mode 100644 tests/canvas_animate_resize_shell.html create mode 100644 tests/gl_in_proxy_pthread.cpp create mode 100644 tests/gl_in_two_pthreads.cpp create mode 100644 tests/resize_offscreencanvas_from_main_thread.cpp diff --git a/emscripten-version.txt b/emscripten-version.txt index ac412a311a26f..50d60d276054a 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1 +1 @@ -"1.38.18" +"1.38.19" diff --git a/site/source/docs/api_reference/html5.h.rst b/site/source/docs/api_reference/html5.h.rst index 474e599417769..412fae0c7b0c0 100644 --- a/site/source/docs/api_reference/html5.h.rst +++ b/site/source/docs/api_reference/html5.h.rst @@ -1969,6 +1969,17 @@ Struct Because supporting offscreen framebuffer adds some amount of extra code to the compiled output, support for it must explicitly be enabled via the ``-s OFFSCREEN_FRAMEBUFFER=1`` Emscripten linker flag. When building simultaneously with both ``-s OFFSCREEN_FRAMEBUFFER=1`` and ``-s OFFSCREENCANVAS_SUPPORT=1`` linker flags enabled, offscreen backbuffer can be used as a polyfill-like compatibility fallback to enable rendering WebGL from a pthread when the browser does not support the OffscreenCanvas API. + .. c:member:: EM_BOOL proxyContextToMainThread + + This member specifies the threading model that will be used for the created WebGL context, when the WebGL context is created in a pthread. Three values are possible: ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW``, ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK`` or ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS``. If ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW`` is specified, the WebGLRenderingContext object will be created inside the pthread that is calling the ``emscripten_webgl_create_context()`` function as an OffscreenCanvas-based rendering context. This is only possible if 1) current browser supports OffscreenCanvas specification, 2) code was compiled with ``-s OFFSCREENCANVAS_SUPPORT=1`` linker flag enabled, 3) the Canvas object that the context is being created on was transferred over to the calling pthread with function ``emscripten_pthread_attr_settransferredcanvases()`` when the pthread was originally created, and 4) no OffscreenCanvas-based context already exists from the given Canvas at the same time. + + If a WebGL rendering context is created as an OffscreenCanvas-based context, it will have the limitation that only the pthread that created the context can enable access to it (via ``emscripten_webgl_make_context_current()`` function). Other threads will not be able to activate rendering to the context, i.e. OffscreenCanvas-based contexts are essentially "pinned" to the pthread that created them. + + If the current browser does not support OffscreenCanvas, you can specify the ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS`` WebGL context creation flag. If this flag is passed, and code was compiled with ``-s OFFSCREEN_FRAMEBUFFER=1`` enabled, the WebGL context will be created as a "proxied context". In this context mode, the WebGLRenderingContext object will actually be created on the main browser thread, and all WebGL API calls will be proxied as asynchronous messages from the pthread into the main thread. This will have a performance and latency impact in comparison to OffscreenCanvas contexts, however unlike OffscreenCanvas-based contexts, proxied contexts can be shared across any number of pthreads: you can use the ``emscripten_webgl_make_context_current()`` function in any pthread to activate and deactivate access to the WebGL context: for example, you could have one WebGL loading thread, and another WebGL rendering thread that coordinate shared access to the WebGL rendering context by cooperatively acquiring and releasing access to the WebGL rendering context via the ``emscripten_webgl_make_context_current()`` function. Proxied contexts do not require any special support from the browser, so any WebGL capable browser can create a proxied WebGL context. + + The ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS`` WebGL context creation flag will always create a proxied context, even if the browser did support OffscreenCanvas. If you would like to prefer to create a higher performance OffscreenCanvas context whenever suppported by the browser, but only fall back to a proxied WebGL context to keep compatibility with browsers that do not yet have OffscreenCanvas support, you can specify the ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK`` context creation flag. In order to use this flag, code should be compiled with both ``-s OFFSCREEN_FRAMEBUFFER=1`` and ``-s OFFSCREENCANVAS_SUPPORT=1`` linker flags. + + Default value of ``proxyContextToMainThread`` after calling ``emscripten_webgl_init_context_attributes()`` is ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK``. Callback functions ------------------ diff --git a/src/deps_info.json b/src/deps_info.json index f98eab649ae5d..2c30e88391f8a 100644 --- a/src/deps_info.json +++ b/src/deps_info.json @@ -67,6 +67,8 @@ "localtime_r": ["_get_tzname", "_get_daylight", "_get_timezone"], "mktime": ["_get_tzname", "_get_daylight", "_get_timezone"], "timegm": ["_get_tzname", "_get_daylight", "_get_timezone"], - "tzset": ["_get_tzname", "_get_daylight", "_get_timezone"] + "tzset": ["_get_tzname", "_get_daylight", "_get_timezone"], + "emscripten_set_canvas_element_size_calling_thread": ["emscripten_async_queue_on_thread_"], + "emscripten_webgl_destroy_context": "emscripten_webgl_make_context_current" } diff --git a/src/library_browser.js b/src/library_browser.js index 427ea495c5927..356539f069728 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -1196,7 +1196,7 @@ var LibraryBrowser = { #if OFFSCREEN_FRAMEBUFFER // If the current GL context is a proxied regular WebGL context, and was initialized with implicit swap mode on the main thread, and we are on the parent thread, // perform the swap on behalf of the user. - if (typeof GL !== 'undefined' && GL.currentContext && GLctxIsOnParentThread) { + if (typeof GL !== 'undefined' && GL.currentContext && GL.currentContextIsProxied) { var explicitSwapControl = {{{ makeGetValue('GL.currentContext', 0, 'i32') }}}; if (!explicitSwapControl) _emscripten_webgl_commit_frame(); } @@ -1205,7 +1205,7 @@ var LibraryBrowser = { #if OFFSCREENCANVAS_SUPPORT // If the current GL context is an OffscreenCanvas, but it was initialized with implicit swap mode, perform the swap on behalf of the user. - if (typeof GL !== 'undefined' && GL.currentContext && !GLctxIsOnParentThread && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) { + if (typeof GL !== 'undefined' && GL.currentContext && !GL.currentContextIsProxied && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) { GL.currentContext.GLctx.commit(); } #endif diff --git a/src/library_gl.js b/src/library_gl.js index a45d2a486c83a..a3215f0b5d6cc 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -26,7 +26,7 @@ var LibraryGL = { uniforms: [], shaders: [], vaos: [], - contexts: [], + contexts: {}, currentContext: null, offscreenCanvases: {}, // DOM ID -> OffscreenCanvas mappings of elements that have their rendering control transferred to offscreen. timerQueriesEXT: [], @@ -709,7 +709,11 @@ var LibraryGL = { #endif registerContext: function(ctx, webGLContextAttributes) { - var handle = GL.getNewId(GL.contexts); + var handle = _malloc(8); // Make space on the heap to store GL context attributes that need to be accessible as shared between threads. + {{{ makeSetValue('handle', 0, 'webGLContextAttributes["explicitSwapControl"]', 'i32')}}}; // explicitSwapControl +#if USE_PTHREADS + {{{ makeSetValue('handle', 4, '_pthread_self()', 'i32')}}}; // the thread pointer of the thread that owns the control of the context +#endif var context = { handle: handle, attributes: webGLContextAttributes, @@ -809,6 +813,7 @@ var LibraryGL = { if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; if (typeof JSEvents === 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. + _free(GL.contexts[contextHandle]); GL.contexts[contextHandle] = null; }, diff --git a/src/library_html5.js b/src/library_html5.js index f30c4595d2f73..6014b860b321c 100644 --- a/src/library_html5.js +++ b/src/library_html5.js @@ -5,6 +5,7 @@ var LibraryJSEvents = { $JSEvents__postset: 'JSEvents.staticInit();', + $JSEvents__deps: ['_get_canvas_element_size', '_set_canvas_element_size'], $JSEvents: { // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop. keyEvent: 0, @@ -63,8 +64,10 @@ var LibraryJSEvents = { } JSEvents.eventHandlers = []; JSEvents.deferredCalls = []; - window.removeEventListener("gamepadconnected", JSEvents._onGamepadConnected); - window.removeEventListener("gamepaddisconnected", JSEvents._onGamepadDisconnected); + if (typeof window !== 'undefined') { + window.removeEventListener("gamepadconnected", JSEvents._onGamepadConnected); + window.removeEventListener("gamepaddisconnected", JSEvents._onGamepadDisconnected); + } }, registerRemoveEventListeners: function() { @@ -74,24 +77,35 @@ var LibraryJSEvents = { } }, + // Find a DOM element with the given ID. findEventTarget: function(target) { - if (target) { - if (typeof target == "number") { - target = Pointer_stringify(target); - } - if (target == '#window') return window; - else if (target == '#document') return document; - else if (target == '#screen') return window.screen; - else if (target == '#canvas') return Module['canvas']; - - if (typeof target == 'string') return document.getElementById(target); - else return target; - } else { - // The sensible target varies between events, but use window as the default + try { + // The sensible "default" target varies between events, but use window as the default // since DOM events mostly can default to that. Specific callback registrations // override their own defaults. - return window; + if (!target) return window; + if (typeof target === "number") target = Pointer_stringify(target); + if (target === '#window') return window; + else if (target === '#document') return document; + else if (target === '#screen') return window.screen; + else if (target === '#canvas') return Module['canvas']; + return (typeof target === 'string') ? document.getElementById(target) : target; + } catch(e) { + // In Web Workers, some objects above, such as '#document' do not exist. Gracefully + // return null for them. + return null; + } + }, + + // Like findEventTarget, but looks for OffscreenCanvas elements first + findCanvasEventTarget: function(target) { + if (typeof target === 'number') target = Pointer_stringify(target); + if (!target || target === '#canvas') { + if (typeof GL !== 'undefined' && GL.offscreenCanvases['canvas']) return GL.offscreenCanvases['canvas']; // TODO: Remove this line, target '#canvas' should refer only to Module['canvas'], not to GL.offscreenCanvases['canvas'] - but need stricter tests to be able to remove this line. + return Module['canvas']; } + if (typeof GL !== 'undefined' && GL.offscreenCanvases[target]) return GL.offscreenCanvases[target]; + return JSEvents.findEventTarget(target); }, deferredCalls: [], @@ -746,8 +760,9 @@ var LibraryJSEvents = { var rect = target.getBoundingClientRect(); var windowedCssWidth = rect.right - rect.left; var windowedCssHeight = rect.bottom - rect.top; - var windowedRttWidth = target.width; - var windowedRttHeight = target.height; + var canvasSize = __get_canvas_element_size(target); + var windowedRttWidth = canvasSize[0]; + var windowedRttHeight = canvasSize[1]; if (strategy.scaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_CENTER') }}}) { __setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2); @@ -787,9 +802,10 @@ var LibraryJSEvents = { var dpiScale = (strategy.canvasResolutionScaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF') }}}) ? window.devicePixelRatio : 1; if (strategy.canvasResolutionScaleMode != {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE') }}}) { - target.width = cssWidth * dpiScale; - target.height = cssHeight * dpiScale; - if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, target.width, target.height); + var newWidth = (cssWidth * dpiScale)|0; + var newHeight = (cssHeight * dpiScale)|0; + __set_canvas_element_size(target, newWidth, newHeight); + if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, newWidth, newHeight); } return restoreOldStyle; }, @@ -1487,9 +1503,11 @@ var LibraryJSEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, + _registerRestoreOldStyle__deps: ['_get_canvas_element_size', '_set_canvas_element_size'], _registerRestoreOldStyle: function(canvas) { - var oldWidth = canvas.width; - var oldHeight = canvas.height; + var canvasSize = __get_canvas_element_size(canvas); + var oldWidth = canvasSize[0]; + var oldHeight = canvasSize[1]; var oldCssWidth = canvas.style.width; var oldCssHeight = canvas.style.height; var oldBackgroundColor = canvas.style.backgroundColor; // Chrome reads color from here. @@ -1516,8 +1534,8 @@ var LibraryJSEvents = { document.removeEventListener('webkitfullscreenchange', restoreOldStyle); document.removeEventListener('MSFullscreenChange', restoreOldStyle); - canvas.width = oldWidth; - canvas.height = oldHeight; + __set_canvas_element_size(canvas, oldWidth, oldHeight); + canvas.style.width = oldCssWidth; canvas.style.height = oldCssHeight; canvas.style.backgroundColor = oldBackgroundColor; // Chrome @@ -1606,7 +1624,7 @@ var LibraryJSEvents = { _currentFullscreenStrategy: {}, _restoreOldWindowedStyle: null, - _softFullscreenResizeWebGLRenderTarget__deps: ['_setLetterbox', '_currentFullscreenStrategy'], + _softFullscreenResizeWebGLRenderTarget__deps: ['_setLetterbox', '_currentFullscreenStrategy', '_get_canvas_element_size', '_set_canvas_element_size'], _softFullscreenResizeWebGLRenderTarget: function() { var inHiDPIFullscreenMode = __currentFullscreenStrategy.canvasResolutionScaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF') }}}; var inAspectRatioFixedFullscreenMode = __currentFullscreenStrategy.scaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT') }}}; @@ -1617,8 +1635,9 @@ var LibraryJSEvents = { var w = screenWidth; var h = screenHeight; var canvas = __currentFullscreenStrategy.target; - var x = canvas.width; - var y = canvas.height; + var canvasSize = __get_canvas_element_size(canvas); + var x = canvasSize[0]; + var y = canvasSize[1]; var topMargin; if (inAspectRatioFixedFullscreenMode) { @@ -1628,9 +1647,8 @@ var LibraryJSEvents = { } if (inPixelPerfectFullscreenMode) { - canvas.width = w; - canvas.height = h; - if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, canvas.width, canvas.height); + __set_canvas_element_size(canvas, w, h); + if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, w, h); } // Back to CSS pixels. @@ -1665,7 +1683,7 @@ var LibraryJSEvents = { }, // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode - _emscripten_do_request_fullscreen__deps: ['_setLetterbox', 'emscripten_set_canvas_element_size', 'emscripten_get_canvas_element_size'], + _emscripten_do_request_fullscreen__deps: ['_setLetterbox', 'emscripten_set_canvas_element_size', 'emscripten_get_canvas_element_size', '_get_canvas_element_size', '_set_canvas_element_size'], _emscripten_do_request_fullscreen: function(target, strategy) { if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; if (!JSEvents.fullscreenEnabled()) return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; @@ -1726,7 +1744,7 @@ var LibraryJSEvents = { return __emscripten_do_request_fullscreen(target, strategy); }, - emscripten_enter_soft_fullscreen__deps: ['_setLetterbox', '_hideEverythingExceptGivenElement', '_restoreOldWindowedStyle', '_registerRestoreOldStyle', '_restoreHiddenElements', '_currentFullscreenStrategy', '_softFullscreenResizeWebGLRenderTarget'], + emscripten_enter_soft_fullscreen__deps: ['_setLetterbox', '_hideEverythingExceptGivenElement', '_restoreOldWindowedStyle', '_registerRestoreOldStyle', '_restoreHiddenElements', '_currentFullscreenStrategy', '_softFullscreenResizeWebGLRenderTarget', '_get_canvas_element_size', '_set_canvas_element_size'], emscripten_enter_soft_fullscreen__proxy: 'sync', emscripten_enter_soft_fullscreen__sig: 'iii', emscripten_enter_soft_fullscreen: function(target, fullscreenStrategy) { @@ -2091,11 +2109,19 @@ var LibraryJSEvents = { {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion, 0, 'i32') }}}; {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.enableExtensionsByDefault, 1, 'i32') }}}; {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.explicitSwapControl, 0, 'i32') }}}; + {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.proxyContextToMainThread, 1, 'i32') }}}; {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.renderViaOffscreenBackBuffer, 0, 'i32') }}}; }, - emscripten_webgl_create_context__deps: ['$GL'], - emscripten_webgl_create_context: function(target, attributes) { +#if !USE_PTHREADS + emscripten_webgl_create_context: 'emscripten_webgl_do_create_context', + emscripten_webgl_get_current_context: 'emscripten_webgl_do_get_current_context', + emscripten_webgl_commit_frame: 'emscripten_webgl_do_commit_frame', +#endif + + emscripten_webgl_do_create_context__deps: ['$GL'], + // This function performs proxying manually, depending on the style of context that is to be created. + emscripten_webgl_do_create_context: function(target, attributes) { var contextAttributes = {}; contextAttributes['alpha'] = !!{{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.alpha, 'i32') }}}; contextAttributes['depth'] = !!{{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.depth, 'i32') }}}; @@ -2109,22 +2135,53 @@ var LibraryJSEvents = { contextAttributes['minorVersion'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion, 'i32') }}}; contextAttributes['enableExtensionsByDefault'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.enableExtensionsByDefault, 'i32') }}}; contextAttributes['explicitSwapControl'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.explicitSwapControl, 'i32') }}}; + contextAttributes['proxyContextToMainThread'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.proxyContextToMainThread, 'i32') }}}; contextAttributes['renderViaOffscreenBackBuffer'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.renderViaOffscreenBackBuffer, 'i32') }}}; target = Pointer_stringify(target); var canvas; if ((!target || target === '#canvas') && Module['canvas']) { - canvas = Module['canvas'].id ? (GL.offscreenCanvases[Module['canvas'].id] || JSEvents.findEventTarget(Module['canvas'].id)) : Module['canvas']; + canvas = (Module['canvas'].id && GL.offscreenCanvases[Module['canvas'].id]) ? (GL.offscreenCanvases[Module['canvas'].id].offscreenCanvas || JSEvents.findEventTarget(Module['canvas'].id)) : Module['canvas']; } else { - canvas = GL.offscreenCanvases[target] || JSEvents.findEventTarget(target); + canvas = GL.offscreenCanvases[target] ? GL.offscreenCanvases[target].offscreenCanvas : JSEvents.findEventTarget(target); } + +#if USE_PTHREADS + // Create a WebGL context that is proxied to main thread if canvas was not found on worker, or if explicitly requested to do so. + if (ENVIRONMENT_IS_PTHREAD) { + if (contextAttributes['proxyContextToMainThread'] === {{{ cDefine('EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS') }}} || + (!canvas && contextAttributes['proxyContextToMainThread'] === {{{ cDefine('EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK') }}})) { + // When WebGL context is being proxied via the main thread, we must render using an offscreen FBO render target to avoid WebGL's + // "implicit swap when callback exits" behavior. TODO: If OffscreenCanvas is supported, explicitSwapControl=true and still proxying, + // then this can be avoided, since OffscreenCanvas enables explicit swap control. +#if GL_DEBUG + if (contextAttributes['proxyContextToMainThread'] === {{{ cDefine('EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS') }}}) console.error('EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS enabled, proxying WebGL rendering from pthread to main thread.'); + if (!canvas && contextAttributes['proxyContextToMainThread'] === {{{ cDefine('EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK') }}}) console.error('Specified canvas target "' + target + '" is not an OffscreenCanvas in the current pthread, but EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK is set. Proxying WebGL rendering from pthread to main thread.'); + console.error('Performance warning: forcing renderViaOffscreenBackBuffer=true and preserveDrawingBuffer=true since proxying WebGL rendering.'); +#endif + // We will be proxying - if OffscreenCanvas is supported, we can proxy a bit more efficiently by avoiding having to create an Offscreen FBO. + if (typeof OffscreenCanvas === 'undefined') { + {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.renderViaOffscreenBackBuffer, '1', 'i32') }}} + {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.preserveDrawingBuffer, '1', 'i32') }}} + } + return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_CREATE_CONTEXT') }}}, target, attributes); + } + } +#endif + if (!canvas) { #if GL_DEBUG - console.error('emscripten_webgl_create_context failed: Unknown target!'); + console.error('emscripten_webgl_create_context failed: Unknown canvas target "' + target + '"!'); #endif return 0; } + #if OFFSCREENCANVAS_SUPPORT +#if GL_DEBUG + if (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) console.log('emscripten_webgl_create_context: Creating an OffscreenCanvas-based WebGL context on target "' + target + '"'); + else if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement) console.log('emscripten_webgl_create_context: Creating an HTMLCanvasElement-based WebGL context on target "' + target + '"'); +#endif + if (contextAttributes['explicitSwapControl']) { var supportsOffscreenCanvas = canvas.transferControlToOffscreen || (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas); @@ -2145,8 +2202,19 @@ var LibraryJSEvents = { } if (canvas.transferControlToOffscreen) { - GL.offscreenCanvases[canvas.id] = canvas.transferControlToOffscreen(); - GL.offscreenCanvases[canvas.id].id = canvas.id; +#if GL_DEBUG + console.log('explicitSwapControl requested: canvas.transferControlToOffscreen() on canvas "' + target + '" to get .commit() function and not rely on implicit WebGL swap'); +#endif + if (!canvas.controlTransferredOffscreen) { + GL.offscreenCanvases[canvas.id] = canvas.transferControlToOffscreen(); + canvas.controlTransferredOffscreen = true; + GL.offscreenCanvases[canvas.id].id = canvas.id; + } else if (!GL.offscreenCanvases[canvas.id]) { +#if GL_DEBUG + console.error('OffscreenCanvas is supported, and canvas "' + canvas.id + '" has already before been transferred offscreen, but there is no known OffscreenCanvas with that name!'); +#endif + return 0; + } canvas = GL.offscreenCanvases[canvas.id]; } } @@ -2172,17 +2240,33 @@ var LibraryJSEvents = { var contextHandle = GL.createContext(canvas, contextAttributes); return contextHandle; }, - +#if USE_PTHREADS + // Runs on the calling thread, proxies if needed. + emscripten_webgl_make_context_current_calling_thread: function(contextHandle) { + var success = GL.makeContextCurrent(contextHandle); + if (success) GL.currentContextIsProxied = false; // If succeeded above, we will have a local GL context from this thread (worker or main). + return success ? {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}} : {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_PARAM') }}}; + }, + // This function gets called in a pthread, after it has successfully activated (with make_current()) a proxied GL context to itself from the main thread. + // In this scenario, the pthread does not hold a high-level JS object to the GL context, because it lives on the main thread, in which case we record + // an integer pointer as a token value to represent the GL context activation from another thread. (when this function is called, the main browser thread + // has already accepted the GL context activation for our pthread, so that side is good) + _emscripten_proxied_gl_context_activated_from_main_browser_thread: function(contextHandle) { + GLctx = Module.ctx = GL.currentContext = contextHandle; + GL.currentContextIsProxied = true; + }, +#else emscripten_webgl_make_context_current: function(contextHandle) { var success = GL.makeContextCurrent(contextHandle); return success ? {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}} : {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_PARAM') }}}; }, +#endif - emscripten_webgl_get_current_context: function() { + emscripten_webgl_do_get_current_context: function() { return GL.currentContext ? GL.currentContext.handle : 0; }, - emscripten_webgl_get_drawing_buffer_size: function(contextHandle, width, height) { + emscripten_webgl_get_drawing_buffer_size_calling_thread: function(contextHandle, width, height) { var GLContext = GL.getContext(contextHandle); if (!GLContext || !GLContext.GLctx || !width || !height) { @@ -2193,7 +2277,26 @@ var LibraryJSEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, - emscripten_webgl_commit_frame: function() { +#if USE_PTHREADS + emscripten_webgl_get_drawing_buffer_size_main_thread__proxy: 'sync', + emscripten_webgl_get_drawing_buffer_size_main_thread__sig: 'iiii', + emscripten_webgl_get_drawing_buffer_size_main_thread__deps: ['emscripten_webgl_get_drawing_buffer_size_calling_thread'], + emscripten_webgl_get_drawing_buffer_size_main_thread: function(contextHandle, width, height) { return _emscripten_webgl_get_drawing_buffer_size_calling_thread(contextHandle, width, height); }, + + emscripten_webgl_get_drawing_buffer_size__deps: ['emscripten_webgl_get_drawing_buffer_size_calling_thread', 'emscripten_webgl_get_drawing_buffer_size_main_thread'], + emscripten_webgl_get_drawing_buffer_size: function(contextHandle, width, height) { + if (GL.contexts[contextHandle]) return _emscripten_webgl_get_drawing_buffer_size_calling_thread(contextHandle, width, height); + else _emscripten_webgl_get_drawing_buffer_size_main_thread(contextHandle, width, height); + }, +#else + emscripten_webgl_get_drawing_buffer_size: 'emscripten_webgl_get_drawing_buffer_size_calling_thread', +#endif + + emscripten_webgl_do_commit_frame: function() { +#if TRACE_WEBGL_CALLS + var threadId = (typeof _pthread_self !== 'undefined') ? _pthread_self : function() { return 1; }; + console.error('[Thread ' + threadId() + ', GL ctx: ' + GL.currentContext.handle + ']: emscripten_webgl_do_commit_frame()'); +#endif if (!GL.currentContext || !GL.currentContext.GLctx) { #if GL_DEBUG console.error('emscripten_webgl_commit_frame() failed: no GL context set current via emscripten_webgl_make_context_current()!'); @@ -2226,18 +2329,56 @@ var LibraryJSEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, - emscripten_webgl_destroy_context: function(contextHandle) { + emscripten_webgl_destroy_context_calling_thread: function(contextHandle) { + if (GL.currentContext == contextHandle) GL.currentContext = 0; GL.deleteContext(contextHandle); }, - emscripten_webgl_enable_extension: function(contextHandle, extension) { +#if USE_PTHREADS + emscripten_webgl_destroy_context_main_thread__proxy: 'sync', + emscripten_webgl_destroy_context_main_thread__sig: 'vi', + emscripten_webgl_destroy_context_main_thread__deps: ['emscripten_webgl_destroy_context_calling_thread'], + emscripten_webgl_destroy_context_main_thread: function(contextHandle) { return _emscripten_webgl_destroy_context_calling_thread(contextHandle); }, + + emscripten_webgl_destroy_context__deps: ['emscripten_webgl_destroy_context_main_thread', 'emscripten_webgl_destroy_context_calling_thread', 'emscripten_webgl_get_current_context', 'emscripten_webgl_make_context_current'], + emscripten_webgl_destroy_context: function(contextHandle) { + if (_emscripten_webgl_get_current_context() == contextHandle) _emscripten_webgl_make_context_current(0); + return GL.contexts[contextHandle] ? _emscripten_webgl_destroy_context_calling_thread(contextHandle) : _emscripten_webgl_destroy_context_main_thread(contextHandle); + }, +#else + emscripten_webgl_destroy_context: 'emscripten_webgl_destroy_context_calling_thread', +#endif + + emscripten_webgl_enable_extension_calling_thread: function(contextHandle, extension) { var context = GL.getContext(contextHandle); var extString = Pointer_stringify(extension); if (extString.indexOf('GL_') == 0) extString = extString.substr(3); // Allow enabling extensions both with "GL_" prefix and without. var ext = context.GLctx.getExtension(extString); - return ext ? 1 : 0; + return !!ext; }, + emscripten_supports_offscreencanvas: function() { +#if OFFSCREENCANVAS_SUPPORT + return typeof OffscreenCanvas !== 'undefined'; +#else + return 0; +#endif + }, + +#if USE_PTHREADS + emscripten_webgl_enable_extension_main_thread__proxy: 'sync', + emscripten_webgl_enable_extension_main_thread__sig: 'iii', + emscripten_webgl_enable_extension_main_thread__deps: ['emscripten_webgl_enable_extension_calling_thread'], + emscripten_webgl_enable_extension_main_thread: function(contextHandle, extension) { return _emscripten_webgl_enable_extension_calling_thread(contextHandle, extension); }, + + emscripten_webgl_enable_extension__deps: ['emscripten_webgl_enable_extension_main_thread', 'emscripten_webgl_enable_extension_calling_thread'], + emscripten_webgl_enable_extension: function(contextHandle, extension) { + return GL.contexts[contextHandle] ? _emscripten_webgl_enable_extension_calling_thread(contextHandle, extension) : _emscripten_webgl_enable_extension_main_thread(contextHandle, extension); + }, +#else + emscripten_webgl_enable_extension: 'emscripten_webgl_enable_extension_calling_thread', +#endif + emscripten_set_webglcontextlost_callback_on_thread__proxy: 'sync', emscripten_set_webglcontextlost_callback_on_thread__sig: 'iiiiii', emscripten_set_webglcontextlost_callback_on_thread: function(target, userData, useCapture, callbackfunc, targetThread) { @@ -2256,40 +2397,200 @@ var LibraryJSEvents = { emscripten_is_webgl_context_lost__sig: 'ii', emscripten_is_webgl_context_lost: function(target) { // TODO: In the future if multiple GL contexts are supported, use the 'target' parameter to find the canvas to query. - if (!Module.ctx) return true; // No context ~> lost context. - return Module.ctx.isContextLost(); + return Module.ctx ? Module.ctx.isContextLost() : true; // No context ~> lost context. }, - emscripten_set_canvas_element_size: function(target, width, height) { - if (target) target = JSEvents.findEventTarget(target); - else target = Module['canvas']; - if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; +#if USE_PTHREADS + emscripten_set_canvas_element_size_calling_thread__deps: ['emscripten_set_offscreencanvas_size_on_target_thread'], + emscripten_set_canvas_element_size_calling_thread: function(target, width, height) { + var canvas = JSEvents.findCanvasEventTarget(target); + if (!canvas) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + + if (canvas.canvasSharedPtr) { + // N.B. We hold the canvasSharedPtr info structure as the authoritative source for specifying the size of a canvas + // since the actual canvas size changes are asynchronous if the canvas is owned by an OffscreenCanvas on another thread. + // Therefore when setting the size, eagerly set the size of the canvas on the calling thread here, though this thread + // might not be the one that actually ends up specifying the size, but the actual size change may be dispatched + // as an asynchronous event below. + {{{ makeSetValue('canvas.canvasSharedPtr', 0, 'width', 'i32') }}}; + {{{ makeSetValue('canvas.canvasSharedPtr', 4, 'height', 'i32') }}}; + } - target.width = width; - target.height = height; + if (canvas.offscreenCanvas || !canvas.controlTransferredOffscreen) { + if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; + var autoResizeViewport = false; + if (canvas.GLctxObject && canvas.GLctxObject.GLctx) { + var prevViewport = canvas.GLctxObject.GLctx.getParameter(canvas.GLctxObject.GLctx.VIEWPORT); + // TODO: Perhaps autoResizeViewport should only be true if FBO 0 is currently active? + autoResizeViewport = (prevViewport[0] === 0 && prevViewport[1] === 0 && prevViewport[2] === canvas.width && prevViewport[3] === canvas.height); +#if GL_DEBUG + console.error('Resizing canvas from ' + canvas.width + 'x' + canvas.height + ' to ' + width + 'x' + height + '. Previous GL viewport size was ' + + prevViewport + ', so autoResizeViewport=' + autoResizeViewport); +#endif + } + canvas.width = width; + canvas.height = height; + if (autoResizeViewport) { +#if GL_DEBUG + console.error('Automatically resizing GL viewport to cover whole render target ' + width + 'x' + height); +#endif + // TODO: Add -s CANVAS_RESIZE_SETS_GL_VIEWPORT=0/1 option (default=1). This is commonly done and several graphics engines depend on this, + // but this can be quite disruptive. + canvas.GLctxObject.GLctx.viewport(0, 0, width, height); + } + } else if (canvas.canvasSharedPtr) { + var targetThread = {{{ makeGetValue('canvas.canvasSharedPtr', 8, 'i32') }}}; + _emscripten_set_offscreencanvas_size_on_target_thread(targetThread, target, width, height); + return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; // This will have to be done asynchronously + } else { +#if GL_DEBUG + console.error('canvas.controlTransferredOffscreen but we do not own the canvas, and do not know who has (no canvas.canvasSharedPtr present, an internal bug?)!\n'); +#endif + return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + } #if OFFSCREEN_FRAMEBUFFER if (canvas.GLctxObject) GL.resizeOffscreenFramebuffer(canvas.GLctxObject); #endif return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, - emscripten_get_canvas_element_size: function(target, width, height) { - if (target) target = JSEvents.findEventTarget(target); - else target = Module['canvas']; - if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + emscripten_set_offscreencanvas_size_on_target_thread: function(targetThread, targetCanvas, width, height) { + var stackTop = stackSave(); + var varargs = stackAlloc(12); + + // TODO: This could be optimized a bit (basically a dumb encoding agnostic strdup) + var targetStr = targetCanvas ? Pointer_stringify(targetCanvas) : 0; + var targetStrHeap = targetStr ? _malloc(targetStr.length+1) : 0; + if (targetStrHeap) stringToUTF8(targetStr, targetStrHeap, targetStr.length+1); + + {{{ makeSetValue('varargs', 0, 'targetStrHeap', 'i32')}}}; + {{{ makeSetValue('varargs', 4, 'width', 'i32')}}}; + {{{ makeSetValue('varargs', 8, 'height', 'i32')}}}; + // Note: If we are also a pthread, the call below could theoretically be done synchronously. However if the target pthread is waiting for a mutex from us, then + // these two threads will deadlock. At the moment, we'd like to consider that this kind of deadlock would be an Emscripten runtime bug, although if + // emscripten_set_canvas_element_size() was documented to require running an event in the queue of thread that owns the OffscreenCanvas, then that might be ok. + // (safer this way however) + _emscripten_async_queue_on_thread_(targetThread, {{{ cDefine('EM_PROXIED_RESIZE_OFFSCREENCANVAS') }}}, 0, targetStrHeap /* satellite data */, varargs); + stackRestore(stackTop); + }, - {{{ makeSetValue('width', '0', 'target.width', 'i32') }}}; - {{{ makeSetValue('height', '0', 'target.height', 'i32') }}}; + emscripten_set_canvas_element_size_main_thread__proxy: 'sync', + emscripten_set_canvas_element_size_main_thread__sig: 'iiii', + emscripten_set_canvas_element_size_main_thread__deps: ['emscripten_set_canvas_element_size_calling_thread'], + emscripten_set_canvas_element_size_main_thread: function(target, width, height) { return _emscripten_set_canvas_element_size_calling_thread(target, width, height); }, + + emscripten_set_canvas_element_size__deps: ['emscripten_set_canvas_element_size_calling_thread', 'emscripten_set_canvas_element_size_main_thread'], + emscripten_set_canvas_element_size: function(target, width, height) { +#if GL_DEBUG + console.error('emscripten_set_canvas_element_size(target='+target+',width='+width+',height='+height); +#endif + var canvas = JSEvents.findCanvasEventTarget(target); + if (canvas) return _emscripten_set_canvas_element_size_calling_thread(target, width, height); + else return _emscripten_set_canvas_element_size_main_thread(target, width, height); + }, +#else + emscripten_set_canvas_element_size: function(target, width, height) { +#if GL_DEBUG + console.error('emscripten_set_canvas_element_size(target='+target+',width='+width+',height='+height); +#endif + var canvas = JSEvents.findCanvasEventTarget(target); + if (!canvas) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + canvas.width = width; + canvas.height = height; +#if OFFSCREEN_FRAMEBUFFER + if (canvas.GLctxObject) GL.resizeOffscreenFramebuffer(canvas.GLctxObject); +#endif return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, +#endif - emscripten_set_element_css_size: function(target, width, height) { - if (!target) { - target = Module['canvas']; + _set_canvas_element_size__deps: ['emscripten_set_canvas_element_size'], + _set_canvas_element_size: function(target, width, height) { +#if GL_DEBUG + console.error('_set_canvas_element_size(target='+target+',width='+width+',height='+height); +#endif + if (!target.controlTransferredOffscreen) { + target.width = width; + target.height = height; } else { - target = JSEvents.findEventTarget(target); + // This function is being called from high-level JavaScript code instead of asm.js/Wasm, + // and it needs to synchronously proxy over to another thread, so marshal the string onto the heap to do the call. + var stackTop = stackSave(); + var targetInt = stackAlloc(target.id.length+1); + stringToUTF8(target.id, targetInt, target.id.length+1); + _emscripten_set_canvas_element_size(targetInt, width, height); + stackRestore(stackTop); } + }, +#if USE_PTHREADS + emscripten_get_canvas_element_size_calling_thread: function(target, width, height) { + var canvas = JSEvents.findCanvasEventTarget(target); + if (!canvas) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + + if (canvas.canvasSharedPtr) { + // N.B. Reading the size of the Canvas takes priority from our shared state structure, which is not the actual size. + // However if is possible that there is a canvas size set event pending on an OffscreenCanvas owned by another thread, + // so that the real sizes of the canvas have not updated yet. Therefore reading the real values would be racy. + var w = {{{ makeGetValue('canvas.canvasSharedPtr', 0, 'i32') }}}; + var h = {{{ makeGetValue('canvas.canvasSharedPtr', 4, 'i32') }}}; + {{{ makeSetValue('width', 0, 'w', 'i32') }}}; + {{{ makeSetValue('height', 0, 'h', 'i32') }}}; + } else if (canvas.offscreenCanvas) { + {{{ makeSetValue('width', 0, 'canvas.offscreenCanvas.width', 'i32') }}}; + {{{ makeSetValue('height', 0, 'canvas.offscreenCanvas.height', 'i32') }}}; + } else if (!canvas.controlTransferredOffscreen) { + {{{ makeSetValue('width', 0, 'canvas.width', 'i32') }}}; + {{{ makeSetValue('height', 0, 'canvas.height', 'i32') }}}; + } else { +#if GL_DEBUG + console.error('canvas.controlTransferredOffscreen but we do not own the canvas, and do not know who has (no canvas.canvasSharedPtr present, an internal bug?)!\n'); +#endif + return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_canvas_element_size_main_thread__proxy: 'sync', + emscripten_get_canvas_element_size_main_thread__sig: 'iiii', + emscripten_get_canvas_element_size_main_thread__deps: ['emscripten_get_canvas_element_size_calling_thread'], + emscripten_get_canvas_element_size_main_thread: function(target, width, height) { return _emscripten_get_canvas_element_size_calling_thread(target, width, height); }, + + emscripten_get_canvas_element_size__deps: ['emscripten_get_canvas_element_size_calling_thread', 'emscripten_get_canvas_element_size_main_thread'], + emscripten_get_canvas_element_size: function(target, width, height) { + var canvas = JSEvents.findCanvasEventTarget(target); + if (canvas) return _emscripten_get_canvas_element_size_calling_thread(target, width, height); + else return _emscripten_get_canvas_element_size_main_thread(target, width, height); + }, +#else + emscripten_get_canvas_element_size: function(target, width, height) { + var canvas = JSEvents.findCanvasEventTarget(target); + if (!canvas) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + {{{ makeSetValue('width', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('height', '0', 'canvas.height', 'i32') }}}; + }, +#endif + + // JavaScript-friendly API, returns pair [width, height] + _get_canvas_element_size__deps: ['emscripten_get_canvas_element_size'], + _get_canvas_element_size: function(target) { + var stackTop = stackSave(); + var w = stackAlloc(8); + var h = w + 4; + + var targetInt = stackAlloc(target.id.length+1); + stringToUTF8(target.id, targetInt, target.id.length+1); + var ret = _emscripten_get_canvas_element_size(targetInt, w, h); + var size = [{{{ makeGetValue('w', 0, 'i32')}}}, {{{ makeGetValue('h', 0, 'i32')}}}]; + stackRestore(stackTop); + return size; + }, + + emscripten_set_element_css_size__proxy: 'sync', + emscripten_set_element_css_size__sig: 'iiii', + emscripten_set_element_css_size: function(target, width, height) { + if (target) target = JSEvents.findEventTarget(target); + else target = Module['canvas']; if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; target.style.setProperty("width", width + "px"); diff --git a/src/library_pthread.js b/src/library_pthread.js index 1c0657a5d7f1e..ce052a296ac1b 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -166,8 +166,11 @@ var LibraryPThread = { offscreenCanvases = GL.offscreenCanvases; GL.offscreenCanvases = {}; } +#if PTHREADS_DEBUG + console.error('[thread ' + _pthread_self() + ', ENVIRONMENT_IS_PTHREAD: ' + ENVIRONMENT_IS_PTHREAD + ']: returning ' + Object.keys(offscreenCanvases).length + ' OffscreenCanvases to parent thread ' + parentThreadId); +#endif for (var i in offscreenCanvases) { - if (offscreenCanvases[i]) transferList.push(offscreenCanvases[i]); + if (offscreenCanvases[i]) transferList.push(offscreenCanvases[i].offscreenCanvas); } if (transferList.length > 0) { postMessage({ @@ -242,9 +245,11 @@ var LibraryPThread = { if (typeof GL !== 'undefined') { for (var i in data.offscreenCanvases) { GL.offscreenCanvases[i] = data.offscreenCanvases[i]; - GL.offscreenCanvases[i].id = i; // https://bugzilla.mozilla.org/show_bug.cgi?id=1281909 } - if (!Module['canvas']) Module['canvas'] = GL.offscreenCanvases[data.moduleCanvasId]; + if (!Module['canvas'] && data.moduleCanvasId && GL.offscreenCanvases[data.moduleCanvasId]) { + Module['canvas'] = GL.offscreenCanvases[data.moduleCanvasId].offscreenCanvas; + Module['canvas'].id = data.moduleCanvasId; + } } #endif }, @@ -551,23 +556,29 @@ var LibraryPThread = { } var transferList = []; // List of JS objects that will transfer ownership to the Worker hosting the thread + var error = 0; #if OFFSCREENCANVAS_SUPPORT // Deduce which WebGL canvases (HTMLCanvasElements or OffscreenCanvases) should be passed over to the // Worker that hosts the spawned pthread. - var transferredCanvasNames = {{{ makeGetValue('attr', 36, 'i32') }}}; // Comma-delimited list of IDs "canvas1, canvas2, ..." - if (transferredCanvasNames) transferredCanvasNames = Pointer_stringify(transferredCanvasNames).split(','); + var transferredCanvasNames = attr ? {{{ makeGetValue('attr', 36, 'i32') }}} : 0; // Comma-delimited list of IDs "canvas1, canvas2, ..." + if (transferredCanvasNames) transferredCanvasNames = Pointer_stringify(transferredCanvasNames).trim(); + if (transferredCanvasNames) transferredCanvasNames = transferredCanvasNames.split(','); +#if GL_DEBUG + console.log('pthread_create: transferredCanvasNames="' + transferredCanvasNames + '"'); +#endif var offscreenCanvases = {}; // Dictionary of OffscreenCanvas objects we'll transfer to the created thread to own var moduleCanvasId = Module['canvas'] ? Module['canvas'].id : ''; for (var i in transferredCanvasNames) { var name = transferredCanvasNames[i].trim(); - var offscreenCanvas; + var offscreenCanvasInfo; try { if (name == '#canvas') { if (!Module['canvas']) { err('pthread_create: could not find canvas with ID "' + name + '" to transfer to thread!'); - return {{{ cDefine('EINVAL') }}}; + error = {{{ cDefine('EINVAL') }}}; + break; } name = Module['canvas'].id; } @@ -575,29 +586,53 @@ var LibraryPThread = { assert(typeof GL === 'object', 'OFFSCREENCANVAS_SUPPORT assumes GL is in use (you can force-include it with -s \'DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=["$GL"]\')'); #endif if (GL.offscreenCanvases[name]) { - offscreenCanvas = GL.offscreenCanvases[name]; + offscreenCanvasInfo = GL.offscreenCanvases[name]; GL.offscreenCanvases[name] = null; // This thread no longer owns this canvas. if (Module['canvas'] instanceof OffscreenCanvas && name === Module['canvas'].id) Module['canvas'] = null; } else { - var canvas = (Module['canvas'] && Module['canvas'].id === name) ? Module['canvas'] : document.getElementByID(name); + var canvas = (Module['canvas'] && Module['canvas'].id === name) ? Module['canvas'] : document.getElementById(name); if (!canvas) { err('pthread_create: could not find canvas with ID "' + name + '" to transfer to thread!'); - return {{{ cDefine('EINVAL') }}}; + error = {{{ cDefine('EINVAL') }}}; + break; } if (canvas.controlTransferredOffscreen) { err('pthread_create: cannot transfer canvas with ID "' + name + '" to thread, since the current thread does not have control over it!'); - return {{{ cDefine('EPERM') }}}; // Operation not permitted, some other thread is accessing the canvas. + error = {{{ cDefine('EPERM') }}}; // Operation not permitted, some other thread is accessing the canvas. + break; } - if (!canvas.transferControlToOffscreen) { + if (canvas.transferControlToOffscreen) { +#if GL_DEBUG + Module['printErr']('pthread_create: canvas.transferControlToOffscreen(), transferring canvas by name "' + name + '" (DOM id="' + canvas.id + '") from main thread to pthread'); +#endif + // Create a shared information block in heap so that we can control the canvas size from any thread. + if (!canvas.canvasSharedPtr) { + canvas.canvasSharedPtr = _malloc(12); + {{{ makeSetValue('canvas.canvasSharedPtr', 0, 'canvas.width', 'i32') }}}; + {{{ makeSetValue('canvas.canvasSharedPtr', 4, 'canvas.height', 'i32') }}}; + {{{ makeSetValue('canvas.canvasSharedPtr', 8, 0, 'i32') }}}; // pthread ptr to the thread that owns this canvas, filled in below. + } + offscreenCanvasInfo = { + offscreenCanvas: canvas.transferControlToOffscreen(), + canvasSharedPtr: canvas.canvasSharedPtr, + id: canvas.id + } + // After calling canvas.transferControlToOffscreen(), it is no longer possible to access certain operations on the canvas, such as resizing it or obtaining GL contexts via it. + // Use this field to remember that we have permanently converted this Canvas to be controlled via an OffscreenCanvas (there is no way to undo this in the spec) + canvas.controlTransferredOffscreen = true; + } else { err('pthread_create: cannot transfer control of canvas "' + name + '" to pthread, because current browser does not support OffscreenCanvas!'); + // If building with OFFSCREEN_FRAMEBUFFER=1 mode, we don't need to be able to transfer control to offscreen, but WebGL can be proxied from worker to main thread. +#if !OFFSCREEN_FRAMEBUFFER + Module['printErr']('pthread_create: Build with -s OFFSCREEN_FRAMEBUFFER=1 to enable fallback proxying of GL commands from pthread to main thread.'); return {{{ cDefine('ENOSYS') }}}; // Function not implemented, browser doesn't have support for this. +#endif } - offscreenCanvas = canvas.transferControlToOffscreen(); - canvas.controlTransferredOffscreen = true; - offscreenCanvas.id = canvas.id; } - transferList.push(offscreenCanvas); - offscreenCanvases[offscreenCanvas.id] = offscreenCanvas; + if (offscreenCanvasInfo) { + transferList.push(offscreenCanvasInfo.offscreenCanvas); + offscreenCanvases[offscreenCanvasInfo.id] = offscreenCanvasInfo; + } } catch(e) { err('pthread_create: failed to transfer control of canvas "' + name + '" to OffscreenCanvas! Error: ' + e); return {{{ cDefine('EINVAL') }}}; // Hitting this might indicate an implementation bug or some other internal error @@ -607,10 +642,13 @@ var LibraryPThread = { // Synchronously proxy the thread creation to main thread if possible. If we need to transfer ownership of objects, then // proxy asynchronously via postMessage. - if (ENVIRONMENT_IS_PTHREAD && transferList.length == 0) { + if (ENVIRONMENT_IS_PTHREAD && (transferList.length == 0 || error)) { return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_PTHREAD_CREATE') }}}, pthread_ptr, attr, start_routine, arg); } + // If on the main thread, and accessing Canvas/OffscreenCanvas failed, abort with the detected error. + if (error) return error; + var stackSize = 0; var stackBase = 0; var detached = 0; // Default thread attr is PTHREAD_CREATE_JOINABLE, i.e. start as not detached. @@ -668,6 +706,13 @@ var LibraryPThread = { var headPtr = threadInfoStruct + {{{ C_STRUCTS.pthread.robust_list }}}; {{{ makeSetValue('headPtr', 0, 'headPtr', 'i32') }}}; +#if OFFSCREENCANVAS_SUPPORT + // Register for each of the transferred canvases that the new thread now owns the OffscreenCanvas. + for (var i in offscreenCanvases) { + {{{ makeSetValue('offscreenCanvases[i].canvasSharedPtr', 8, 'threadInfoStruct', 'i32') }}}; // pthread ptr to the thread that owns this canvas. + } +#endif + var threadParams = { stackBase: stackBase, stackSize: stackSize, diff --git a/src/struct_info.json b/src/struct_info.json index 5d04643b806ce..ae7543fa45b7e 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1238,6 +1238,10 @@ "EM_HTML5_MEDIUM_STRING_LEN_BYTES", "EM_HTML5_LONG_STRING_LEN_BYTES", + "EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW", + "EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK", + "EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS", + "EM_CALLBACK_THREAD_CONTEXT_MAIN_BROWSER_THREAD", "EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD" ], @@ -1394,6 +1398,7 @@ "minorVersion", "enableExtensionsByDefault", "explicitSwapControl", + "proxyContextToMainThread", "renderViaOffscreenBackBuffer" ], "EmscriptenFullscreenStrategy": [ @@ -1490,6 +1495,8 @@ "defines": [ "EM_PROXIED_PTHREAD_CREATE", "EM_PROXIED_SYSCALL", + "EM_PROXIED_CREATE_CONTEXT", + "EM_PROXIED_RESIZE_OFFSCREENCANVAS", "EM_THREAD_STATUS_NOTSTARTED", "EM_THREAD_STATUS_RUNNING", "EM_THREAD_STATUS_SLEEPING", diff --git a/system/include/emscripten/html5.h b/system/include/emscripten/html5.h index fc4a9fbdb3f32..f1c920a094481 100644 --- a/system/include/emscripten/html5.h +++ b/system/include/emscripten/html5.h @@ -420,6 +420,11 @@ extern EMSCRIPTEN_RESULT emscripten_set_beforeunload_callback_on_thread(void *us typedef int EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; +typedef int EMSCRIPTEN_WEBGL_CONTEXT_PROXY_MODE; +#define EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW 0 +#define EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK 1 +#define EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS 2 + typedef struct EmscriptenWebGLContextAttributes { EM_BOOL alpha; EM_BOOL depth; @@ -435,6 +440,7 @@ typedef struct EmscriptenWebGLContextAttributes { EM_BOOL enableExtensionsByDefault; EM_BOOL explicitSwapControl; + EMSCRIPTEN_WEBGL_CONTEXT_PROXY_MODE proxyContextToMainThread; EM_BOOL renderViaOffscreenBackBuffer; } EmscriptenWebGLContextAttributes; @@ -460,6 +466,8 @@ extern EM_BOOL emscripten_is_webgl_context_lost(const char *target); extern EMSCRIPTEN_RESULT emscripten_webgl_commit_frame(void); +extern EM_BOOL emscripten_supports_offscreencanvas(void); + extern EMSCRIPTEN_RESULT emscripten_set_canvas_element_size(const char *target, int width, int height); extern EMSCRIPTEN_RESULT emscripten_get_canvas_element_size(const char *target, int *width, int *height); diff --git a/system/include/emscripten/threading.h b/system/include/emscripten/threading.h index 6fad056568f6e..ec2197ec67793 100644 --- a/system/include/emscripten/threading.h +++ b/system/include/emscripten/threading.h @@ -191,6 +191,13 @@ typedef int (*em_func_iiiiiiiiii)(int, int, int, int, int, int, int, int, int); #define EM_FUNC_SIG_PARAM_D 0x3U #define EM_FUNC_SIG_SET_PARAM(i, type) ((EM_FUNC_SIGNATURE)(type) << (2*i)) +// In total, the above encoding scheme gives the following 32-bit structure for the proxied function signatures (highest -> lowest bit order): +// RRRiiiiSbbaa99887766554433221100 +// where RRR is return type +// iiii is the number of inputs +// S denotes a special function (internal proxying mechanism for functions related to built-in threading APIs, like thread creation itself) +// 00-bb encode the type of up to 12 function parameters + #define EM_FUNC_SIG_V (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) #define EM_FUNC_SIG_VI (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) #define EM_FUNC_SIG_VF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F)) @@ -226,6 +233,15 @@ typedef int (*em_func_iiiiiiiiii)(int, int, int, int, int, int, int, int, int); #define EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(x) ((((EM_FUNC_SIGNATURE)x) & EM_FUNC_SIG_NUM_PARAMETERS_MASK) >> EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) +// There are some built-in special proxied functions, that embed the signatures inside the above encoding scheme +#define EM_FUNC_SIG_SPECIAL_INTERNAL (1 << 24) +#define EM_PROXIED_FUNC_SPECIAL(x) (EM_FUNC_SIG_SPECIAL_INTERNAL | ((x) << 20)) + +#define EM_PROXIED_PTHREAD_CREATE (EM_PROXIED_FUNC_SPECIAL(0) | EM_FUNC_SIG_IIIII) +#define EM_PROXIED_SYSCALL (EM_PROXIED_FUNC_SPECIAL(1) | EM_FUNC_SIG_III) +#define EM_PROXIED_CREATE_CONTEXT (EM_PROXIED_FUNC_SPECIAL(2) | EM_FUNC_SIG_III) +#define EM_PROXIED_RESIZE_OFFSCREENCANVAS (EM_PROXIED_FUNC_SPECIAL(3) | EM_FUNC_SIG_IIII) + // Runs the given function synchronously on the main Emscripten runtime thread. // If this thread is the main thread, the operation is immediately performed, and the result is returned. // If the current thread is not the main Emscripten runtime thread (but a pthread), the function @@ -341,9 +357,6 @@ struct thread_profiler_block char name[32]; }; -#define EM_PROXIED_PTHREAD_CREATE 137 -#define EM_PROXIED_SYSCALL 138 - #ifdef __cplusplus } #endif diff --git a/system/lib/gl.symbols b/system/lib/gl.symbols index 8ba8d6f77fc0e..6c3b04a8dab8e 100644 --- a/system/lib/gl.symbols +++ b/system/lib/gl.symbols @@ -1 +1,327 @@ - T emscripten_GetProcAddress +-------- T emscripten_GetProcAddress + U emscripten_asm_const_int + U emscripten_async_run_in_main_runtime_thread_ + U emscripten_glActiveTexture + U emscripten_glAttachShader + U emscripten_glBindAttribLocation + U emscripten_glBindBuffer + U emscripten_glBindFramebuffer + U emscripten_glBindProgramARB + U emscripten_glBindRenderbuffer + U emscripten_glBindTexture + U emscripten_glBindVertexArray + U emscripten_glBlendColor + U emscripten_glBlendEquation + U emscripten_glBlendEquationSeparate + U emscripten_glBlendFunc + U emscripten_glBlendFuncSeparate + U emscripten_glBufferData + U emscripten_glBufferSubData + U emscripten_glCheckFramebufferStatus + U emscripten_glClear + U emscripten_glClearColor + U emscripten_glClearDepth + U emscripten_glClearDepthf + U emscripten_glClearStencil + U emscripten_glClientActiveTexture + U emscripten_glColorMask + U emscripten_glColorPointer + U emscripten_glCompileShader + U emscripten_glCompressedTexImage2D + U emscripten_glCompressedTexSubImage2D + U emscripten_glCopyTexImage2D + U emscripten_glCopyTexSubImage2D + U emscripten_glCreateProgram + U emscripten_glCreateShader + U emscripten_glCullFace + U emscripten_glDeleteBuffers + U emscripten_glDeleteFramebuffers + U emscripten_glDeleteObjectARB + U emscripten_glDeleteProgram + U emscripten_glDeleteRenderbuffers + U emscripten_glDeleteShader + U emscripten_glDeleteTextures + U emscripten_glDeleteVertexArrays + U emscripten_glDepthFunc + U emscripten_glDepthMask + U emscripten_glDepthRange + U emscripten_glDepthRangef + U emscripten_glDetachShader + U emscripten_glDisable + U emscripten_glDisableVertexAttribArray + U emscripten_glDrawArrays + U emscripten_glDrawArraysInstanced + U emscripten_glDrawBuffers + U emscripten_glDrawElements + U emscripten_glDrawElementsInstanced + U emscripten_glDrawRangeElements + U emscripten_glEnable + U emscripten_glEnableClientState + U emscripten_glEnableVertexAttribArray + U emscripten_glFinish + U emscripten_glFlush + U emscripten_glFramebufferRenderbuffer + U emscripten_glFramebufferTexture2D + U emscripten_glFrontFace + U emscripten_glFrustum + U emscripten_glGenBuffers + U emscripten_glGenFramebuffers + U emscripten_glGenRenderbuffers + U emscripten_glGenTextures + U emscripten_glGenVertexArrays + U emscripten_glGenerateMipmap + U emscripten_glGetActiveAttrib + U emscripten_glGetActiveUniform + U emscripten_glGetAttachedShaders + U emscripten_glGetAttribLocation + U emscripten_glGetBooleanv + U emscripten_glGetBufferParameteriv + U emscripten_glGetError + U emscripten_glGetFloatv + U emscripten_glGetFramebufferAttachmentParameteriv + U emscripten_glGetInfoLogARB + U emscripten_glGetIntegerv + U emscripten_glGetObjectParameterivARB + U emscripten_glGetPointerv + U emscripten_glGetProgramInfoLog + U emscripten_glGetProgramiv + U emscripten_glGetRenderbufferParameteriv + U emscripten_glGetShaderInfoLog + U emscripten_glGetShaderPrecisionFormat + U emscripten_glGetShaderSource + U emscripten_glGetShaderiv + U emscripten_glGetString + U emscripten_glGetTexParameterfv + U emscripten_glGetTexParameteriv + U emscripten_glGetUniformLocation + U emscripten_glGetUniformfv + U emscripten_glGetUniformiv + U emscripten_glGetVertexAttribPointerv + U emscripten_glGetVertexAttribfv + U emscripten_glGetVertexAttribiv + U emscripten_glHint + U emscripten_glIsBuffer + U emscripten_glIsEnabled + U emscripten_glIsFramebuffer + U emscripten_glIsProgram + U emscripten_glIsRenderbuffer + U emscripten_glIsShader + U emscripten_glIsTexture + U emscripten_glLineWidth + U emscripten_glLinkProgram + U emscripten_glLoadIdentity + U emscripten_glLoadMatrixf + U emscripten_glMatrixMode + U emscripten_glNormalPointer + U emscripten_glPixelStorei + U emscripten_glPolygonOffset + U emscripten_glReadPixels + U emscripten_glReleaseShaderCompiler + U emscripten_glRenderbufferStorage + U emscripten_glRotatef + U emscripten_glSampleCoverage + U emscripten_glScissor + U emscripten_glShaderBinary + U emscripten_glShaderSource + U emscripten_glStencilFunc + U emscripten_glStencilFuncSeparate + U emscripten_glStencilMask + U emscripten_glStencilMaskSeparate + U emscripten_glStencilOp + U emscripten_glStencilOpSeparate + U emscripten_glTexCoordPointer + U emscripten_glTexImage2D + U emscripten_glTexParameterf + U emscripten_glTexParameterfv + U emscripten_glTexParameteri + U emscripten_glTexParameteriv + U emscripten_glTexSubImage2D + U emscripten_glUniform1f + U emscripten_glUniform1fv + U emscripten_glUniform1i + U emscripten_glUniform1iv + U emscripten_glUniform2f + U emscripten_glUniform2fv + U emscripten_glUniform2i + U emscripten_glUniform2iv + U emscripten_glUniform3f + U emscripten_glUniform3fv + U emscripten_glUniform3i + U emscripten_glUniform3iv + U emscripten_glUniform4f + U emscripten_glUniform4fv + U emscripten_glUniform4i + U emscripten_glUniform4iv + U emscripten_glUniformMatrix2fv + U emscripten_glUniformMatrix3fv + U emscripten_glUniformMatrix4fv + U emscripten_glUseProgram + U emscripten_glValidateProgram + U emscripten_glVertexAttrib1f + U emscripten_glVertexAttrib1fv + U emscripten_glVertexAttrib2f + U emscripten_glVertexAttrib2fv + U emscripten_glVertexAttrib3f + U emscripten_glVertexAttrib3fv + U emscripten_glVertexAttrib4f + U emscripten_glVertexAttrib4fv + U emscripten_glVertexAttribDivisor + U emscripten_glVertexAttribPointer + U emscripten_glVertexPointer + U emscripten_glViewport + U emscripten_sync_run_in_main_runtime_thread_ +-------- T emscripten_webgl_commit_frame +-------- T emscripten_webgl_create_context + U emscripten_webgl_do_commit_frame + U emscripten_webgl_do_create_context + U emscripten_webgl_do_get_current_context + U emscripten_webgl_make_context_current_calling_thread +-------- T emscripten_webgl_get_current_context +-------- T emscripten_webgl_make_context_current +-------- T glActiveTexture +-------- T glAttachShader +-------- T glBindAttribLocation +-------- T glBindBuffer +-------- T glBindFramebuffer +-------- T glBindRenderbuffer +-------- T glBindTexture +-------- T glBlendColor +-------- T glBlendEquation +-------- T glBlendEquationSeparate +-------- T glBlendFunc +-------- T glBlendFuncSeparate +-------- T glBufferData +-------- T glBufferSubData +-------- T glCheckFramebufferStatus +-------- T glClear +-------- T glClearColor +-------- T glClearDepthf +-------- T glClearStencil +-------- T glColorMask +-------- T glCompileShader +-------- T glCompressedTexImage2D +-------- T glCompressedTexSubImage2D +-------- T glCopyTexImage2D +-------- T glCopyTexSubImage2D +-------- T glCreateProgram +-------- T glCreateShader +-------- T glCullFace +-------- T glDeleteBuffers +-------- T glDeleteFramebuffers +-------- T glDeleteProgram +-------- T glDeleteRenderbuffers +-------- T glDeleteShader +-------- T glDeleteTextures +-------- T glDepthFunc +-------- T glDepthMask +-------- T glDepthRangef +-------- T glDetachShader +-------- T glDisable +-------- T glDisableVertexAttribArray +-------- T glDrawArrays +-------- T glDrawElements +-------- T glEnable +-------- T glEnableVertexAttribArray +-------- T glFinish +-------- T glFlush +-------- T glFramebufferRenderbuffer +-------- T glFramebufferTexture2D +-------- T glFrontFace +-------- T glGenBuffers +-------- T glGenFramebuffers +-------- T glGenRenderbuffers +-------- T glGenTextures +-------- T glGenerateMipmap +-------- T glGetActiveAttrib +-------- T glGetActiveUniform +-------- T glGetAttachedShaders +-------- T glGetAttribLocation +-------- T glGetBooleanv +-------- T glGetBufferParameteriv +-------- T glGetError +-------- T glGetFloatv +-------- T glGetFramebufferAttachmentParameteriv +-------- T glGetIntegerv +-------- T glGetProgramInfoLog +-------- T glGetProgramiv +-------- T glGetRenderbufferParameteriv +-------- T glGetShaderInfoLog +-------- T glGetShaderPrecisionFormat +-------- T glGetShaderSource +-------- T glGetShaderiv +-------- T glGetString +-------- T glGetTexParameterfv +-------- T glGetTexParameteriv +-------- T glGetUniformLocation +-------- T glGetUniformfv +-------- T glGetUniformiv +-------- T glGetVertexAttribPointerv +-------- T glGetVertexAttribfv +-------- T glGetVertexAttribiv +-------- T glHint +-------- T glIsBuffer +-------- T glIsEnabled +-------- T glIsFramebuffer +-------- T glIsProgram +-------- T glIsRenderbuffer +-------- T glIsShader +-------- T glIsTexture +-------- T glLineWidth +-------- T glLinkProgram +-------- T glPixelStorei +-------- T glPolygonOffset +-------- T glReadPixels +-------- T glReleaseShaderCompiler +-------- T glRenderbufferStorage +-------- T glSampleCoverage +-------- T glScissor +-------- T glShaderBinary +-------- T glShaderSource +-------- T glStencilFunc +-------- T glStencilFuncSeparate +-------- T glStencilMask +-------- T glStencilMaskSeparate +-------- T glStencilOp +-------- T glStencilOpSeparate +-------- T glTexImage2D +-------- T glTexParameterf +-------- T glTexParameterfv +-------- T glTexParameteri +-------- T glTexParameteriv +-------- T glTexSubImage2D +-------- T glUniform1f +-------- T glUniform1fv +-------- T glUniform1i +-------- T glUniform1iv +-------- T glUniform2f +-------- T glUniform2fv +-------- T glUniform2i +-------- T glUniform2iv +-------- T glUniform3f +-------- T glUniform3fv +-------- T glUniform3i +-------- T glUniform3iv +-------- T glUniform4f +-------- T glUniform4fv +-------- T glUniform4i +-------- T glUniform4iv +-------- T glUniformMatrix2fv +-------- T glUniformMatrix3fv +-------- T glUniformMatrix4fv +-------- T glUseProgram +-------- T glValidateProgram +-------- T glVertexAttrib1f +-------- T glVertexAttrib1fv +-------- T glVertexAttrib2f +-------- T glVertexAttrib2fv +-------- T glVertexAttrib3f +-------- T glVertexAttrib3fv +-------- T glVertexAttrib4f +-------- T glVertexAttrib4fv +-------- T glVertexAttribPointer +-------- T glViewport + U malloc + U strcmp + U strcpy + U strlen + U strstr diff --git a/system/lib/gl.c b/system/lib/gl/gl.c similarity index 100% rename from system/lib/gl.c rename to system/lib/gl/gl.c diff --git a/system/lib/gl/webgl1.c b/system/lib/gl/webgl1.c new file mode 100644 index 0000000000000..d2da92093aaab --- /dev/null +++ b/system/lib/gl/webgl1.c @@ -0,0 +1,627 @@ +#ifdef __EMSCRIPTEN_PTHREADS__ + +#include +#include +#include +#include + +#include "webgl1.h" +#include "webgl1_ext.h" +#include "webgl2.h" + +extern EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emscripten_webgl_do_create_context(const char *target, const EmscriptenWebGLContextAttributes *attributes); +extern EMSCRIPTEN_RESULT emscripten_webgl_make_context_current_calling_thread(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context); +extern EMSCRIPTEN_RESULT emscripten_webgl_do_commit_frame(void); +extern EM_BOOL emscripten_supports_offscreencanvas(void); +extern EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emscripten_webgl_do_get_current_context(void); +extern void _emscripten_proxied_gl_context_activated_from_main_browser_thread(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context); + +pthread_key_t currentActiveWebGLContext; +pthread_key_t currentThreadOwnsItsWebGLContext; +static pthread_once_t tlsInit = PTHREAD_ONCE_INIT; + +static void InitWebGLTls() +{ + pthread_key_create(¤tActiveWebGLContext, NULL); + pthread_key_create(¤tThreadOwnsItsWebGLContext, NULL); +} + +EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emscripten_webgl_create_context(const char *target, const EmscriptenWebGLContextAttributes *attributes) +{ + GL_FUNCTION_TRACE(__func__); + if (!attributes) + { + EM_ASM(console.error('emscripten_webgl_create_context: attributes pointer is null!')); + return 0; + } + pthread_once(&tlsInit, InitWebGLTls); + + if (attributes->proxyContextToMainThread == EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS || + (attributes->proxyContextToMainThread == EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK && !emscripten_supports_offscreencanvas())) + { + EmscriptenWebGLContextAttributes attrs = *attributes; + attrs.renderViaOffscreenBackBuffer = EM_TRUE; + return (EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_III, &emscripten_webgl_do_create_context, target, &attrs); + } + else + { + return emscripten_webgl_do_create_context(target, attributes); + } +} + +EMSCRIPTEN_RESULT emscripten_webgl_make_context_current(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) +{ + GL_FUNCTION_TRACE(__func__); + if (emscripten_webgl_get_current_context() == context) return; + + void *owningThread = *(void**)(context + 4); + if (owningThread == pthread_self()) + { + EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current_calling_thread(context); + if (r == EMSCRIPTEN_RESULT_SUCCESS) + { + pthread_setspecific(currentActiveWebGLContext, (void*)context); + pthread_setspecific(currentThreadOwnsItsWebGLContext, (void*)1); + } + return r; + } + else + { + EMSCRIPTEN_RESULT r = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_II, &emscripten_webgl_make_context_current_calling_thread, context); + if (r == EMSCRIPTEN_RESULT_SUCCESS) + { + pthread_setspecific(currentActiveWebGLContext, (void*)context); + pthread_setspecific(currentThreadOwnsItsWebGLContext, (void*)0); + _emscripten_proxied_gl_context_activated_from_main_browser_thread(context); + } + return r; + } +} + +EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emscripten_webgl_get_current_context(void) +{ + return (EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)pthread_getspecific(currentActiveWebGLContext); +} + +EMSCRIPTEN_RESULT EMSCRIPTEN_KEEPALIVE emscripten_webgl_commit_frame(void) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + return emscripten_webgl_do_commit_frame(); + else + return (EMSCRIPTEN_RESULT)emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_I, &emscripten_webgl_do_commit_frame); +} + +static void *memdup(const void *ptr, size_t sz) +{ + if (!ptr) return 0; + void *dup = malloc(sz); + if (dup) memcpy(dup, ptr, sz); + return dup; +} + +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glActiveTexture, GLenum); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glAttachShader, GLuint, GLuint); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glBindAttribLocation, GLuint, GLuint, const GLchar*); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBindBuffer, GLenum, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBindFramebuffer, GLenum, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBindRenderbuffer, GLenum, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBindTexture, GLenum, GLuint); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VFFFF, void, glBlendColor, GLfloat, GLfloat, GLfloat, GLfloat); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glBlendEquation, GLenum); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBlendEquationSeparate, GLenum, GLenum); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBlendFunc, GLenum, GLenum); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glBlendFuncSeparate, GLenum, GLenum, GLenum, GLenum); + +void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glBufferData(target, size, data, usage); + else + { + if (size < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(data, size); + if (ptr || !data) // glBufferData(data=0) can always be handled asynchronously + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIII, &emscripten_glBufferData, ptr, target, size, ptr, usage); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIII, &emscripten_glBufferData, target, size, data, usage); + } +} + +void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glBufferSubData(target, offset, size, data); + else + { + if (size < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(data, size); + if (ptr || !data) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIII, &emscripten_glBufferSubData, ptr, target, offset, size, ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIII, &emscripten_glBufferSubData, target, offset, size, data); + } +} + +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLenum, glCheckFramebufferStatus, GLenum); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glClear, GLbitfield); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VFFFF, void, glClearColor, GLfloat, GLfloat, GLfloat, GLfloat); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VF, void, glClearDepthf, GLfloat); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glClearStencil, GLint); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glColorMask, GLboolean, GLboolean, GLboolean, GLboolean); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glCompileShader, GLuint); +VOID_SYNC_GL_FUNCTION_8(EM_FUNC_SIG_VIIIIIIII, void, glCompressedTexImage2D, GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void *); +VOID_SYNC_GL_FUNCTION_9(EM_FUNC_SIG_VIIIIIIIII, void, glCompressedTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void *); +ASYNC_GL_FUNCTION_8(EM_FUNC_SIG_VIIIIIIII, void, glCopyTexImage2D, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint); +ASYNC_GL_FUNCTION_8(EM_FUNC_SIG_VIIIIIIII, void, glCopyTexSubImage2D, GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei); +RET_SYNC_GL_FUNCTION_0(EM_FUNC_SIG_I, GLuint, glCreateProgram); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLuint, glCreateShader, GLenum); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glCullFace, GLenum); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteBuffers, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteFramebuffers, GLsizei, const GLuint *); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDeleteProgram, GLuint); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteRenderbuffers, GLsizei, const GLuint *); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDeleteShader, GLuint); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteTextures, GLsizei, const GLuint *); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDepthFunc, GLenum); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDepthMask, GLboolean); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VFF, void, glDepthRangef, GLfloat, GLfloat); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDetachShader, GLuint, GLuint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDisable, GLenum); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDisableVertexAttribArray, GLuint); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glDrawArrays, GLenum, GLint, GLsizei); +// TODO: The following #define FULL_ES2 does not yet exist, we'll need to compile this file twice, for FULL_ES2 mode and without +#if FULL_ES2 +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glDrawElements, GLenum, GLsizei, GLenum, const void *); +#else +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glDrawElements, GLenum, GLsizei, GLenum, const void *); +#endif +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glEnable, GLenum); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glEnableVertexAttribArray, GLuint); +VOID_SYNC_GL_FUNCTION_0(EM_FUNC_SIG_V, void, glFinish); +VOID_SYNC_GL_FUNCTION_0(EM_FUNC_SIG_V, void, glFlush); // TODO: THIS COULD POTENTIALLY BE ASYNC +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glFramebufferRenderbuffer, GLenum, GLenum, GLenum, GLuint); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glFramebufferTexture2D, GLenum, GLenum, GLenum, GLuint, GLint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glFrontFace, GLenum); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenBuffers, GLsizei, GLuint *); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glGenerateMipmap, GLenum); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenFramebuffers, GLsizei, GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenRenderbuffers, GLsizei, GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenTextures, GLsizei, GLuint *); +VOID_SYNC_GL_FUNCTION_7(EM_FUNC_SIG_VIIIIIII, void, glGetActiveAttrib, GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *); +VOID_SYNC_GL_FUNCTION_7(EM_FUNC_SIG_VIIIIIII, void, glGetActiveUniform, GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetAttachedShaders, GLuint, GLsizei, GLsizei *, GLuint *); +RET_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_III, GLint, glGetAttribLocation, GLuint, const GLchar *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGetBooleanv, GLenum, GLboolean *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetBufferParameteriv, GLenum, GLenum, GLint *); +RET_SYNC_GL_FUNCTION_0(EM_FUNC_SIG_I, GLenum, glGetError); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGetFloatv, GLenum, GLfloat *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetFramebufferAttachmentParameteriv, GLenum, GLenum, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGetIntegerv, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetProgramiv, GLuint, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetProgramInfoLog, GLuint, GLsizei, GLsizei *, GLchar *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetRenderbufferParameteriv, GLenum, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetShaderiv, GLuint, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetShaderInfoLog, GLuint, GLsizei, GLsizei *, GLchar *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetShaderPrecisionFormat, GLenum, GLenum, GLint *, GLint *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetShaderSource, GLuint, GLsizei, GLsizei *, GLchar *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, const GLubyte *, glGetString, GLenum); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetTexParameterfv, GLenum, GLenum, GLfloat *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetTexParameteriv, GLenum, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetUniformfv, GLuint, GLint, GLfloat *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetUniformiv, GLuint, GLint, GLint *); +RET_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_III, GLint, glGetUniformLocation, GLuint, const GLchar *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetVertexAttribfv, GLuint, GLenum, GLfloat *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetVertexAttribiv, GLuint, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetVertexAttribPointerv, GLuint, GLenum, void **); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glHint, GLenum, GLenum); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsBuffer, GLuint); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsEnabled, GLenum); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsFramebuffer, GLuint); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsProgram, GLuint); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsRenderbuffer, GLuint); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsShader, GLuint); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsTexture, GLuint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VF, void, glLineWidth, GLfloat); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glLinkProgram, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glPixelStorei, GLenum, GLint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VFF, void, glPolygonOffset, GLfloat, GLfloat); +VOID_SYNC_GL_FUNCTION_7(EM_FUNC_SIG_VIIIIIII, void, glReadPixels, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void *); +ASYNC_GL_FUNCTION_0(EM_FUNC_SIG_V, void, glReleaseShaderCompiler); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glRenderbufferStorage, GLenum, GLenum, GLsizei, GLsizei); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glSampleCoverage, GLfloat, GLboolean); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glScissor, GLint, GLint, GLsizei, GLsizei); +VOID_SYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glShaderBinary, GLsizei, const GLuint *, GLenum, const void *, GLsizei); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glShaderSource, GLuint, GLsizei, const GLchar *const*, const GLint *); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glStencilFunc, GLenum, GLint, GLuint); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glStencilFuncSeparate, GLenum, GLenum, GLint, GLuint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glStencilMask, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glStencilMaskSeparate, GLenum, GLuint); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glStencilOp, GLenum, GLenum, GLenum); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glStencilOpSeparate, GLenum, GLenum, GLenum, GLenum); + +static ssize_t ImageSize(int width, int height, GLenum format, GLenum type) +{ + int numChannels; + switch(format) + { + case GL_ALPHA: case GL_LUMINANCE: case GL_DEPTH_COMPONENT: case GL_RED: case GL_RED_INTEGER: numChannels = 1; break; + case GL_RG: case GL_RG_INTEGER: numChannels = 2; break; + case GL_RGB: case 0x8C40/*GL_SRGB_EXT*/: case GL_RGB_INTEGER: numChannels = 3; break; + case GL_RGBA: case 0x8C42/*GL_SRGB_ALPHA_EXT*/: case GL_RGBA_INTEGER: numChannels = 4; break; + default: return -1; + } + int sizePerPixel; + switch(type) + { + case GL_UNSIGNED_BYTE: case GL_BYTE: sizePerPixel = numChannels; break; + case GL_UNSIGNED_SHORT: case 0x8D61/*GL_HALF_FLOAT_OES*/: case GL_HALF_FLOAT: case GL_SHORT: case GL_UNSIGNED_SHORT_5_6_5: case GL_UNSIGNED_SHORT_4_4_4_4: case GL_UNSIGNED_SHORT_5_5_5_1: sizePerPixel = numChannels*2; break; + case GL_UNSIGNED_INT: case GL_FLOAT: case GL_INT: case GL_UNSIGNED_INT_5_9_9_9_REV: case GL_UNSIGNED_INT_2_10_10_10_REV: case GL_UNSIGNED_INT_10F_11F_11F_REV: case GL_UNSIGNED_INT_24_8: sizePerPixel = numChannels*4; break; + default: return -1; + } + return width*height*sizePerPixel; +} + +void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + else + { + ssize_t sz = ImageSize(width, height, format, type); + if (!pixels || (sz >= 0 && sz < 256*1024)) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(pixels, sz); + if (ptr || !pixels) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIIIIIIII, &emscripten_glTexImage2D, ptr, target, level, internalformat, width, height, border, format, type, ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIIIIIIII, &emscripten_glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels); + } +} + +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIIF, void, glTexParameterf, GLenum, GLenum, GLfloat); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glTexParameterfv, GLenum, GLenum, const GLfloat *); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glTexParameteri, GLenum, GLenum, GLint); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glTexParameteriv, GLenum, GLenum, const GLint *); + +void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + else + { + ssize_t sz = ImageSize(width, height, format, type); + if (!pixels || (sz >= 0 && sz < 256*1024)) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(pixels, sz); + if (ptr || !pixels) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIIIIIIII, &emscripten_glTexSubImage2D, ptr, target, level, xoffset, yoffset, width, height, format, type, ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIIIIIIII, &emscripten_glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels); + } +} + +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VIF, void, glUniform1f, GLint, GLfloat); + +void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform1fv(location, count, value); + else + { + size_t sz = sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform1fv, ptr, location, count, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform1fv, location, count, value); + } +} + +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glUniform1i, GLint, GLint); + +void glUniform1iv(GLint location, GLsizei count, const GLint *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform1iv(location, count, value); + else + { + size_t sz = sizeof(GLint)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform1iv, ptr, location, count, (GLint*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform1iv, location, count, value); + } +} +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIFF, void, glUniform2f, GLint, GLfloat, GLfloat); + +void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform2fv(location, count, value); + else + { + size_t sz = 2*sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform2fv, ptr, location, count, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform2fv, location, count, value); + } +} + +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniform2i, GLint, GLint, GLint); + +void glUniform2iv(GLint location, GLsizei count, const GLint *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform2iv(location, count, value); + else + { + size_t sz = 2*sizeof(GLint)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform2iv, ptr, location, count, (GLint*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform2iv, location, count, value); + } +} +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIFFF, void, glUniform3f, GLint, GLfloat, GLfloat, GLfloat); + +void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform3fv(location, count, value); + else + { + size_t sz = 3*sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform3fv, ptr, location, count, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform3fv, location, count, value); + } +} + +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniform3i, GLint, GLint, GLint, GLint); + +void glUniform3iv(GLint location, GLsizei count, const GLint *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform3iv(location, count, value); + else + { + size_t sz = 3*sizeof(GLint)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform3iv, ptr, location, count, (GLint*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform3iv, location, count, value); + } +} +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIFFFF, void, glUniform4f, GLint, GLfloat, GLfloat, GLfloat, GLfloat); + +void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform4fv(location, count, value); + else + { + size_t sz = 4*sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform4fv, ptr, location, count, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform4fv, location, count, value); + } +} + +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glUniform4i, GLint, GLint, GLint, GLint, GLint); + +void glUniform4iv(GLint location, GLsizei count, const GLint *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniform4iv(location, count, value); + else + { + size_t sz = 4*sizeof(GLint)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIII, &emscripten_glUniform4iv, ptr, location, count, (GLint*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, &emscripten_glUniform4iv, location, count, value); + } +} + +void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniformMatrix2fv(location, count, transpose, value); + else + { + size_t sz = 2*2*sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIII, &emscripten_glUniformMatrix2fv, ptr, location, count, transpose, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIII, &emscripten_glUniformMatrix2fv, location, count, transpose, value); + } +} + +void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniformMatrix3fv(location, count, transpose, value); + else + { + size_t sz = 3*3*sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIII, &emscripten_glUniformMatrix3fv, ptr, location, count, transpose, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIII, &emscripten_glUniformMatrix3fv, location, count, transpose, value); + } +} + +void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +{ + GL_FUNCTION_TRACE(__func__); + if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) + emscripten_glUniformMatrix4fv(location, count, transpose, value); + else + { + size_t sz = 4*4*sizeof(GLfloat)*count; + if (sz < 256*1024) // run small buffer sizes asynchronously by copying - large buffers run synchronously + { + void *ptr = memdup(value, sz); + if (ptr) + { + emscripten_async_queue_on_thread(*(void**)(pthread_getspecific(currentActiveWebGLContext) + 4), EM_FUNC_SIG_VIIII, &emscripten_glUniformMatrix4fv, ptr, location, count, transpose, (GLfloat*)ptr); + return; + } + // Fall through on allocation failure and run synchronously. + } + + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIIII, &emscripten_glUniformMatrix4fv, location, count, transpose, value); + } +} + +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glUseProgram, GLuint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glValidateProgram, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VIF, void, glVertexAttrib1f, GLuint, GLfloat); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttrib1fv, GLuint, const GLfloat *); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIFF, void, glVertexAttrib2f, GLuint, GLfloat, GLfloat); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttrib2fv, GLuint, const GLfloat *); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIFFF, void, glVertexAttrib3f, GLuint, GLfloat, GLfloat, GLfloat); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttrib3fv, GLuint, const GLfloat *); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIFFFF, void, glVertexAttrib4f, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttrib4fv, GLuint, const GLfloat *); + +// TODO: The following #define FULL_ES2 does not yet exist, we'll need to compile this file twice, for FULL_ES2 mode and without +#if FULL_ES2 +VOID_SYNC_GL_FUNCTION_6(EM_FUNC_SIG_VIIIIII, void, glVertexAttribPointer, GLuint, GLint, GLenum, GLboolean, GLsizei, const void *); +#else +ASYNC_GL_FUNCTION_6(EM_FUNC_SIG_VIIIIII, void, glVertexAttribPointer, GLuint, GLint, GLenum, GLboolean, GLsizei, const void *); +#endif +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glViewport, GLint, GLint, GLsizei, GLsizei); + +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenQueriesEXT, GLsizei, GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteQueriesEXT, GLsizei, const GLuint *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsQueryEXT, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBeginQueryEXT, GLenum, GLuint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glEndQueryEXT, GLenum); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glQueryCounterEXT, GLuint, GLenum); +RET_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryivEXT, GLenum, GLenum, GLint *); +RET_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryObjectivEXT, GLenum, GLenum, GLint *); +RET_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryObjectuivEXT, GLenum, GLenum, GLuint *); +RET_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryObjecti64vEXT, GLenum, GLenum, GLint64 *); +RET_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryObjectui64vEXT, GLenum, GLenum, GLuint64 *); + +#endif // ~__EMSCRIPTEN_PTHREADS__ diff --git a/system/lib/gl/webgl1.h b/system/lib/gl/webgl1.h new file mode 100644 index 0000000000000..73604ea530d32 --- /dev/null +++ b/system/lib/gl/webgl1.h @@ -0,0 +1,194 @@ +#pragma once + +#include + +GL_APICALL void GL_APIENTRY emscripten_glActiveTexture (GLenum texture); +GL_APICALL void GL_APIENTRY emscripten_glAttachShader (GLuint program, GLuint shader); +GL_APICALL void GL_APIENTRY emscripten_glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glBindBuffer (GLenum target, GLuint buffer); +GL_APICALL void GL_APIENTRY emscripten_glBindFramebuffer (GLenum target, GLuint framebuffer); +GL_APICALL void GL_APIENTRY emscripten_glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GL_APICALL void GL_APIENTRY emscripten_glBindTexture (GLenum target, GLuint texture); +GL_APICALL void GL_APIENTRY emscripten_glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GL_APICALL void GL_APIENTRY emscripten_glBlendEquation (GLenum mode); +GL_APICALL void GL_APIENTRY emscripten_glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GL_APICALL void GL_APIENTRY emscripten_glBlendFunc (GLenum sfactor, GLenum dfactor); +GL_APICALL void GL_APIENTRY emscripten_glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GL_APICALL void GL_APIENTRY emscripten_glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GL_APICALL void GL_APIENTRY emscripten_glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GL_APICALL GLenum GL_APIENTRY emscripten_glCheckFramebufferStatus (GLenum target); +GL_APICALL void GL_APIENTRY emscripten_glClear (GLbitfield mask); +GL_APICALL void GL_APIENTRY emscripten_glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GL_APICALL void GL_APIENTRY emscripten_glClearDepthf (GLfloat d); +GL_APICALL void GL_APIENTRY emscripten_glClearStencil (GLint s); +GL_APICALL void GL_APIENTRY emscripten_glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GL_APICALL void GL_APIENTRY emscripten_glCompileShader (GLuint shader); +GL_APICALL void GL_APIENTRY emscripten_glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GL_APICALL void GL_APIENTRY emscripten_glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GL_APICALL void GL_APIENTRY emscripten_glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GL_APICALL void GL_APIENTRY emscripten_glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL GLuint GL_APIENTRY emscripten_glCreateProgram (void); +GL_APICALL GLuint GL_APIENTRY emscripten_glCreateShader (GLenum type); +GL_APICALL void GL_APIENTRY emscripten_glCullFace (GLenum mode); +GL_APICALL void GL_APIENTRY emscripten_glDeleteBuffers (GLsizei n, const GLuint *buffers); +GL_APICALL void GL_APIENTRY emscripten_glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GL_APICALL void GL_APIENTRY emscripten_glDeleteProgram (GLuint program); +GL_APICALL void GL_APIENTRY emscripten_glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GL_APICALL void GL_APIENTRY emscripten_glDeleteShader (GLuint shader); +GL_APICALL void GL_APIENTRY emscripten_glDeleteTextures (GLsizei n, const GLuint *textures); +GL_APICALL void GL_APIENTRY emscripten_glDepthFunc (GLenum func); +GL_APICALL void GL_APIENTRY emscripten_glDepthMask (GLboolean flag); +GL_APICALL void GL_APIENTRY emscripten_glDepthRangef (GLfloat n, GLfloat f); +GL_APICALL void GL_APIENTRY emscripten_glDetachShader (GLuint program, GLuint shader); +GL_APICALL void GL_APIENTRY emscripten_glDisable (GLenum cap); +GL_APICALL void GL_APIENTRY emscripten_glDisableVertexAttribArray (GLuint index); +GL_APICALL void GL_APIENTRY emscripten_glDrawArrays (GLenum mode, GLint first, GLsizei count); +GL_APICALL void GL_APIENTRY emscripten_glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); +GL_APICALL void GL_APIENTRY emscripten_glEnable (GLenum cap); +GL_APICALL void GL_APIENTRY emscripten_glEnableVertexAttribArray (GLuint index); +GL_APICALL void GL_APIENTRY emscripten_glFinish (void); +GL_APICALL void GL_APIENTRY emscripten_glFlush (void); +GL_APICALL void GL_APIENTRY emscripten_glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GL_APICALL void GL_APIENTRY emscripten_glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GL_APICALL void GL_APIENTRY emscripten_glFrontFace (GLenum mode); +GL_APICALL void GL_APIENTRY emscripten_glGenBuffers (GLsizei n, GLuint *buffers); +GL_APICALL void GL_APIENTRY emscripten_glGenerateMipmap (GLenum target); +GL_APICALL void GL_APIENTRY emscripten_glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GL_APICALL void GL_APIENTRY emscripten_glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GL_APICALL void GL_APIENTRY emscripten_glGenTextures (GLsizei n, GLuint *textures); +GL_APICALL void GL_APIENTRY emscripten_glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GL_APICALL GLint GL_APIENTRY emscripten_glGetAttribLocation (GLuint program, const GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glGetBooleanv (GLenum pname, GLboolean *data); +GL_APICALL void GL_APIENTRY emscripten_glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GL_APICALL GLenum GL_APIENTRY emscripten_glGetError (void); +GL_APICALL void GL_APIENTRY emscripten_glGetFloatv (GLenum pname, GLfloat *data); +GL_APICALL void GL_APIENTRY emscripten_glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetIntegerv (GLenum pname, GLint *data); +GL_APICALL void GL_APIENTRY emscripten_glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GL_APICALL void GL_APIENTRY emscripten_glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GL_APICALL void GL_APIENTRY emscripten_glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GL_APICALL void GL_APIENTRY emscripten_glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GL_APICALL const GLubyte *GL_APIENTRY emscripten_glGetString (GLenum name); +GL_APICALL void GL_APIENTRY emscripten_glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GL_APICALL void GL_APIENTRY emscripten_glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GL_APICALL void GL_APIENTRY emscripten_glGetUniformiv (GLuint program, GLint location, GLint *params); +GL_APICALL GLint GL_APIENTRY emscripten_glGetUniformLocation (GLuint program, const GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GL_APICALL void GL_APIENTRY emscripten_glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GL_APICALL void GL_APIENTRY emscripten_glHint (GLenum target, GLenum mode); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsBuffer (GLuint buffer); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsEnabled (GLenum cap); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsFramebuffer (GLuint framebuffer); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsProgram (GLuint program); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsRenderbuffer (GLuint renderbuffer); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsShader (GLuint shader); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsTexture (GLuint texture); +GL_APICALL void GL_APIENTRY emscripten_glLineWidth (GLfloat width); +GL_APICALL void GL_APIENTRY emscripten_glLinkProgram (GLuint program); +GL_APICALL void GL_APIENTRY emscripten_glPixelStorei (GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY emscripten_glPolygonOffset (GLfloat factor, GLfloat units); +GL_APICALL void GL_APIENTRY emscripten_glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +GL_APICALL void GL_APIENTRY emscripten_glReleaseShaderCompiler (void); +GL_APICALL void GL_APIENTRY emscripten_glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY emscripten_glSampleCoverage (GLfloat value, GLboolean invert); +GL_APICALL void GL_APIENTRY emscripten_glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY emscripten_glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +GL_APICALL void GL_APIENTRY emscripten_glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GL_APICALL void GL_APIENTRY emscripten_glStencilFunc (GLenum func, GLint ref, GLuint mask); +GL_APICALL void GL_APIENTRY emscripten_glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GL_APICALL void GL_APIENTRY emscripten_glStencilMask (GLuint mask); +GL_APICALL void GL_APIENTRY emscripten_glStencilMaskSeparate (GLenum face, GLuint mask); +GL_APICALL void GL_APIENTRY emscripten_glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GL_APICALL void GL_APIENTRY emscripten_glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GL_APICALL void GL_APIENTRY emscripten_glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GL_APICALL void GL_APIENTRY emscripten_glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GL_APICALL void GL_APIENTRY emscripten_glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GL_APICALL void GL_APIENTRY emscripten_glTexParameteri (GLenum target, GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY emscripten_glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GL_APICALL void GL_APIENTRY emscripten_glUniform1f (GLint location, GLfloat v0); +GL_APICALL void GL_APIENTRY emscripten_glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform1i (GLint location, GLint v0); +GL_APICALL void GL_APIENTRY emscripten_glUniform1iv (GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GL_APICALL void GL_APIENTRY emscripten_glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform2i (GLint location, GLint v0, GLint v1); +GL_APICALL void GL_APIENTRY emscripten_glUniform2iv (GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GL_APICALL void GL_APIENTRY emscripten_glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GL_APICALL void GL_APIENTRY emscripten_glUniform3iv (GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GL_APICALL void GL_APIENTRY emscripten_glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GL_APICALL void GL_APIENTRY emscripten_glUniform4iv (GLint location, GLsizei count, const GLint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUseProgram (GLuint program); +GL_APICALL void GL_APIENTRY emscripten_glValidateProgram (GLuint program); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib1f (GLuint index, GLfloat x); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib1fv (GLuint index, const GLfloat *v); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib2fv (GLuint index, const GLfloat *v); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib3fv (GLuint index, const GLfloat *v); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttrib4fv (GLuint index, const GLfloat *v); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +GL_APICALL void GL_APIENTRY emscripten_glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +#ifdef EMSCRIPTEN_WEBGL_TRACE +#define GL_FUNCTION_TRACE(func) printf(#func "\n") +#else +#define GL_FUNCTION_TRACE(func) ((void)0) +#endif + +#define ASYNC_GL_FUNCTION_0(sig, ret, functionName) ret functionName(void) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName); } +#define ASYNC_GL_FUNCTION_1(sig, ret, functionName, t0) ret functionName(t0 p0) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0); } +#define ASYNC_GL_FUNCTION_2(sig, ret, functionName, t0, t1) ret functionName(t0 p0, t1 p1) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1); } +#define ASYNC_GL_FUNCTION_3(sig, ret, functionName, t0, t1, t2) ret functionName(t0 p0, t1 p1, t2 p2) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2); } +#define ASYNC_GL_FUNCTION_4(sig, ret, functionName, t0, t1, t2, t3) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3); } +#define ASYNC_GL_FUNCTION_5(sig, ret, functionName, t0, t1, t2, t3, t4) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4); } +#define ASYNC_GL_FUNCTION_6(sig, ret, functionName, t0, t1, t2, t3, t4, t5) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5); } +#define ASYNC_GL_FUNCTION_7(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6); } +#define ASYNC_GL_FUNCTION_8(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7); } +#define ASYNC_GL_FUNCTION_9(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#define ASYNC_GL_FUNCTION_10(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#define ASYNC_GL_FUNCTION_11(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9, t10 p10) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); else emscripten_async_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } + +#define RET_SYNC_GL_FUNCTION_0(sig, ret, functionName) ret functionName(void) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName); } +#define RET_SYNC_GL_FUNCTION_1(sig, ret, functionName, t0) ret functionName(t0 p0) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0); } +#define RET_SYNC_GL_FUNCTION_2(sig, ret, functionName, t0, t1) ret functionName(t0 p0, t1 p1) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1); } +#define RET_SYNC_GL_FUNCTION_3(sig, ret, functionName, t0, t1, t2) ret functionName(t0 p0, t1 p1, t2 p2) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2); } +#define RET_SYNC_GL_FUNCTION_4(sig, ret, functionName, t0, t1, t2, t3) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3); } +#define RET_SYNC_GL_FUNCTION_5(sig, ret, functionName, t0, t1, t2, t3, t4) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4); } +#define RET_SYNC_GL_FUNCTION_6(sig, ret, functionName, t0, t1, t2, t3, t4, t5) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4, p5); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5); } +#define RET_SYNC_GL_FUNCTION_7(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6); } +#define RET_SYNC_GL_FUNCTION_8(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7); } +#define RET_SYNC_GL_FUNCTION_9(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#define RET_SYNC_GL_FUNCTION_10(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#define RET_SYNC_GL_FUNCTION_11(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9, t10 p10) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) return emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); else return (ret)emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } + +#define VOID_SYNC_GL_FUNCTION_0(sig, ret, functionName) ret functionName(void) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName); } +#define VOID_SYNC_GL_FUNCTION_1(sig, ret, functionName, t0) ret functionName(t0 p0) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0); } +#define VOID_SYNC_GL_FUNCTION_2(sig, ret, functionName, t0, t1) ret functionName(t0 p0, t1 p1) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1); } +#define VOID_SYNC_GL_FUNCTION_3(sig, ret, functionName, t0, t1, t2) ret functionName(t0 p0, t1 p1, t2 p2) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2); } +#define VOID_SYNC_GL_FUNCTION_4(sig, ret, functionName, t0, t1, t2, t3) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3); } +#define VOID_SYNC_GL_FUNCTION_5(sig, ret, functionName, t0, t1, t2, t3, t4) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4); } +#define VOID_SYNC_GL_FUNCTION_6(sig, ret, functionName, t0, t1, t2, t3, t4, t5) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5); } +#define VOID_SYNC_GL_FUNCTION_7(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6); } +#define VOID_SYNC_GL_FUNCTION_8(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7); } +#define VOID_SYNC_GL_FUNCTION_9(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#define VOID_SYNC_GL_FUNCTION_10(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#define VOID_SYNC_GL_FUNCTION_11(sig, ret, functionName, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) ret functionName(t0 p0, t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9, t10 p10) { GL_FUNCTION_TRACE(functionName); if (pthread_getspecific(currentThreadOwnsItsWebGLContext)) emscripten_##functionName(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); else emscripten_sync_run_in_main_runtime_thread(sig, &emscripten_##functionName, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } + +extern pthread_key_t currentActiveWebGLContext; +extern pthread_key_t currentThreadOwnsItsWebGLContext; diff --git a/system/lib/gl/webgl1_ext.h b/system/lib/gl/webgl1_ext.h new file mode 100644 index 0000000000000..09a50028c7546 --- /dev/null +++ b/system/lib/gl/webgl1_ext.h @@ -0,0 +1,229 @@ +#pragma once + +#include "webgl2.h" +#include + +// 1. https://www.khronos.org/registry/webgl/extensions/OES_texture_float/ +// + +// 2. https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/ +#define GL_HALF_FLOAT_OES 0x8D61 + +// 3. https://www.khronos.org/registry/webgl/extensions/WEBGL_lose_context/ +GL_APICALL EMSCRIPTEN_RESULT GL_APIENTRY emscripten_webgl_loseContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle); +GL_APICALL EMSCRIPTEN_RESULT GL_APIENTRY emscripten_webgl_restoreContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle); + +// 4. https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/ +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B + +// 5. https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +GL_APICALL void GL_APIENTRY emscripten_glBindVertexArrayOES(GLuint array); +GL_APICALL void GL_APIENTRY emscripten_glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays); +GL_APICALL void GL_APIENTRY emscripten_glGenVertexArraysOES(GLsizei n, GLuint *arrays); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsVertexArrayOES(GLuint array); +GL_APICALL void GL_APIENTRY glBindVertexArrayOES(GLuint array); +GL_APICALL void GL_APIENTRY glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays); +GL_APICALL void GL_APIENTRY glGenVertexArraysOES(GLsizei n, GLuint *arrays); +GL_APICALL GLboolean GL_APIENTRY glIsVertexArrayOES(GLuint array); + +// 6. https://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/ +#define GL_UNMASKED_VENDOR_WEBGL 0x9245 +#define GL_UNMASKED_RENDERER_WEBGL 0x9246 + +// 7. https://www.khronos.org/registry/webgl/extensions/WEBGL_debug_shaders/ +GL_APICALL void GL_APIENTRY emscripten_webgl_getTranslatedShaderSource(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); + +// 8. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +// 9. https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/ +#define GL_UNSIGNED_INT_24_8_WEBGL 0x84FA + +// 10. https://www.khronos.org/registry/webgl/extensions/OES_element_index_uint/ +// + +// 11. https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + +// 16. https://www.khronos.org/registry/webgl/extensions/EXT_frag_depth/ +// + +// 18. https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/ +#define GL_COLOR_ATTACHMENT0_WEBGL 0x8CE0 +#define GL_COLOR_ATTACHMENT1_WEBGL 0x8CE1 +#define GL_COLOR_ATTACHMENT2_WEBGL 0x8CE2 +#define GL_COLOR_ATTACHMENT3_WEBGL 0x8CE3 +#define GL_COLOR_ATTACHMENT4_WEBGL 0x8CE4 +#define GL_COLOR_ATTACHMENT5_WEBGL 0x8CE5 +#define GL_COLOR_ATTACHMENT6_WEBGL 0x8CE6 +#define GL_COLOR_ATTACHMENT7_WEBGL 0x8CE7 +#define GL_COLOR_ATTACHMENT8_WEBGL 0x8CE8 +#define GL_COLOR_ATTACHMENT9_WEBGL 0x8CE9 +#define GL_COLOR_ATTACHMENT10_WEBGL 0x8CEA +#define GL_COLOR_ATTACHMENT11_WEBGL 0x8CEB +#define GL_COLOR_ATTACHMENT12_WEBGL 0x8CEC +#define GL_COLOR_ATTACHMENT13_WEBGL 0x8CED +#define GL_COLOR_ATTACHMENT14_WEBGL 0x8CEE +#define GL_COLOR_ATTACHMENT15_WEBGL 0x8CEF +#define GL_DRAW_BUFFER0_WEBGL 0x8825 +#define GL_DRAW_BUFFER1_WEBGL 0x8826 +#define GL_DRAW_BUFFER2_WEBGL 0x8827 +#define GL_DRAW_BUFFER3_WEBGL 0x8828 +#define GL_DRAW_BUFFER4_WEBGL 0x8829 +#define GL_DRAW_BUFFER5_WEBGL 0x882A +#define GL_DRAW_BUFFER6_WEBGL 0x882B +#define GL_DRAW_BUFFER7_WEBGL 0x882C +#define GL_DRAW_BUFFER8_WEBGL 0x882D +#define GL_DRAW_BUFFER9_WEBGL 0x882E +#define GL_DRAW_BUFFER10_WEBGL 0x882F +#define GL_DRAW_BUFFER11_WEBGL 0x8830 +#define GL_DRAW_BUFFER12_WEBGL 0x8831 +#define GL_DRAW_BUFFER13_WEBGL 0x8832 +#define GL_DRAW_BUFFER14_WEBGL 0x8833 +#define GL_DRAW_BUFFER15_WEBGL 0x8834 +#define GL_MAX_COLOR_ATTACHMENTS_WEBGL 0x8CDF +#define GL_MAX_DRAW_BUFFERS_WEBGL 0x8824 +GL_APICALL void GL_APIENTRY emscripten_glDrawBuffersWEBGL(GLsizei n, const GLenum *buffers); +GL_APICALL void GL_APIENTRY glDrawBuffersWEBGL(GLsizei n, const GLenum *buffers); + +// 19. https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/ +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE 0x88FE +GL_APICALL void GL_APIENTRY emscripten_glDrawArraysInstancedANGLE(GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GL_APICALL void GL_APIENTRY emscripten_glDrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei primcount); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribDivisorANGLE(GLuint index, GLuint divisor); +GL_APICALL void GL_APIENTRY glDrawArraysInstancedANGLE(GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GL_APICALL void GL_APIENTRY glDrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei primcount); +GL_APICALL void GL_APIENTRY glVertexAttribDivisorANGLE(GLuint index, GLuint divisor); + +// 20. https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/ +// + +// 21. https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float_linear/ +// + +// 25. https://www.khronos.org/registry/webgl/extensions/EXT_blend_minmax/ +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 + +// 27. https://www.khronos.org/registry/webgl/extensions/EXT_shader_texture_lod/ +// + +// 12. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/ +#define GL_COMPRESSED_RGB_ATC_WEBGL 0x8C92 +#define GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL 0x8C93 +#define GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL 0x87EE + +// 13. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/ +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 + +// 14. https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_half_float/ +#define GL_RGBA16F_EXT 0x881A +#define GL_RGB16F_EXT 0x881B +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT 0x8211 +#define GL_UNSIGNED_NORMALIZED_EXT 0x8C17 + +// 15. https://www.khronos.org/registry/webgl/extensions/WEBGL_color_buffer_float/ +#define GL_RGBA32F_EXT 0x8814 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT 0x8211 +#define GL_UNSIGNED_NORMALIZED_EXT 0x8C17 + +// 17. https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT 0x8210 + +// 24. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/ +#define GL_COMPRESSED_RGB_ETC1_WEBGL 0x8D64 + +// 26. https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query/ +#define GL_QUERY_COUNTER_BITS_EXT 0x8864 +#define GL_CURRENT_QUERY_EXT 0x8865 +#define GL_QUERY_RESULT_EXT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867 +#define GL_TIME_ELAPSED_EXT 0x88BF +#define GL_TIMESTAMP_EXT 0x8E28 +#define GL_GPU_DISJOINT_EXT 0x8FBB +GL_APICALL void GL_APIENTRY emscripten_glGenQueriesEXT(GLsizei n, GLuint *ids); +GL_APICALL void GL_APIENTRY emscripten_glDeleteQueriesEXT(GLsizei n, const GLuint *ids); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsQueryEXT(GLuint id); +GL_APICALL void GL_APIENTRY emscripten_glBeginQueryEXT(GLenum target, GLuint id); +GL_APICALL void GL_APIENTRY emscripten_glEndQueryEXT(GLenum target); +GL_APICALL void GL_APIENTRY emscripten_glQueryCounterEXT(GLuint id, GLenum target); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryivEXT(GLenum target, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryObjectivEXT(GLuint id, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64 *params); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64 *params); +GL_APICALL void GL_APIENTRY glGenQueriesEXT(GLsizei n, GLuint *ids); +GL_APICALL void GL_APIENTRY glDeleteQueriesEXT(GLsizei n, const GLuint *ids); +GL_APICALL GLboolean GL_APIENTRY glIsQueryEXT(GLuint id); +GL_APICALL void GL_APIENTRY glBeginQueryEXT(GLenum target, GLuint id); +GL_APICALL void GL_APIENTRY glEndQueryEXT(GLenum target); +GL_APICALL void GL_APIENTRY glQueryCounterEXT(GLuint id, GLenum target); +GL_APICALL void GL_APIENTRY glGetQueryivEXT(GLenum target, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY glGetQueryObjectivEXT(GLuint id, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params); +GL_APICALL void GL_APIENTRY glGetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64 *params); +GL_APICALL void GL_APIENTRY glGetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64 *params); + + +// 29. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/ +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + +// 30. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/ +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +void emscripten_webgl_getSupportedAstcProfiles(GLsizei bufSize, GLsizei *length, GLchar *buf); + +// 31. https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_float/ +// + +// 32. https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc_srgb/ +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F diff --git a/system/lib/gl/webgl2.c b/system/lib/gl/webgl2.c new file mode 100644 index 0000000000000..85dfba7b96397 --- /dev/null +++ b/system/lib/gl/webgl2.c @@ -0,0 +1,136 @@ +#ifdef __EMSCRIPTEN_PTHREADS__ + +#include +#include +#include +#include + +#include "webgl1.h" +#include "webgl2.h" + +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glReadBuffer, GLenum); +ASYNC_GL_FUNCTION_6(EM_FUNC_SIG_VIIIIII, void, glDrawRangeElements, GLenum, GLuint, GLuint, GLsizei, GLenum, const void *); // TODO: Not async if rendering from client side memory +VOID_SYNC_GL_FUNCTION_10(EM_FUNC_SIG_VIIIIIIIIII, void, glTexImage3D, GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void *); +VOID_SYNC_GL_FUNCTION_11(EM_FUNC_SIG_VIIIIIIIIIII, void, glTexSubImage3D, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void *); +ASYNC_GL_FUNCTION_9(EM_FUNC_SIG_VIIIIIIIII, void, glCopyTexSubImage3D, GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei); +VOID_SYNC_GL_FUNCTION_9(EM_FUNC_SIG_VIIIIIIIII, void, glCompressedTexImage3D, GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const void *); +VOID_SYNC_GL_FUNCTION_11(EM_FUNC_SIG_VIIIIIIIIIII, void, glCompressedTexSubImage3D, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const void *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenQueries, GLsizei, GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteQueries, GLsizei, const GLuint *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsQuery, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBeginQuery, GLenum, GLuint); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glEndQuery, GLenum); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryiv, GLenum, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetQueryObjectuiv, GLuint, GLenum, GLuint *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glUnmapBuffer, GLenum); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetBufferPointerv, GLenum, GLenum, void **); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDrawBuffers, GLsizei, const GLenum *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniformMatrix2x3fv, GLint, GLsizei, GLboolean, const GLfloat *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniformMatrix3x2fv, GLint, GLsizei, GLboolean, const GLfloat *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniformMatrix2x4fv, GLint, GLsizei, GLboolean, const GLfloat *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniformMatrix4x2fv, GLint, GLsizei, GLboolean, const GLfloat *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniformMatrix3x4fv, GLint, GLsizei, GLboolean, const GLfloat *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniformMatrix4x3fv, GLint, GLsizei, GLboolean, const GLfloat *); +ASYNC_GL_FUNCTION_10(EM_FUNC_SIG_VIIIIIIIIII, void, glBlitFramebuffer, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glRenderbufferStorageMultisample, GLenum, GLsizei, GLenum, GLsizei, GLsizei); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glFramebufferTextureLayer, GLenum, GLenum, GLuint, GLint, GLint); +RET_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void *, glMapBufferRange, GLenum, GLintptr, GLsizeiptr, GLbitfield); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glFlushMappedBufferRange, GLenum, GLintptr, GLsizeiptr); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glBindVertexArray, GLuint); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteVertexArrays, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenVertexArrays, GLsizei, GLuint *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsVertexArray, GLuint); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetIntegeri_v, GLenum, GLuint, GLint *); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glBeginTransformFeedback, GLenum); +ASYNC_GL_FUNCTION_0(EM_FUNC_SIG_V, void, glEndTransformFeedback); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glBindBufferRange, GLenum, GLuint, GLuint, GLintptr, GLsizeiptr); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glBindBufferBase, GLenum, GLuint, GLuint); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glTransformFeedbackVaryings, GLuint, GLsizei, const GLchar *const*, GLenum); +VOID_SYNC_GL_FUNCTION_7(EM_FUNC_SIG_VIIIIIII, void, glGetTransformFeedbackVarying, GLuint, GLuint, GLsizei, GLsizei *, GLsizei *, GLenum *, GLchar *); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glVertexAttribIPointer, GLuint, GLint, GLenum, GLsizei, const void *); // TODO: Not async if not rendering from client side memory +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetVertexAttribIiv, GLuint, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetVertexAttribIuiv, GLuint, GLenum, GLuint *); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glVertexAttribI4i, GLuint, GLint, GLint, GLint, GLint); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glVertexAttribI4ui, GLuint, GLuint, GLuint, GLuint, GLuint); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttribI4iv, GLuint, const GLint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttribI4uiv, GLuint, const GLuint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetUniformuiv, GLuint, GLint, GLuint *); +RET_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_III, GLint, glGetFragDataLocation, GLuint, const GLchar *); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glUniform1ui, GLint, GLuint); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniform2ui, GLint, GLuint, GLuint); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glUniform3ui, GLint, GLuint, GLuint, GLuint); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glUniform4ui, GLint, GLuint, GLuint, GLuint, GLuint); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniform1uiv, GLint, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniform2uiv, GLint, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniform3uiv, GLint, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniform4uiv, GLint, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glClearBufferiv, GLenum, GLint, const GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glClearBufferuiv, GLenum, GLint, const GLuint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glClearBufferfv, GLenum, GLint, const GLfloat *); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIFI, void, glClearBufferfi, GLenum, GLint, GLfloat, GLint); +RET_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_III, const GLubyte *, glGetStringi, GLenum, GLuint); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glCopyBufferSubData, GLenum, GLenum, GLintptr, GLintptr, GLsizeiptr); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetUniformIndices, GLuint, GLsizei, const GLchar *const*, GLuint *); +VOID_SYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glGetActiveUniformsiv, GLuint, GLsizei, const GLuint *, GLenum, GLint *); +RET_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_III, GLuint, glGetUniformBlockIndex, GLuint, const GLchar *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glGetActiveUniformBlockiv, GLuint, GLuint, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glGetActiveUniformBlockName, GLuint, GLuint, GLsizei, GLsizei *, GLchar *); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glUniformBlockBinding, GLuint, GLuint, GLuint); +ASYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glDrawArraysInstanced, GLenum, GLint, GLsizei, GLsizei); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glDrawElementsInstanced, GLenum, GLsizei, GLenum, const void *, GLsizei); // TODO: Not async if rendering from client side memory +RET_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_III, GLsync, glFenceSync, GLenum, GLbitfield); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsSync, GLsync); +ASYNC_GL_FUNCTION_1(EM_FUNC_SIG_VI, void, glDeleteSync, GLsync); +RET_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_IIII, GLenum, glClientWaitSync, GLsync, GLbitfield, GLuint64); // XXX TODO: 64-bit integer proxying, this is not right, proxying as 32-bit +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glWaitSync, GLsync, GLbitfield, GLuint64); // XXX TODO: 64-bit integer proxying, this is not right, proxying as 32-bit +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGetInteger64v, GLenum, GLint64 *); +VOID_SYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glGetSynciv, GLsync, GLenum, GLsizei, GLsizei *, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetInteger64i_v, GLenum, GLuint, GLint64 *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetBufferParameteri64v, GLenum, GLenum, GLint64 *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenSamplers, GLsizei, GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteSamplers, GLsizei, const GLuint *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsSampler, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBindSampler, GLuint, GLuint); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glSamplerParameteri, GLuint, GLenum, GLint); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glSamplerParameteriv, GLuint, GLenum, const GLint *); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIIF, void, glSamplerParameterf, GLuint, GLenum, GLfloat); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glSamplerParameterfv, GLuint, GLenum, const GLfloat *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetSamplerParameteriv, GLuint, GLenum, GLint *); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glGetSamplerParameterfv, GLuint, GLenum, GLfloat *); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glVertexAttribDivisor, GLuint, GLuint); +ASYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glBindTransformFeedback, GLenum, GLuint); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glDeleteTransformFeedbacks, GLsizei, const GLuint *); +VOID_SYNC_GL_FUNCTION_2(EM_FUNC_SIG_VII, void, glGenTransformFeedbacks, GLsizei, GLuint *); +RET_SYNC_GL_FUNCTION_1(EM_FUNC_SIG_II, GLboolean, glIsTransformFeedback, GLuint); +ASYNC_GL_FUNCTION_0(EM_FUNC_SIG_V, void, glPauseTransformFeedback); +ASYNC_GL_FUNCTION_0(EM_FUNC_SIG_V, void, glResumeTransformFeedback); +VOID_SYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glGetProgramBinary, GLuint, GLsizei, GLsizei *, GLenum *, void *); +VOID_SYNC_GL_FUNCTION_4(EM_FUNC_SIG_VIIII, void, glProgramBinary, GLuint, GLenum, const void *, GLsizei); +ASYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glProgramParameteri, GLuint, GLenum, GLint); +VOID_SYNC_GL_FUNCTION_3(EM_FUNC_SIG_VIII, void, glInvalidateFramebuffer, GLenum, GLsizei, const GLenum *); +VOID_SYNC_GL_FUNCTION_7(EM_FUNC_SIG_VIIIIIII, void, glInvalidateSubFramebuffer, GLenum, GLsizei, const GLenum *, GLint, GLint, GLsizei, GLsizei); +ASYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glTexStorage2D, GLenum, GLsizei, GLenum, GLsizei, GLsizei); +ASYNC_GL_FUNCTION_6(EM_FUNC_SIG_VIIIIII, void, glTexStorage3D, GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei); +VOID_SYNC_GL_FUNCTION_5(EM_FUNC_SIG_VIIIII, void, glGetInternalformativ, GLenum, GLenum, GLenum, GLsizei, GLint *); + +// Extensions: +GL_APICALL void GL_APIENTRY glVertexAttribDivisorNV(GLuint index, GLuint divisor) { glVertexAttribDivisor(index, divisor); } +GL_APICALL void GL_APIENTRY glVertexAttribDivisorEXT(GLuint index, GLuint divisor) { glVertexAttribDivisor(index, divisor); } +GL_APICALL void GL_APIENTRY glVertexAttribDivisorARB(GLuint index, GLuint divisor) { glVertexAttribDivisor(index, divisor); } +GL_APICALL void GL_APIENTRY glVertexAttribDivisorANGLE(GLuint index, GLuint divisor) { glVertexAttribDivisor(index, divisor); } +GL_APICALL void GL_APIENTRY glDrawArraysInstancedNV(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { glDrawArraysInstanced(mode, first, count, instancecount); } +GL_APICALL void GL_APIENTRY glDrawArraysInstancedEXT(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { glDrawArraysInstanced(mode, first, count, instancecount); } +GL_APICALL void GL_APIENTRY glDrawArraysInstancedARB(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { glDrawArraysInstanced(mode, first, count, instancecount); } +GL_APICALL void GL_APIENTRY glDrawArraysInstancedANGLE(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { glDrawArraysInstanced(mode, first, count, instancecount); } +GL_APICALL void GL_APIENTRY glDrawElementsInstancedNV(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { glDrawElementsInstanced(mode, count, type, indices, instancecount); } +GL_APICALL void GL_APIENTRY glDrawElementsInstancedEXT(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { glDrawElementsInstanced(mode, count, type, indices, instancecount); } +GL_APICALL void GL_APIENTRY glDrawElementsInstancedARB(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { glDrawElementsInstanced(mode, count, type, indices, instancecount); } +GL_APICALL void GL_APIENTRY glDrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { glDrawElementsInstanced(mode, count, type, indices, instancecount); } +GL_APICALL void GL_APIENTRY glBindVertexArrayOES(GLuint array) { glBindVertexArray(array); } +GL_APICALL void GL_APIENTRY glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays) { glDeleteVertexArrays(n, arrays); } +GL_APICALL void GL_APIENTRY glGenVertexArraysOES(GLsizei n, GLuint *arrays) { glGenVertexArrays(n, arrays); } +GL_APICALL GLboolean GL_APIENTRY glIsVertexArrayOES(GLuint array) { return glIsVertexArray(array); } +GL_APICALL void GL_APIENTRY glDrawBuffersEXT(GLsizei n, const GLenum *bufs) { glDrawBuffers(n, bufs); } +GL_APICALL void GL_APIENTRY glDrawBuffersWEBGL(GLsizei n, const GLenum *bufs) { glDrawBuffers(n, bufs); } + +#endif // ~__EMSCRIPTEN_PTHREADS__ diff --git a/system/lib/gl/webgl2.h b/system/lib/gl/webgl2.h new file mode 100644 index 0000000000000..187c0a704cd68 --- /dev/null +++ b/system/lib/gl/webgl2.h @@ -0,0 +1,108 @@ +#pragma once + +#include + +GL_APICALL void GL_APIENTRY emscripten_glReadBuffer (GLenum src); +GL_APICALL void GL_APIENTRY emscripten_glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GL_APICALL void GL_APIENTRY emscripten_glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GL_APICALL void GL_APIENTRY emscripten_glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GL_APICALL void GL_APIENTRY emscripten_glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY emscripten_glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GL_APICALL void GL_APIENTRY emscripten_glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GL_APICALL void GL_APIENTRY emscripten_glGenQueries (GLsizei n, GLuint *ids); +GL_APICALL void GL_APIENTRY emscripten_glDeleteQueries (GLsizei n, const GLuint *ids); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsQuery (GLuint id); +GL_APICALL void GL_APIENTRY emscripten_glBeginQuery (GLenum target, GLuint id); +GL_APICALL void GL_APIENTRY emscripten_glEndQuery (GLenum target); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GL_APICALL GLboolean GL_APIENTRY emscripten_glUnmapBuffer (GLenum target); +GL_APICALL void GL_APIENTRY emscripten_glGetBufferPointerv (GLenum target, GLenum pname, void **params); +GL_APICALL void GL_APIENTRY emscripten_glDrawBuffers (GLsizei n, const GLenum *bufs); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GL_APICALL void GL_APIENTRY emscripten_glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY emscripten_glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GL_APICALL void *GL_APIENTRY emscripten_glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GL_APICALL void GL_APIENTRY emscripten_glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GL_APICALL void GL_APIENTRY emscripten_glBindVertexArray (GLuint array); +GL_APICALL void GL_APIENTRY emscripten_glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GL_APICALL void GL_APIENTRY emscripten_glGenVertexArrays (GLsizei n, GLuint *arrays); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsVertexArray (GLuint array); +GL_APICALL void GL_APIENTRY emscripten_glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GL_APICALL void GL_APIENTRY emscripten_glBeginTransformFeedback (GLenum primitiveMode); +GL_APICALL void GL_APIENTRY emscripten_glEndTransformFeedback (void); +GL_APICALL void GL_APIENTRY emscripten_glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GL_APICALL void GL_APIENTRY emscripten_glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GL_APICALL void GL_APIENTRY emscripten_glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GL_APICALL void GL_APIENTRY emscripten_glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GL_APICALL void GL_APIENTRY emscripten_glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribI4iv (GLuint index, const GLint *v); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribI4uiv (GLuint index, const GLuint *v); +GL_APICALL void GL_APIENTRY emscripten_glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GL_APICALL GLint GL_APIENTRY emscripten_glGetFragDataLocation (GLuint program, const GLchar *name); +GL_APICALL void GL_APIENTRY emscripten_glUniform1ui (GLint location, GLuint v0); +GL_APICALL void GL_APIENTRY emscripten_glUniform2ui (GLint location, GLuint v0, GLuint v1); +GL_APICALL void GL_APIENTRY emscripten_glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GL_APICALL void GL_APIENTRY emscripten_glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GL_APICALL void GL_APIENTRY emscripten_glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GL_APICALL void GL_APIENTRY emscripten_glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GL_APICALL void GL_APIENTRY emscripten_glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GL_APICALL void GL_APIENTRY emscripten_glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GL_APICALL void GL_APIENTRY emscripten_glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GL_APICALL void GL_APIENTRY emscripten_glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GL_APICALL const GLubyte *GL_APIENTRY emscripten_glGetStringi (GLenum name, GLuint index); +GL_APICALL void GL_APIENTRY emscripten_glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GL_APICALL void GL_APIENTRY emscripten_glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GL_APICALL void GL_APIENTRY emscripten_glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GL_APICALL GLuint GL_APIENTRY emscripten_glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GL_APICALL void GL_APIENTRY emscripten_glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GL_APICALL void GL_APIENTRY emscripten_glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +GL_APICALL void GL_APIENTRY emscripten_glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GL_APICALL void GL_APIENTRY emscripten_glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GL_APICALL GLsync GL_APIENTRY emscripten_glFenceSync (GLenum condition, GLbitfield flags); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsSync (GLsync sync); +GL_APICALL void GL_APIENTRY emscripten_glDeleteSync (GLsync sync); +GL_APICALL GLenum GL_APIENTRY emscripten_glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GL_APICALL void GL_APIENTRY emscripten_glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GL_APICALL void GL_APIENTRY emscripten_glGetInteger64v (GLenum pname, GLint64 *data); +GL_APICALL void GL_APIENTRY emscripten_glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GL_APICALL void GL_APIENTRY emscripten_glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GL_APICALL void GL_APIENTRY emscripten_glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GL_APICALL void GL_APIENTRY emscripten_glGenSamplers (GLsizei count, GLuint *samplers); +GL_APICALL void GL_APIENTRY emscripten_glDeleteSamplers (GLsizei count, const GLuint *samplers); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsSampler (GLuint sampler); +GL_APICALL void GL_APIENTRY emscripten_glBindSampler (GLuint unit, GLuint sampler); +GL_APICALL void GL_APIENTRY emscripten_glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY emscripten_glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GL_APICALL void GL_APIENTRY emscripten_glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GL_APICALL void GL_APIENTRY emscripten_glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GL_APICALL void GL_APIENTRY emscripten_glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY emscripten_glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GL_APICALL void GL_APIENTRY emscripten_glVertexAttribDivisor (GLuint index, GLuint divisor); +GL_APICALL void GL_APIENTRY emscripten_glBindTransformFeedback (GLenum target, GLuint id); +GL_APICALL void GL_APIENTRY emscripten_glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GL_APICALL void GL_APIENTRY emscripten_glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GL_APICALL GLboolean GL_APIENTRY emscripten_glIsTransformFeedback (GLuint id); +GL_APICALL void GL_APIENTRY emscripten_glPauseTransformFeedback (void); +GL_APICALL void GL_APIENTRY emscripten_glResumeTransformFeedback (void); +GL_APICALL void GL_APIENTRY emscripten_glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GL_APICALL void GL_APIENTRY emscripten_glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GL_APICALL void GL_APIENTRY emscripten_glProgramParameteri (GLuint program, GLenum pname, GLint value); +GL_APICALL void GL_APIENTRY emscripten_glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GL_APICALL void GL_APIENTRY emscripten_glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY emscripten_glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY emscripten_glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GL_APICALL void GL_APIENTRY emscripten_glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); diff --git a/system/lib/gl/webgl2_ext.h b/system/lib/gl/webgl2_ext.h new file mode 100644 index 0000000000000..28128aedfdc0d --- /dev/null +++ b/system/lib/gl/webgl2_ext.h @@ -0,0 +1,10 @@ +#pragma once + +#include "webgl2.h" + +// 33. https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query_webgl2/ +#define GL_QUERY_COUNTER_BITS_EXT 0x8864 +#define GL_TIME_ELAPSED_EXT 0x88BF +#define GL_TIMESTAMP_EXT 0x8E28 +#define GL_GPU_DISJOINT_EXT 0x8FBB +void emscripten_webgl2_queryCounterEXT(GLuint query, GLenum target); diff --git a/system/lib/pthread/library_pthread.c b/system/lib/pthread/library_pthread.c index 0780fd34b1d55..b39a1839de442 100644 --- a/system/lib/pthread/library_pthread.c +++ b/system/lib/pthread/library_pthread.c @@ -200,6 +200,8 @@ static void _do_call(em_queued_call *q) { case EM_PROXIED_PTHREAD_CREATE: q->returnValue.i = pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp); break; case EM_PROXIED_SYSCALL: q->returnValue.i = emscripten_syscall(q->args[0].i, q->args[1].vp); break; + case EM_PROXIED_CREATE_CONTEXT: q->returnValue.i = emscripten_webgl_create_context(q->args[0].cp, q->args[1].vp); break; + case EM_PROXIED_RESIZE_OFFSCREENCANVAS: q->returnValue.i = emscripten_set_canvas_element_size(q->args[0].cp, q->args[1].i, q->args[2].i); break; case EM_FUNC_SIG_V: ((em_func_v)q->functionPtr)(); break; case EM_FUNC_SIG_VI: ((em_func_vi)q->functionPtr)(q->args[0].i); break; case EM_FUNC_SIG_VF: ((em_func_vf)q->functionPtr)(q->args[0].f); break; diff --git a/tests/canvas_animate_resize.cpp b/tests/canvas_animate_resize.cpp new file mode 100644 index 0000000000000..e041f8cffae96 --- /dev/null +++ b/tests/canvas_animate_resize.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define TEST_EXPLICIT_CONTEXT_SWAP +// - If set, test WebGL context creation .explicitSwapControl=true and call emscripten_webgl_commit_frame() to swap. Otherwise utilize implicit swapping. +//#define TEST_EMSCRIPTEN_SET_MAIN_LOOP +// - If set, test emscripten_set_main_loop(). If unset, run our own synchronously blocking loop +//#define TEST_MANUALLY_SET_ELEMENT_CSS_SIZE +// - If set, manually update CSS pixel size of the canvas. If unset, leave the CSS size to auto and expect the browser to resize the canvas size. + +#if !defined(TEST_EMSCRIPTEN_SET_MAIN_LOOP) && !defined(__EMSCRIPTEN_PTHREADS__) +#error TEST_EMSCRIPTEN_SET_MAIN_LOOP=false, but blocking main loops require using -s PROXY_TO_PTHREADS= and multithreading (build with -s PROXY_TO_PTHREAD=1 and -s USE_PTHREADS=1) +#endif + +#if !defined(TEST_EMSCRIPTEN_SET_MAIN_LOOP) && !defined(TEST_EXPLICIT_CONTEXT_SWAP) +#error If using own main loop, must use explicit context swapping (explicitSwapControl=true) (build with either -DTEST_EMSCRIPTEN_SET_MAIN_LOOP=1 or -DTEST_EXPLICIT_CONTEXT_SWAP=1) +#endif + +#define NUM_FRAMES_TO_RENDER 100 + +GLuint vb; +int program; +int frameNumber = 0; +EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; + +void tick() +{ + double sizeScale = 21.0 + 20.0 * sin(emscripten_get_now()*0.001); + double w = 18.0*sizeScale; + double h = 11.0*sizeScale; + + EMSCRIPTEN_RESULT r; + +#if TEST_MANUALLY_SET_ELEMENT_CSS_SIZE + r = emscripten_set_element_css_size("#canvas", w / emscripten_get_device_pixel_ratio(), h / emscripten_get_device_pixel_ratio()); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); +#endif + + r = emscripten_set_canvas_element_size("#canvas", (int)w, (int)h); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + int verifyWidth, verifyHeight; + r = emscripten_get_canvas_element_size("#canvas", &verifyWidth, &verifyHeight); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + assert(verifyWidth == (int)w); + assert(verifyHeight == (int)h); + + glViewport(0, 0, (int)w, (int)h); + glUseProgram(program); + glBindBuffer(GL_ARRAY_BUFFER, vb); + glDisable(GL_DEPTH_TEST); + int posLoc = glGetAttribLocation(program, "pos"); + glVertexAttribPointer(posLoc, 2, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(posLoc); + glUniform1f(glGetUniformLocation(program, "t"), emscripten_get_now()*0.0001f); + glClearColor(0,1,0,1); // Clear to green color which should never be visible + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + +#if TEST_EXPLICIT_CONTEXT_SWAP + r = emscripten_webgl_commit_frame(); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); +#endif + + ++frameNumber; + if (frameNumber >= NUM_FRAMES_TO_RENDER) + { +#if TEST_EMSCRIPTEN_SET_MAIN_LOOP + emscripten_cancel_main_loop(); +#endif + emscripten_webgl_make_context_current(0); + emscripten_webgl_destroy_context(ctx); + printf("quit\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif + exit(0); + } +} + +void init() +{ + glGenBuffers(1, &vb); + glBindBuffer(GL_ARRAY_BUFFER, vb); + float vertices[] = { -1, -1, -1, 1, 1, -1, 1, 1 }; + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + const char *vsCode = + "attribute vec2 pos;" + "varying lowp vec2 tex;" + "void main() { tex = pos * 0.5 + vec2(0.5,0.5); gl_Position = vec4(pos, 0.0, 1.0); }"; + int vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vsCode, NULL); + glCompileShader(vs); + + const char *fsCode = + "varying lowp vec2 tex;" + "uniform highp float t;" + "uniform sampler2D sampler;" + "void main() { highp float x = tex.x + sin((t + tex.y)*10.0)*0.005; highp float y = tex.y + sin((t + tex.x)*10.0)*0.02; x = x*1.3 - 0.1; y = y * 1.3 - 0.23;" + "gl_FragColor = (tex.x > 0.05 && tex.x < 0.09) ? vec4(0,0,0,1) : ((x >= 0.0 && x <= 1.0 && y >= 0.0 && y <= 1.0) ? (((x >= 5.0/18.0 && x <= 8.0/18.0) || (x >= 0.0 && x <= 1.0 && y >= 4.0/11.0 && y <= 7.0/11.0)) ? vec4(0,0,1,1) : vec4(1,1,1,1)) : vec4(0.75,0.75,0.75,1)); }"; + int fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fsCode, NULL); + glCompileShader(fs); + program = glCreateProgram(); + glAttachShader(program, vs); + glAttachShader(program, fs); + glLinkProgram(program); +} + +int main() +{ + EmscriptenWebGLContextAttributes attr; + emscripten_webgl_init_context_attributes(&attr); +#if TEST_EXPLICIT_CONTEXT_SWAP + attr.explicitSwapControl = EM_TRUE; +#endif + ctx = emscripten_webgl_create_context(0, &attr); + printf("Created context with handle %u\n", (unsigned int)ctx); + if (!ctx) + { + EM_ASM({ + if (typeof OffscreenCanvas === 'undefined') { + xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://localhost:8888/report_result?skipped:%20OffscreenCanvas%20is%20not%20supported!'); + xhr.send(); + setTimeout(function() { window.close() }, 2000); + } + }); + return 0; + } + emscripten_webgl_make_context_current(ctx); + + init(); + + printf("You should see the flag of Finland.\n"); +#if TEST_EMSCRIPTEN_SET_MAIN_LOOP + emscripten_set_main_loop(tick, 0, 0); +#else + for(int i = 0; i < NUM_FRAMES_TO_RENDER; ++i) + { + tick(); + emscripten_current_thread_process_queued_calls(); + usleep(16*1000); + } +#endif +} diff --git a/tests/canvas_animate_resize_shell.html b/tests/canvas_animate_resize_shell.html new file mode 100644 index 0000000000000..90535d74a58cd --- /dev/null +++ b/tests/canvas_animate_resize_shell.html @@ -0,0 +1,56 @@ + + + + + + Emscripten-Generated Code + + + + +
+ + + {{{ SCRIPT }}} + + diff --git a/tests/gl_in_proxy_pthread.cpp b/tests/gl_in_proxy_pthread.cpp new file mode 100644 index 0000000000000..c4a66676b6766 --- /dev/null +++ b/tests/gl_in_proxy_pthread.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(TEST_OFFSCREEN_CANVAS) +// Defining EMSCRIPTEN_PTHREAD_TRANSFERRED_CANVASES to empty string will cause no canvases to be transferred. +#define EMSCRIPTEN_PTHREAD_TRANSFERRED_CANVASES "" +#elif TEST_OFFSCREEN_CANVAS == 1 +// Specifying #canvas should take Module.canvas to be transferred +#define EMSCRIPTEN_PTHREAD_TRANSFERRED_CANVASES "#canvas" +#elif TEST_OFFSCREEN_CANVAS == 2 +// Leaving EMSCRIPTEN_PTHREAD_TRANSFERRED_CANVASES undefined will also transfer the default Module.canvas object +#endif + +int main() +{ + if (!emscripten_supports_offscreencanvas()) + { + printf("Current browser does not support OffscreenCanvas. Skipping this test.\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif + return 0; + } + EmscriptenWebGLContextAttributes attr; + emscripten_webgl_init_context_attributes(&attr); + attr.explicitSwapControl = EM_TRUE; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(0, &attr); + printf("Created context with handle %u\n", (unsigned int)ctx); + emscripten_webgl_make_context_current(ctx); + + printf("You should see the canvas fade from black to red.\n"); + double color = 0; + for(int i = 0; i < 100; ++i) + { + color += 0.01; + glClearColor(color, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + EMSCRIPTEN_RESULT r = emscripten_webgl_commit_frame(); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + double now = emscripten_get_now(); + while(emscripten_get_now() - now < 16) /*no-op*/; + } + + emscripten_webgl_make_context_current(0); + emscripten_webgl_destroy_context(ctx); + printf("Thread quit\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif +} diff --git a/tests/gl_in_pthread.cpp b/tests/gl_in_pthread.cpp index 7337e257503e0..d878ec9b34d4c 100644 --- a/tests/gl_in_pthread.cpp +++ b/tests/gl_in_pthread.cpp @@ -19,13 +19,13 @@ pthread_t thread; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; -int threadRunning = 0; int numThreadsCreated = 0; int result = 0; void *ThreadMain(void *arg) { + printf("ThreadMain\n"); switch(numThreadsCreated) { case 1: printf("Thread 1 started: you should see the WebGL canvas fade from black to red.\n"); break; @@ -58,7 +58,6 @@ void *ThreadMain(void *arg) emscripten_webgl_make_context_current(0); emscripten_webgl_destroy_context(ctx); - emscripten_atomic_store_u32(&threadRunning, 0); printf("Thread quit\n"); pthread_exit(0); } @@ -68,6 +67,7 @@ void CreateThread() pthread_attr_t attr; pthread_attr_init(&attr); emscripten_pthread_attr_settransferredcanvases(&attr, "#canvas"); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); int rc = pthread_create(&thread, &attr, ThreadMain, 0); if (rc == ENOSYS) { @@ -77,48 +77,45 @@ void CreateThread() #endif exit(0); } + if (rc) + { + printf("Failed to create thread! error: %d\n", rc); + exit(0); + } pthread_attr_destroy(&attr); - emscripten_atomic_store_u32(&threadRunning, 1); ++numThreadsCreated; } -void PollThreadExit(void *) +void *mymain(void*) { - if (!emscripten_atomic_load_u32(&threadRunning)) + for(int i = 0; i < 3; ++i) { - if (numThreadsCreated >= 3) - { - emscripten_atomic_store_u32(&result, 1); -#ifdef REPORT_RESULT - REPORT_RESULT(result); -#endif - return; - } - else - { - CreateThread(); - } + printf("Creating thread %d\n", i+1); + CreateThread(); + printf("Waiting for thread to finish.\n"); + pthread_join(thread, 0); + thread = 0; } - emscripten_async_call(PollThreadExit, 0, 1000); -} - -void *mymain(void*) -{ - EM_ASM(Module['noExitRuntime'] = true;); - CreateThread(); - emscripten_async_call(PollThreadExit, 0, 1000); + printf("All done!\n"); return 0; } +// Tests that the OffscreenCanvas context can travel from main thread -> thread 1 -> thread 2 // #define TEST_CHAINED_WEBGL_CONTEXT_PASSING +// If set, the OffscreenCanvas is transferred from the main thread directly to thread 2, without giving it to thread 1 +// in between. +// #define TRANSFER_TO_CHAINED_THREAD_FROM_MAIN_THREAD + int main() { #ifdef TEST_CHAINED_WEBGL_CONTEXT_PASSING EM_ASM(Module['noExitRuntime'] = true;); pthread_attr_t attr; pthread_attr_init(&attr); +#ifndef TRANSFER_TO_CHAINED_THREAD_FROM_MAIN_THREAD emscripten_pthread_attr_settransferredcanvases(&attr, "#canvas"); +#endif int rc = pthread_create(&thread, &attr, mymain, 0); if (rc == ENOSYS) { diff --git a/tests/gl_in_two_pthreads.cpp b/tests/gl_in_two_pthreads.cpp new file mode 100644 index 0000000000000..6ed86201d9275 --- /dev/null +++ b/tests/gl_in_two_pthreads.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Pass #define TEST_OFFSCREENCANVAS when testing against OffscreenCanvas support + +void fill(int color) +{ + switch(color) + { + case 1: glClearColor(1, 0, 0, 1); printf("On thread %d: you should see a red canvas.\n", (int)pthread_self()); break; + case 2: glClearColor(0, 1, 0, 1); printf("On thread %d: you should see a green canvas.\n", (int)pthread_self()); break; + case 3: glClearColor(0, 0, 1, 1); printf("On thread %d: you should see a blue canvas.\n", (int)pthread_self()); break; + } + glClear(GL_COLOR_BUFFER_BIT); + EMSCRIPTEN_RESULT r = emscripten_webgl_commit_frame(); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + emscripten_thread_sleep(2000); +} + +void *thread_main(void *param) +{ + EmscriptenWebGLContextAttributes attr; + emscripten_webgl_init_context_attributes(&attr); + attr.explicitSwapControl = EM_TRUE; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr); + assert(ctx); + + EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(ctx); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + fill((int)param); + + r = emscripten_webgl_make_context_current(0); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + pthread_exit(0); +} + +void run_thread(int param) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + emscripten_pthread_attr_settransferredcanvases(&attr, "#canvas"); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_t thread; + int rc = pthread_create(&thread, &attr, thread_main, (void*)param); + assert(rc == 0); + rc = pthread_join(thread, NULL); + assert(rc == 0); +} + +int main() +{ +#ifdef TEST_OFFSCREENCANVAS + if (!emscripten_supports_offscreencanvas()) + { + printf("Current browser does not support OffscreenCanvas. Skipping this test.\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif + return 0; + } +#endif + + // Create a context + EmscriptenWebGLContextAttributes attr; + emscripten_webgl_init_context_attributes(&attr); + attr.explicitSwapControl = EM_TRUE; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(0, &attr); + assert(ctx); + + // Activate it + EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(ctx); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + // Draw + fill(3); + + // Release context for the threads to capture the canvas + r = emscripten_webgl_make_context_current(0); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + // TODO: Optionally, also delete the above context + // TODO: Test that threads share the context (main thread creates, pthread uses) + run_thread(1); + run_thread(2); + run_thread(3); + + // Reactivate the context we got + r = emscripten_webgl_make_context_current(ctx); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + // And render with it again + fill(1); + +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif +} diff --git a/tests/resize_offscreencanvas_from_main_thread.cpp b/tests/resize_offscreencanvas_from_main_thread.cpp new file mode 100644 index 0000000000000..51d349f0585dc --- /dev/null +++ b/tests/resize_offscreencanvas_from_main_thread.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Tests that the size of an OffscreenCanvas can be queried and set from the main thread. +// Supporting this is needed since it is very common to resize Canvas size on main thread while relayouting DOM in JavaScript code. + +// Define the following to test the scenario where the pthread that owns the OffscreenCanvas is running its own synchronously blocking loop (never yields to event loop). +// If not defined, the pthread that owns the OffscreenCanvas is using emscripten_set_main_loop() so periodically yields back to the event loop. +// #define TEST_SYNC_BLOCKING_LOOP + +void thread_local_main_loop() +{ + int w = 0, h = 0; + emscripten_get_canvas_element_size("#canvas", &w, &h); + if (w == 699 && h == 299) + { + printf("Observed OffscreenCanvas resize to 699x299 from main thread! Test passed!\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif + +#ifndef TEST_SYNC_BLOCKING_LOOP + emscripten_cancel_main_loop(); +#endif + + EM_ASM(Module['noExitRuntime']=false); + exit(0); + } + printf("%dx%d\n", w, h); +} + +void *thread_main(void *arg) +{ + EmscriptenWebGLContextAttributes attr; + emscripten_webgl_init_context_attributes(&attr); + attr.explicitSwapControl = EM_TRUE; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(0, &attr); + assert(ctx); + + // To start off, change the size of the OffscreenCanvas, main thread will test that it sees this change. + printf("pthread resizing OffscreenCanvas to size 355x233.\n"); + emscripten_set_canvas_element_size("#canvas", 355, 233); + + // In pthread, keep polling the canvas size until we see it being changed from the main thread. +#ifdef TEST_SYNC_BLOCKING_LOOP + for(;;) + { + thread_local_main_loop(); + emscripten_current_thread_process_queued_calls(); + usleep(16*1000); + } +#else + emscripten_set_main_loop(thread_local_main_loop, 1, 0); +#endif + + EM_ASM(Module['noExitRuntime']=true); + return 0; +} + +void resize_canvas(void *) +{ + // Test that on the main thread, we can observe size changes to the canvas size. + int w, h; + emscripten_get_canvas_element_size("#canvas", &w, &h); + assert(w == 355 && "We did not observe the effect of pthread having resized OffscreenCanvas"); + assert(h == 233); + printf("Main thread saw canvas to get resized to %dx%d.\n", w, h); + + // Test that on the main thread, we can also change the size. (pthread listens to see this) + printf("Main thread resizing OffscreenCanvas to size 699x299.\n"); + emscripten_set_canvas_element_size("#canvas", 699, 299); +} + +int main() +{ + if (!emscripten_supports_offscreencanvas()) + { + printf("Current browser does not support OffscreenCanvas. Skipping this test.\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(1); +#endif + return 0; + } + + pthread_attr_t attr; + pthread_attr_init(&attr); + emscripten_pthread_attr_settransferredcanvases(&attr, "#canvas"); + + pthread_t thread; + printf("Creating thread.\n"); + pthread_create(&thread, &attr, thread_main, NULL); + EM_ASM(Module['noExitRuntime']=true); + + // Wait for a while, then change the canvas size on the main thread. + printf("Waiting for 5 seconds for good measure.\n"); + emscripten_async_call(resize_canvas, 0, 5000); +} diff --git a/tests/test_browser.py b/tests/test_browser.py index df5a538737483..d1a2307a1d9b9 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1819,8 +1819,10 @@ def test_gl_glteximage(self): self.btest('gl_teximage.c', '1', args=['-lGL', '-lSDL']) @requires_graphics_hardware + @requires_threads def test_gl_textures(self): - self.btest('gl_textures.cpp', '0', args=['-lGL']) + for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: + self.btest('gl_textures.cpp', '0', args=['-lGL']) @requires_graphics_hardware def test_gl_ps(self): @@ -2456,7 +2458,7 @@ def test_doublestart_bug(self): self.btest('doublestart.c', args=['--pre-js', 'pre.js', '-o', 'test.html'], expected='1') def test_html5(self): - for opts in [[], ['-O2', '-g1', '--closure', '1']]: + for opts in [[], ['-O2', '-g1', '--closure', '1'], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: print(opts) self.btest(path_from_root('tests', 'test_html5.c'), args=opts, expected='0', timeout=20) @@ -2469,7 +2471,7 @@ def test_html5_webgl_create_context_no_antialias(self): # This test supersedes the one above, but it's skipped in the CI because anti-aliasing is not well supported by the Mesa software renderer. @requires_graphics_hardware def test_html5_webgl_create_context(self): - for opts in [[], ['-O2', '-g1', '--closure', '1'], ['-s', 'FULL_ES2=1']]: + for opts in [[], ['-O2', '-g1', '--closure', '1'], ['-s', 'FULL_ES2=1'], ['-s', 'USE_PTHREADS=1']]: print(opts) self.btest(path_from_root('tests', 'webgl_create_context.cpp'), args=opts + ['-lGL'], expected='0', timeout=20) @@ -3871,6 +3873,26 @@ def test_webgl_offscreen_framebuffer(self): def test_webgl_workaround_webgl_uniform_upload_bug(self): self.btest('webgl_draw_triangle_with_uniform_color.c', '0', args=['-lGL', '-s', 'WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG=1']) + # Tests that if a WebGL context is created in a pthread on a canvas that has not been transferred to that pthread, WebGL calls are then proxied to the main thread + # -DTEST_OFFSCREEN_CANVAS=1: Tests that if a WebGL context is created on a pthread that has the canvas transferred to it via using Emscripten's EMSCRIPTEN_PTHREAD_TRANSFERRED_CANVASES="#canvas", then OffscreenCanvas is used + # -DTEST_OFFSCREEN_CANVAS=2: Tests that if a WebGL context is created on a pthread that has the canvas transferred to it via automatic transferring of Module.canvas when EMSCRIPTEN_PTHREAD_TRANSFERRED_CANVASES is not defined, then OffscreenCanvas is also used + @requires_threads + @requires_graphics_hardware + def test_webgl_offscreen_canvas_in_proxied_pthread(self): + for args in [[], ['-DTEST_OFFSCREEN_CANVAS=1'], ['-DTEST_OFFSCREEN_CANVAS=2']]: + cmd = args + ['-s', 'USE_PTHREADS=1', '-s', 'OFFSCREENCANVAS_SUPPORT=1', '-lGL', '-s', 'GL_DEBUG=1', '-s', 'PROXY_TO_PTHREAD=1'] + print(str(cmd)) + self.btest('gl_in_proxy_pthread.cpp', expected='1', args=cmd) + + @requires_threads + @requires_graphics_hardware + def test_webgl_resize_offscreencanvas_from_main_thread(self): + for args1 in [[], ['-s', 'PROXY_TO_PTHREAD=1']]: + for args2 in [[], ['-DTEST_SYNC_BLOCKING_LOOP=1']]: + cmd = args1 + args2 + ['-s', 'USE_PTHREADS=1', '-s', 'OFFSCREENCANVAS_SUPPORT=1', '-lGL', '-s', 'GL_DEBUG=1'] + print(str(cmd)) + self.btest('resize_offscreencanvas_from_main_thread.cpp', expected='1', args=cmd) + # Tests the feature that shell html page can preallocate the typed array and place it to Module.buffer before loading the script page. # In this build mode, the -s TOTAL_MEMORY=xxx option will be ignored. # Preallocating the buffer in this was is asm.js only (wasm needs a Memory). @@ -4031,11 +4053,23 @@ def test_emscripten_get_device_pixel_ratio(self): # Tests that emscripten_run_script() variants of functions work in pthreads. @requires_threads def test_pthread_run_script(self): - self.btest(path_from_root('tests', 'pthread', 'test_pthread_run_script.cpp'), expected='1', args=['-O3', '--separate-asm'], timeout=30) + for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: + self.btest(path_from_root('tests', 'pthread', 'test_pthread_run_script.cpp'), expected='1', args=['-O3', '--separate-asm'] + args, timeout=30) + # Tests emscripten_set_canvas_element_size() and OffscreenCanvas functionality in different build configurations. @requires_threads - def test_pthread_run_script_pthreads(self): - self.btest(path_from_root('tests', 'pthread', 'test_pthread_run_script.cpp'), expected='1', args=['-O3', '--separate-asm', '-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1'], timeout=30) + @requires_graphics_hardware + def test_emscripten_animate_canvas_element_size(self): + for args in [ + ['-DTEST_EMSCRIPTEN_SET_MAIN_LOOP=1'], + ['-DTEST_EMSCRIPTEN_SET_MAIN_LOOP=1', '-s', 'PROXY_TO_PTHREAD=1', '-s', 'USE_PTHREADS=1', '-s', 'OFFSCREEN_FRAMEBUFFER=1'], + ['-DTEST_EMSCRIPTEN_SET_MAIN_LOOP=1', '-s', 'PROXY_TO_PTHREAD=1', '-s', 'USE_PTHREADS=1', '-s', 'OFFSCREEN_FRAMEBUFFER=1', '-DTEST_EXPLICIT_CONTEXT_SWAP=1'], + ['-DTEST_EXPLICIT_CONTEXT_SWAP=1', '-s', 'PROXY_TO_PTHREAD=1', '-s', 'USE_PTHREADS=1', '-s', 'OFFSCREEN_FRAMEBUFFER=1'], + ['-DTEST_EXPLICIT_CONTEXT_SWAP=1', '-s', 'PROXY_TO_PTHREAD=1', '-s', 'USE_PTHREADS=1', '-s', 'OFFSCREEN_FRAMEBUFFER=1', '-DTEST_MANUALLY_SET_ELEMENT_CSS_SIZE=1'], + ]: + cmd = ['-lGL', '-O3', '-g2', '--shell-file', path_from_root('tests', 'canvas_animate_resize_shell.html'), '--separate-asm', '-s', 'GL_DEBUG=1', '--threadprofiler'] + args + print(' '.join(cmd)) + self.btest('canvas_animate_resize.cpp', expected='1', args=cmd) # Tests the absolute minimum pthread-enabled application. @requires_threads diff --git a/tests/test_interactive.py b/tests/test_interactive.py index 40256d2077b51..97e134df29ccb 100644 --- a/tests/test_interactive.py +++ b/tests/test_interactive.py @@ -110,8 +110,9 @@ def test_openal_playback(self): shutil.copyfile(path_from_root('tests', 'sounds', 'audio.wav'), os.path.join(self.get_dir(), 'audio.wav')) open(os.path.join(self.get_dir(), 'openal_playback.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'openal_playback.cpp')).read())) - Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?1') + for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html'] + args).communicate() + self.run_browser('page.html', '', '/report_result?1') def test_openal_buffers(self): self.btest('openal_buffers.c', '0', args=['--preload-file', path_from_root('tests', 'sounds', 'the_entertainer.wav') + '@/'],) @@ -205,3 +206,14 @@ def test_html5_callback_on_two_threads(self): # TODO: Make this automatic by injecting enter key press in e.g. shell html file. for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: self.btest('html5_event_callback_in_two_threads.c', expected='1', args=args) + + # Test that emscripten_hide_mouse() is callable from pthreads (and proxies to main thread to obtain the proper window.devicePixelRatio value). + def test_emscripten_hide_mouse(self): + for args in [[], ['-s', 'USE_PTHREADS=1']]: + self.btest('emscripten_hide_mouse.c', expected='0', args=args) + + # Tests that WebGL can be run on another thread after first having run it on one thread (and that thread has exited). The intent of this is to stress graceful deinit semantics, so that it is not possible to "taint" a Canvas + # to a bad state after a rendering thread in a program quits and restarts. (perhaps e.g. between level loads, or subsystem loads/restarts or something like that) + def test_webgl_offscreen_canvas_in_two_pthreads(self): + for args in [['-s', 'OFFSCREENCANVAS_SUPPORT=1', '-DTEST_OFFSCREENCANVAS=1'], ['-s', 'OFFSCREEN_FRAMEBUFFER=1']]: + self.btest('gl_in_two_pthreads.cpp', expected='1', args=args + ['-s', 'USE_PTHREADS=1', '-lGL', '-s', 'GL_DEBUG=1', '-s', 'PROXY_TO_PTHREAD=1']) diff --git a/tests/test_other.py b/tests/test_other.py index a6844f5dc7d47..eede89bdbc563 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -7932,7 +7932,7 @@ def test(filename, expectations): 0, [], [], 8, 0, 0, 0), # noqa; totally empty! # but we don't metadce with linkable code! other modules may want it (['-O3', '-s', 'MAIN_MODULE=1'], - 1494, [], [], 226057, 30, 75, None), # noqa; don't compare the # of functions in a main module, which changes a lot + 1503, [], [], 226057, 30, 75, None), # noqa; don't compare the # of functions in a main module, which changes a lot ]) # noqa print('test on a minimal pure computational thing') diff --git a/tools/system_libs.py b/tools/system_libs.py index e746575eca4b6..c746893f4ed9d 100755 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -132,6 +132,15 @@ def build_libcxx(src_dirname, lib_filename, files, lib_opts, has_noexcept_versio return in_temp(lib_filename) + # Returns linker flags specific to singlethreading or multithreading + def threading_flags(libname): + if shared.Settings.USE_PTHREADS: + assert '-mt' in libname + return ['-s', 'USE_PTHREADS=1'] + else: + assert '-mt' not in libname + return [] + # libc def create_libc(libname): logging.debug(' building libc for cache') @@ -200,11 +209,7 @@ def create_libc(libname): # like the standard library. This can cause unexpected bugs when we use our # custom standard library. The same for other libc/libm builds. args = ['-Os', '-fno-builtin'] - if shared.Settings.USE_PTHREADS: - args += ['-s', 'USE_PTHREADS=1'] - assert '-mt' in libname - else: - assert '-mt' not in libname + args += threading_flags(libname) return build_libc(libname, libc_files, args) def create_pthreads(libname): @@ -333,10 +338,15 @@ def create_libcxxabi(libname): ['-Oz', '-I' + libcxxabi_include]) # gl - def create_gl(libname): # libname is ignored, this is just one .o file - o = in_temp('gl.o') - check_call([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', 'gl.c'), '-o', o] + get_cflags()) - return o + def create_gl(libname): + src_dir = shared.path_from_root('system', 'lib', 'gl') + files = [] + for dirpath, dirnames, filenames in os.walk(src_dir): + filenames = filter(lambda f: f.endswith('.c'), filenames) + files += map(lambda f: os.path.join(src_dir, f), filenames) + flags = ['-Oz'] + flags += threading_flags(libname) + return build_libc(libname, files, flags) # al def create_al(libname): # libname is ignored, this is just one .o file @@ -577,7 +587,6 @@ class Dummy(object): system_libs = [Library('libcxx', 'a', create_libcxx, libcxx_symbols, ['libcxxabi'], True), # noqa Library('libcxxabi', ext, create_libcxxabi, libcxxabi_symbols, [libc_name], False), # noqa - Library('gl', ext, create_gl, gl_symbols, [libc_name], False), # noqa Library('al', ext, create_al, al_symbols, [libc_name], False), # noqa Library('html5', ext, create_html5, html5_symbols, [], False), # noqa Library('compiler-rt', 'a', create_compiler_rt, compiler_rt_symbols, [libc_name], False), # noqa @@ -585,7 +594,10 @@ class Dummy(object): if shared.Settings.USE_PTHREADS: system_libs += [Library('pthreads', ext, create_pthreads, pthreads_symbols, [libc_name], False), # noqa - Library('pthreads_asmjs', ext, create_pthreads_asmjs, asmjs_pthreads_symbols, [libc_name], False)] # noqa + Library('pthreads_asmjs', ext, create_pthreads_asmjs, asmjs_pthreads_symbols, [libc_name], False), # noqa + Library('gl-mt', 'bc', create_gl, gl_symbols, [libc_name], False)] # noqa + else: + system_libs += [Library('gl', ext, create_gl, gl_symbols, [libc_name], False)] # noqa system_libs.append(Library(libc_name, ext, create_libc, libc_symbols, libc_deps, False))