diff options
| author | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2020-10-24 16:02:09 +0200 | 
|---|---|---|
| committer | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2020-12-05 00:52:43 +0100 | 
| commit | ca34b5e57a79d843bb5a57f13fb2f674e1d801e7 (patch) | |
| tree | fa79e016fa767abfdf568c10205a7293bb858c13 /platform/javascript | |
| parent | 1167ab96e9be2a86b1315693b843820eef978685 (diff) | |
[HTML5] GDNative support via SIDE_MODULE.
Working with emscripten >= 2.0.10
Diffstat (limited to 'platform/javascript')
| -rw-r--r-- | platform/javascript/SCsub | 61 | ||||
| -rw-r--r-- | platform/javascript/detect.py | 25 | ||||
| -rw-r--r-- | platform/javascript/javascript_main.cpp | 2 | ||||
| -rw-r--r-- | platform/javascript/javascript_runtime.cpp | 35 | ||||
| -rw-r--r-- | platform/javascript/js/dynlink.pre.js | 1 | ||||
| -rw-r--r-- | platform/javascript/js/engine/engine.js | 6 | ||||
| -rw-r--r-- | platform/javascript/js/engine/utils.js | 2 | 
7 files changed, 104 insertions, 28 deletions
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 627ae778b1..3501e17c6c 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -12,13 +12,8 @@ javascript_files = [      "api/javascript_tools_editor_plugin.cpp",  ] -build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] -if env["threads_enabled"]: -    build_targets.append("#bin/godot${PROGSUFFIX}.worker.js") - -build = env.add_program(build_targets, javascript_files) - -env.AddJSLibraries( +sys_env = env.Clone() +sys_env.AddJSLibraries(      [          "js/libs/library_godot_audio.js",          "js/libs/library_godot_display.js", @@ -29,12 +24,47 @@ env.AddJSLibraries(  )  if env["tools"]: -    env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"]) +    sys_env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"])  if env["javascript_eval"]: -    env.AddJSLibraries(["js/libs/library_godot_eval.js"]) -for lib in env["JS_LIBS"]: -    env.Append(LINKFLAGS=["--js-library", lib]) -env.Depends(build, env["JS_LIBS"]) +    sys_env.AddJSLibraries(["js/libs/library_godot_eval.js"]) +for lib in sys_env["JS_LIBS"]: +    sys_env.Append(LINKFLAGS=["--js-library", lib]) + +build = [] +if env["gdnative_enabled"]: +    build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] +    # Reset libraries. The main runtime will only link emscripten libraries, not godot ones. +    sys_env["LIBS"] = [] +    # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. +    sys_env.Append(LIBS=["idbfs.js"]) +    # JS prepended to the module code loading the side library. +    sys_env.Append(LINKFLAGS=["--pre-js", sys_env.File("js/dynlink.pre.js")]) +    # Configure it as a main module (dynamic linking support). +    sys_env.Append(CCFLAGS=["-s", "MAIN_MODULE=1"]) +    sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"]) +    sys_env.Append(CCFLAGS=["-s", "EXPORT_ALL=1"]) +    sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"]) +    # Force exporting the standard library (printf, malloc, etc.) +    sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi" +    # The main emscripten runtime, with exported standard libraries. +    sys = sys_env.Program(build_targets, ["javascript_runtime.cpp"]) +    sys_env.Depends(sys, "js/dynlink.pre.js") + +    # The side library, containing all Godot code. +    wasm_env = env.Clone() +    wasm_env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) +    wasm_env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) +    wasm = wasm_env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", javascript_files) +    build = [sys[0], sys[1], wasm[0]] +else: +    build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] +    if env["threads_enabled"]: +        build_targets.append("#bin/godot${PROGSUFFIX}.worker.js") +    # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. +    sys_env.Append(LIBS=["idbfs.js"]) +    build = sys_env.Program(build_targets, javascript_files + ["javascript_runtime.cpp"]) + +sys_env.Depends(build[0], sys_env["JS_LIBS"])  engine = [      "js/engine/preloader.js", @@ -61,8 +91,11 @@ out_files = [  ]  html_file = "#misc/dist/html/editor.html" if env["tools"] else "#misc/dist/html/full-size.html"  in_files = [js_wrapped, build[1], html_file, "#platform/javascript/js/libs/audio.worklet.js"] -if env["threads_enabled"]: -    in_files.append(build[2]) +if env["gdnative_enabled"]: +    in_files.append(build[2])  # Runtime +    out_files.append(zip_dir.File(binary_name + ".side.wasm")) +elif env["threads_enabled"]: +    in_files.append(build[2])  # Worker      out_files.append(zip_dir.File(binary_name + ".worker.js"))  zip_files = env.InstallAs(out_files, in_files) diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 9f584d0899..f4fa5fb218 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -23,6 +23,7 @@ def get_opts():          # eval() can be a security concern, so it can be disabled.          BoolVariable("javascript_eval", "Enable JavaScript eval interface", True),          BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", False), +        BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False),          BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),      ] @@ -85,10 +86,8 @@ def configure(env):      # LTO      if env["use_lto"]: -        env.Append(CCFLAGS=["-s", "WASM_OBJECT_FILES=0"]) -        env.Append(LINKFLAGS=["-s", "WASM_OBJECT_FILES=0"]) -        env.Append(CCFLAGS=["-flto"]) -        env.Append(LINKFLAGS=["-flto"]) +        env.Append(CCFLAGS=["-flto=full"]) +        env.Append(LINKFLAGS=["-flto=full"])      # Closure compiler      if env["use_closure_compiler"]: @@ -135,6 +134,9 @@ def configure(env):      if env["javascript_eval"]:          env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) +    if env["threads_enabled"] and env["gdnative_enabled"]: +        raise Exception("Threads and GDNative support can't be both enabled due to WebAssembly limitations") +      # Thread support (via SharedArrayBuffer).      if env["threads_enabled"]:          env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) @@ -146,14 +148,15 @@ def configure(env):      else:          env.Append(CPPDEFINES=["NO_THREADS"]) +    if env["gdnative_enabled"]: +        env.Append(CCFLAGS=["-s", "RELOCATABLE=1"]) +        env.Append(LINKFLAGS=["-s", "RELOCATABLE=1"]) +        env.extra_suffix = ".gdnative" + env.extra_suffix +      # Reduce code size by generating less support code (e.g. skip NodeJS support).      env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"]) -    # We use IDBFS in javascript_main.cpp. Since Emscripten 1.39.1 it needs to -    # be linked explicitly. -    env.Append(LIBS=["idbfs.js"]) - -    env.Append(LINKFLAGS=["-s", "BINARYEN=1"]) +    # Wrap the JavaScript support code around a closure named Godot.      env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", "EXPORT_NAME='Godot'"])      # Allow increasing memory buffer size during runtime. This is efficient @@ -164,12 +167,14 @@ def configure(env):      # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.      env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"]) +    # Do not call main immediately when the support code is ready.      env.Append(LINKFLAGS=["-s", "INVOKE_RUN=0"])      # Allow use to take control of swapping WebGL buffers.      env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"]) -    # callMain for manual start, FS for preloading, PATH and ERRNO_CODES for BrowserFS. +    # callMain for manual start.      env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']"]) +      # Add code that allow exiting runtime.      env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"]) diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 2d28a63566..b4985a4f36 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -75,7 +75,7 @@ void main_loop_callback() {  }  /// When calling main, it is assumed FS is setup and synced. -int main(int argc, char *argv[]) { +extern EMSCRIPTEN_KEEPALIVE int godot_js_main(int argc, char *argv[]) {  	os = new OS_JavaScript();  	// We must override main when testing is enabled diff --git a/platform/javascript/javascript_runtime.cpp b/platform/javascript/javascript_runtime.cpp new file mode 100644 index 0000000000..bfe9fbd1bc --- /dev/null +++ b/platform/javascript/javascript_runtime.cpp @@ -0,0 +1,35 @@ +/*************************************************************************/ +/*  javascript_runtime.cpp                                               */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +extern int godot_js_main(int argc, char *argv[]); + +int main(int argc, char *argv[]) { +	return godot_js_main(argc, argv); +} diff --git a/platform/javascript/js/dynlink.pre.js b/platform/javascript/js/dynlink.pre.js new file mode 100644 index 0000000000..6ac993bad0 --- /dev/null +++ b/platform/javascript/js/dynlink.pre.js @@ -0,0 +1 @@ +Module['dynamicLibraries'] = [Module['thisProgram'] + '.side.wasm']; diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js index 74153b672a..f0abdff640 100644 --- a/platform/javascript/js/engine/engine.js +++ b/platform/javascript/js/engine/engine.js @@ -58,6 +58,9 @@ const Engine = (function () {  		initPromise = new Promise(function (resolve, reject) {  			config['locateFile'] = Utils.createLocateRewrite(loadPath);  			config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise); +			// Emscripten configuration. +			config['thisProgram'] = me.executableName; +			config['noExitRuntime'] = true;  			Godot(config).then(function (module) {  				module['initFS'](me.persistentPaths).then(function (fs_err) {  					me.rtenv = module; @@ -119,9 +122,6 @@ const Engine = (function () {  				locale = navigator.languages ? navigator.languages[0] : navigator.language;  				locale = locale.split('.')[0];  			} -			// Emscripten configuration. -			me.rtenv['thisProgram'] = me.executableName; -			me.rtenv['noExitRuntime'] = true;  			// Godot configuration.  			me.rtenv['initConfig']({  				'resizeCanvasOnStart': me.resizeCanvasOnStart, diff --git a/platform/javascript/js/engine/utils.js b/platform/javascript/js/engine/utils.js index d0fca4e1cb..9273bbad42 100644 --- a/platform/javascript/js/engine/utils.js +++ b/platform/javascript/js/engine/utils.js @@ -8,6 +8,8 @@ const Utils = { // eslint-disable-line no-unused-vars  				return `${execName}.audio.worklet.js`;  			} else if (path.endsWith('.js')) {  				return `${execName}.js`; +			} else if (path.endsWith('.side.wasm')) { +				return `${execName}.side.wasm`;  			} else if (path.endsWith('.wasm')) {  				return `${execName}.wasm`;  			}  |