From e874cd84f0b21387ddc3091b6d594b2d6a8c4b6d Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Thu, 13 Jun 2024 23:40:45 +0200 Subject: [PATCH 1/4] [Web] Remove space between emscripten "-s" flags and the option --- platform/web/SCsub | 4 ++-- platform/web/detect.py | 50 +++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/platform/web/SCsub b/platform/web/SCsub index fea2fa7df95..3d36a888d6d 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -65,8 +65,8 @@ if env["dlink_enabled"]: # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. sys_env.Append(LIBS=["idbfs.js"]) # Configure it as a main module (dynamic linking support). - sys_env["CCFLAGS"].remove("SIDE_MODULE=2") - sys_env["LINKFLAGS"].remove("SIDE_MODULE=2") + sys_env["CCFLAGS"].remove("-sSIDE_MODULE=2") + sys_env["LINKFLAGS"].remove("-sSIDE_MODULE=2") sys_env.Append(CCFLAGS=["-s", "MAIN_MODULE=1"]) sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"]) sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"]) diff --git a/platform/web/detect.py b/platform/web/detect.py index a9be1471ae3..7d6a8052ce1 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -107,13 +107,13 @@ def configure(env: "SConsEnvironment"): env["use_assertions"] = True if env["use_assertions"]: - env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) + env.Append(LINKFLAGS=["-sASSERTIONS=1"]) if env.editor_build and env["initial_memory"] < 64: print("Note: Forcing `initial_memory=64` as it is required for the web editor.") env["initial_memory"] = 64 - env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) + env.Append(LINKFLAGS=["-sINITIAL_MEMORY=%sMB" % env["initial_memory"]]) ## Copy env variables. env["ENV"] = os.environ @@ -142,7 +142,7 @@ def configure(env: "SConsEnvironment"): env.Append(CCFLAGS=["-fsanitize=leak"]) env.Append(LINKFLAGS=["-fsanitize=leak"]) if env["use_safe_heap"]: - env.Append(LINKFLAGS=["-s", "SAFE_HEAP=1"]) + env.Append(LINKFLAGS=["-sSAFE_HEAP=1"]) # Closure compiler if env["use_closure_compiler"]: @@ -204,29 +204,29 @@ def configure(env: "SConsEnvironment"): if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. - env.Append(LINKFLAGS=["-s", "MAX_WEBGL_VERSION=2"]) + env.Append(LINKFLAGS=["-sMAX_WEBGL_VERSION=2"]) # Allow use to take control of swapping WebGL buffers. - env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"]) + env.Append(LINKFLAGS=["-sOFFSCREEN_FRAMEBUFFER=1"]) # Breaking change since emscripten 3.1.51 # https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#3151---121323 if cc_semver >= (3, 1, 51): # Enables the use of *glGetProcAddress() - env.Append(LINKFLAGS=["-s", "GL_ENABLE_GET_PROC_ADDRESS=1"]) + env.Append(LINKFLAGS=["-sGL_ENABLE_GET_PROC_ADDRESS=1"]) if env["javascript_eval"]: env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) stack_size_opt = "STACK_SIZE" if cc_semver >= (3, 1, 25) else "TOTAL_STACK" - env.Append(LINKFLAGS=["-s", "%s=%sKB" % (stack_size_opt, env["stack_size"])]) + env.Append(LINKFLAGS=["-s%s=%sKB" % (stack_size_opt, env["stack_size"])]) if env["threads"]: # Thread support (via SharedArrayBuffer). env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) - env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=%sKB" % env["default_pthread_stack_size"]]) - env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) - env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) + env.Append(CCFLAGS=["-sUSE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-sUSE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-sDEFAULT_PTHREAD_STACK_SIZE=%sKB" % env["default_pthread_stack_size"]]) + env.Append(LINKFLAGS=["-sPTHREAD_POOL_SIZE=8"]) + env.Append(LINKFLAGS=["-sWASM_MEM_MAX=2048MB"]) elif env["proxy_to_pthread"]: print_warning('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.') env["proxy_to_pthread"] = False @@ -248,8 +248,8 @@ def configure(env: "SConsEnvironment"): print_error("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) sys.exit(255) - env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) - env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) + env.Append(CCFLAGS=["-sSIDE_MODULE=2"]) + env.Append(LINKFLAGS=["-sSIDE_MODULE=2"]) env.Append(CCFLAGS=["-fvisibility=hidden"]) env.Append(LINKFLAGS=["-fvisibility=hidden"]) env.extra_suffix = ".dlink" + env.extra_suffix @@ -259,37 +259,37 @@ def configure(env: "SConsEnvironment"): # Run the main application in a web worker if env["proxy_to_pthread"]: - env.Append(LINKFLAGS=["-s", "PROXY_TO_PTHREAD=1"]) + env.Append(LINKFLAGS=["-sPROXY_TO_PTHREAD=1"]) env.Append(CPPDEFINES=["PROXY_TO_PTHREAD_ENABLED"]) - env.Append(LINKFLAGS=["-s", "EXPORTED_RUNTIME_METHODS=['_emscripten_proxy_main']"]) + env.Append(LINKFLAGS=["-sEXPORTED_RUNTIME_METHODS=['_emscripten_proxy_main']"]) # https://github.com/emscripten-core/emscripten/issues/18034#issuecomment-1277561925 - env.Append(LINKFLAGS=["-s", "TEXTDECODER=0"]) + env.Append(LINKFLAGS=["-sTEXTDECODER=0"]) # BigInt support to pass object pointers between contexts needs_wasm_bigint = True if needs_wasm_bigint: - env.Append(LINKFLAGS=["-s", "WASM_BIGINT"]) + env.Append(LINKFLAGS=["-sWASM_BIGINT"]) # Reduce code size by generating less support code (e.g. skip NodeJS support). - env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"]) + env.Append(LINKFLAGS=["-sENVIRONMENT=web,worker"]) # Wrap the JavaScript support code around a closure named Godot. - env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", "EXPORT_NAME='Godot'"]) + env.Append(LINKFLAGS=["-sMODULARIZE=1", "-sEXPORT_NAME='Godot'"]) # Allow increasing memory buffer size during runtime. This is efficient # when using WebAssembly (in comparison to asm.js) and works well for # us since we don't know requirements at compile-time. - env.Append(LINKFLAGS=["-s", "ALLOW_MEMORY_GROWTH=1"]) + env.Append(LINKFLAGS=["-sALLOW_MEMORY_GROWTH=1"]) # Do not call main immediately when the support code is ready. - env.Append(LINKFLAGS=["-s", "INVOKE_RUN=0"]) + env.Append(LINKFLAGS=["-sINVOKE_RUN=0"]) # callMain for manual start, cwrap for the mono version. - env.Append(LINKFLAGS=["-s", "EXPORTED_RUNTIME_METHODS=['callMain','cwrap']"]) + env.Append(LINKFLAGS=["-sEXPORTED_RUNTIME_METHODS=['callMain','cwrap']"]) # Add code that allow exiting runtime. - env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"]) + env.Append(LINKFLAGS=["-sEXIT_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"]) + env.Append(LINKFLAGS=["-sGL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0"]) From 7af8c494ab885911f5796c7b16a1a0314f2d6c67 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Thu, 13 Jun 2024 23:42:41 +0200 Subject: [PATCH 2/4] [Web] Force emcc to use "wasm" longjmp mode SUPPORT_LONGJMP have changed since emscripten 3.1.32 to default to "wasm" mode when exceptions are enabled, and "emscripten" mode when disabled. While we generally doesn't use exception in core, linked libraries may need them, and emscripten don't plan to support WASM EH + Emscripten SjLj in the long term. --- platform/web/detect.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/web/detect.py b/platform/web/detect.py index 7d6a8052ce1..1b15ff8e2ea 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -276,6 +276,10 @@ def configure(env: "SConsEnvironment"): # Wrap the JavaScript support code around a closure named Godot. env.Append(LINKFLAGS=["-sMODULARIZE=1", "-sEXPORT_NAME='Godot'"]) + # Force long jump mode to 'wasm' + env.Append(CCFLAGS=["-sSUPPORT_LONGJMP='wasm'"]) + env.Append(LINKFLAGS=["-sSUPPORT_LONGJMP='wasm'"]) + # Allow increasing memory buffer size during runtime. This is efficient # when using WebAssembly (in comparison to asm.js) and works well for # us since we don't know requirements at compile-time. From c95bd2d264c7e8fe49fe04760e51f221d4df42df Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Thu, 13 Jun 2024 23:54:59 +0200 Subject: [PATCH 3/4] [Web] Disable thorvg threads on the Web They're of little help, and just adds to the possibility of causing deadlocks. --- thirdparty/thorvg/inc/config.h | 2 ++ thirdparty/thorvg/update-thorvg.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 699076fdf1a..e2a61a9384a 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -5,7 +5,9 @@ #define THORVG_SVG_LOADER_SUPPORT #define THORVG_PNG_LOADER_SUPPORT #define THORVG_JPG_LOADER_SUPPORT +#ifndef WEB_ENABLED #define THORVG_THREAD_SUPPORT +#endif // Added conditionally if webp module is enabled. //#define THORVG_WEBP_LOADER_SUPPORT diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index c980b89c4b6..c0034ba888e 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -38,7 +38,9 @@ cat << EOF > ../inc/config.h #define THORVG_SVG_LOADER_SUPPORT #define THORVG_PNG_LOADER_SUPPORT #define THORVG_JPG_LOADER_SUPPORT +#ifndef WEB_ENABLED #define THORVG_THREAD_SUPPORT +#endif // Added conditionally if webp module is enabled. //#define THORVG_WEBP_LOADER_SUPPORT From 27d67b5ae6678e264a08d43679677b19301f9710 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Fri, 14 Jun 2024 00:24:30 +0200 Subject: [PATCH 4/4] [Web] Use the module config to preload GDExtension libraries. Instead of calling loadDynamicLibraries ourselves, we add the GDExtension libraries to preload to the "dynamicLibraries" module config property. This seems to fix some threading issue with some browsers during the init phase. --- platform/web/js/engine/config.js | 5 ++++- platform/web/js/engine/engine.js | 24 +++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js index a7134da6faa..8c4e1b1b248 100644 --- a/platform/web/js/engine/config.js +++ b/platform/web/js/engine/config.js @@ -271,12 +271,13 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- */ Config.prototype.getModuleConfig = function (loadPath, response) { let r = response; + const gdext = this.gdextensionLibs; return { 'print': this.onPrint, 'printErr': this.onPrintError, 'thisProgram': this.executable, 'noExitRuntime': false, - 'dynamicLibraries': [`${loadPath}.side.wasm`], + 'dynamicLibraries': [`${loadPath}.side.wasm`].concat(this.gdextensionLibs), 'instantiateWasm': function (imports, onSuccess) { function done(result) { onSuccess(result['instance'], result['module']); @@ -300,6 +301,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- return `${loadPath}.audio.worklet.js`; } else if (path.endsWith('.js')) { return `${loadPath}.js`; + } else if (path in gdext) { + return path; } else if (path.endsWith('.side.wasm')) { return `${loadPath}.side.wasm`; } else if (path.endsWith('.wasm')) { diff --git a/platform/web/js/engine/engine.js b/platform/web/js/engine/engine.js index 7e24ad9ae23..04c4c44c5ee 100644 --- a/platform/web/js/engine/engine.js +++ b/platform/web/js/engine/engine.js @@ -163,25 +163,19 @@ const Engine = (function () { me.rtenv['initConfig'](config); // Preload GDExtension libraries. - const libs = []; if (me.config.gdextensionLibs.length > 0 && !me.rtenv['loadDynamicLibrary']) { return Promise.reject(new Error('GDExtension libraries are not supported by this engine version. ' + 'Enable "Extensions Support" for your export preset and/or build your custom template with "dlink_enabled=yes".')); } - me.config.gdextensionLibs.forEach(function (lib) { - libs.push(me.rtenv['loadDynamicLibrary'](lib, { 'loadAsync': true })); - }); - return Promise.all(libs).then(function () { - return new Promise(function (resolve, reject) { - preloader.preloadedFiles.forEach(function (file) { - me.rtenv['copyToFS'](file.path, file.buffer); - }); - preloader.preloadedFiles.length = 0; // Clear memory - me.rtenv['callMain'](me.config.args); - initPromise = null; - me.installServiceWorker(); - resolve(); - }); + return new Promise(function (resolve, reject) { + for (const file of preloader.preloadedFiles) { + me.rtenv['copyToFS'](file.path, file.buffer); + } + preloader.preloadedFiles.length = 0; // Clear memory + me.rtenv['callMain'](me.config.args); + initPromise = null; + me.installServiceWorker(); + resolve(); }); }); },