Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.2] [HTML5] Optional GDNative Support #44170

Merged
merged 5 commits into from
Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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