Browse Source

[Debugger] Move most profilers to ServersDebugger.

Also splits bandwidth/rpc profiler (RPCProfiler is now in
SceneDebugger).
Fabio Alessandrelli 3 years ago
parent
commit
87f4bbd668

+ 0 - 183
core/debugger/debugger_marshalls.cpp

@@ -35,159 +35,6 @@
 #define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
 #define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
 
-Array DebuggerMarshalls::ResourceUsage::serialize() {
-	infos.sort();
-
-	Array arr;
-	arr.push_back(infos.size() * 4);
-	for (const ResourceInfo &E : infos) {
-		arr.push_back(E.path);
-		arr.push_back(E.format);
-		arr.push_back(E.type);
-		arr.push_back(E.vram);
-	}
-	return arr;
-}
-
-bool DebuggerMarshalls::ResourceUsage::deserialize(const Array &p_arr) {
-	CHECK_SIZE(p_arr, 1, "ResourceUsage");
-	uint32_t size = p_arr[0];
-	CHECK_SIZE(p_arr, size, "ResourceUsage");
-	int idx = 1;
-	for (uint32_t i = 0; i < size / 4; i++) {
-		ResourceInfo info;
-		info.path = p_arr[idx];
-		info.format = p_arr[idx + 1];
-		info.type = p_arr[idx + 2];
-		info.vram = p_arr[idx + 3];
-		infos.push_back(info);
-	}
-	CHECK_END(p_arr, idx, "ResourceUsage");
-	return true;
-}
-
-Array DebuggerMarshalls::ScriptFunctionSignature::serialize() {
-	Array arr;
-	arr.push_back(name);
-	arr.push_back(id);
-	return arr;
-}
-
-bool DebuggerMarshalls::ScriptFunctionSignature::deserialize(const Array &p_arr) {
-	CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");
-	name = p_arr[0];
-	id = p_arr[1];
-	CHECK_END(p_arr, 2, "ScriptFunctionSignature");
-	return true;
-}
-
-Array DebuggerMarshalls::NetworkProfilerFrame::serialize() {
-	Array arr;
-	arr.push_back(infos.size() * 6);
-	for (int i = 0; i < infos.size(); ++i) {
-		arr.push_back(uint64_t(infos[i].node));
-		arr.push_back(infos[i].node_path);
-		arr.push_back(infos[i].incoming_rpc);
-		arr.push_back(infos[i].incoming_rset);
-		arr.push_back(infos[i].outgoing_rpc);
-		arr.push_back(infos[i].outgoing_rset);
-	}
-	return arr;
-}
-
-bool DebuggerMarshalls::NetworkProfilerFrame::deserialize(const Array &p_arr) {
-	CHECK_SIZE(p_arr, 1, "NetworkProfilerFrame");
-	uint32_t size = p_arr[0];
-	CHECK_SIZE(p_arr, size, "NetworkProfilerFrame");
-	infos.resize(size);
-	int idx = 1;
-	for (uint32_t i = 0; i < size / 6; ++i) {
-		infos.write[i].node = uint64_t(p_arr[idx]);
-		infos.write[i].node_path = p_arr[idx + 1];
-		infos.write[i].incoming_rpc = p_arr[idx + 2];
-		infos.write[i].incoming_rset = p_arr[idx + 3];
-		infos.write[i].outgoing_rpc = p_arr[idx + 4];
-		infos.write[i].outgoing_rset = p_arr[idx + 5];
-	}
-	CHECK_END(p_arr, idx, "NetworkProfilerFrame");
-	return true;
-}
-
-Array DebuggerMarshalls::ServersProfilerFrame::serialize() {
-	Array arr;
-	arr.push_back(frame_number);
-	arr.push_back(frame_time);
-	arr.push_back(idle_time);
-	arr.push_back(physics_time);
-	arr.push_back(physics_frame_time);
-	arr.push_back(script_time);
-
-	arr.push_back(servers.size());
-	for (int i = 0; i < servers.size(); i++) {
-		ServerInfo &s = servers[i];
-		arr.push_back(s.name);
-		arr.push_back(s.functions.size() * 2);
-		for (int j = 0; j < s.functions.size(); j++) {
-			ServerFunctionInfo &f = s.functions[j];
-			arr.push_back(f.name);
-			arr.push_back(f.time);
-		}
-	}
-
-	arr.push_back(script_functions.size() * 4);
-	for (int i = 0; i < script_functions.size(); i++) {
-		arr.push_back(script_functions[i].sig_id);
-		arr.push_back(script_functions[i].call_count);
-		arr.push_back(script_functions[i].self_time);
-		arr.push_back(script_functions[i].total_time);
-	}
-	return arr;
-}
-
-bool DebuggerMarshalls::ServersProfilerFrame::deserialize(const Array &p_arr) {
-	CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");
-	frame_number = p_arr[0];
-	frame_time = p_arr[1];
-	idle_time = p_arr[2];
-	physics_time = p_arr[3];
-	physics_frame_time = p_arr[4];
-	script_time = p_arr[5];
-	int servers_size = p_arr[6];
-	int idx = 7;
-	while (servers_size) {
-		CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");
-		servers_size--;
-		ServerInfo si;
-		si.name = p_arr[idx];
-		int sub_data_size = p_arr[idx + 1];
-		idx += 2;
-		CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");
-		for (int j = 0; j < sub_data_size / 2; j++) {
-			ServerFunctionInfo sf;
-			sf.name = p_arr[idx];
-			sf.time = p_arr[idx + 1];
-			idx += 2;
-			si.functions.push_back(sf);
-		}
-		servers.push_back(si);
-	}
-	CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame");
-	int func_size = p_arr[idx];
-	idx += 1;
-	CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");
-	for (int i = 0; i < func_size / 4; i++) {
-		ScriptFunctionInfo fi;
-		fi.sig_id = p_arr[idx];
-		fi.call_count = p_arr[idx + 1];
-		fi.self_time = p_arr[idx + 2];
-		fi.total_time = p_arr[idx + 3];
-		script_functions.push_back(fi);
-		idx += 4;
-	}
-	CHECK_END(p_arr, idx, "ServersProfilerFrame");
-	return true;
-}
-
 Array DebuggerMarshalls::ScriptStackDump::serialize() {
 	Array arr;
 	arr.push_back(frames.size() * 3);
@@ -298,33 +145,3 @@ bool DebuggerMarshalls::OutputError::deserialize(const Array &p_arr) {
 	CHECK_END(p_arr, idx, "OutputError");
 	return true;
 }
-
-Array DebuggerMarshalls::VisualProfilerFrame::serialize() {
-	Array arr;
-	arr.push_back(frame_number);
-	arr.push_back(areas.size() * 3);
-	for (int i = 0; i < areas.size(); i++) {
-		arr.push_back(areas[i].name);
-		arr.push_back(areas[i].cpu_msec);
-		arr.push_back(areas[i].gpu_msec);
-	}
-	return arr;
-}
-
-bool DebuggerMarshalls::VisualProfilerFrame::deserialize(const Array &p_arr) {
-	CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");
-	frame_number = p_arr[0];
-	int size = p_arr[1];
-	CHECK_SIZE(p_arr, size, "VisualProfilerFrame");
-	int idx = 2;
-	areas.resize(size / 3);
-	RS::FrameProfileArea *w = areas.ptrw();
-	for (int i = 0; i < size / 3; i++) {
-		w[i].name = p_arr[idx];
-		w[i].cpu_msec = p_arr[idx + 1];
-		w[i].gpu_msec = p_arr[idx + 2];
-		idx += 3;
-	}
-	CHECK_END(p_arr, idx, "VisualProfilerFrame");
-	return true;
-}

+ 0 - 86
core/debugger/debugger_marshalls.h

@@ -35,83 +35,6 @@
 #include "servers/rendering_server.h"
 
 struct DebuggerMarshalls {
-	// Memory usage
-	struct ResourceInfo {
-		String path;
-		String format;
-		String type;
-		RID id;
-		int vram = 0;
-		bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
-	};
-
-	struct ResourceUsage {
-		List<ResourceInfo> infos;
-
-		Array serialize();
-		bool deserialize(const Array &p_arr);
-	};
-
-	// Network profiler
-	struct MultiplayerNodeInfo {
-		ObjectID node;
-		String node_path;
-		int incoming_rpc = 0;
-		int incoming_rset = 0;
-		int outgoing_rpc = 0;
-		int outgoing_rset = 0;
-	};
-
-	struct NetworkProfilerFrame {
-		Vector<MultiplayerNodeInfo> infos;
-
-		Array serialize();
-		bool deserialize(const Array &p_arr);
-	};
-
-	// Script Profiler
-	class ScriptFunctionSignature {
-	public:
-		StringName name;
-		int id = -1;
-
-		Array serialize();
-		bool deserialize(const Array &p_arr);
-	};
-
-	struct ScriptFunctionInfo {
-		StringName name;
-		int sig_id = -1;
-		int call_count = 0;
-		double self_time = 0;
-		double total_time = 0;
-	};
-
-	// Servers profiler
-	struct ServerFunctionInfo {
-		StringName name;
-		double time = 0;
-	};
-
-	struct ServerInfo {
-		StringName name;
-		List<ServerFunctionInfo> functions;
-	};
-
-	struct ServersProfilerFrame {
-		int frame_number = 0;
-		double frame_time = 0;
-		double idle_time = 0;
-		double physics_time = 0;
-		double physics_frame_time = 0;
-		double script_time = 0;
-		List<ServerInfo> servers;
-		Vector<ScriptFunctionInfo> script_functions;
-
-		Array serialize();
-		bool deserialize(const Array &p_arr);
-	};
-
 	struct ScriptStackVariable {
 		String name;
 		Variant value;
@@ -145,15 +68,6 @@ struct DebuggerMarshalls {
 		Array serialize();
 		bool deserialize(const Array &p_arr);
 	};
-
-	// Visual Profiler
-	struct VisualProfilerFrame {
-		uint64_t frame_number = 0;
-		Vector<RS::FrameProfileArea> areas;
-
-		Array serialize();
-		bool deserialize(const Array &p_arr);
-	};
 };
 
 #endif // DEBUGGER_MARSHARLLS_H

+ 23 - 319
core/debugger/remote_debugger.cpp

@@ -33,32 +33,13 @@
 #include "core/config/project_settings.h"
 #include "core/debugger/debugger_marshalls.h"
 #include "core/debugger/engine_debugger.h"
+#include "core/debugger/engine_profiler.h"
 #include "core/debugger/script_debugger.h"
 #include "core/input/input.h"
 #include "core/object/script_language.h"
 #include "core/os/os.h"
-#include "scene/main/node.h"
-#include "servers/display_server.h"
-
-template <typename T>
-void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) {
-	EngineDebugger::Profiler prof(
-			p_prof,
-			[](void *p_user, bool p_enable, const Array &p_opts) {
-				((T *)p_user)->toggle(p_enable, p_opts);
-			},
-			[](void *p_user, const Array &p_data) {
-				((T *)p_user)->add(p_data);
-			},
-			[](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
-				((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
-			});
-	EngineDebugger::register_profiler(p_name, prof);
-}
 
-struct RemoteDebugger::NetworkProfiler {
-public:
-	typedef DebuggerMarshalls::MultiplayerNodeInfo NodeInfo;
+class RemoteDebugger::MultiplayerProfiler : public EngineProfiler {
 	struct BandwidthFrame {
 		uint32_t timestamp;
 		int packet_size;
@@ -70,11 +51,6 @@ public:
 	Vector<BandwidthFrame> bandwidth_out;
 	uint64_t last_bandwidth_time = 0;
 
-	Map<ObjectID, NodeInfo> multiplayer_node_data;
-	uint64_t last_profile_time = 0;
-
-	NetworkProfiler() {}
-
 	int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
 		ERR_FAIL_COND_V(p_buffer.size() == 0, 0);
 		int total_bandwidth = 0;
@@ -96,22 +72,8 @@ public:
 		return total_bandwidth;
 	}
 
-	void init_node(const ObjectID p_node) {
-		if (multiplayer_node_data.has(p_node)) {
-			return;
-		}
-		multiplayer_node_data.insert(p_node, DebuggerMarshalls::MultiplayerNodeInfo());
-		multiplayer_node_data[p_node].node = p_node;
-		multiplayer_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
-		multiplayer_node_data[p_node].incoming_rpc = 0;
-		multiplayer_node_data[p_node].incoming_rset = 0;
-		multiplayer_node_data[p_node].outgoing_rpc = 0;
-		multiplayer_node_data[p_node].outgoing_rset = 0;
-	}
-
+public:
 	void toggle(bool p_enable, const Array &p_opts) {
-		multiplayer_node_data.clear();
-
 		if (!p_enable) {
 			bandwidth_in.clear();
 			bandwidth_out.clear();
@@ -130,37 +92,18 @@ public:
 	}
 
 	void add(const Array &p_data) {
-		ERR_FAIL_COND(p_data.size() < 1);
-		const String type = p_data[0];
-		if (type == "node") {
-			ERR_FAIL_COND(p_data.size() < 3);
-			const ObjectID id = p_data[1];
-			const String what = p_data[2];
-			init_node(id);
-			NodeInfo &info = multiplayer_node_data[id];
-			if (what == "rpc_in") {
-				info.incoming_rpc++;
-			} else if (what == "rpc_out") {
-				info.outgoing_rpc++;
-			} else if (what == "rset_in") {
-				info.incoming_rset = 0;
-			} else if (what == "rset_out") {
-				info.outgoing_rset++;
-			}
-		} else if (type == "bandwidth") {
-			ERR_FAIL_COND(p_data.size() < 4);
-			const String inout = p_data[1];
-			int time = p_data[2];
-			int size = p_data[3];
-			if (inout == "in") {
-				bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
-				bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
-				bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
-			} else if (inout == "out") {
-				bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
-				bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
-				bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
-			}
+		ERR_FAIL_COND(p_data.size() < 3);
+		const String inout = p_data[0];
+		int time = p_data[1];
+		int size = p_data[2];
+		if (inout == "in") {
+			bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
+			bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
+			bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
+		} else if (inout == "out") {
+			bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
+			bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
+			bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
 		}
 	}
 
@@ -174,208 +117,17 @@ public:
 			Array arr;
 			arr.push_back(incoming_bandwidth);
 			arr.push_back(outgoing_bandwidth);
-			EngineDebugger::get_singleton()->send_message("network:bandwidth", arr);
-		}
-		if (pt - last_profile_time > 100) {
-			last_profile_time = pt;
-			DebuggerMarshalls::NetworkProfilerFrame frame;
-			for (const KeyValue<ObjectID, NodeInfo> &E : multiplayer_node_data) {
-				frame.infos.push_back(E.value);
-			}
-			multiplayer_node_data.clear();
-			EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize());
-		}
-	}
-};
-
-struct RemoteDebugger::ScriptsProfiler {
-	typedef DebuggerMarshalls::ScriptFunctionSignature FunctionSignature;
-	typedef DebuggerMarshalls::ScriptFunctionInfo FunctionInfo;
-	struct ProfileInfoSort {
-		bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
-			return A->total_time < B->total_time;
-		}
-	};
-	Vector<ScriptLanguage::ProfilingInfo> info;
-	Vector<ScriptLanguage::ProfilingInfo *> ptrs;
-	Map<StringName, int> sig_map;
-	int max_frame_functions = 16;
-
-	void toggle(bool p_enable, const Array &p_opts) {
-		if (p_enable) {
-			sig_map.clear();
-			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-				ScriptServer::get_language(i)->profiling_start();
-			}
-			if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) {
-				max_frame_functions = MAX(0, int(p_opts[0]));
-			}
-		} else {
-			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-				ScriptServer::get_language(i)->profiling_stop();
-			}
-		}
-	}
-
-	void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
-		int ofs = 0;
-		for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-			if (p_accumulated) {
-				ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
-			} else {
-				ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
-			}
-		}
-
-		for (int i = 0; i < ofs; i++) {
-			ptrs.write[i] = &info.write[i];
-		}
-
-		SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
-		sa.sort(ptrs.ptrw(), ofs);
-
-		int to_send = MIN(ofs, max_frame_functions);
-
-		// Check signatures first, and compute total time.
-		r_total = 0;
-		for (int i = 0; i < to_send; i++) {
-			if (!sig_map.has(ptrs[i]->signature)) {
-				int idx = sig_map.size();
-				FunctionSignature sig;
-				sig.name = ptrs[i]->signature;
-				sig.id = idx;
-				EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
-				sig_map[ptrs[i]->signature] = idx;
-			}
-			r_total += ptrs[i]->self_time;
-		}
-
-		// Send frame, script time, functions information then
-		r_funcs.resize(to_send);
-
-		FunctionInfo *w = r_funcs.ptrw();
-		for (int i = 0; i < to_send; i++) {
-			if (sig_map.has(ptrs[i]->signature)) {
-				w[i].sig_id = sig_map[ptrs[i]->signature];
-			}
-			w[i].call_count = ptrs[i]->call_count;
-			w[i].total_time = ptrs[i]->total_time / 1000000.0;
-			w[i].self_time = ptrs[i]->self_time / 1000000.0;
-		}
-	}
-
-	ScriptsProfiler() {
-		info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
-		ptrs.resize(info.size());
-	}
-};
-
-struct RemoteDebugger::ServersProfiler {
-	bool skip_profile_frame = false;
-	typedef DebuggerMarshalls::ServerInfo ServerInfo;
-	typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
-
-	Map<StringName, ServerInfo> server_data;
-	ScriptsProfiler scripts_profiler;
-
-	double frame_time = 0;
-	double idle_time = 0;
-	double physics_time = 0;
-	double physics_frame_time = 0;
-
-	void toggle(bool p_enable, const Array &p_opts) {
-		skip_profile_frame = false;
-		if (p_enable) {
-			server_data.clear(); // Clear old profiling data.
-		} else {
-			_send_frame_data(true); // Send final frame.
-		}
-		scripts_profiler.toggle(p_enable, p_opts);
-	}
-
-	void add(const Array &p_data) {
-		String name = p_data[0];
-		if (!server_data.has(name)) {
-			ServerInfo info;
-			info.name = name;
-			server_data[name] = info;
-		}
-		ServerInfo &srv = server_data[name];
-
-		ServerFunctionInfo fi;
-		fi.name = p_data[1];
-		fi.time = p_data[2];
-		srv.functions.push_back(fi);
-	}
-
-	void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
-		frame_time = p_frame_time;
-		idle_time = p_idle_time;
-		physics_time = p_physics_time;
-		physics_frame_time = p_physics_frame_time;
-		_send_frame_data(false);
-	}
-
-	void _send_frame_data(bool p_final) {
-		DebuggerMarshalls::ServersProfilerFrame frame;
-		frame.frame_number = Engine::get_singleton()->get_process_frames();
-		frame.frame_time = frame_time;
-		frame.idle_time = idle_time;
-		frame.physics_time = physics_time;
-		frame.physics_frame_time = physics_frame_time;
-		Map<StringName, ServerInfo>::Element *E = server_data.front();
-		while (E) {
-			if (!p_final) {
-				frame.servers.push_back(E->get());
-			}
-			E->get().functions.clear();
-			E = E->next();
-		}
-		uint64_t time = 0;
-		scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
-		frame.script_time = USEC_TO_SEC(time);
-		if (skip_profile_frame) {
-			skip_profile_frame = false;
-			return;
-		}
-		if (p_final) {
-			EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
-		} else {
-			EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
-		}
-	}
-};
-
-struct RemoteDebugger::VisualProfiler {
-	typedef DebuggerMarshalls::ServerInfo ServerInfo;
-	typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
-
-	Map<StringName, ServerInfo> server_data;
-
-	void toggle(bool p_enable, const Array &p_opts) {
-		RS::get_singleton()->set_frame_profiling_enabled(p_enable);
-	}
-
-	void add(const Array &p_data) {}
-
-	void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
-		Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();
-		DebuggerMarshalls::VisualProfilerFrame frame;
-		if (!profile_areas.size()) {
-			return;
+			EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);
 		}
-
-		frame.frame_number = RS::get_singleton()->get_frame_profile_frame();
-		frame.areas.append_array(profile_areas);
-		EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
 	}
 };
 
-struct RemoteDebugger::PerformanceProfiler {
+class RemoteDebugger::PerformanceProfiler : public EngineProfiler {
 	Object *performance = nullptr;
 	int last_perf_time = 0;
 	uint64_t last_monitor_modification_time = 0;
 
+public:
 	void toggle(bool p_enable, const Array &p_opts) {}
 	void add(const Array &p_data) {}
 	void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
@@ -421,29 +173,6 @@ struct RemoteDebugger::PerformanceProfiler {
 	}
 };
 
-void RemoteDebugger::_send_resource_usage() {
-	DebuggerMarshalls::ResourceUsage usage;
-
-	List<RS::TextureInfo> tinfo;
-	RS::get_singleton()->texture_debug_usage(&tinfo);
-
-	for (const RS::TextureInfo &E : tinfo) {
-		DebuggerMarshalls::ResourceInfo info;
-		info.path = E.path;
-		info.vram = E.bytes;
-		info.id = E.texture;
-		info.type = "Texture";
-		if (E.depth == 0) {
-			info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
-		} else {
-			info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);
-		}
-		usage.infos.push_back(info);
-	}
-
-	EngineDebugger::get_singleton()->send_message("memory:usage", usage.serialize());
-}
-
 Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
 	Array msg;
 	msg.push_back(p_message);
@@ -710,8 +439,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
 	msg.push_back(script_lang->debug_get_stack_level_count() > 0);
 	send_message("debug_enter", msg);
 
-	servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug.
-
 	Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
 	if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
 		Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
@@ -897,8 +624,6 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo
 	} else if (p_cmd == "set_skip_breakpoints") {
 		ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
 		script_debugger->set_skip_breakpoints(p_data[0]);
-	} else if (p_cmd == "memory") {
-		_send_resource_usage();
 	} else if (p_cmd == "break") {
 		script_debugger->debug(script_debugger->get_break_language());
 	} else {
@@ -928,23 +653,15 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
 	max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second");
 	max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second");
 
-	// Network Profiler
-	network_profiler = memnew(NetworkProfiler);
-	_bind_profiler("network", network_profiler);
-
-	// Servers Profiler (audio/physics/...)
-	servers_profiler = memnew(ServersProfiler);
-	_bind_profiler("servers", servers_profiler);
-
-	// Visual Profiler (cpu/gpu times)
-	visual_profiler = memnew(VisualProfiler);
-	_bind_profiler("visual", visual_profiler);
+	// Multiplayer Profiler
+	multiplayer_profiler.instantiate();
+	multiplayer_profiler->bind("multiplayer");
 
 	// Performance Profiler
 	Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
 	if (perf) {
-		performance_profiler = memnew(PerformanceProfiler(perf));
-		_bind_profiler("performance", performance_profiler);
+		performance_profiler = Ref<PerformanceProfiler>(memnew(PerformanceProfiler(perf)));
+		performance_profiler->bind("performance");
 		profiler_enable("performance", true);
 	}
 
@@ -973,17 +690,4 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
 RemoteDebugger::~RemoteDebugger() {
 	remove_print_handler(&phl);
 	remove_error_handler(&eh);
-
-	EngineDebugger::get_singleton()->unregister_profiler("servers");
-	EngineDebugger::get_singleton()->unregister_profiler("network");
-	EngineDebugger::get_singleton()->unregister_profiler("visual");
-	if (EngineDebugger::has_profiler("performance")) {
-		EngineDebugger::get_singleton()->unregister_profiler("performance");
-	}
-	memdelete(servers_profiler);
-	memdelete(network_profiler);
-	memdelete(visual_profiler);
-	if (performance_profiler) {
-		memdelete(performance_profiler);
-	}
 }

+ 4 - 10
core/debugger/remote_debugger.h

@@ -49,16 +49,11 @@ public:
 private:
 	typedef DebuggerMarshalls::OutputError ErrorMessage;
 
-	struct NetworkProfiler;
-	struct ServersProfiler;
-	struct ScriptsProfiler;
-	struct VisualProfiler;
-	struct PerformanceProfiler;
+	class MultiplayerProfiler;
+	class PerformanceProfiler;
 
-	NetworkProfiler *network_profiler = nullptr;
-	ServersProfiler *servers_profiler = nullptr;
-	VisualProfiler *visual_profiler = nullptr;
-	PerformanceProfiler *performance_profiler = nullptr;
+	Ref<MultiplayerProfiler> multiplayer_profiler;
+	Ref<PerformanceProfiler> performance_profiler;
 
 	Ref<RemoteDebuggerPeer> peer;
 
@@ -97,7 +92,6 @@ private:
 	bool is_peer_connected() { return peer->is_peer_connected(); }
 	void flush_output();
 
-	void _send_resource_usage();
 	void _send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type);
 
 	Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured);

+ 0 - 1
core/multiplayer/multiplayer_api.cpp

@@ -47,7 +47,6 @@ MultiplayerCacheInterface *(*MultiplayerAPI::create_default_cache_interface)(Mul
 void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) {
 	if (EngineDebugger::is_profiling("multiplayer")) {
 		Array values;
-		values.push_back("bandwidth");
 		values.push_back(p_inout);
 		values.push_back(OS::get_singleton()->get_ticks_msec());
 		values.push_back(p_size);

+ 5 - 17
editor/debugger/editor_network_profiler.cpp

@@ -56,7 +56,7 @@ void EditorNetworkProfiler::_update_frame() {
 
 	TreeItem *root = counters_display->create_item();
 
-	for (const KeyValue<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> &E : nodes_data) {
+	for (const KeyValue<ObjectID, SceneDebugger::RPCNodeInfo> &E : nodes_data) {
 		TreeItem *node = counters_display->create_item(root);
 
 		for (int j = 0; j < counters_display->get_columns(); ++j) {
@@ -65,9 +65,7 @@ void EditorNetworkProfiler::_update_frame() {
 
 		node->set_text(0, E.value.node_path);
 		node->set_text(1, E.value.incoming_rpc == 0 ? "-" : itos(E.value.incoming_rpc));
-		node->set_text(2, E.value.incoming_rset == 0 ? "-" : itos(E.value.incoming_rset));
-		node->set_text(3, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc));
-		node->set_text(4, E.value.outgoing_rset == 0 ? "-" : itos(E.value.outgoing_rset));
+		node->set_text(2, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc));
 	}
 }
 
@@ -91,14 +89,12 @@ void EditorNetworkProfiler::_clear_pressed() {
 	}
 }
 
-void EditorNetworkProfiler::add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame) {
+void EditorNetworkProfiler::add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame) {
 	if (!nodes_data.has(p_frame.node)) {
 		nodes_data.insert(p_frame.node, p_frame);
 	} else {
 		nodes_data[p_frame.node].incoming_rpc += p_frame.incoming_rpc;
-		nodes_data[p_frame.node].incoming_rset += p_frame.incoming_rset;
 		nodes_data[p_frame.node].outgoing_rpc += p_frame.outgoing_rpc;
-		nodes_data[p_frame.node].outgoing_rset += p_frame.outgoing_rset;
 	}
 
 	if (frame_delay->is_stopped()) {
@@ -174,7 +170,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
 	counters_display->set_v_size_flags(SIZE_EXPAND_FILL);
 	counters_display->set_hide_folding(true);
 	counters_display->set_hide_root(true);
-	counters_display->set_columns(5);
+	counters_display->set_columns(3);
 	counters_display->set_column_titles_visible(true);
 	counters_display->set_column_title(0, TTR("Node"));
 	counters_display->set_column_expand(0, true);
@@ -184,18 +180,10 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
 	counters_display->set_column_expand(1, false);
 	counters_display->set_column_clip_content(1, true);
 	counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE);
-	counters_display->set_column_title(2, TTR("Incoming RSET"));
+	counters_display->set_column_title(2, TTR("Outgoing RPC"));
 	counters_display->set_column_expand(2, false);
 	counters_display->set_column_clip_content(2, true);
 	counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE);
-	counters_display->set_column_title(3, TTR("Outgoing RPC"));
-	counters_display->set_column_expand(3, false);
-	counters_display->set_column_clip_content(3, true);
-	counters_display->set_column_custom_minimum_width(3, 120 * EDSCALE);
-	counters_display->set_column_title(4, TTR("Outgoing RSET"));
-	counters_display->set_column_expand(4, false);
-	counters_display->set_column_clip_content(4, true);
-	counters_display->set_column_custom_minimum_width(4, 120 * EDSCALE);
 	add_child(counters_display);
 
 	frame_delay = memnew(Timer);

+ 3 - 3
editor/debugger/editor_network_profiler.h

@@ -31,7 +31,7 @@
 #ifndef EDITORNETWORKPROFILER_H
 #define EDITORNETWORKPROFILER_H
 
-#include "core/debugger/debugger_marshalls.h"
+#include "scene/debugger/scene_debugger.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/button.h"
 #include "scene/gui/label.h"
@@ -50,7 +50,7 @@ private:
 
 	Timer *frame_delay;
 
-	Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> nodes_data;
+	Map<ObjectID, SceneDebugger::RPCNodeInfo> nodes_data;
 
 	void _update_frame();
 
@@ -62,7 +62,7 @@ protected:
 	static void _bind_methods();
 
 public:
-	void add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame);
+	void add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame);
 	void set_bandwidth(int p_incoming, int p_outgoing);
 	bool is_profiling();
 

+ 14 - 12
editor/debugger/script_editor_debugger.cpp

@@ -64,6 +64,7 @@
 #include "scene/gui/texture_button.h"
 #include "scene/gui/tree.h"
 #include "scene/resources/packed_scene.h"
+#include "servers/debugger/servers_debugger.h"
 #include "servers/display_server.h"
 
 using CameraOverride = EditorDebuggerNode::CameraOverride;
@@ -278,7 +279,7 @@ void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const
 }
 
 void ScriptEditorDebugger::_video_mem_request() {
-	_put_msg("core:memory", Array());
+	_put_msg("servers:memory", Array());
 }
 
 void ScriptEditorDebugger::_video_mem_export() {
@@ -344,15 +345,15 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 		if (id.is_valid()) {
 			emit_signal(SNAME("remote_object_updated"), id);
 		}
-	} else if (p_msg == "memory:usage") {
+	} else if (p_msg == "servers:memory_usage") {
 		vmem_tree->clear();
 		TreeItem *root = vmem_tree->create_item();
-		DebuggerMarshalls::ResourceUsage usage;
+		ServersDebugger::ResourceUsage usage;
 		usage.deserialize(p_data);
 
 		uint64_t total = 0;
 
-		for (const DebuggerMarshalls::ResourceInfo &E : usage.infos) {
+		for (const ServersDebugger::ResourceInfo &E : usage.infos) {
 			TreeItem *it = vmem_tree->create_item(root);
 			String type = E.type;
 			int bytes = E.vram;
@@ -445,7 +446,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 		performance_profiler->add_profile_frame(frame_data);
 
 	} else if (p_msg == "visual:profile_frame") {
-		DebuggerMarshalls::VisualProfilerFrame frame;
+		ServersDebugger::VisualProfilerFrame frame;
 		frame.deserialize(p_data);
 
 		EditorVisualProfiler::Metric metric;
@@ -592,13 +593,13 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 
 	} else if (p_msg == "servers:function_signature") {
 		// Cache a profiler signature.
-		DebuggerMarshalls::ScriptFunctionSignature sig;
+		ServersDebugger::ScriptFunctionSignature sig;
 		sig.deserialize(p_data);
 		profiler_signature[sig.id] = sig.name;
 
 	} else if (p_msg == "servers:profile_frame" || p_msg == "servers:profile_total") {
 		EditorProfiler::Metric metric;
-		DebuggerMarshalls::ServersProfilerFrame frame;
+		ServersDebugger::ServersProfilerFrame frame;
 		frame.deserialize(p_data);
 		metric.valid = true;
 		metric.frame_number = frame.frame_number;
@@ -642,7 +643,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 		}
 
 		for (int i = 0; i < frame.servers.size(); i++) {
-			const DebuggerMarshalls::ServerInfo &srv = frame.servers[i];
+			const ServersDebugger::ServerInfo &srv = frame.servers[i];
 			EditorProfiler::Metric::Category c;
 			const String name = srv.name;
 			c.name = name.capitalize();
@@ -709,14 +710,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 			profiler->add_frame_metric(metric, true);
 		}
 
-	} else if (p_msg == "network:profile_frame") {
-		DebuggerMarshalls::NetworkProfilerFrame frame;
+	} else if (p_msg == "multiplayer:rpc") {
+		SceneDebugger::RPCProfilerFrame frame;
 		frame.deserialize(p_data);
 		for (int i = 0; i < frame.infos.size(); i++) {
 			network_profiler->add_node_frame_data(frame.infos[i]);
 		}
 
-	} else if (p_msg == "network:bandwidth") {
+	} else if (p_msg == "multiplayer:bandwidth") {
 		ERR_FAIL_COND(p_data.size() < 2);
 		network_profiler->set_bandwidth(p_data[0], p_data[1]);
 
@@ -971,7 +972,8 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {
 	data.push_back(p_enable);
 	switch (p_type) {
 		case PROFILER_NETWORK:
-			_put_msg("profiler:network", data);
+			_put_msg("profiler:multiplayer", data);
+			_put_msg("profiler:rpc", data);
 			break;
 		case PROFILER_VISUAL:
 			_put_msg("profiler:visual", data);

+ 2 - 2
main/main.cpp

@@ -2793,8 +2793,6 @@ void Main::cleanup(bool p_force) {
 		ERR_FAIL_COND(!_start_success);
 	}
 
-	EngineDebugger::deinitialize();
-
 	ResourceLoader::remove_custom_loaders();
 	ResourceSaver::remove_custom_savers();
 
@@ -2837,6 +2835,8 @@ void Main::cleanup(bool p_force) {
 	unregister_scene_types();
 	unregister_server_types();
 
+	EngineDebugger::deinitialize();
+
 	if (xr_server) {
 		memdelete(xr_server);
 	}

+ 97 - 6
scene/debugger/scene_debugger.cpp

@@ -31,30 +31,121 @@
 #include "scene_debugger.h"
 
 #include "core/debugger/engine_debugger.h"
+#include "core/debugger/engine_profiler.h"
 #include "core/io/marshalls.h"
 #include "core/object/script_language.h"
 #include "scene/main/scene_tree.h"
 #include "scene/main/window.h"
 #include "scene/resources/packed_scene.h"
 
-void SceneDebugger::initialize() {
+Array SceneDebugger::RPCProfilerFrame::serialize() {
+	Array arr;
+	arr.push_back(infos.size() * 4);
+	for (int i = 0; i < infos.size(); ++i) {
+		arr.push_back(uint64_t(infos[i].node));
+		arr.push_back(infos[i].node_path);
+		arr.push_back(infos[i].incoming_rpc);
+		arr.push_back(infos[i].outgoing_rpc);
+	}
+	return arr;
+}
+
+bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) {
+	ERR_FAIL_COND_V(p_arr.size() < 1, false);
+	uint32_t size = p_arr[0];
+	ERR_FAIL_COND_V(size % 4, false);
+	ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
+	infos.resize(size / 4);
+	int idx = 1;
+	for (uint32_t i = 0; i < size / 4; ++i) {
+		infos.write[i].node = uint64_t(p_arr[idx]);
+		infos.write[i].node_path = p_arr[idx + 1];
+		infos.write[i].incoming_rpc = p_arr[idx + 2];
+		infos.write[i].outgoing_rpc = p_arr[idx + 3];
+	}
+	return true;
+}
+
+class SceneDebugger::RPCProfiler : public EngineProfiler {
+	Map<ObjectID, RPCNodeInfo> rpc_node_data;
+	uint64_t last_profile_time = 0;
+
+	void init_node(const ObjectID p_node) {
+		if (rpc_node_data.has(p_node)) {
+			return;
+		}
+		rpc_node_data.insert(p_node, RPCNodeInfo());
+		rpc_node_data[p_node].node = p_node;
+		rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
+		rpc_node_data[p_node].incoming_rpc = 0;
+		rpc_node_data[p_node].outgoing_rpc = 0;
+	}
+
+public:
+	void toggle(bool p_enable, const Array &p_opts) {
+		rpc_node_data.clear();
+	}
+
+	void add(const Array &p_data) {
+		ERR_FAIL_COND(p_data.size() < 2);
+		const ObjectID id = p_data[0];
+		const String what = p_data[1];
+		init_node(id);
+		RPCNodeInfo &info = rpc_node_data[id];
+		if (what == "rpc_in") {
+			info.incoming_rpc++;
+		} else if (what == "rpc_out") {
+			info.outgoing_rpc++;
+		}
+	}
+
+	void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
+		uint64_t pt = OS::get_singleton()->get_ticks_msec();
+		if (pt - last_profile_time > 100) {
+			last_profile_time = pt;
+			RPCProfilerFrame frame;
+			for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
+				frame.infos.push_back(E.value);
+			}
+			rpc_node_data.clear();
+			EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
+		}
+	}
+};
+
+SceneDebugger *SceneDebugger::singleton = nullptr;
+
+SceneDebugger::SceneDebugger() {
+	singleton = this;
+	rpc_profiler.instantiate();
+	rpc_profiler->bind("rpc");
 #ifdef DEBUG_ENABLED
 	LiveEditor::singleton = memnew(LiveEditor);
 	EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message));
 #endif
 }
 
-void SceneDebugger::deinitialize() {
+SceneDebugger::~SceneDebugger() {
 #ifdef DEBUG_ENABLED
 	if (LiveEditor::singleton) {
-		// Should be removed automatically when deiniting debugger, but just in case
-		if (EngineDebugger::has_capture("scene")) {
-			EngineDebugger::unregister_message_capture("scene");
-		}
+		EngineDebugger::unregister_message_capture("scene");
 		memdelete(LiveEditor::singleton);
 		LiveEditor::singleton = nullptr;
 	}
 #endif
+	singleton = nullptr;
+}
+
+void SceneDebugger::initialize() {
+	if (EngineDebugger::is_active()) {
+		memnew(SceneDebugger);
+	}
+}
+
+void SceneDebugger::deinitialize() {
+	if (singleton) {
+		memdelete(singleton);
+	}
 }
 
 #ifdef DEBUG_ENABLED

+ 28 - 0
scene/debugger/scene_debugger.h

@@ -32,6 +32,7 @@
 #define SCENE_DEBUGGER_H
 
 #include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
 #include "core/string/ustring.h"
 #include "core/templates/pair.h"
 #include "core/variant/array.h"
@@ -40,10 +41,37 @@ class Script;
 class Node;
 
 class SceneDebugger {
+public:
+	// RPC profiler
+	struct RPCNodeInfo {
+		ObjectID node;
+		String node_path;
+		int incoming_rpc = 0;
+		int outgoing_rpc = 0;
+	};
+
+	struct RPCProfilerFrame {
+		Vector<RPCNodeInfo> infos;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+private:
+	class RPCProfiler;
+
+	static SceneDebugger *singleton;
+
+	Ref<RPCProfiler> rpc_profiler;
+
+	SceneDebugger();
+
 public:
 	static void initialize();
 	static void deinitialize();
 
+	~SceneDebugger();
+
 #ifdef DEBUG_ENABLED
 private:
 	static void _save_node(ObjectID id, const String &p_path);

+ 8 - 0
scene/multiplayer/scene_cache_interface.cpp

@@ -107,6 +107,10 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
 	Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
 	ERR_FAIL_COND(multiplayer_peer.is_null());
 
+#ifdef DEBUG_ENABLED
+	multiplayer->profile_bandwidth("out", packet.size());
+#endif
+
 	multiplayer_peer->set_transfer_channel(0);
 	multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
 	multiplayer_peer->set_target_peer(p_from);
@@ -188,6 +192,10 @@ bool SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Path
 		Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
 		ERR_FAIL_COND_V(multiplayer_peer.is_null(), false);
 
+#ifdef DEBUG_ENABLED
+		multiplayer->profile_bandwidth("out", packet.size() * peers_to_add.size());
+#endif
+
 		for (int &E : peers_to_add) {
 			multiplayer_peer->set_target_peer(E); // To all of you.
 			multiplayer_peer->set_transfer_channel(0);

+ 5 - 0
scene/multiplayer/scene_replication_interface.cpp

@@ -147,6 +147,11 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
 	ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
 	ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
 	ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
+
+#ifdef DEBUG_ENABLED
+	multiplayer->profile_bandwidth("out", p_size);
+#endif
+
 	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
 	peer->set_target_peer(p_peer);
 	peer->set_transfer_channel(0);

+ 4 - 5
scene/multiplayer/scene_rpc_interface.cpp

@@ -46,12 +46,11 @@ void SceneRPCInterface::make_default() {
 
 #ifdef DEBUG_ENABLED
 _FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {
-	if (EngineDebugger::is_profiling("multiplayer")) {
+	if (EngineDebugger::is_profiling("rpc")) {
 		Array values;
-		values.push_back("node");
 		values.push_back(p_id);
 		values.push_back(p_what);
-		EngineDebugger::profiler_add_frame_data("multiplayer", values);
+		EngineDebugger::profiler_add_frame_data("rpc", values);
 	}
 }
 #else
@@ -268,7 +267,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i
 	argp.resize(argc);
 
 #ifdef DEBUG_ENABLED
-	_profile_node_data("in_rpc", p_node->get_instance_id());
+	_profile_node_data("rpc_in", p_node->get_instance_id());
 #endif
 
 	int out;
@@ -471,7 +470,7 @@ void SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_m
 
 	if (p_peer_id != node_id) {
 #ifdef DEBUG_ENABLED
-		_profile_node_data("out_rpc", node->get_instance_id());
+		_profile_node_data("rpc_out", node->get_instance_id());
 #endif
 
 		_send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);

+ 1 - 0
servers/SCsub

@@ -12,6 +12,7 @@ SConscript("physics_2d/SCsub")
 SConscript("rendering/SCsub")
 SConscript("audio/SCsub")
 SConscript("text/SCsub")
+SConscript("debugger/SCsub")
 
 lib = env.add_library("servers", env.servers_sources)
 

+ 5 - 0
servers/debugger/SCsub

@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.servers_sources, "*.cpp")

+ 447 - 0
servers/debugger/servers_debugger.cpp

@@ -0,0 +1,447 @@
+/*************************************************************************/
+/*  servers_debugger.cpp                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "servers_debugger.h"
+
+#include "core/config/project_settings.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/engine_profiler.h"
+#include "core/io/marshalls.h"
+#include "servers/display_server.h"
+
+#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+
+Array ServersDebugger::ResourceUsage::serialize() {
+	infos.sort();
+
+	Array arr;
+	arr.push_back(infos.size() * 4);
+	for (const ResourceInfo &E : infos) {
+		arr.push_back(E.path);
+		arr.push_back(E.format);
+		arr.push_back(E.type);
+		arr.push_back(E.vram);
+	}
+	return arr;
+}
+
+bool ServersDebugger::ResourceUsage::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 1, "ResourceUsage");
+	uint32_t size = p_arr[0];
+	CHECK_SIZE(p_arr, size, "ResourceUsage");
+	int idx = 1;
+	for (uint32_t i = 0; i < size / 4; i++) {
+		ResourceInfo info;
+		info.path = p_arr[idx];
+		info.format = p_arr[idx + 1];
+		info.type = p_arr[idx + 2];
+		info.vram = p_arr[idx + 3];
+		infos.push_back(info);
+	}
+	CHECK_END(p_arr, idx, "ResourceUsage");
+	return true;
+}
+
+Array ServersDebugger::ScriptFunctionSignature::serialize() {
+	Array arr;
+	arr.push_back(name);
+	arr.push_back(id);
+	return arr;
+}
+
+bool ServersDebugger::ScriptFunctionSignature::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");
+	name = p_arr[0];
+	id = p_arr[1];
+	CHECK_END(p_arr, 2, "ScriptFunctionSignature");
+	return true;
+}
+
+Array ServersDebugger::ServersProfilerFrame::serialize() {
+	Array arr;
+	arr.push_back(frame_number);
+	arr.push_back(frame_time);
+	arr.push_back(idle_time);
+	arr.push_back(physics_time);
+	arr.push_back(physics_frame_time);
+	arr.push_back(script_time);
+
+	arr.push_back(servers.size());
+	for (int i = 0; i < servers.size(); i++) {
+		ServerInfo &s = servers[i];
+		arr.push_back(s.name);
+		arr.push_back(s.functions.size() * 2);
+		for (int j = 0; j < s.functions.size(); j++) {
+			ServerFunctionInfo &f = s.functions[j];
+			arr.push_back(f.name);
+			arr.push_back(f.time);
+		}
+	}
+
+	arr.push_back(script_functions.size() * 4);
+	for (int i = 0; i < script_functions.size(); i++) {
+		arr.push_back(script_functions[i].sig_id);
+		arr.push_back(script_functions[i].call_count);
+		arr.push_back(script_functions[i].self_time);
+		arr.push_back(script_functions[i].total_time);
+	}
+	return arr;
+}
+
+bool ServersDebugger::ServersProfilerFrame::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");
+	frame_number = p_arr[0];
+	frame_time = p_arr[1];
+	idle_time = p_arr[2];
+	physics_time = p_arr[3];
+	physics_frame_time = p_arr[4];
+	script_time = p_arr[5];
+	int servers_size = p_arr[6];
+	int idx = 7;
+	while (servers_size) {
+		CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");
+		servers_size--;
+		ServerInfo si;
+		si.name = p_arr[idx];
+		int sub_data_size = p_arr[idx + 1];
+		idx += 2;
+		CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");
+		for (int j = 0; j < sub_data_size / 2; j++) {
+			ServerFunctionInfo sf;
+			sf.name = p_arr[idx];
+			sf.time = p_arr[idx + 1];
+			idx += 2;
+			si.functions.push_back(sf);
+		}
+		servers.push_back(si);
+	}
+	CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame");
+	int func_size = p_arr[idx];
+	idx += 1;
+	CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");
+	for (int i = 0; i < func_size / 4; i++) {
+		ScriptFunctionInfo fi;
+		fi.sig_id = p_arr[idx];
+		fi.call_count = p_arr[idx + 1];
+		fi.self_time = p_arr[idx + 2];
+		fi.total_time = p_arr[idx + 3];
+		script_functions.push_back(fi);
+		idx += 4;
+	}
+	CHECK_END(p_arr, idx, "ServersProfilerFrame");
+	return true;
+}
+
+Array ServersDebugger::VisualProfilerFrame::serialize() {
+	Array arr;
+	arr.push_back(frame_number);
+	arr.push_back(areas.size() * 3);
+	for (int i = 0; i < areas.size(); i++) {
+		arr.push_back(areas[i].name);
+		arr.push_back(areas[i].cpu_msec);
+		arr.push_back(areas[i].gpu_msec);
+	}
+	return arr;
+}
+
+bool ServersDebugger::VisualProfilerFrame::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");
+	frame_number = p_arr[0];
+	int size = p_arr[1];
+	CHECK_SIZE(p_arr, size, "VisualProfilerFrame");
+	int idx = 2;
+	areas.resize(size / 3);
+	RS::FrameProfileArea *w = areas.ptrw();
+	for (int i = 0; i < size / 3; i++) {
+		w[i].name = p_arr[idx];
+		w[i].cpu_msec = p_arr[idx + 1];
+		w[i].gpu_msec = p_arr[idx + 2];
+		idx += 3;
+	}
+	CHECK_END(p_arr, idx, "VisualProfilerFrame");
+	return true;
+}
+class ServersDebugger::ScriptsProfiler : public EngineProfiler {
+	typedef ServersDebugger::ScriptFunctionSignature FunctionSignature;
+	typedef ServersDebugger::ScriptFunctionInfo FunctionInfo;
+	struct ProfileInfoSort {
+		bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
+			return A->total_time < B->total_time;
+		}
+	};
+	Vector<ScriptLanguage::ProfilingInfo> info;
+	Vector<ScriptLanguage::ProfilingInfo *> ptrs;
+	Map<StringName, int> sig_map;
+	int max_frame_functions = 16;
+
+public:
+	void toggle(bool p_enable, const Array &p_opts) {
+		if (p_enable) {
+			sig_map.clear();
+			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+				ScriptServer::get_language(i)->profiling_start();
+			}
+			if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) {
+				max_frame_functions = MAX(0, int(p_opts[0]));
+			}
+		} else {
+			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+				ScriptServer::get_language(i)->profiling_stop();
+			}
+		}
+	}
+
+	void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
+		int ofs = 0;
+		for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+			if (p_accumulated) {
+				ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
+			} else {
+				ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
+			}
+		}
+
+		for (int i = 0; i < ofs; i++) {
+			ptrs.write[i] = &info.write[i];
+		}
+
+		SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
+		sa.sort(ptrs.ptrw(), ofs);
+
+		int to_send = MIN(ofs, max_frame_functions);
+
+		// Check signatures first, and compute total time.
+		r_total = 0;
+		for (int i = 0; i < to_send; i++) {
+			if (!sig_map.has(ptrs[i]->signature)) {
+				int idx = sig_map.size();
+				FunctionSignature sig;
+				sig.name = ptrs[i]->signature;
+				sig.id = idx;
+				EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
+				sig_map[ptrs[i]->signature] = idx;
+			}
+			r_total += ptrs[i]->self_time;
+		}
+
+		// Send frame, script time, functions information then
+		r_funcs.resize(to_send);
+
+		FunctionInfo *w = r_funcs.ptrw();
+		for (int i = 0; i < to_send; i++) {
+			if (sig_map.has(ptrs[i]->signature)) {
+				w[i].sig_id = sig_map[ptrs[i]->signature];
+			}
+			w[i].call_count = ptrs[i]->call_count;
+			w[i].total_time = ptrs[i]->total_time / 1000000.0;
+			w[i].self_time = ptrs[i]->self_time / 1000000.0;
+		}
+	}
+
+	ScriptsProfiler() {
+		info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+		ptrs.resize(info.size());
+	}
+};
+
+class ServersDebugger::ServersProfiler : public EngineProfiler {
+	bool skip_profile_frame = false;
+	typedef ServersDebugger::ServerInfo ServerInfo;
+	typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;
+
+	Map<StringName, ServerInfo> server_data;
+	ScriptsProfiler scripts_profiler;
+
+	double frame_time = 0;
+	double idle_time = 0;
+	double physics_time = 0;
+	double physics_frame_time = 0;
+
+	void _send_frame_data(bool p_final) {
+		ServersDebugger::ServersProfilerFrame frame;
+		frame.frame_number = Engine::get_singleton()->get_process_frames();
+		frame.frame_time = frame_time;
+		frame.idle_time = idle_time;
+		frame.physics_time = physics_time;
+		frame.physics_frame_time = physics_frame_time;
+		Map<StringName, ServerInfo>::Element *E = server_data.front();
+		while (E) {
+			if (!p_final) {
+				frame.servers.push_back(E->get());
+			}
+			E->get().functions.clear();
+			E = E->next();
+		}
+		uint64_t time = 0;
+		scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
+		frame.script_time = USEC_TO_SEC(time);
+		if (skip_profile_frame) {
+			skip_profile_frame = false;
+			return;
+		}
+		if (p_final) {
+			EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
+		} else {
+			EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
+		}
+	}
+
+public:
+	void toggle(bool p_enable, const Array &p_opts) {
+		skip_profile_frame = false;
+		if (p_enable) {
+			server_data.clear(); // Clear old profiling data.
+		} else {
+			_send_frame_data(true); // Send final frame.
+		}
+		scripts_profiler.toggle(p_enable, p_opts);
+	}
+
+	void add(const Array &p_data) {
+		String name = p_data[0];
+		if (!server_data.has(name)) {
+			ServerInfo info;
+			info.name = name;
+			server_data[name] = info;
+		}
+		ServerInfo &srv = server_data[name];
+
+		ServerFunctionInfo fi;
+		fi.name = p_data[1];
+		fi.time = p_data[2];
+		srv.functions.push_back(fi);
+	}
+
+	void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
+		frame_time = p_frame_time;
+		idle_time = p_idle_time;
+		physics_time = p_physics_time;
+		physics_frame_time = p_physics_frame_time;
+		_send_frame_data(false);
+	}
+
+	void skip_frame() {
+		skip_profile_frame = true;
+	}
+};
+
+class ServersDebugger::VisualProfiler : public EngineProfiler {
+	typedef ServersDebugger::ServerInfo ServerInfo;
+	typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;
+
+	Map<StringName, ServerInfo> server_data;
+
+public:
+	void toggle(bool p_enable, const Array &p_opts) {
+		RS::get_singleton()->set_frame_profiling_enabled(p_enable);
+	}
+
+	void add(const Array &p_data) {}
+
+	void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
+		Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();
+		ServersDebugger::VisualProfilerFrame frame;
+		if (!profile_areas.size()) {
+			return;
+		}
+
+		frame.frame_number = RS::get_singleton()->get_frame_profile_frame();
+		frame.areas.append_array(profile_areas);
+		EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
+	}
+};
+
+ServersDebugger *ServersDebugger::singleton = nullptr;
+
+void ServersDebugger::initialize() {
+	if (EngineDebugger::is_active()) {
+		memnew(ServersDebugger);
+	}
+}
+
+void ServersDebugger::deinitialize() {
+	if (singleton) {
+		memdelete(singleton);
+	}
+}
+
+Error ServersDebugger::_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+	ERR_FAIL_COND_V(!singleton, ERR_BUG);
+	if (p_cmd == "memory") {
+		singleton->_send_resource_usage();
+	} else {
+		r_captured = false;
+	}
+	return OK;
+}
+
+void ServersDebugger::_send_resource_usage() {
+	ServersDebugger::ResourceUsage usage;
+
+	List<RS::TextureInfo> tinfo;
+	RS::get_singleton()->texture_debug_usage(&tinfo);
+
+	for (const RS::TextureInfo &E : tinfo) {
+		ServersDebugger::ResourceInfo info;
+		info.path = E.path;
+		info.vram = E.bytes;
+		info.id = E.texture;
+		info.type = "Texture";
+		if (E.depth == 0) {
+			info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
+		} else {
+			info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);
+		}
+		usage.infos.push_back(info);
+	}
+
+	EngineDebugger::get_singleton()->send_message("servers:memory_usage", usage.serialize());
+}
+
+ServersDebugger::ServersDebugger() {
+	singleton = this;
+
+	// Generic servers profiler (audio/physics/...)
+	servers_profiler.instantiate();
+	servers_profiler->bind("servers");
+
+	// Visual Profiler (cpu/gpu times)
+	visual_profiler.instantiate();
+	visual_profiler->bind("visual");
+
+	EngineDebugger::Capture servers_cap(nullptr, &_capture);
+	EngineDebugger::register_message_capture("servers", servers_cap);
+}
+
+ServersDebugger::~ServersDebugger() {
+	EngineDebugger::unregister_message_capture("servers");
+	singleton = nullptr;
+}

+ 129 - 0
servers/debugger/servers_debugger.h

@@ -0,0 +1,129 @@
+/*************************************************************************/
+/*  servers_debugger.h                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef SERVER_DEBUGGER_H
+#define SERVER_DEBUGGER_H
+
+#include "core/debugger/debugger_marshalls.h"
+
+class ServersDebugger {
+public:
+	// Memory usage
+	struct ResourceInfo {
+		String path;
+		String format;
+		String type;
+		RID id;
+		int vram = 0;
+		bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
+	};
+
+	struct ResourceUsage {
+		List<ResourceInfo> infos;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	// Script Profiler
+	struct ScriptFunctionSignature {
+		StringName name;
+		int id = -1;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	struct ScriptFunctionInfo {
+		StringName name;
+		int sig_id = -1;
+		int call_count = 0;
+		double self_time = 0;
+		double total_time = 0;
+	};
+
+	// Servers profiler
+	struct ServerFunctionInfo {
+		StringName name;
+		double time = 0;
+	};
+
+	struct ServerInfo {
+		StringName name;
+		List<ServerFunctionInfo> functions;
+	};
+
+	struct ServersProfilerFrame {
+		int frame_number = 0;
+		double frame_time = 0;
+		double idle_time = 0;
+		double physics_time = 0;
+		double physics_frame_time = 0;
+		double script_time = 0;
+		List<ServerInfo> servers;
+		Vector<ScriptFunctionInfo> script_functions;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	// Visual Profiler
+	struct VisualProfilerFrame {
+		uint64_t frame_number = 0;
+		Vector<RS::FrameProfileArea> areas;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+private:
+	class ScriptsProfiler;
+	class ServersProfiler;
+	class VisualProfiler;
+
+	Ref<ServersProfiler> servers_profiler;
+	Ref<VisualProfiler> visual_profiler;
+
+	static ServersDebugger *singleton;
+
+	static Error _capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
+
+	void _send_resource_usage();
+
+	ServersDebugger();
+
+public:
+	static void initialize();
+	static void deinitialize();
+
+	~ServersDebugger();
+};
+
+#endif // SERVERS_DEBUGGER_H

+ 5 - 0
servers/register_server_types.cpp

@@ -56,6 +56,7 @@
 #include "camera/camera_feed.h"
 #include "camera_server.h"
 #include "core/extension/native_extension_manager.h"
+#include "debugger/servers_debugger.h"
 #include "display_server.h"
 #include "navigation_server_2d.h"
 #include "navigation_server_3d.h"
@@ -223,6 +224,8 @@ void register_server_types() {
 	GDREGISTER_CLASS(PhysicsTestMotionParameters3D);
 	GDREGISTER_CLASS(PhysicsTestMotionResult3D);
 
+	ServersDebugger::initialize();
+
 	// Physics 2D
 	GLOBAL_DEF(PhysicsServer2DManager::setting_property_name, "DEFAULT");
 	ProjectSettings::get_singleton()->set_custom_property_info(PhysicsServer2DManager::setting_property_name, PropertyInfo(Variant::STRING, PhysicsServer2DManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"));
@@ -241,6 +244,8 @@ void register_server_types() {
 }
 
 void unregister_server_types() {
+	ServersDebugger::deinitialize();
+
 	NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SERVERS);
 
 	memdelete(shader_types);