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

Update export names (web[_dlink]_[release|debug].zip).

The Build with dynamic linking is broken due to high number of imports
in output wasm (likely emscripten regression issue 15487).
This commit is contained in:
Fabio Alessandrelli
2022-08-29 15:56:28 +02:00
parent 4c0f5f751e
commit f958f00283
8 changed files with 61 additions and 123 deletions

View File

@ -6,7 +6,7 @@ env:
# Only used for the cache key. Increment version to force clean build. # Only used for the cache key. Increment version to force clean build.
GODOT_BASE_BRANCH: master GODOT_BASE_BRANCH: master
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no 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" EM_CACHE_FOLDER: "emsdk-cache"
concurrency: concurrency:

View File

@ -140,15 +140,9 @@ if env["builtin_harfbuzz"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/graphite/include"]) env_harfbuzz.Prepend(CPPPATH=["#thirdparty/graphite/include"])
env_harfbuzz.Append(CCFLAGS=["-DGRAPHITE2_STATIC"]) 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"]) 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"]) env_text_server_adv.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources) lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources)

View File

@ -35,18 +35,17 @@ for ext in env["JS_EXTERNS"]:
sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.abspath sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.abspath
build = [] build = []
if env["gdnative_enabled"]: build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm", "#bin/godot${PROGSUFFIX}.worker.js"]
build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] if env["dlink_enabled"]:
if env["threads_enabled"]:
build_targets.append("#bin/godot${PROGSUFFIX}.worker.js")
# Reset libraries. The main runtime will only link emscripten libraries, not godot ones. # Reset libraries. The main runtime will only link emscripten libraries, not godot ones.
sys_env["LIBS"] = [] sys_env["LIBS"] = []
# We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly.
sys_env.Append(LIBS=["idbfs.js"]) sys_env.Append(LIBS=["idbfs.js"])
# Configure it as a main module (dynamic linking support). # 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(CCFLAGS=["-s", "MAIN_MODULE=1"])
sys_env.Append(LINKFLAGS=["-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", "EXPORT_ALL=1"])
sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"]) sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"])
# Force exporting the standard library (printf, malloc, etc.) # Force exporting the standard library (printf, malloc, etc.)
@ -55,16 +54,9 @@ if env["gdnative_enabled"]:
sys = sys_env.Program(build_targets, ["web_runtime.cpp"]) sys = sys_env.Program(build_targets, ["web_runtime.cpp"])
# The side library, containing all Godot code. # The side library, containing all Godot code.
wasm_env = env.Clone() wasm = env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", web_files)
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)
build = sys + [wasm[0]] build = sys + [wasm[0]]
else: 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. # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly.
sys_env.Append(LIBS=["idbfs.js"]) sys_env.Append(LIBS=["idbfs.js"])
build = sys_env.Program(build_targets, web_files + ["web_runtime.cpp"]) build = sys_env.Program(build_targets, web_files + ["web_runtime.cpp"])
@ -88,6 +80,8 @@ wrap_list = [
] ]
js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js") 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 # 0 - unwrapped js file (use wrapped one instead)
extra = build[2:] if len(build) > 2 else None # 1 - wasm file
env.CreateTemplateZip(js_wrapped, build[1], extra) # 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)

View File

@ -38,8 +38,9 @@ def get_opts():
BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False), BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False),
# eval() can be a security concern, so it can be disabled. # eval() can be a security concern, so it can be disabled.
BoolVariable("javascript_eval", "Enable JavaScript eval interface", True), BoolVariable("javascript_eval", "Enable JavaScript eval interface", True),
BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", True), BoolVariable(
BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False), "dlink_enabled", "Enable WebAssembly dynamic linking (GDExtension support). Produces bigger binaries", False
),
BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False), BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
] ]
@ -50,6 +51,13 @@ def get_flags():
("tools", False), ("tools", False),
("builtin_pcre2_with_jit", False), ("builtin_pcre2_with_jit", False),
("vulkan", 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"),
] ]
@ -71,15 +79,12 @@ def configure(env):
## Build type ## Build type
if env["target"].startswith("release"): if env["target"].startswith("release"):
# Use -Os to prioritize optimizing for reduced file size. This is if env["optimize"] == "size":
# 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":
env.Append(CCFLAGS=["-Os"]) env.Append(CCFLAGS=["-Os"])
env.Append(LINKFLAGS=["-Os"]) env.Append(LINKFLAGS=["-Os"])
elif env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
env.Append(LINKFLAGS=["-O3"])
if env["target"] == "release_debug": if env["target"] == "release_debug":
# Retain function names for backtraces at the cost of file size. # Retain function names for backtraces at the cost of file size.
@ -93,21 +98,11 @@ def configure(env):
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
if env["tools"]: 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: if env["initial_memory"] < 64:
print('Note: Forcing "initial_memory=64" as it is required for the web editor.') print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
env["initial_memory"] = 64 env["initial_memory"] = 64
env.Append(CCFLAGS=["-frtti"])
elif env["builtin_icu"]:
env.Append(CCFLAGS=["-fno-exceptions", "-frtti"])
else: else:
# Disable exceptions and rtti on non-tools (template) builds env.Append(CPPFLAGS=["-fno-exceptions"])
# 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(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
@ -171,9 +166,9 @@ def configure(env):
env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix") env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}" env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
# All intermediate files are just LLVM bitcode. # All intermediate files are just object files.
env["OBJPREFIX"] = "" env["OBJPREFIX"] = ""
env["OBJSUFFIX"] = ".bc" env["OBJSUFFIX"] = ".o"
env["PROGPREFIX"] = "" env["PROGPREFIX"] = ""
# Program() output consists of multiple files, so specify suffixes manually at builder. # Program() output consists of multiple files, so specify suffixes manually at builder.
env["PROGSUFFIX"] = "" env["PROGSUFFIX"] = ""
@ -196,31 +191,22 @@ def configure(env):
env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])
# Thread support (via SharedArrayBuffer). # Thread support (via SharedArrayBuffer).
if env["threads_enabled"]:
env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
env.extra_suffix = ".threads" + env.extra_suffix
else:
env.Append(CPPDEFINES=["NO_THREADS"])
if env["gdnative_enabled"]: if env["dlink_enabled"]:
cc_version = get_compiler_version(env) cc_version = get_compiler_version(env)
cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"])) cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"]))
if cc_semver < (2, 0, 10): if cc_semver < (3, 1, 14):
print("GDNative support requires emscripten >= 2.0.10, detected: %s.%s.%s" % cc_semver) print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
sys.exit(255) sys.exit(255)
if env["threads_enabled"] and cc_semver < (3, 1, 14): env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
print("Threads and GDNative requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"])
sys.exit(255) env.extra_suffix = ".dlink" + env.extra_suffix
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
# Reduce code size by generating less support code (e.g. skip NodeJS support). # Reduce code size by generating less support code (e.g. skip NodeJS support).
env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"]) env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"])

View File

@ -37,26 +37,25 @@ def create_engine_file(env, target, source, externs):
return env.Textfile(target, [env.File(s) for s in source]) 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" binary_name = "godot.tools" if env["tools"] else "godot"
zip_dir = env.Dir("#bin/.web_zip") zip_dir = env.Dir("#bin/.web_zip")
in_files = [ in_files = [
js, js,
wasm, wasm,
worker,
"#platform/web/js/libs/audio.worklet.js", "#platform/web/js/libs/audio.worklet.js",
] ]
out_files = [ out_files = [
zip_dir.File(binary_name + ".js"), zip_dir.File(binary_name + ".js"),
zip_dir.File(binary_name + ".wasm"), zip_dir.File(binary_name + ".wasm"),
zip_dir.File(binary_name + ".worker.js"),
zip_dir.File(binary_name + ".audio.worklet.js"), zip_dir.File(binary_name + ".audio.worklet.js"),
] ]
# GDNative/Threads specific # Dynamic linking (extensions) specific.
if env["gdnative_enabled"]: if env["dlink_enabled"]:
in_files.append(extra.pop()) # Runtime in_files.append(side) # Side wasm (contains the actual Godot code).
out_files.append(zip_dir.File(binary_name + ".side.wasm")) 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" service_worker = "#misc/dist/html/service-worker.js"
if env["tools"]: if env["tools"]:

View File

@ -201,7 +201,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
// Service worker // Service worker
const String dir = p_path.get_base_dir(); const String dir = p_path.get_base_dir();
const String name = p_path.get_file().get_basename(); 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; 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_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); replaces["@GODOT_NAME@"] = proj_name.substr(0, 16);
@ -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 + ".icon.png");
cache_files.push_back(name + ".apple-touch-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 + ".worker.js");
cache_files.push_back(name + ".audio.worklet.js"); cache_files.push_back(name + ".audio.worklet.js");
}
replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string(); replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string();
// Heavy files that are cached on demand. // Heavy files that are cached on demand.
Array opt_cache_files; Array opt_cache_files;
opt_cache_files.push_back(name + ".wasm"); opt_cache_files.push_back(name + ".wasm");
opt_cache_files.push_back(name + ".pck"); opt_cache_files.push_back(name + ".pck");
if (mode & EXPORT_MODE_GDNATIVE) { if (extensions) {
opt_cache_files.push_back(name + ".side.wasm"); opt_cache_files.push_back(name + ".side.wasm");
for (int i = 0; i < p_shared_objects.size(); i++) { for (int i = 0; i < p_shared_objects.size(); i++) {
opt_cache_files.push_back(p_shared_objects[i].path.get_file()); opt_cache_files.push_back(p_shared_objects[i].path.get_file());
@ -317,20 +315,14 @@ void EditorExportPlatformWeb::get_preset_features(const Ref<EditorExportPreset>
r_features->push_back("etc2"); 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) { 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/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::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_desktop"), true)); // S3TC
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
@ -374,11 +366,11 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp
String err; String err;
bool valid = false; 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). // Look for export templates (first official, and if defined custom templates).
bool dvalid = exists_export_template(_get_template_name(mode, true), &err); bool dvalid = exists_export_template(_get_template_name(extensions, true), &err);
bool rvalid = exists_export_template(_get_template_name(mode, false), &err); bool rvalid = exists_export_template(_get_template_name(extensions, false), &err);
if (p_preset->get("custom_template/debug") != "") { if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
@ -456,8 +448,8 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
String template_path = p_debug ? custom_debug : custom_release; String template_path = p_debug ? custom_debug : custom_release;
template_path = template_path.strip_edges(); template_path = template_path.strip_edges();
if (template_path.is_empty()) { if (template_path.is_empty()) {
ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); bool extensions = (bool)p_preset->get("variant/extensions_support");
template_path = find_export_template(_get_template_name(mode, p_debug)); template_path = find_export_template(_get_template_name(extensions, p_debug));
} }
if (!DirAccess::exists(base_dir)) { if (!DirAccess::exists(base_dir)) {

View File

@ -57,20 +57,10 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
Mutex server_lock; Mutex server_lock;
Thread server_thread; Thread server_thread;
enum ExportMode { String _get_template_name(bool p_extension, bool p_debug) const {
EXPORT_MODE_NORMAL = 0, String name = "web";
EXPORT_MODE_THREADS = 1, if (p_extension) {
EXPORT_MODE_GDNATIVE = 2, name += "_dlink";
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";
} }
if (p_debug) { if (p_debug) {
name += "_debug.zip"; name += "_debug.zip";

View File

@ -140,26 +140,9 @@ int OS_Web::get_processor_count() const {
} }
bool OS_Web::_check_internal_feature_support(const String &p_feature) { 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") { if (p_feature == "web") {
return true; 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; return false;
} }