From 7e8bf88376c031f35766ecd520c84ef2088df378 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Tue, 27 Oct 2026 16:14:14 -0700 Subject: [PATCH] [html5] Fix canvas size functions with OffscreenCanvas When a canvas is transferred offscreen via `explicitSwapControl` in `_emscripten_webgl_do_create_context` (`src/lib/libhtml5_webgl.js`), it was storing the `OffscreenCanvas` in `GL.offscreenCanvases[canvas.id]` under `.canvas` rather than `.offscreenCanvas` (unlike `libpthread.js` and `libhtml5.js`), and was allocating an uninitialized `canvasSharedPtr`. In addition, the single-threaded implementations of `emscripten_set_canvas_element_size` and `emscripten_get_canvas_element_size` in `src/lib/libhtml5.js` did not unwrap `canvas.offscreenCanvas` when `-sOFFSCREENCANVAS_SUPPORT` was enabled, and `emscripten_get_canvas_element_size` was missing its `EMSCRIPTEN_RESULT_SUCCESS` return statement. Align `GL.offscreenCanvases` records to consistently use `.offscreenCanvas` (removing the workarounds from #22958 and #23518) and unwrap `canvas.offscreenCanvas` in single-threaded canvas size helpers. Fixes: #26394 --- src/lib/libhtml5.js | 15 +++++++++++---- src/lib/libhtml5_webgl.js | 12 ++---------- .../browser/webgl_create_context_swapcontrol.c | 18 ++++++++++++++++++ .../test_codesize_hello_dylink_all.json | 4 ++-- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/lib/libhtml5.js b/src/lib/libhtml5.js index ef749dba34a1c..828ee5758f563 100644 --- a/src/lib/libhtml5.js +++ b/src/lib/libhtml5.js @@ -2104,13 +2104,16 @@ var LibraryHTML5 = { } return setCanvasElementSizeMainThread(target, width, height); }, -#else +#else // PTHREADS emscripten_set_canvas_element_size: (target, width, height) => { #if GL_DEBUG dbg(`emscripten_set_canvas_element_size(target=${target},width=${width},height=${height}`); #endif var canvas = findCanvasEventTarget(target); if (!canvas) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; +#if OFFSCREENCANVAS_SUPPORT + if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; +#endif canvas.width = width; canvas.height = height; #if OFFSCREEN_FRAMEBUFFER @@ -2118,7 +2121,7 @@ var LibraryHTML5 = { #endif return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, -#endif +#endif // !PTHREADS $setCanvasElementSize: (target, width, height) => { #if GL_DEBUG @@ -2178,14 +2181,18 @@ var LibraryHTML5 = { } return getCanvasSizeMainThread(target, width, height); }, -#else +#else // PTHREADS emscripten_get_canvas_element_size: (target, width, height) => { var canvas = findCanvasEventTarget(target); if (!canvas) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; +#if OFFSCREENCANVAS_SUPPORT + if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; +#endif {{{ makeSetValue('width', '0', 'canvas.width', 'i32') }}}; {{{ makeSetValue('height', '0', 'canvas.height', 'i32') }}}; + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, -#endif +#endif // !PTHREADS // JavaScript-friendly API, returns pair [width, height] $getCanvasElementSize: (target) => { diff --git a/src/lib/libhtml5_webgl.js b/src/lib/libhtml5_webgl.js index 786599b10c18d..f2741c0b73194 100644 --- a/src/lib/libhtml5_webgl.js +++ b/src/lib/libhtml5_webgl.js @@ -57,9 +57,6 @@ var LibraryHtml5WebGL = { });`, #endif -#if OFFSCREENCANVAS_SUPPORT - _emscripten_webgl_do_create_context__deps: ['malloc'], -#endif // This function performs proxying manually, depending on the style of context that is to be created. _emscripten_webgl_do_create_context: (target, attributes) => { #if ASSERTIONS @@ -104,10 +101,6 @@ var LibraryHtml5WebGL = { #endif var canvas = findCanvasEventTarget(target); -#if OFFSCREENCANVAS_SUPPORT - // If our canvas from findCanvasEventTarget is actually an offscreen canvas record, we should extract the inner canvas. - if (canvas?.canvas) { canvas = canvas.canvas; } -#endif #if GL_DEBUG var targetStr = UTF8ToString(target); #endif @@ -175,8 +168,7 @@ var LibraryHtml5WebGL = { #endif if (!canvas.controlTransferredOffscreen) { GL.offscreenCanvases[canvas.id] = { - canvas: canvas.transferControlToOffscreen(), - canvasSharedPtr: _malloc(12), + offscreenCanvas: canvas.transferControlToOffscreen(), id: canvas.id }; canvas.controlTransferredOffscreen = true; @@ -186,7 +178,7 @@ var LibraryHtml5WebGL = { #endif return 0; } - canvas = GL.offscreenCanvases[canvas.id].canvas; + canvas = GL.offscreenCanvases[canvas.id].offscreenCanvas; } } #else // !OFFSCREENCANVAS_SUPPORT diff --git a/test/browser/webgl_create_context_swapcontrol.c b/test/browser/webgl_create_context_swapcontrol.c index 5fec62cf8749f..0286b7880970f 100644 --- a/test/browser/webgl_create_context_swapcontrol.c +++ b/test/browser/webgl_create_context_swapcontrol.c @@ -23,6 +23,24 @@ int main() { EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(context); assert(res == EMSCRIPTEN_RESULT_SUCCESS); assert(emscripten_webgl_get_current_context() == context); + + int w, h; + res = emscripten_get_canvas_element_size("#canvas", &w, &h); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + assert(w == 300); + assert(h == 150); + + res = emscripten_set_canvas_element_size("#canvas", 400, 200); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + res = emscripten_get_canvas_element_size("#canvas", &w, &h); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + assert(w == 400); + assert(h == 200); + res = emscripten_webgl_get_drawing_buffer_size(context, &w, &h); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + assert(w == 400); + assert(h == 200); + emscripten_webgl_destroy_context(context); assert(emscripten_webgl_get_current_context() == 0); // Test that creating a context for a canvas that's already been used once succeeds diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index 77fefb993680c..d0544962b3ba1 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": 272917, + "a.out.js": 272926, "a.out.nodebug.wasm": 589443, - "total": 862360, + "total": 862369, "sent": [ "IMG_Init", "IMG_Load",