From 27f22b29f866d5cd807e70289ab771dabc79207c Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Thu, 8 Sep 2022 09:44:14 +0200 Subject: [Web] Small fixes and enhancements. - "Definitive" fix for ENOENT randomly disappearing from emscripten. - Proper shutdown when setup fails. - Re-enable WebGL explicit buffer swap. - Re-enable optional per-pixel transparency. - Add type cast to make closure compiler happy. - Remove emscripten Safari WebGL workaround. - Improve AudioWorklet cleanup. --- platform/web/detect.py | 4 ++++ platform/web/display_server_web.cpp | 4 ++-- platform/web/js/engine/config.js | 3 ++- platform/web/js/libs/audio.worklet.js | 2 ++ platform/web/js/libs/library_godot_audio.js | 9 +++++++-- platform/web/js/libs/library_godot_os.js | 14 +++++++++----- platform/web/web_main.cpp | 26 +++++++++++++++++++++++++- 7 files changed, 51 insertions(+), 11 deletions(-) (limited to 'platform/web') diff --git a/platform/web/detect.py b/platform/web/detect.py index e055af8400..08f964db92 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -227,3 +227,7 @@ def configure(env): # Add code that allow exiting runtime. env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"]) + + # This workaround creates a closure that prevents the garbage collector from freeing the WebGL context. + # We also only use WebGL2, and changing context version is not widely supported anyway. + env.Append(LINKFLAGS=["-s", "GL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0"]) diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index b36f9d14a4..38ac6b23f4 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -764,10 +764,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode if (wants_webgl2 && !webgl2_init_failed) { EmscriptenWebGLContextAttributes attributes; emscripten_webgl_init_context_attributes(&attributes); - //attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed"); - attributes.alpha = true; + attributes.alpha = OS::get_singleton()->is_layered_allowed(); attributes.antialias = false; attributes.majorVersion = 2; + attributes.explicitSwapControl = true; webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes); if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) { diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js index 9c4b6c2012..41be7b2512 100644 --- a/platform/web/js/engine/config.js +++ b/platform/web/js/engine/config.js @@ -317,7 +317,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- if (!(this.canvas instanceof HTMLCanvasElement)) { const nodes = document.getElementsByTagName('canvas'); if (nodes.length && nodes[0] instanceof HTMLCanvasElement) { - this.canvas = nodes[0]; + const first = nodes[0]; + this.canvas = /** @type {!HTMLCanvasElement} */ (first); } if (!this.canvas) { throw new Error('No canvas found in page'); diff --git a/platform/web/js/libs/audio.worklet.js b/platform/web/js/libs/audio.worklet.js index ea4d8cb221..daf5c9ef12 100644 --- a/platform/web/js/libs/audio.worklet.js +++ b/platform/web/js/libs/audio.worklet.js @@ -133,6 +133,8 @@ class GodotProcessor extends AudioWorkletProcessor { this.running = false; this.output = null; this.input = null; + this.lock = null; + this.notifier = null; } else if (p_cmd === 'start_nothreads') { this.output = new RingBuffer(p_data[0], p_data[0].length, false); } else if (p_cmd === 'chunk') { diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index 756c1ac595..68e100cca0 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -339,16 +339,21 @@ const GodotAudioWorklet = { if (GodotAudioWorklet.promise === null) { return; } - GodotAudioWorklet.promise.then(function () { + const p = GodotAudioWorklet.promise; + p.then(function () { GodotAudioWorklet.worklet.port.postMessage({ 'cmd': 'stop', 'data': null, }); GodotAudioWorklet.worklet.disconnect(); + GodotAudioWorklet.worklet.port.onmessage = null; GodotAudioWorklet.worklet = null; GodotAudioWorklet.promise = null; resolve(); - }).catch(function (err) { /* aborted? */ }); + }).catch(function (err) { + // Aborted? + GodotRuntime.error(err); + }); }); }, }, diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js index 377eec3234..ce64fb98c0 100644 --- a/platform/web/js/libs/library_godot_os.js +++ b/platform/web/js/libs/library_godot_os.js @@ -106,12 +106,14 @@ autoAddDeps(GodotConfig, '$GodotConfig'); mergeInto(LibraryManager.library, GodotConfig); const GodotFS = { - $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'], + $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'], $GodotFS__postset: [ 'Module["initFS"] = GodotFS.init;', 'Module["copyToFS"] = GodotFS.copy_to_fs;', ].join(''), $GodotFS: { + // ERRNO_CODES works every odd version of emscripten, but this will break too eventually. + ENOENT: 44, _idbfs: false, _syncing: false, _mount_points: [], @@ -138,8 +140,9 @@ const GodotFS = { try { FS.stat(dir); } catch (e) { - if (e.errno !== ERRNO_CODES.ENOENT) { - throw e; + if (e.errno !== GodotFS.ENOENT) { + // Let mkdirTree throw in case, we cannot trust the above check. + GodotRuntime.error(e); } FS.mkdirTree(dir); } @@ -208,8 +211,9 @@ const GodotFS = { try { FS.stat(dir); } catch (e) { - if (e.errno !== ERRNO_CODES.ENOENT) { - throw e; + if (e.errno !== GodotFS.ENOENT) { + // Let mkdirTree throw in case, we cannot trust the above check. + GodotRuntime.error(e); } FS.mkdirTree(dir); } diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp index 0f4411727a..287fe48c4d 100644 --- a/platform/web/web_main.cpp +++ b/platform/web/web_main.cpp @@ -55,6 +55,18 @@ void cleanup_after_sync() { emscripten_set_main_loop(exit_callback, -1, false); } +void early_cleanup() { + emscripten_cancel_main_loop(); // After this, we can exit! + int exit_code = OS_Web::get_singleton()->get_exit_code(); + memdelete(os); + os = nullptr; + emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing. +} + +void early_cleanup_sync() { + emscripten_set_main_loop(early_cleanup, -1, false); +} + void main_loop_callback() { uint64_t current_ticks = os->get_ticks_usec(); @@ -87,7 +99,19 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) { // We must override main when testing is enabled TEST_MAIN_OVERRIDE - Main::setup(argv[0], argc - 1, &argv[1]); + Error err = Main::setup(argv[0], argc - 1, &argv[1]); + + // Proper shutdown in case of setup failure. + if (err != OK) { + int exit_code = (int)err; + if (err == ERR_HELP) { + exit_code = 0; // Called with --help. + } + os->set_exit_code(exit_code); + // Will only exit after sync. + godot_js_os_finish_async(early_cleanup_sync); + return exit_code; + } // Ease up compatibility. ResourceLoader::set_abort_on_missing_resources(false); -- cgit v1.2.3