فهرست منبع

Web: Add godot_pool_size/emscripten_pool_size config

Marcos Casagrande 4 ماه پیش
والد
کامیت
a7d18f51a2

+ 1 - 1
platform/web/detect.py

@@ -240,7 +240,7 @@ def configure(env: "SConsEnvironment"):
         env.Append(CCFLAGS=["-sUSE_PTHREADS=1"])
         env.Append(LINKFLAGS=["-sUSE_PTHREADS=1"])
         env.Append(LINKFLAGS=["-sDEFAULT_PTHREAD_STACK_SIZE=%sKB" % env["default_pthread_stack_size"]])
-        env.Append(LINKFLAGS=["-sPTHREAD_POOL_SIZE=8"])
+        env.Append(LINKFLAGS=["-sPTHREAD_POOL_SIZE='Module[\"emscriptenPoolSize\"]||8'"])
         env.Append(LINKFLAGS=["-sWASM_MEM_MAX=2048MB"])
         if not env["dlink_enabled"]:
             # Workaround https://github.com/emscripten-core/emscripten/issues/21844#issuecomment-2116936414.

+ 8 - 0
platform/web/doc_classes/EditorExportPlatformWeb.xml

@@ -79,9 +79,17 @@
 			- [b]Landscape:[/b] Forces a horizontal layout (wider than it is taller).
 			- [b]Portrait:[/b] Forces a vertical layout (taller than it is wider).
 		</member>
+		<member name="variant/emscripten_pool_size" type="int" setter="" getter="">
+			The number of threads that emscripten will allocate at startup. A smaller value will allocate fewer threads and consume fewer system resources, but you may run the risk of running out of threads in the pool and needing to allocate more threads at run time which may cause a deadlock.
+			[b]Note:[/b] Some browsers have a hard cap on the number of threads that can be allocated, so it is best to be cautious and keep this number low.
+		</member>
 		<member name="variant/extensions_support" type="bool" setter="" getter="">
 			If [code]true[/code] enables [GDExtension] support for this web build.
 		</member>
+		<member name="variant/godot_pool_size" type="int" setter="" getter="">
+			Override for the default size of the [WorkerThreadPool]. This setting is used when [member ProjectSettings.threading/worker_pool/max_threads] size is set to -1 (which it is by default). This size must be smaller than [member variant/emscripten_pool_size] otherwise deadlocks may occur.
+			When using threads this size needs to be large enough to accommodate features that rely on having a dedicated thread like [member ProjectSettings.physics/2d/run_on_separate_thread] or [member ProjectSettings.rendering/driver/threads/thread_model]. In general, it is best to ensure that this is at least 4 and is at least 2 or 3 less than [member variant/emscripten_pool_size].
+		</member>
 		<member name="variant/thread_support" type="bool" setter="" getter="">
 			If [code]true[/code], the exported game will support threads. It requires [url=https://web.dev/articles/coop-coep]a "cross-origin isolated" website[/url], which may be difficult to set up and is limited for security reasons (such as not being able to communicate with third-party websites).
 			If [code]false[/code], the exported game will not support threads. As a result, it is more prone to performance and audio issues, but will only require to be run on an HTTPS website.

+ 4 - 0
platform/web/export/export_plugin.cpp

@@ -143,11 +143,13 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito
 	config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy");
 	config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard");
 	config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start");
+	config["godotPoolSize"] = p_preset->get("variant/godot_pool_size");
 	config["gdextensionLibs"] = libs;
 	config["executable"] = p_name;
 	config["args"] = args;
 	config["fileSizes"] = p_file_sizes;
 	config["ensureCrossOriginIsolationHeaders"] = (bool)p_preset->get("progressive_web_app/ensure_cross_origin_isolation_headers");
+	config["emscriptenPoolSize"] = p_preset->get("variant/emscripten_pool_size");
 
 	String head_include;
 	if (p_preset->get("html/export_icon")) {
@@ -360,6 +362,8 @@ void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options)
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // GDExtension support.
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/thread_support"), false)); // Thread support (i.e. run with or without COEP/COOP headers).
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/emscripten_pool_size"), 8));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/godot_pool_size"), 4));
 	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
 

+ 1 - 0
platform/web/godot_js.h

@@ -50,6 +50,7 @@ extern void godot_js_os_fs_sync(void (*p_callback)());
 extern int godot_js_os_execute(const char *p_json);
 extern void godot_js_os_shell_open(const char *p_uri);
 extern int godot_js_os_hw_concurrency_get();
+extern int godot_js_os_thread_pool_size_get();
 extern int godot_js_os_has_feature(const char *p_ftr);
 extern int godot_js_pwa_cb(void (*p_callback)());
 extern int godot_js_pwa_update();

+ 14 - 0
platform/web/js/engine/config.js

@@ -133,6 +133,16 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 		 * @type {Array.<string>}
 		 */
 		fileSizes: [],
+		/**
+		 * @ignore
+		 * @type {number}
+		 */
+		emscriptenPoolSize: 8,
+		/**
+		 * @ignore
+		 * @type {number}
+		 */
+		godotPoolSize: 4,
 		/**
 		 * A callback function for handling Godot's ``OS.execute`` calls.
 		 *
@@ -259,6 +269,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 		this.serviceWorker = parse('serviceWorker', this.serviceWorker);
 		this.gdextensionLibs = parse('gdextensionLibs', this.gdextensionLibs);
 		this.fileSizes = parse('fileSizes', this.fileSizes);
+		this.emscriptenPoolSize = parse('emscriptenPoolSize', this.emscriptenPoolSize);
+		this.godotPoolSize = parse('godotPoolSize', this.godotPoolSize);
 		this.args = parse('args', this.args);
 		this.onExecute = parse('onExecute', this.onExecute);
 		this.onExit = parse('onExit', this.onExit);
@@ -278,6 +290,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 			'thisProgram': this.executable,
 			'noExitRuntime': false,
 			'dynamicLibraries': [`${loadPath}.side.wasm`].concat(this.gdextensionLibs),
+			'emscriptenPoolSize': this.emscriptenPoolSize,
 			'instantiateWasm': function (imports, onSuccess) {
 				function done(result) {
 					onSuccess(result['instance'], result['module']);
@@ -350,6 +363,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 			'locale': locale,
 			'persistentDrops': this.persistentDrops,
 			'virtualKeyboard': this.experimentalVK,
+			'godotPoolSize': this.godotPoolSize,
 			'focusCanvas': this.focusCanvas,
 			'onExecute': this.onExecute,
 			'onExit': function (p_code) {

+ 13 - 0
platform/web/js/libs/library_godot_os.js

@@ -61,6 +61,7 @@ const GodotConfig = {
 		canvas_resize_policy: 2, // Adaptive
 		virtual_keyboard: false,
 		persistent_drops: false,
+		godot_pool_size: 4,
 		on_execute: null,
 		on_exit: null,
 
@@ -70,6 +71,7 @@ const GodotConfig = {
 			GodotConfig.locale = p_opts['locale'] || GodotConfig.locale;
 			GodotConfig.virtual_keyboard = p_opts['virtualKeyboard'];
 			GodotConfig.persistent_drops = !!p_opts['persistentDrops'];
+			GodotConfig.godot_pool_size = p_opts['godotPoolSize'];
 			GodotConfig.on_execute = p_opts['onExecute'];
 			GodotConfig.on_exit = p_opts['onExit'];
 			if (p_opts['focusCanvas']) {
@@ -346,6 +348,17 @@ const GodotOS = {
 		return concurrency < 2 ? concurrency : 2;
 	},
 
+	godot_js_os_thread_pool_size_get__proxy: 'sync',
+	godot_js_os_thread_pool_size_get__sig: 'i',
+	godot_js_os_thread_pool_size_get: function () {
+		if (typeof PThread === 'undefined') {
+			// Threads aren't supported, so default to `1`.
+			return 1;
+		}
+
+		return GodotConfig.godot_pool_size;
+	},
+
 	godot_js_os_download_buffer__proxy: 'sync',
 	godot_js_os_download_buffer__sig: 'viiii',
 	godot_js_os_download_buffer: function (p_ptr, p_size, p_name, p_mime) {

+ 8 - 0
platform/web/os_web.cpp

@@ -148,6 +148,14 @@ String OS_Web::get_unique_id() const {
 	ERR_FAIL_V_MSG("", "OS::get_unique_id() is not available on the Web platform.");
 }
 
+int OS_Web::get_default_thread_pool_size() const {
+#ifdef THREADS_ENABLED
+	return godot_js_os_thread_pool_size_get();
+#else // No threads.
+	return 1;
+#endif
+}
+
 bool OS_Web::_check_internal_feature_support(const String &p_feature) {
 	if (p_feature == "web") {
 		return true;

+ 1 - 1
platform/web/os_web.h

@@ -91,7 +91,7 @@ public:
 	int get_process_exit_code(const ProcessID &p_pid) const override;
 	int get_processor_count() const override;
 	String get_unique_id() const override;
-	int get_default_thread_pool_size() const override { return 1; }
+	int get_default_thread_pool_size() const override;
 
 	String get_executable_path() const override;
 	Error shell_open(const String &p_uri) override;