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

[Web] Require threads, rtti, allow optimize=speed. #65094

Merged
merged 1 commit into from
Aug 30, 2022
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: 1 addition & 1 deletion .github/workflows/web_builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ env:
# Only used for the cache key. Increment version to force clean build.
GODOT_BASE_BRANCH: master
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no
EM_VERSION: 3.1.10
EM_VERSION: 3.1.20
EM_CACHE_FOLDER: "emsdk-cache"

concurrency:
Expand Down
8 changes: 1 addition & 7 deletions modules/text_server_adv/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,9 @@ if env["builtin_harfbuzz"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/graphite/include"])
env_harfbuzz.Append(CCFLAGS=["-DGRAPHITE2_STATIC"])

if env["platform"] == "android" or env["platform"] == "linuxbsd":
if env["platform"] in ["android", "linuxbsd", "web"]:
env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])

if env["platform"] == "web":
if env["threads_enabled"]:
env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
else:
env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"])

env_text_server_adv.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])

lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources)
Expand Down
26 changes: 10 additions & 16 deletions platform/web/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,17 @@ for ext in env["JS_EXTERNS"]:
sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.abspath

build = []
if env["gdnative_enabled"]:
build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"]
if env["threads_enabled"]:
build_targets.append("#bin/godot${PROGSUFFIX}.worker.js")
build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm", "#bin/godot${PROGSUFFIX}.worker.js"]
if env["dlink_enabled"]:
# 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"])
# 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.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"])
sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"])
# Force exporting the standard library (printf, malloc, etc.)
Expand All @@ -55,16 +54,9 @@ if env["gdnative_enabled"]:
sys = sys_env.Program(build_targets, ["web_runtime.cpp"])

# 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", web_files)
wasm = env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", web_files)
build = sys + [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, web_files + ["web_runtime.cpp"])
Expand All @@ -88,6 +80,8 @@ wrap_list = [
]
js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js")

# Extra will be the thread worker, or the GDNative side, or None
extra = build[2:] if len(build) > 2 else None
env.CreateTemplateZip(js_wrapped, build[1], extra)
# 0 - unwrapped js file (use wrapped one instead)
# 1 - wasm file
# 2 - worker file
# 3 - wasm side (when dlink is enabled).
env.CreateTemplateZip(js_wrapped, build[1], build[2], build[3] if len(build) > 3 else None)
70 changes: 28 additions & 42 deletions platform/web/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ def get_opts():
BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False),
# 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)", True),
BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False),
BoolVariable(
"dlink_enabled", "Enable WebAssembly dynamic linking (GDExtension support). Produces bigger binaries", False
),
BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
]

Expand All @@ -50,6 +51,13 @@ def get_flags():
("tools", False),
("builtin_pcre2_with_jit", False),
("vulkan", False),
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
# decreases download time.
# -Os reduces file size by around 5 MiB over -O3. -Oz only saves about
# 100 KiB over -Os, which does not justify the negative impact on
# run-time performance.
("optimize", "size"),
]


Expand All @@ -71,15 +79,12 @@ def configure(env):

## Build type
if env["target"].startswith("release"):
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
# decreases download time.
# -Os reduces file size by around 5 MiB over -O3. -Oz only saves about
# 100 KiB over -Os, which does not justify the negative impact on
# run-time performance.
if env["optimize"] != "none":
if env["optimize"] == "size":
env.Append(CCFLAGS=["-Os"])
env.Append(LINKFLAGS=["-Os"])
elif env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
env.Append(LINKFLAGS=["-O3"])

if env["target"] == "release_debug":
# Retain function names for backtraces at the cost of file size.
Expand All @@ -93,21 +98,11 @@ def configure(env):
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])

if env["tools"]:
if not env["threads_enabled"]:
print('Note: Forcing "threads_enabled=yes" as it is required for the web editor.')
env["threads_enabled"] = "yes"
if env["initial_memory"] < 64:
print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
env["initial_memory"] = 64
env.Append(CCFLAGS=["-frtti"])
elif env["builtin_icu"]:
env.Append(CCFLAGS=["-fno-exceptions", "-frtti"])
else:
# Disable exceptions and rtti on non-tools (template) builds
# These flags help keep the file size down.
env.Append(CCFLAGS=["-fno-exceptions", "-fno-rtti"])
# Don't use dynamic_cast, necessary with no-rtti.
env.Append(CPPDEFINES=["NO_SAFE_CAST"])
env.Append(CPPFLAGS=["-fno-exceptions"])

env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])

Expand Down Expand Up @@ -171,9 +166,9 @@ def configure(env):
env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"

# All intermediate files are just LLVM bitcode.
# All intermediate files are just object files.
env["OBJPREFIX"] = ""
env["OBJSUFFIX"] = ".bc"
env["OBJSUFFIX"] = ".o"
env["PROGPREFIX"] = ""
# Program() output consists of multiple files, so specify suffixes manually at builder.
env["PROGSUFFIX"] = ""
Expand All @@ -196,31 +191,22 @@ def configure(env):
env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])

# Thread support (via SharedArrayBuffer).
if env["threads_enabled"]:
env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
env.extra_suffix = ".threads" + env.extra_suffix
else:
env.Append(CPPDEFINES=["NO_THREADS"])
env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])

if env["gdnative_enabled"]:
if env["dlink_enabled"]:
cc_version = get_compiler_version(env)
cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"]))
if cc_semver < (2, 0, 10):
print("GDNative support requires emscripten >= 2.0.10, detected: %s.%s.%s" % cc_semver)
if cc_semver < (3, 1, 14):
print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
sys.exit(255)

if env["threads_enabled"] and cc_semver < (3, 1, 14):
print("Threads and GDNative requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
sys.exit(255)
env.Append(CCFLAGS=["-s", "RELOCATABLE=1"])
env.Append(LINKFLAGS=["-s", "RELOCATABLE=1"])
# Weak symbols are broken upstream: https://github.com/emscripten-core/emscripten/issues/12819
env.Append(CPPDEFINES=["ZSTD_HAVE_WEAK_SYMBOLS=0"])
env.extra_suffix = ".gdnative" + env.extra_suffix
env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"])
env.extra_suffix = ".dlink" + env.extra_suffix

# Reduce code size by generating less support code (e.g. skip NodeJS support).
env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"])
Expand Down
13 changes: 6 additions & 7 deletions platform/web/emscripten_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,25 @@ def create_engine_file(env, target, source, externs):
return env.Textfile(target, [env.File(s) for s in source])


def create_template_zip(env, js, wasm, extra):
def create_template_zip(env, js, wasm, worker, side):
binary_name = "godot.tools" if env["tools"] else "godot"
zip_dir = env.Dir("#bin/.web_zip")
in_files = [
js,
wasm,
worker,
"#platform/web/js/libs/audio.worklet.js",
]
out_files = [
zip_dir.File(binary_name + ".js"),
zip_dir.File(binary_name + ".wasm"),
zip_dir.File(binary_name + ".worker.js"),
zip_dir.File(binary_name + ".audio.worklet.js"),
]
# GDNative/Threads specific
if env["gdnative_enabled"]:
in_files.append(extra.pop()) # Runtime
# Dynamic linking (extensions) specific.
if env["dlink_enabled"]:
in_files.append(side) # Side wasm (contains the actual Godot code).
out_files.append(zip_dir.File(binary_name + ".side.wasm"))
if env["threads_enabled"]:
in_files.append(extra.pop()) # Worker
out_files.append(zip_dir.File(binary_name + ".worker.js"))

service_worker = "#misc/dist/html/service-worker.js"
if env["tools"]:
Expand Down
30 changes: 11 additions & 19 deletions platform/web/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
// Service worker
const String dir = p_path.get_base_dir();
const String name = p_path.get_file().get_basename();
const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
bool extensions = (bool)p_preset->get("variant/extensions_support");
HashMap<String, String> replaces;
replaces["@GODOT_VERSION@"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec());
replaces["@GODOT_NAME@"] = proj_name.substr(0, 16);
Expand All @@ -216,17 +216,15 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
cache_files.push_back(name + ".icon.png");
cache_files.push_back(name + ".apple-touch-icon.png");
}
if (mode & EXPORT_MODE_THREADS) {
cache_files.push_back(name + ".worker.js");
cache_files.push_back(name + ".audio.worklet.js");
}
cache_files.push_back(name + ".worker.js");
cache_files.push_back(name + ".audio.worklet.js");
replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string();

// Heavy files that are cached on demand.
Array opt_cache_files;
opt_cache_files.push_back(name + ".wasm");
opt_cache_files.push_back(name + ".pck");
if (mode & EXPORT_MODE_GDNATIVE) {
if (extensions) {
opt_cache_files.push_back(name + ".side.wasm");
for (int i = 0; i < p_shared_objects.size(); i++) {
opt_cache_files.push_back(p_shared_objects[i].path.get_file());
Expand Down Expand Up @@ -317,20 +315,14 @@ void EditorExportPlatformWeb::get_preset_features(const Ref<EditorExportPreset>
r_features->push_back("etc2");
}
}
ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
if (mode & EXPORT_MODE_THREADS) {
r_features->push_back("threads");
}
if (mode & EXPORT_MODE_GDNATIVE) {
r_features->push_back("wasm32");
}
r_features->push_back("wasm32");
}

void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));

r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type.
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // Export type.
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer

Expand Down Expand Up @@ -374,11 +366,11 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp

String err;
bool valid = false;
ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
bool extensions = (bool)p_preset->get("variant/extensions_support");

// Look for export templates (first official, and if defined custom templates).
bool dvalid = exists_export_template(_get_template_name(mode, true), &err);
bool rvalid = exists_export_template(_get_template_name(mode, false), &err);
bool dvalid = exists_export_template(_get_template_name(extensions, true), &err);
bool rvalid = exists_export_template(_get_template_name(extensions, false), &err);

if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
Expand Down Expand Up @@ -456,8 +448,8 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
String template_path = p_debug ? custom_debug : custom_release;
template_path = template_path.strip_edges();
if (template_path.is_empty()) {
ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
template_path = find_export_template(_get_template_name(mode, p_debug));
bool extensions = (bool)p_preset->get("variant/extensions_support");
template_path = find_export_template(_get_template_name(extensions, p_debug));
}

if (!DirAccess::exists(base_dir)) {
Expand Down
18 changes: 4 additions & 14 deletions platform/web/export/export_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,10 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
Mutex server_lock;
Thread server_thread;

enum ExportMode {
EXPORT_MODE_NORMAL = 0,
EXPORT_MODE_THREADS = 1,
EXPORT_MODE_GDNATIVE = 2,
EXPORT_MODE_THREADS_GDNATIVE = 3,
};

String _get_template_name(ExportMode p_mode, bool p_debug) const {
String name = "webassembly";
if (p_mode & EXPORT_MODE_GDNATIVE) {
name += "_gdnative";
}
if (p_mode & EXPORT_MODE_THREADS) {
name += "_threads";
String _get_template_name(bool p_extension, bool p_debug) const {
String name = "web";
if (p_extension) {
name += "_dlink";
}
if (p_debug) {
name += "_debug.zip";
Expand Down
17 changes: 0 additions & 17 deletions platform/web/os_web.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,26 +140,9 @@ int OS_Web::get_processor_count() const {
}

bool OS_Web::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "html5" || p_feature == "web") {
return true;
}

#ifdef JAVASCRIPT_EVAL_ENABLED
if (p_feature == "web") {
return true;
}
#endif
#ifndef NO_THREADS
if (p_feature == "threads") {
return true;
}
#endif
#if WASM_GDNATIVE
if (p_feature == "wasm32") {
return true;
}
#endif

return false;
}

Expand Down