diff --git a/AUTHORS b/AUTHORS index aa0e91eebc7ef..ab0f876b503fe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -603,3 +603,4 @@ a license to everyone to use it as detailed in LICENSE.) * Sean Morris * Mitchell Wills (copyright owned by Google, Inc.) * Han Jiang +* Abdelrahman Teima diff --git a/site/source/docs/api_reference/html5.h.rst b/site/source/docs/api_reference/html5.h.rst index 6d55bd1e5c681..0db900d141b27 100644 --- a/site/source/docs/api_reference/html5.h.rst +++ b/site/source/docs/api_reference/html5.h.rst @@ -2050,6 +2050,10 @@ Struct Default value of ``proxyContextToMainThread`` after calling ``emscripten_webgl_init_context_attributes()`` is ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW``, if the WebGL context is being created on the main thread. This means that by default WebGL contexts created on the main thread are not shareable between multiple threads (to avoid accidental performance loss from enabling proxying when/if it is not needed). To create a context that can be shared between multiple pthreads, set the ``proxyContextToMainThread`` flag ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS``. + .. c:member:: bool desynchronized + + If ``true``, requests a "desynchronized" WebGL context, which can lower latency by bypassing the browser's normal compositing/double-buffering of the canvas. This maps to the ``desynchronized`` attribute on the JavaScript ``getContext()`` call. Whether the request is honored depends on the browser and platform; use ``emscripten_webgl_get_context_attributes()`` to read back the value the context was actually created with. Default value is ``false``. + Callback functions ------------------ diff --git a/src/lib/libhtml5_webgl.js b/src/lib/libhtml5_webgl.js index e35696c84b00b..0c85cfdc7e4bb 100644 --- a/src/lib/libhtml5_webgl.js +++ b/src/lib/libhtml5_webgl.js @@ -82,6 +82,7 @@ var LibraryHtml5WebGL = { 'preserveDrawingBuffer': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.preserveDrawingBuffer }}}], 'powerPreference': webglPowerPreferences[powerPreference], 'failIfMajorPerformanceCaveat': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.failIfMajorPerformanceCaveat }}}], + 'desynchronized': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.desynchronized }}}], // The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure. majorVersion: HEAP32[attr32 + ({{{ C_STRUCTS.EmscriptenWebGLContextAttributes.majorVersion }}}>>2)], minorVersion: HEAP32[attr32 + ({{{ C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion }}}>>2)], @@ -318,6 +319,7 @@ var LibraryHtml5WebGL = { var power = t['powerPreference'] && webglPowerPreferences.indexOf(t['powerPreference']); {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.powerPreference, 'power', 'i32') }}}; {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.failIfMajorPerformanceCaveat, 't.failIfMajorPerformanceCaveat', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.desynchronized, 't.desynchronized', 'i8') }}}; {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.majorVersion, 'c.version', 'i32') }}}; {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion, 0, 'i32') }}}; #if GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS diff --git a/src/struct_info.json b/src/struct_info.json index 0362f1a08eecc..507ca5169b295 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1020,7 +1020,8 @@ "enableExtensionsByDefault", "explicitSwapControl", "proxyContextToMainThread", - "renderViaOffscreenBackBuffer" + "renderViaOffscreenBackBuffer", + "desynchronized" ], "EmscriptenFullscreenStrategy": [ "scaleMode", diff --git a/src/struct_info_generated.json b/src/struct_info_generated.json index c0dded01e8e71..0758137f932b1 100644 --- a/src/struct_info_generated.json +++ b/src/struct_info_generated.json @@ -698,6 +698,7 @@ "alpha": 0, "antialias": 3, "depth": 1, + "desynchronized": 33, "enableExtensionsByDefault": 24, "explicitSwapControl": 25, "failIfMajorPerformanceCaveat": 12, diff --git a/src/struct_info_generated_wasm64.json b/src/struct_info_generated_wasm64.json index 036fe0ec64f20..6f0981f682ca6 100644 --- a/src/struct_info_generated_wasm64.json +++ b/src/struct_info_generated_wasm64.json @@ -698,6 +698,7 @@ "alpha": 0, "antialias": 3, "depth": 1, + "desynchronized": 33, "enableExtensionsByDefault": 24, "explicitSwapControl": 25, "failIfMajorPerformanceCaveat": 12, diff --git a/system/include/emscripten/html5_webgl.h b/system/include/emscripten/html5_webgl.h index 37edb5b978a4e..bfad39731f1ea 100644 --- a/system/include/emscripten/html5_webgl.h +++ b/system/include/emscripten/html5_webgl.h @@ -45,6 +45,7 @@ typedef struct EmscriptenWebGLContextAttributes { bool explicitSwapControl; EMSCRIPTEN_WEBGL_CONTEXT_PROXY_MODE proxyContextToMainThread; bool renderViaOffscreenBackBuffer; + bool desynchronized; } EmscriptenWebGLContextAttributes; void emscripten_webgl_init_context_attributes(EmscriptenWebGLContextAttributes * _Nonnull attributes); diff --git a/test/browser/html5_webgl.c b/test/browser/html5_webgl.c index 24dbeeec19fab..84f01f81c96f4 100644 --- a/test/browser/html5_webgl.c +++ b/test/browser/html5_webgl.c @@ -6,6 +6,11 @@ #include #include +// Expected desynchronized round-trip value, set by the test runner; -1 = skip. +#ifndef EXPECT_DESYNCHRONIZED +#define EXPECT_DESYNCHRONIZED -1 +#endif + GLuint compile_shader(GLenum shaderType, const char *src) { GLuint shader = glCreateShader(shaderType); glShaderSource(shader, 1, &src, NULL); @@ -29,9 +34,18 @@ int main() { attrs.majorVersion = 2; attrs.proxyContextToMainThread = EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK; attrs.renderViaOffscreenBackBuffer = true; + attrs.desynchronized = true; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context("#canvas", &attrs); emscripten_webgl_make_context_current(context); + // #8406: desynchronized should round-trip on browsers that support it. + // Skipped for OffscreenCanvas, which doesn't honor it. + if (EXPECT_DESYNCHRONIZED >= 0) { + EmscriptenWebGLContextAttributes actualAttrs; + emscripten_webgl_get_context_attributes(context, &actualAttrs); + assert(actualAttrs.desynchronized == EXPECT_DESYNCHRONIZED); + } + // Test emscripten_webgl_get_supported_extensions() API char *extensions = emscripten_webgl_get_supported_extensions(); assert(extensions); diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index ed50856c112d4..eb27bc739ba6a 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { - "a.out.js": 268445, + "a.out.js": 268495, "a.out.nodebug.wasm": 587520, - "total": 855965, + "total": 856015, "sent": [ "IMG_Init", "IMG_Load", diff --git a/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm.json b/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm.json index b796ae2e3afb9..227653dcc03e8 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm.json @@ -1,10 +1,10 @@ { "a.html": 450, "a.html.gz": 318, - "a.js": 4398, - "a.js.gz": 2259, + "a.js": 4423, + "a.js.gz": 2270, "a.wasm": 8313, "a.wasm.gz": 5646, - "total": 13161, - "total_gz": 8223 + "total": 13186, + "total_gz": 8234 } diff --git a/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm2js.json b/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm2js.json index 08ef9b5c54814..39c2e3644a679 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm2js.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm2js.json @@ -1,8 +1,8 @@ { "a.html": 342, "a.html.gz": 252, - "a.js": 18192, - "a.js.gz": 9813, - "total": 18534, - "total_gz": 10065 + "a.js": 18217, + "a.js.gz": 9828, + "total": 18559, + "total_gz": 10080 } diff --git a/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm_singlefile.json b/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm_singlefile.json index 57b149f07a2dc..b8bee5125d38e 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm_singlefile.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_webgl2_wasm_singlefile.json @@ -1,4 +1,4 @@ { - "a.html": 15015, - "a.html.gz": 8990 + "a.html": 15040, + "a.html.gz": 9004 } diff --git a/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm.json b/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm.json index 9dc6d3a08181b..ecb88a72d5678 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm.json @@ -1,10 +1,10 @@ { "a.html": 450, "a.html.gz": 318, - "a.js": 3936, - "a.js.gz": 2095, + "a.js": 3961, + "a.js.gz": 2109, "a.wasm": 8313, "a.wasm.gz": 5646, - "total": 12699, - "total_gz": 8059 + "total": 12724, + "total_gz": 8073 } diff --git a/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm2js.json b/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm2js.json index 88b024fee1321..1913104dfaf56 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm2js.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_webgl_wasm2js.json @@ -1,8 +1,8 @@ { "a.html": 342, "a.html.gz": 252, - "a.js": 17718, - "a.js.gz": 9647, - "total": 18060, - "total_gz": 9899 + "a.js": 17743, + "a.js.gz": 9663, + "total": 18085, + "total_gz": 9915 } diff --git a/test/test_browser.py b/test/test_browser.py index 66c57f788b9cc..c18d43535428d 100644 --- a/test/test_browser.py +++ b/test/test_browser.py @@ -2668,7 +2668,12 @@ def test_webgl2_objects(self): def test_html5_webgl_api(self, args): if '-sOFFSCREENCANVAS_SUPPORT' in args and os.getenv('EMTEST_LACKS_OFFSCREEN_CANVAS'): return - self.btest_exit('html5_webgl.c', cflags=['-sMAX_WEBGL_VERSION=2', '-lGL'] + args) + cflags = ['-sMAX_WEBGL_VERSION=2', '-lGL'] + args + # Check the desynchronized round-trip only on a normal canvas; OffscreenCanvas + # doesn't honor it. Chrome supports it, other browsers report it back as false. + if not args: + cflags.append('-DEXPECT_DESYNCHRONIZED=' + ('1' if is_chrome() else '0')) + self.btest_exit('html5_webgl.c', cflags=cflags) @parameterized({ 'webgl1': (['-DWEBGL_VERSION=1'],),