diff --git a/doc/classes/NavigationServer.xml b/doc/classes/NavigationServer.xml
index eb2f51136..cd80869c7 100644
--- a/doc/classes/NavigationServer.xml
+++ b/doc/classes/NavigationServer.xml
@@ -140,6 +140,13 @@
 				Returns all created navigation map [RID]s on the NavigationServer. This returns both 2D and 3D created navigation maps as there is technically no distinction between them.
 			</description>
 		</method>
+		<method name="get_process_info" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="process_info" type="int" enum="NavigationServer3D.ProcessInfo" />
+			<description>
+				Returns information about the current state of the NavigationServer. See [enum ProcessInfo] for a list of available states.
+			</description>
+		</method>
 		<method name="link_create" qualifiers="const">
 			<return type="RID" />
 			<description>
@@ -577,5 +584,32 @@
 		</signal>
 	</signals>
 	<constants>
+		<constant name="INFO_ACTIVE_MAPS" value="0" enum="ProcessInfo">
+			Constant to get the number of active navigation maps.
+		</constant>
+		<constant name="INFO_REGION_COUNT" value="1" enum="ProcessInfo">
+			Constant to get the number of active navigation regions.
+		</constant>
+		<constant name="INFO_AGENT_COUNT" value="2" enum="ProcessInfo">
+			Constant to get the number of active navigation agents processing avoidance.
+		</constant>
+		<constant name="INFO_LINK_COUNT" value="3" enum="ProcessInfo">
+			Constant to get the number of active navigation links.
+		</constant>
+		<constant name="INFO_POLYGON_COUNT" value="4" enum="ProcessInfo">
+			Constant to get the number of navigation mesh polygons.
+		</constant>
+		<constant name="INFO_EDGE_COUNT" value="5" enum="ProcessInfo">
+			Constant to get the number of navigation mesh polygon edges.
+		</constant>
+		<constant name="INFO_EDGE_MERGE_COUNT" value="6" enum="ProcessInfo">
+			Constant to get the number of navigation mesh polygon edges that were merged due to edge key overlap.
+		</constant>
+		<constant name="INFO_EDGE_CONNECTION_COUNT" value="7" enum="ProcessInfo">
+			Constant to get the number of navigation mesh polygon edges that are considered connected by edge proximity.
+		</constant>
+		<constant name="INFO_EDGE_FREE_COUNT" value="8" enum="ProcessInfo">
+			Constant to get the number of navigation mesh polygon edges that could not be merged but may be still connected by edge proximity or with links.
+		</constant>
 	</constants>
 </class>
diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml
index 1d182ec4e..ac4809384 100644
--- a/doc/classes/Performance.xml
+++ b/doc/classes/Performance.xml
@@ -32,91 +32,121 @@
 		<constant name="TIME_PHYSICS_PROCESS" value="2" enum="Monitor">
 			Time it took to complete one physics frame, in seconds.
 		</constant>
-		<constant name="MEMORY_STATIC" value="3" enum="Monitor">
+		<constant name="TIME_NAVIGATION_PROCESS" value="3" enum="Monitor">
+			Time it took to complete one navigation step, in seconds. This includes navigation map updates as well as agent avoidance calculations. [i]Lower is better.[/i]
+		</constant>
+		<constant name="MEMORY_STATIC" value="4" enum="Monitor">
 			Static memory currently used, in bytes. Not available in release builds.
 		</constant>
-		<constant name="MEMORY_DYNAMIC" value="4" enum="Monitor">
+		<constant name="MEMORY_DYNAMIC" value="5" enum="Monitor">
 			Dynamic memory currently used, in bytes. Not available in release builds.
 		</constant>
-		<constant name="MEMORY_STATIC_MAX" value="5" enum="Monitor">
+		<constant name="MEMORY_STATIC_MAX" value="6" enum="Monitor">
 			Available static memory. Not available in release builds.
 		</constant>
-		<constant name="MEMORY_DYNAMIC_MAX" value="6" enum="Monitor">
+		<constant name="MEMORY_DYNAMIC_MAX" value="7" enum="Monitor">
 			Available dynamic memory. Not available in release builds.
 		</constant>
-		<constant name="MEMORY_MESSAGE_BUFFER_MAX" value="7" enum="Monitor">
+		<constant name="MEMORY_MESSAGE_BUFFER_MAX" value="8" enum="Monitor">
 			Largest amount of memory the message queue buffer has used, in bytes. The message queue is used for deferred functions calls and notifications.
 		</constant>
-		<constant name="OBJECT_COUNT" value="8" enum="Monitor">
+		<constant name="OBJECT_COUNT" value="9" enum="Monitor">
 			Number of objects currently instanced (including nodes).
 		</constant>
-		<constant name="OBJECT_RESOURCE_COUNT" value="9" enum="Monitor">
+		<constant name="OBJECT_RESOURCE_COUNT" value="10" enum="Monitor">
 			Number of resources currently used.
 		</constant>
-		<constant name="OBJECT_NODE_COUNT" value="10" enum="Monitor">
+		<constant name="OBJECT_NODE_COUNT" value="11" enum="Monitor">
 			Number of nodes currently instanced in the scene tree. This also includes the root node.
 		</constant>
-		<constant name="OBJECT_ORPHAN_NODE_COUNT" value="11" enum="Monitor">
+		<constant name="OBJECT_ORPHAN_NODE_COUNT" value="12" enum="Monitor">
 			Number of orphan nodes, i.e. nodes which are not parented to a node of the scene tree.
 		</constant>
-		<constant name="RENDER_OBJECTS_IN_FRAME" value="12" enum="Monitor">
+		<constant name="RENDER_OBJECTS_IN_FRAME" value="13" enum="Monitor">
 			3D objects drawn per frame.
 		</constant>
-		<constant name="RENDER_VERTICES_IN_FRAME" value="13" enum="Monitor">
+		<constant name="RENDER_VERTICES_IN_FRAME" value="14" enum="Monitor">
 			Vertices drawn per frame. 3D only.
 		</constant>
-		<constant name="RENDER_MATERIAL_CHANGES_IN_FRAME" value="14" enum="Monitor">
+		<constant name="RENDER_MATERIAL_CHANGES_IN_FRAME" value="15" enum="Monitor">
 			Material changes per frame. 3D only.
 		</constant>
-		<constant name="RENDER_SHADER_CHANGES_IN_FRAME" value="15" enum="Monitor">
+		<constant name="RENDER_SHADER_CHANGES_IN_FRAME" value="16" enum="Monitor">
 			Shader changes per frame. 3D only.
 		</constant>
-		<constant name="RENDER_SURFACE_CHANGES_IN_FRAME" value="16" enum="Monitor">
+		<constant name="RENDER_SURFACE_CHANGES_IN_FRAME" value="17" enum="Monitor">
 			Render surface changes per frame. 3D only.
 		</constant>
-		<constant name="RENDER_DRAW_CALLS_IN_FRAME" value="17" enum="Monitor">
+		<constant name="RENDER_DRAW_CALLS_IN_FRAME" value="18" enum="Monitor">
 			Draw calls per frame. 3D only.
 		</constant>
-		<constant name="RENDER_2D_ITEMS_IN_FRAME" value="18" enum="Monitor">
+		<constant name="RENDER_2D_ITEMS_IN_FRAME" value="19" enum="Monitor">
 			Items or joined items drawn per frame.
 		</constant>
-		<constant name="RENDER_2D_DRAW_CALLS_IN_FRAME" value="19" enum="Monitor">
+		<constant name="RENDER_2D_DRAW_CALLS_IN_FRAME" value="20" enum="Monitor">
 			Draw calls per frame.
 		</constant>
-		<constant name="RENDER_VIDEO_MEM_USED" value="20" enum="Monitor">
+		<constant name="RENDER_VIDEO_MEM_USED" value="21" enum="Monitor">
 			The amount of video memory used, i.e. texture and vertex memory combined.
 		</constant>
-		<constant name="RENDER_TEXTURE_MEM_USED" value="21" enum="Monitor">
+		<constant name="RENDER_TEXTURE_MEM_USED" value="22" enum="Monitor">
 			The amount of texture memory used.
 		</constant>
-		<constant name="RENDER_VERTEX_MEM_USED" value="22" enum="Monitor">
+		<constant name="RENDER_VERTEX_MEM_USED" value="23" enum="Monitor">
 			The amount of vertex memory used.
 		</constant>
-		<constant name="RENDER_USAGE_VIDEO_MEM_TOTAL" value="23" enum="Monitor">
+		<constant name="RENDER_USAGE_VIDEO_MEM_TOTAL" value="24" enum="Monitor">
 			Unimplemented in the GLES2 and GLES3 rendering backends, always returns 0.
 		</constant>
-		<constant name="PHYSICS_2D_ACTIVE_OBJECTS" value="24" enum="Monitor">
+		<constant name="PHYSICS_2D_ACTIVE_OBJECTS" value="25" enum="Monitor">
 			Number of active [RigidBody2D] nodes in the game.
 		</constant>
-		<constant name="PHYSICS_2D_COLLISION_PAIRS" value="25" enum="Monitor">
+		<constant name="PHYSICS_2D_COLLISION_PAIRS" value="26" enum="Monitor">
 			Number of collision pairs in the 2D physics engine.
 		</constant>
-		<constant name="PHYSICS_2D_ISLAND_COUNT" value="26" enum="Monitor">
+		<constant name="PHYSICS_2D_ISLAND_COUNT" value="27" enum="Monitor">
 			Number of islands in the 2D physics engine.
 		</constant>
-		<constant name="PHYSICS_3D_ACTIVE_OBJECTS" value="27" enum="Monitor">
+		<constant name="PHYSICS_3D_ACTIVE_OBJECTS" value="28" enum="Monitor">
 			Number of active [RigidBody] and [VehicleBody] nodes in the game.
 		</constant>
-		<constant name="PHYSICS_3D_COLLISION_PAIRS" value="28" enum="Monitor">
+		<constant name="PHYSICS_3D_COLLISION_PAIRS" value="29" enum="Monitor">
 			Number of collision pairs in the 3D physics engine.
 		</constant>
-		<constant name="PHYSICS_3D_ISLAND_COUNT" value="29" enum="Monitor">
+		<constant name="PHYSICS_3D_ISLAND_COUNT" value="30" enum="Monitor">
 			Number of islands in the 3D physics engine.
 		</constant>
-		<constant name="AUDIO_OUTPUT_LATENCY" value="30" enum="Monitor">
+		<constant name="AUDIO_OUTPUT_LATENCY" value="31" enum="Monitor">
 			Output latency of the [AudioServer].
 		</constant>
-		<constant name="MONITOR_MAX" value="31" enum="Monitor">
+		<constant name="NAVIGATION_ACTIVE_MAPS" value="32" enum="Monitor">
+			Number of active navigation maps in the [NavigationServer3D]. This also includes the two empty default navigation maps created by World2D and World3D.
+		</constant>
+		<constant name="NAVIGATION_REGION_COUNT" value="33" enum="Monitor">
+			Number of active navigation regions in the [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_AGENT_COUNT" value="34" enum="Monitor">
+			Number of active navigation agents processing avoidance in the [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_LINK_COUNT" value="35" enum="Monitor">
+			Number of active navigation links in the [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_POLYGON_COUNT" value="36" enum="Monitor">
+			Number of navigation mesh polygons in the [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_EDGE_COUNT" value="37" enum="Monitor">
+			Number of navigation mesh polygon edges in the [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_EDGE_MERGE_COUNT" value="38" enum="Monitor">
+			Number of navigation mesh polygon edges that were merged due to edge key overlap in the [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_EDGE_CONNECTION_COUNT" value="39" enum="Monitor">
+			Number of polygon edges that are considered connected by edge proximity [NavigationServer3D].
+		</constant>
+		<constant name="NAVIGATION_EDGE_FREE_COUNT" value="40" enum="Monitor">
+			Number of navigation mesh polygon edges that could not be merged in the [NavigationServer3D]. The edges still may be connected by edge proximity or with links.
+		</constant>
+		<constant name="MONITOR_MAX" value="41" enum="Monitor">
 			Represents the size of the [enum Monitor] enum.
 		</constant>
 	</constants>
diff --git a/main/performance.cpp b/main/performance.cpp
index f4e521380..52b826ed1 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -35,6 +35,7 @@
 #include "scene/main/node.h"
 #include "scene/main/scene_tree.h"
 #include "servers/audio_server.h"
+#include "servers/navigation_server.h"
 #include "servers/physics_2d_server.h"
 #include "servers/physics_server.h"
 #include "servers/rendering_server.h"
@@ -47,6 +48,7 @@ void Performance::_bind_methods() {
 	BIND_ENUM_CONSTANT(TIME_FPS);
 	BIND_ENUM_CONSTANT(TIME_PROCESS);
 	BIND_ENUM_CONSTANT(TIME_PHYSICS_PROCESS);
+	BIND_ENUM_CONSTANT(TIME_NAVIGATION_PROCESS);
 	BIND_ENUM_CONSTANT(MEMORY_STATIC);
 	BIND_ENUM_CONSTANT(MEMORY_DYNAMIC);
 	BIND_ENUM_CONSTANT(MEMORY_STATIC_MAX);
@@ -75,6 +77,15 @@ void Performance::_bind_methods() {
 	BIND_ENUM_CONSTANT(PHYSICS_3D_COLLISION_PAIRS);
 	BIND_ENUM_CONSTANT(PHYSICS_3D_ISLAND_COUNT);
 	BIND_ENUM_CONSTANT(AUDIO_OUTPUT_LATENCY);
+	BIND_ENUM_CONSTANT(NAVIGATION_ACTIVE_MAPS);
+	BIND_ENUM_CONSTANT(NAVIGATION_REGION_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_AGENT_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_LINK_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_POLYGON_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_EDGE_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_EDGE_MERGE_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_EDGE_CONNECTION_COUNT);
+	BIND_ENUM_CONSTANT(NAVIGATION_EDGE_FREE_COUNT);
 
 	BIND_ENUM_CONSTANT(MONITOR_MAX);
 }
@@ -95,6 +106,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
 		"time/fps",
 		"time/process",
 		"time/physics_process",
+		"time/navigation_process",
 		"memory/static",
 		"memory/dynamic",
 		"memory/static_max",
@@ -123,6 +135,15 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
 		"physics_3d/collision_pairs",
 		"physics_3d/islands",
 		"audio/output_latency",
+		"navigation/active_maps",
+		"navigation/regions",
+		"navigation/agents",
+		"navigation/links",
+		"navigation/polygons",
+		"navigation/edges",
+		"navigation/edges_merged",
+		"navigation/edges_connected",
+		"navigation/edges_free",
 
 	};
 
@@ -137,6 +158,8 @@ float Performance::get_monitor(Monitor p_monitor) const {
 			return _process_time;
 		case TIME_PHYSICS_PROCESS:
 			return _physics_process_time;
+		case TIME_NAVIGATION_PROCESS:
+			return _navigation_process_time;
 		case MEMORY_STATIC:
 			return Memory::get_mem_usage();
 		case MEMORY_DYNAMIC:
@@ -193,6 +216,24 @@ float Performance::get_monitor(Monitor p_monitor) const {
 			return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_ISLAND_COUNT);
 		case AUDIO_OUTPUT_LATENCY:
 			return AudioServer::get_singleton()->get_output_latency();
+		case NAVIGATION_ACTIVE_MAPS:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_ACTIVE_MAPS);
+		case NAVIGATION_REGION_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_REGION_COUNT);
+		case NAVIGATION_AGENT_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_AGENT_COUNT);
+		case NAVIGATION_LINK_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_LINK_COUNT);
+		case NAVIGATION_POLYGON_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_POLYGON_COUNT);
+		case NAVIGATION_EDGE_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_EDGE_COUNT);
+		case NAVIGATION_EDGE_MERGE_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_EDGE_MERGE_COUNT);
+		case NAVIGATION_EDGE_CONNECTION_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_EDGE_CONNECTION_COUNT);
+		case NAVIGATION_EDGE_FREE_COUNT:
+			return NavigationServer::get_singleton()->get_process_info(NavigationServer::INFO_EDGE_FREE_COUNT);
 
 		default: {
 		}
@@ -209,6 +250,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const
 		MONITOR_TYPE_QUANTITY,
 		MONITOR_TYPE_TIME,
 		MONITOR_TYPE_TIME,
+		MONITOR_TYPE_TIME,
 		MONITOR_TYPE_MEMORY,
 		MONITOR_TYPE_MEMORY,
 		MONITOR_TYPE_MEMORY,
@@ -237,6 +279,12 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const
 		MONITOR_TYPE_QUANTITY,
 		MONITOR_TYPE_QUANTITY,
 		MONITOR_TYPE_TIME,
+		MONITOR_TYPE_QUANTITY,
+		MONITOR_TYPE_QUANTITY,
+		MONITOR_TYPE_QUANTITY,
+		MONITOR_TYPE_QUANTITY,
+		MONITOR_TYPE_QUANTITY,
+		MONITOR_TYPE_QUANTITY,
 
 	};
 
@@ -251,8 +299,13 @@ void Performance::set_physics_process_time(float p_pt) {
 	_physics_process_time = p_pt;
 }
 
+void Performance::set_navigation_process_time(float p_pt) {
+	_navigation_process_time = p_pt;
+}
+
 Performance::Performance() {
 	_process_time = 0;
 	_physics_process_time = 0;
+	_navigation_process_time = 0;
 	singleton = this;
 }
diff --git a/main/performance.h b/main/performance.h
index 0717ad742..2e1603d91 100644
--- a/main/performance.h
+++ b/main/performance.h
@@ -45,6 +45,7 @@ class Performance : public Object {
 
 	float _process_time;
 	float _physics_process_time;
+	float _navigation_process_time;
 
 public:
 	enum Monitor {
@@ -52,6 +53,7 @@ public:
 		TIME_FPS,
 		TIME_PROCESS,
 		TIME_PHYSICS_PROCESS,
+		TIME_NAVIGATION_PROCESS,
 		MEMORY_STATIC,
 		MEMORY_DYNAMIC,
 		MEMORY_STATIC_MAX,
@@ -79,8 +81,16 @@ public:
 		PHYSICS_3D_ACTIVE_OBJECTS,
 		PHYSICS_3D_COLLISION_PAIRS,
 		PHYSICS_3D_ISLAND_COUNT,
-		//physics
 		AUDIO_OUTPUT_LATENCY,
+		NAVIGATION_ACTIVE_MAPS,
+		NAVIGATION_REGION_COUNT,
+		NAVIGATION_AGENT_COUNT,
+		NAVIGATION_LINK_COUNT,
+		NAVIGATION_POLYGON_COUNT,
+		NAVIGATION_EDGE_COUNT,
+		NAVIGATION_EDGE_MERGE_COUNT,
+		NAVIGATION_EDGE_CONNECTION_COUNT,
+		NAVIGATION_EDGE_FREE_COUNT,
 		MONITOR_MAX
 	};
 
@@ -97,6 +107,7 @@ public:
 
 	void set_process_time(float p_pt);
 	void set_physics_process_time(float p_pt);
+	void set_navigation_process_time(float p_pt);
 
 	static Performance *get_singleton() { return singleton; }
 
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 94c38fa10..c5aae67ef 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -547,6 +547,16 @@ void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
 }
 
 void NavMap::sync() {
+	// Performance Monitor
+	int _new_pm_region_count = regions.size();
+	int _new_pm_agent_count = agents.size();
+	int _new_pm_link_count = links.size();
+	int _new_pm_polygon_count = pm_polygon_count;
+	int _new_pm_edge_count = pm_edge_count;
+	int _new_pm_edge_merge_count = pm_edge_merge_count;
+	int _new_pm_edge_connection_count = pm_edge_connection_count;
+	int _new_pm_edge_free_count = pm_edge_free_count;
+
 	// Check if we need to update the links.
 	if (regenerate_polygons) {
 		for (uint32_t r = 0; r < regions.size(); r++) {
@@ -568,6 +578,12 @@ void NavMap::sync() {
 	}
 
 	if (regenerate_links) {
+		_new_pm_polygon_count = 0;
+		_new_pm_edge_count = 0;
+		_new_pm_edge_merge_count = 0;
+		_new_pm_edge_connection_count = 0;
+		_new_pm_edge_free_count = 0;
+
 		// Remove regions connections.
 		for (uint32_t r = 0; r < regions.size(); r++) {
 			regions[r]->get_connections().clear();
@@ -592,6 +608,8 @@ void NavMap::sync() {
 			count += regions[r]->get_polygons().size();
 		}
 
+		_new_pm_polygon_count = polygons.size();
+
 		// Group all edges per key.
 		RBMap<gd::EdgeKey, Vector<gd::Edge::Connection>> connections;
 
@@ -606,6 +624,7 @@ void NavMap::sync() {
 
 				if (!connection) {
 					connections[ek] = Vector<gd::Edge::Connection>();
+					_new_pm_edge_count += 1;
 				}
 
 				if (connections[ek].size() <= 1) {
@@ -632,6 +651,7 @@ void NavMap::sync() {
 				c1.polygon->edges[c1.edge].connections.push_back(c2);
 				c2.polygon->edges[c2.edge].connections.push_back(c1);
 				// Note: The pathway_start/end are full for those connection and do not need to be modified.
+				_new_pm_edge_merge_count += 1;
 			} else {
 				CRASH_COND_MSG(E->get().size() != 1, vformat("Number of connection != 1. Found: %d", E->get().size()));
 				free_edges.push_back(E->get()[0]);
@@ -645,6 +665,8 @@ void NavMap::sync() {
 		// to be connected, create new polygons to remove that small gap is
 		// not really useful and would result in wasteful computation during
 		// connection, integration and path finding.
+		_new_pm_edge_free_count = free_edges.size();
+
 		for (int i = 0; i < free_edges.size(); i++) {
 			const gd::Edge::Connection &free_edge = free_edges[i];
 			Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
@@ -699,6 +721,7 @@ void NavMap::sync() {
 
 				// Add the connection to the region_connection map.
 				((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection);
+				_new_pm_edge_connection_count += 1;
 			}
 		}
 
@@ -835,6 +858,16 @@ void NavMap::sync() {
 	regenerate_polygons = false;
 	regenerate_links = false;
 	agents_dirty = false;
+
+	// Performance Monitor
+	pm_region_count = _new_pm_region_count;
+	pm_agent_count = _new_pm_agent_count;
+	pm_link_count = _new_pm_link_count;
+	pm_polygon_count = _new_pm_polygon_count;
+	pm_edge_count = _new_pm_edge_count;
+	pm_edge_merge_count = _new_pm_edge_merge_count;
+	pm_edge_connection_count = _new_pm_edge_connection_count;
+	pm_edge_free_count = _new_pm_edge_free_count;
 }
 
 void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
@@ -915,6 +948,16 @@ NavMap::NavMap() {
 	deltatime = 0.0;
 	map_update_id = 0;
 	link_connection_radius = 1.0;
+
+	// Performance Monitor
+	pm_region_count = 0;
+	pm_agent_count = 0;
+	pm_link_count = 0;
+	pm_polygon_count = 0;
+	pm_edge_count = 0;
+	pm_edge_merge_count = 0;
+	pm_edge_connection_count = 0;
+	pm_edge_free_count = 0;
 }
 
 NavMap::~NavMap() {
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index fdcd8a6de..a05d93751 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -91,6 +91,16 @@ class NavMap : public NavRid {
 	/// Change the id each time the map is updated.
 	uint32_t map_update_id;
 
+	// Performance Monitor
+	int pm_region_count;
+	int pm_agent_count;
+	int pm_link_count;
+	int pm_polygon_count;
+	int pm_edge_count;
+	int pm_edge_merge_count;
+	int pm_edge_connection_count;
+	int pm_edge_free_count;
+
 #ifndef NO_THREADS
 	/// Pooled threads for computing steps
 	ThreadWorkPool step_work_pool;
@@ -164,6 +174,16 @@ public:
 	void step(real_t p_deltatime);
 	void dispatch_callbacks();
 
+	// Performance Monitor
+	int get_pm_region_count() const { return pm_region_count; }
+	int get_pm_agent_count() const { return pm_agent_count; }
+	int get_pm_link_count() const { return pm_link_count; }
+	int get_pm_polygon_count() const { return pm_polygon_count; }
+	int get_pm_edge_count() const { return pm_edge_count; }
+	int get_pm_edge_merge_count() const { return pm_edge_merge_count; }
+	int get_pm_edge_connection_count() const { return pm_edge_connection_count; }
+	int get_pm_edge_free_count() const { return pm_edge_free_count; }
+
 private:
 	void compute_single_step(uint32_t index, RvoAgent **agent);
 	void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const;
diff --git a/modules/navigation/pandemonium_navigation_server.cpp b/modules/navigation/pandemonium_navigation_server.cpp
index 398559c2f..5dc83eec3 100644
--- a/modules/navigation/pandemonium_navigation_server.cpp
+++ b/modules/navigation/pandemonium_navigation_server.cpp
@@ -818,6 +818,15 @@ void PandemoniumNavigationServer::process(real_t p_delta_time) {
 		return;
 	}
 
+	int _new_pm_region_count = 0;
+	int _new_pm_agent_count = 0;
+	int _new_pm_link_count = 0;
+	int _new_pm_polygon_count = 0;
+	int _new_pm_edge_count = 0;
+	int _new_pm_edge_merge_count = 0;
+	int _new_pm_edge_connection_count = 0;
+	int _new_pm_edge_free_count = 0;
+
 	// In c++ we can't be sure that this is performed in the main thread
 	// even with mutable functions.
 	MutexLock lock(operations_mutex);
@@ -826,6 +835,15 @@ void PandemoniumNavigationServer::process(real_t p_delta_time) {
 		active_maps[i]->step(p_delta_time);
 		active_maps[i]->dispatch_callbacks();
 
+		_new_pm_region_count += active_maps[i]->get_pm_region_count();
+		_new_pm_agent_count += active_maps[i]->get_pm_agent_count();
+		_new_pm_link_count += active_maps[i]->get_pm_link_count();
+		_new_pm_polygon_count += active_maps[i]->get_pm_polygon_count();
+		_new_pm_edge_count += active_maps[i]->get_pm_edge_count();
+		_new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
+		_new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
+		_new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
+
 		// Emit a signal if a map changed.
 		const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
 		if (new_map_update_id != active_maps_update_id[i]) {
@@ -833,6 +851,15 @@ void PandemoniumNavigationServer::process(real_t p_delta_time) {
 			active_maps_update_id[i] = new_map_update_id;
 		}
 	}
+
+	pm_region_count = _new_pm_region_count;
+	pm_agent_count = _new_pm_agent_count;
+	pm_link_count = _new_pm_link_count;
+	pm_polygon_count = _new_pm_polygon_count;
+	pm_edge_count = _new_pm_edge_count;
+	pm_edge_merge_count = _new_pm_edge_merge_count;
+	pm_edge_connection_count = _new_pm_edge_connection_count;
+	pm_edge_free_count = _new_pm_edge_free_count;
 }
 
 NavigationUtilities::PathQueryResult PandemoniumNavigationServer::_query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const {
@@ -872,6 +899,40 @@ NavigationUtilities::PathQueryResult PandemoniumNavigationServer::_query_path(co
 	return r_query_result;
 }
 
+int PandemoniumNavigationServer::get_process_info(ProcessInfo p_info) const {
+	switch (p_info) {
+		case INFO_ACTIVE_MAPS: {
+			return active_maps.size();
+		} break;
+		case INFO_REGION_COUNT: {
+			return pm_region_count;
+		} break;
+		case INFO_AGENT_COUNT: {
+			return pm_agent_count;
+		} break;
+		case INFO_LINK_COUNT: {
+			return pm_link_count;
+		} break;
+		case INFO_POLYGON_COUNT: {
+			return pm_polygon_count;
+		} break;
+		case INFO_EDGE_COUNT: {
+			return pm_edge_count;
+		} break;
+		case INFO_EDGE_MERGE_COUNT: {
+			return pm_edge_merge_count;
+		} break;
+		case INFO_EDGE_CONNECTION_COUNT: {
+			return pm_edge_connection_count;
+		} break;
+		case INFO_EDGE_FREE_COUNT: {
+			return pm_edge_free_count;
+		} break;
+	}
+
+	return 0;
+}
+
 #undef COMMAND_1
 #undef COMMAND_2
 #undef COMMAND_4
diff --git a/modules/navigation/pandemonium_navigation_server.h b/modules/navigation/pandemonium_navigation_server.h
index baa8fdc44..20d3e865a 100644
--- a/modules/navigation/pandemonium_navigation_server.h
+++ b/modules/navigation/pandemonium_navigation_server.h
@@ -79,6 +79,16 @@ class PandemoniumNavigationServer : public NavigationServer {
 	LocalVector<NavMap *> active_maps;
 	LocalVector<uint32_t> active_maps_update_id;
 
+	// Performance Monitor
+	int pm_region_count = 0;
+	int pm_agent_count = 0;
+	int pm_link_count = 0;
+	int pm_polygon_count = 0;
+	int pm_edge_count = 0;
+	int pm_edge_merge_count = 0;
+	int pm_edge_connection_count = 0;
+	int pm_edge_free_count = 0;
+
 public:
 	PandemoniumNavigationServer();
 	virtual ~PandemoniumNavigationServer();
@@ -177,6 +187,8 @@ public:
 	void flush_queries();
 	virtual void process(real_t p_delta_time);
 
+	virtual int get_process_info(ProcessInfo p_info) const;
+
 	virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const;
 };
 
diff --git a/modules/navigation_dummy/dummy_navigation_server.h b/modules/navigation_dummy/dummy_navigation_server.h
index 848728719..9032bd152 100644
--- a/modules/navigation_dummy/dummy_navigation_server.h
+++ b/modules/navigation_dummy/dummy_navigation_server.h
@@ -88,6 +88,8 @@ public:
 
 	virtual void process(real_t delta_time){};
 
+	virtual int get_process_info(ProcessInfo p_info) const { return 0; };
+
 	virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const;
 
 	DummyNavigationServer();
diff --git a/servers/navigation_server.h b/servers/navigation_server.h
index 8397e2e64..7006e6b10 100644
--- a/servers/navigation_server.h
+++ b/servers/navigation_server.h
@@ -260,6 +260,20 @@ public:
 
 	virtual void init();
 
+	enum ProcessInfo {
+		INFO_ACTIVE_MAPS,
+		INFO_REGION_COUNT,
+		INFO_AGENT_COUNT,
+		INFO_LINK_COUNT,
+		INFO_POLYGON_COUNT,
+		INFO_EDGE_COUNT,
+		INFO_EDGE_MERGE_COUNT,
+		INFO_EDGE_CONNECTION_COUNT,
+		INFO_EDGE_FREE_COUNT,
+	};
+
+	virtual int get_process_info(ProcessInfo p_info) const = 0;
+
 	NavigationServer();
 	virtual ~NavigationServer();
 
@@ -351,6 +365,8 @@ protected:
 #endif // DEBUG_ENABLED
 };
 
+VARIANT_ENUM_CAST(NavigationServer::ProcessInfo);
+
 typedef NavigationServer *(*CreateNavigationServerCallback)();
 
 /// Manager used for the server singleton registration