Skip to content

Commit

Permalink
Merge pull request #44170 from Faless/js/3.x_gdnative
Browse files Browse the repository at this point in the history
[3.2] [HTML5] Optional GDNative Support
  • Loading branch information
akien-mga committed Dec 7, 2020
2 parents 364d019 + 79a1418 commit 2beb36f
Show file tree
Hide file tree
Showing 22 changed files with 309 additions and 62 deletions.
2 changes: 2 additions & 0 deletions misc/dist/html/full-size.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@

const EXECUTABLE_NAME = '$GODOT_BASENAME';
const MAIN_PACK = '$GODOT_BASENAME.pck';
const GDNATIVE_LIBS = [$GODOT_GDNATIVE_LIBS];
const INDETERMINATE_STATUS_STEP_MS = 100;
const FULL_WINDOW = $GODOT_FULL_WINDOW;

Expand Down Expand Up @@ -267,6 +268,7 @@
} else {
setStatusMode('indeterminate');
engine.setCanvas(canvas);
engine.setGDNativeLibraries(GDNATIVE_LIBS);
engine.startGame(EXECUTABLE_NAME, MAIN_PACK).then(() => {
setStatusMode('hidden');
initializing = false;
Expand Down
10 changes: 5 additions & 5 deletions modules/gdnative/gdnative_library_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,11 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platform_android.library_extension = "*.so";
platforms["Android"] = platform_android;

// TODO: Javascript platform is not supported yet
// NativePlatformConfig platform_html5;
// platform_html5.name = "HTML5";
// platform_html5.library_extension = "*.wasm";
// platforms["Javascript"] = platform_html5;
NativePlatformConfig platform_html5;
platform_html5.name = "HTML5";
platform_html5.entries.push_back("wasm32");
platform_html5.library_extension = "*.wasm";
platforms["HTML5"] = platform_html5;

NativePlatformConfig platform_ios;
platform_ios.name = "iOS";
Expand Down
22 changes: 21 additions & 1 deletion modules/webrtc/library_godot_webrtc.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const GodotRTCDataChannel = {
},
},

godot_js_rtc_datachannel_ready_state_get__sig: 'ii',
godot_js_rtc_datachannel_ready_state_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -109,6 +110,7 @@ const GodotRTCDataChannel = {
}
},

godot_js_rtc_datachannel_send__sig: 'iiiii',
godot_js_rtc_datachannel_send: function (p_id, p_buffer, p_length, p_raw) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -129,14 +131,17 @@ const GodotRTCDataChannel = {
return 0;
},

godot_js_rtc_datachannel_is_ordered__sig: 'ii',
godot_js_rtc_datachannel_is_ordered: function (p_id) {
return IDHandler.get_prop(p_id, 'ordered', true);
},

godot_js_rtc_datachannel_id_get__sig: 'ii',
godot_js_rtc_datachannel_id_get: function (p_id) {
return IDHandler.get_prop(p_id, 'id', 65535);
},

godot_js_rtc_datachannel_max_packet_lifetime_get__sig: 'ii',
godot_js_rtc_datachannel_max_packet_lifetime_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -151,14 +156,17 @@ const GodotRTCDataChannel = {
return 65535;
},

godot_js_rtc_datachannel_max_retransmits_get__sig: 'ii',
godot_js_rtc_datachannel_max_retransmits_get: function (p_id) {
return IDHandler.get_prop(p_id, 'maxRetransmits', 65535);
},

godot_js_rtc_datachannel_is_negotiated: function (p_id, p_def) {
godot_js_rtc_datachannel_is_negotiated__sig: 'ii',
godot_js_rtc_datachannel_is_negotiated: function (p_id) {
return IDHandler.get_prop(p_id, 'negotiated', 65535);
},

godot_js_rtc_datachannel_label_get__sig: 'ii',
godot_js_rtc_datachannel_label_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref || !ref.label) {
Expand All @@ -167,6 +175,7 @@ const GodotRTCDataChannel = {
return GodotRuntime.allocString(ref.label);
},

godot_js_rtc_datachannel_protocol_get__sig: 'ii',
godot_js_rtc_datachannel_protocol_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref || !ref.protocol) {
Expand All @@ -175,11 +184,13 @@ const GodotRTCDataChannel = {
return GodotRuntime.allocString(ref.protocol);
},

godot_js_rtc_datachannel_destroy__sig: 'vi',
godot_js_rtc_datachannel_destroy: function (p_id) {
GodotRTCDataChannel.close(p_id);
IDHandler.remove(p_id);
},

godot_js_rtc_datachannel_connect__sig: 'viiiiii',
godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
Expand All @@ -188,6 +199,7 @@ const GodotRTCDataChannel = {
GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
},

godot_js_rtc_datachannel_close__sig: 'vi',
godot_js_rtc_datachannel_close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand Down Expand Up @@ -280,6 +292,7 @@ const GodotRTCPeerConnection = {
},
},

godot_js_rtc_pc_create__sig: 'iiiiii',
godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
Expand All @@ -302,6 +315,7 @@ const GodotRTCPeerConnection = {
return id;
},

godot_js_rtc_pc_close__sig: 'vi',
godot_js_rtc_pc_close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -310,6 +324,7 @@ const GodotRTCPeerConnection = {
ref.close();
},

godot_js_rtc_pc_destroy__sig: 'vi',
godot_js_rtc_pc_destroy: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -321,6 +336,7 @@ const GodotRTCPeerConnection = {
IDHandler.remove(p_id);
},

godot_js_rtc_pc_offer_create__sig: 'viiii',
godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -335,6 +351,7 @@ const GodotRTCPeerConnection = {
});
},

godot_js_rtc_pc_local_description_set__sig: 'viiiii',
godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -351,6 +368,7 @@ const GodotRTCPeerConnection = {
});
},

godot_js_rtc_pc_remote_description_set__sig: 'viiiiii',
godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -375,6 +393,7 @@ const GodotRTCPeerConnection = {
});
},

godot_js_rtc_pc_ice_candidate_add__sig: 'viiii',
godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) {
const ref = IDHandler.get(p_id);
if (!ref) {
Expand All @@ -390,6 +409,7 @@ const GodotRTCPeerConnection = {
},

godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
godot_js_rtc_pc_datachannel_create__sig: 'iiii',
godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) {
try {
const ref = IDHandler.get(p_id);
Expand Down
4 changes: 4 additions & 0 deletions modules/websocket/library_godot_websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const GodotWebSocket = {
},
},

godot_js_websocket_create__sig: 'iiiiiiii',
godot_js_websocket_create: function (p_ref, p_url, p_proto, p_on_open, p_on_message, p_on_error, p_on_close) {
const on_open = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
const on_message = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
Expand All @@ -156,6 +157,7 @@ const GodotWebSocket = {
return GodotWebSocket.create(socket, on_open, on_message, on_error, on_close);
},

godot_js_websocket_send__sig: 'iiiii',
godot_js_websocket_send: function (p_id, p_buf, p_buf_len, p_raw) {
const bytes_array = new Uint8Array(p_buf_len);
let i = 0;
Expand All @@ -169,12 +171,14 @@ const GodotWebSocket = {
return GodotWebSocket.send(p_id, out);
},

godot_js_websocket_close__sig: 'viii',
godot_js_websocket_close: function (p_id, p_code, p_reason) {
const code = p_code;
const reason = GodotRuntime.parseString(p_reason);
GodotWebSocket.close(p_id, code, reason);
},

godot_js_websocket_destroy__sig: 'vi',
godot_js_websocket_destroy: function (p_id) {
GodotWebSocket.destroy(p_id);
},
Expand Down
62 changes: 48 additions & 14 deletions platform/javascript/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,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",
Expand All @@ -28,12 +23,48 @@ 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(CPPDEFINES=["WASM_GDNATIVE"]) # So that OS knows it can run GDNative libraries.
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",
Expand All @@ -60,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)
Expand Down
25 changes: 15 additions & 10 deletions platform/javascript/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]

Expand Down Expand Up @@ -83,10 +84,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"]:
Expand Down Expand Up @@ -133,6 +132,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"])
Expand All @@ -144,14 +146,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
Expand All @@ -162,12 +165,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"])
Loading

0 comments on commit 2beb36f

Please sign in to comment.