Sfoglia il codice sorgente

Add multi-threaded NavMesh baking to NavigationServer

Adds multi-threaded NavMesh baking to NavigationServer.
smix8 2 anni fa
parent
commit
8686e84b44

+ 9 - 0
doc/classes/NavigationServer3D.xml

@@ -216,6 +216,15 @@
 				Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called.
 			</description>
 		</method>
+		<method name="bake_from_source_geometry_data_async">
+			<return type="void" />
+			<param index="0" name="navigation_mesh" type="NavigationMesh" />
+			<param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData3D" />
+			<param index="2" name="callback" type="Callable" default="Callable()" />
+			<description>
+				Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
+			</description>
+		</method>
 		<method name="free_rid">
 			<return type="void" />
 			<param index="0" name="rid" type="RID" />

+ 6 - 0
doc/classes/ProjectSettings.xml

@@ -1991,6 +1991,12 @@
 		<member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
 			If enabled the avoidance calculations use multiple threads.
 		</member>
+		<member name="navigation/baking/thread_model/baking_use_high_priority_threads" type="bool" setter="" getter="" default="true">
+			If enabled and async navmesh baking uses multiple threads the threads run with high priority.
+		</member>
+		<member name="navigation/baking/thread_model/baking_use_multiple_threads" type="bool" setter="" getter="" default="true">
+			If enabled the async navmesh baking uses multiple threads.
+		</member>
 		<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
 			Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
 		</member>

+ 22 - 12
modules/navigation/godot_navigation_server.cpp

@@ -930,7 +930,7 @@ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
 	obstacle->set_avoidance_layers(p_layers);
 }
 
-void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
+void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
 #ifndef _3D_DISABLED
 	ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
 	ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
@@ -942,26 +942,26 @@ void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh>
 #endif // _3D_DISABLED
 }
 
-void GodotNavigationServer::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+void GodotNavigationServer::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
 #ifndef _3D_DISABLED
 	ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
 	ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
 
-	if (!p_source_geometry_data->has_data()) {
-		p_navigation_mesh->clear();
-		if (p_callback.is_valid()) {
-			Callable::CallError ce;
-			Variant result;
-			p_callback.callp(nullptr, 0, result, ce);
-		}
-		return;
-	}
-
 	ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
 	NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
 #endif // _3D_DISABLED
 }
 
+void GodotNavigationServer::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+#ifndef _3D_DISABLED
+	ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
+	ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
+
+	ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
+	NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
+#endif // _3D_DISABLED
+}
+
 COMMAND_1(free, RID, p_object) {
 	if (map_owner.owns(p_object)) {
 		NavMap *map = map_owner.get_or_null(p_object);
@@ -1093,6 +1093,16 @@ void GodotNavigationServer::process(real_t p_delta_time) {
 		return;
 	}
 
+#ifndef _3D_DISABLED
+	// Sync finished navmesh bakes before doing NavMap updates.
+	if (navmesh_generator_3d) {
+		navmesh_generator_3d->sync();
+		// Finished bakes emit callbacks and users might have reacted to those.
+		// Flush queue again so users do not have to wait for the next sync.
+		flush_queries();
+	}
+#endif // _3D_DISABLED
+
 	int _new_pm_region_count = 0;
 	int _new_pm_agent_count = 0;
 	int _new_pm_link_count = 0;

+ 3 - 2
modules/navigation/godot_navigation_server.h

@@ -228,8 +228,9 @@ public:
 	virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override;
 	COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
 
-	virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
-	virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
+	virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
+	virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
+	virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
 
 	COMMAND_1(free, RID, p_object);
 

+ 117 - 8
modules/navigation/nav_mesh_generator_3d.cpp

@@ -32,6 +32,7 @@
 
 #include "nav_mesh_generator_3d.h"
 
+#include "core/config/project_settings.h"
 #include "core/math/convex_hull.h"
 #include "core/os/thread.h"
 #include "scene/3d/mesh_instance_3d.h"
@@ -62,7 +63,12 @@
 
 NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
 Mutex NavMeshGenerator3D::baking_navmesh_mutex;
+Mutex NavMeshGenerator3D::generator_task_mutex;
+bool NavMeshGenerator3D::use_threads = true;
+bool NavMeshGenerator3D::baking_use_multiple_threads = true;
+bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
 HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
+HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
 
 NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
 	return singleton;
@@ -71,15 +77,67 @@ NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
 NavMeshGenerator3D::NavMeshGenerator3D() {
 	ERR_FAIL_COND(singleton != nullptr);
 	singleton = this;
+
+	baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads");
+	baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads");
+
+	// Using threads might cause problems on certain exports or with the Editor on certain devices.
+	// This is the main switch to turn threaded navmesh baking off should the need arise.
+	use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
 }
 
 NavMeshGenerator3D::~NavMeshGenerator3D() {
 	cleanup();
 }
 
+void NavMeshGenerator3D::sync() {
+	if (generator_tasks.size() == 0) {
+		return;
+	}
+
+	baking_navmesh_mutex.lock();
+	generator_task_mutex.lock();
+
+	LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
+
+	for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
+		if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
+			WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
+			finished_task_ids.push_back(E.key);
+
+			NavMeshGeneratorTask3D *generator_task = E.value;
+			DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
+
+			baking_navmeshes.erase(generator_task->navigation_mesh);
+			if (generator_task->callback.is_valid()) {
+				generator_emit_callback(generator_task->callback);
+			}
+			memdelete(generator_task);
+		}
+	}
+
+	for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
+		generator_tasks.erase(finished_task_id);
+	}
+
+	generator_task_mutex.unlock();
+	baking_navmesh_mutex.unlock();
+}
+
 void NavMeshGenerator3D::cleanup() {
 	baking_navmesh_mutex.lock();
+	generator_task_mutex.lock();
+
 	baking_navmeshes.clear();
+
+	for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
+		WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
+		NavMeshGeneratorTask3D *generator_task = E.value;
+		memdelete(generator_task);
+	}
+	generator_tasks.clear();
+
+	generator_task_mutex.unlock();
 	baking_navmesh_mutex.unlock();
 }
 
@@ -87,7 +145,7 @@ void NavMeshGenerator3D::finish() {
 	cleanup();
 }
 
-void NavMeshGenerator3D::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
+void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
 	ERR_FAIL_COND(!Thread::is_main_thread());
 	ERR_FAIL_COND(!p_navigation_mesh.is_valid());
 	ERR_FAIL_COND(p_root_node == nullptr);
@@ -101,19 +159,25 @@ void NavMeshGenerator3D::parse_source_geometry_data(const Ref<NavigationMesh> &p
 	}
 }
 
-void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
 	ERR_FAIL_COND(!p_navigation_mesh.is_valid());
 	ERR_FAIL_COND(!p_source_geometry_data.is_valid());
-	ERR_FAIL_COND(!p_source_geometry_data->has_data());
+
+	if (!p_source_geometry_data->has_data()) {
+		p_navigation_mesh->clear();
+		if (p_callback.is_valid()) {
+			generator_emit_callback(p_callback);
+		}
+		return;
+	}
 
 	baking_navmesh_mutex.lock();
 	if (baking_navmeshes.has(p_navigation_mesh)) {
 		baking_navmesh_mutex.unlock();
 		ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
-	} else {
-		baking_navmeshes.insert(p_navigation_mesh);
-		baking_navmesh_mutex.unlock();
 	}
+	baking_navmeshes.insert(p_navigation_mesh);
+	baking_navmesh_mutex.unlock();
 
 	generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
 
@@ -126,6 +190,51 @@ void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_na
 	}
 }
 
+void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
+	ERR_FAIL_COND(!p_navigation_mesh.is_valid());
+	ERR_FAIL_COND(!p_source_geometry_data.is_valid());
+
+	if (!p_source_geometry_data->has_data()) {
+		p_navigation_mesh->clear();
+		if (p_callback.is_valid()) {
+			generator_emit_callback(p_callback);
+		}
+		return;
+	}
+
+	if (!use_threads) {
+		bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
+		return;
+	}
+
+	baking_navmesh_mutex.lock();
+	if (baking_navmeshes.has(p_navigation_mesh)) {
+		baking_navmesh_mutex.unlock();
+		ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
+		return;
+	}
+	baking_navmeshes.insert(p_navigation_mesh);
+	baking_navmesh_mutex.unlock();
+
+	generator_task_mutex.lock();
+	NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
+	generator_task->navigation_mesh = p_navigation_mesh;
+	generator_task->source_geometry_data = p_source_geometry_data;
+	generator_task->callback = p_callback;
+	generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
+	generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
+	generator_tasks.insert(generator_task->thread_task_id, generator_task);
+	generator_task_mutex.unlock();
+}
+
+void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
+	NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
+
+	generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data);
+
+	generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
+}
+
 void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
 	generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
 	generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
@@ -503,8 +612,8 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
 		return;
 	}
 
-	const Vector<float> vertices = p_source_geometry_data->get_vertices();
-	const Vector<int> indices = p_source_geometry_data->get_indices();
+	const Vector<float> &vertices = p_source_geometry_data->get_vertices();
+	const Vector<int> &indices = p_source_geometry_data->get_indices();
 
 	if (vertices.size() < 3 || indices.size() < 3) {
 		return;

+ 30 - 2
modules/navigation/nav_mesh_generator_3d.h

@@ -34,6 +34,7 @@
 #ifndef _3D_DISABLED
 
 #include "core/object/class_db.h"
+#include "core/object/worker_thread_pool.h"
 #include "modules/modules_enabled.gen.h" // For csg, gridmap.
 
 class Node;
@@ -44,6 +45,31 @@ class NavMeshGenerator3D : public Object {
 	static NavMeshGenerator3D *singleton;
 
 	static Mutex baking_navmesh_mutex;
+	static Mutex generator_task_mutex;
+
+	static bool use_threads;
+	static bool baking_use_multiple_threads;
+	static bool baking_use_high_priority_threads;
+
+	struct NavMeshGeneratorTask3D {
+		enum TaskStatus {
+			BAKING_STARTED,
+			BAKING_FINISHED,
+			BAKING_FAILED,
+			CALLBACK_DISPATCHED,
+			CALLBACK_FAILED,
+		};
+
+		Ref<NavigationMesh> navigation_mesh;
+		Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
+		Callable callback;
+		WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
+		NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
+	};
+
+	static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks;
+
+	static void generator_thread_bake(void *p_arg);
 
 	static HashSet<Ref<NavigationMesh>> baking_navmeshes;
 
@@ -66,11 +92,13 @@ class NavMeshGenerator3D : public Object {
 public:
 	static NavMeshGenerator3D *get_singleton();
 
+	static void sync();
 	static void cleanup();
 	static void finish();
 
-	static void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
-	static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable());
+	static void parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
+	static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
+	static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
 
 	NavMeshGenerator3D();
 	~NavMeshGenerator3D();

+ 8 - 45
scene/3d/navigation_region_3d.cpp

@@ -236,62 +236,29 @@ RID NavigationRegion3D::get_navigation_map() const {
 	return RID();
 }
 
-struct BakeThreadsArgs {
-	NavigationRegion3D *nav_region = nullptr;
-	Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
-};
-
-void _bake_navigation_mesh(void *p_user_data) {
-	BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data);
-
-	if (args->nav_region->get_navigation_mesh().is_valid()) {
-		Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh();
-		Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data;
-
-		NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data);
-		if (!Thread::is_main_thread()) {
-			args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh);
-		} else {
-			args->nav_region->_bake_finished(nav_mesh);
-		}
-		memdelete(args);
-	} else {
-		ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
-		if (!Thread::is_main_thread()) {
-			args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>());
-		} else {
-			args->nav_region->_bake_finished(Ref<NavigationMesh>());
-		}
-		memdelete(args);
-	}
-}
-
 void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
 	ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
 	ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource.");
-	ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh.");
 
 	Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
 	source_geometry_data.instantiate();
 
 	NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this);
 
-	BakeThreadsArgs *args = memnew(BakeThreadsArgs);
-	args->nav_region = this;
-	args->source_geometry_data = source_geometry_data;
-
 	if (p_on_thread) {
-		bake_thread.start(_bake_navigation_mesh, args);
+		NavigationServer3D::get_singleton()->bake_from_source_geometry_data_async(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
 	} else {
-		_bake_navigation_mesh(args);
+		NavigationServer3D::get_singleton()->bake_from_source_geometry_data(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
 	}
 }
 
-void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
-	set_navigation_mesh(p_nav_mesh);
-	if (bake_thread.is_started()) {
-		bake_thread.wait_to_finish();
+void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_navigation_mesh) {
+	if (!Thread::is_main_thread()) {
+		call_deferred(SNAME("_bake_finished"), p_navigation_mesh);
+		return;
 	}
+
+	set_navigation_mesh(p_navigation_mesh);
 	emit_signal(SNAME("bake_finished"));
 }
 
@@ -452,10 +419,6 @@ NavigationRegion3D::NavigationRegion3D() {
 }
 
 NavigationRegion3D::~NavigationRegion3D() {
-	if (bake_thread.is_started()) {
-		bake_thread.wait_to_finish();
-	}
-
 	if (navigation_mesh.is_valid()) {
 		navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
 	}

+ 0 - 2
scene/3d/navigation_region_3d.h

@@ -49,8 +49,6 @@ class NavigationRegion3D : public Node3D {
 
 	Transform3D current_global_transform;
 
-	Thread bake_thread;
-
 	void _navigation_mesh_changed();
 
 #ifdef DEBUG_ENABLED

+ 4 - 0
servers/navigation_server_3d.cpp

@@ -155,6 +155,7 @@ void NavigationServer3D::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
 	ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
+	ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
 
 	ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
 
@@ -204,6 +205,9 @@ NavigationServer3D::NavigationServer3D() {
 	GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
 	GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
 
+	GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true);
+	GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true);
+
 #ifdef DEBUG_ENABLED
 	debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
 	debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));

+ 3 - 2
servers/navigation_server_3d.h

@@ -309,8 +309,9 @@ public:
 
 	virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
 
-	virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
-	virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
+	virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
+	virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
+	virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
 
 	NavigationServer3D();
 	~NavigationServer3D() override;

+ 3 - 2
servers/navigation_server_3d_dummy.h

@@ -145,8 +145,9 @@ public:
 	void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
 	void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
 	void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
-	void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
-	void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
+	void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
+	void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
+	void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
 	void free(RID p_object) override {}
 	void set_active(bool p_active) override {}
 	void process(real_t delta_time) override {}