diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp index 1a746d59a3f..4c69290c2e6 100644 --- a/core/debugger/debugger_marshalls.cpp +++ b/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; -} diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index fae766812a0..1cf8daeac0f 100644 --- a/core/debugger/debugger_marshalls.h +++ b/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 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 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 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 servers; - Vector 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 areas; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; }; #endif // DEBUGGER_MARSHARLLS_H diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 339aa9b61f5..b9c494aacae 100644 --- a/core/debugger/remote_debugger.cpp +++ b/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 -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 bandwidth_out; uint64_t last_bandwidth_time = 0; - Map multiplayer_node_data; - uint64_t last_profile_time = 0; - - NetworkProfiler() {} - int bandwidth_usage(const Vector &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(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 &E : multiplayer_node_data) { - frame.infos.push_back(E.value); - } - multiplayer_node_data.clear(); - EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize()); + EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr); } } }; -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 info; - Vector ptrs; - Map 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 &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 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 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::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 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 profile_areas = RS::get_singleton()->get_frame_profile(); - DebuggerMarshalls::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()); - } -}; - -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 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 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(memnew(PerformanceProfiler(perf))); + performance_profiler->bind("performance"); profiler_enable("performance", true); } @@ -973,17 +690,4 @@ RemoteDebugger::RemoteDebugger(Ref 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); - } } diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index bd64955c892..aada92da602 100644 --- a/core/debugger/remote_debugger.h +++ b/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 multiplayer_profiler; + Ref performance_profiler; Ref 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 &p_names, List &p_vals, int p_type); Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured); diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp index c8cb333e2c0..3533acd103d 100644 --- a/core/multiplayer/multiplayer_api.cpp +++ b/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); diff --git a/editor/debugger/editor_network_profiler.cpp b/editor/debugger/editor_network_profiler.cpp index 698e950f57a..b05134144e3 100644 --- a/editor/debugger/editor_network_profiler.cpp +++ b/editor/debugger/editor_network_profiler.cpp @@ -56,7 +56,7 @@ void EditorNetworkProfiler::_update_frame() { TreeItem *root = counters_display->create_item(); - for (const KeyValue &E : nodes_data) { + for (const KeyValue &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); diff --git a/editor/debugger/editor_network_profiler.h b/editor/debugger/editor_network_profiler.h index 320dd2a826f..3e95eb0de6d 100644 --- a/editor/debugger/editor_network_profiler.h +++ b/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 nodes_data; + Map 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(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 6aedfa6ccb4..378ba5d7c8e 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/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); diff --git a/main/main.cpp b/main/main.cpp index f8088cba1cb..3765f91acc9 100644 --- a/main/main.cpp +++ b/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); } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 41340f281bf..e5b81f9d8d7 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/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 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(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 &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 diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index c8298391bb5..dd0a17c2dcb 100644 --- a/scene/debugger/scene_debugger.h +++ b/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 infos; + + Array serialize(); + bool deserialize(const Array &p_arr); + }; + +private: + class RPCProfiler; + + static SceneDebugger *singleton; + + Ref 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); diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp index de4a94470aa..2f278ed8646 100644 --- a/scene/multiplayer/scene_cache_interface.cpp +++ b/scene/multiplayer/scene_cache_interface.cpp @@ -107,6 +107,10 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac Ref 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 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); diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp index 2088a43ba71..25a704b37e1 100644 --- a/scene/multiplayer/scene_replication_interface.cpp +++ b/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 peer = multiplayer->get_multiplayer_peer(); peer->set_target_peer(p_peer); peer->set_transfer_channel(0); diff --git a/scene/multiplayer/scene_rpc_interface.cpp b/scene/multiplayer/scene_rpc_interface.cpp index 7d7f57b9a1a..0cb703227b0 100644 --- a/scene/multiplayer/scene_rpc_interface.cpp +++ b/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); diff --git a/servers/SCsub b/servers/SCsub index 76c11724d3f..2cd4741d562 100644 --- a/servers/SCsub +++ b/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) diff --git a/servers/debugger/SCsub b/servers/debugger/SCsub new file mode 100644 index 00000000000..86681f9c74d --- /dev/null +++ b/servers/debugger/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.servers_sources, "*.cpp") diff --git a/servers/debugger/servers_debugger.cpp b/servers/debugger/servers_debugger.cpp new file mode 100644 index 00000000000..a0f415eb94b --- /dev/null +++ b/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 info; + Vector ptrs; + Map 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 &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 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 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::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 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 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 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; +} diff --git a/servers/debugger/servers_debugger.h b/servers/debugger/servers_debugger.h new file mode 100644 index 00000000000..3a020e06332 --- /dev/null +++ b/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 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 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 servers; + Vector script_functions; + + Array serialize(); + bool deserialize(const Array &p_arr); + }; + + // Visual Profiler + struct VisualProfilerFrame { + uint64_t frame_number = 0; + Vector areas; + + Array serialize(); + bool deserialize(const Array &p_arr); + }; + +private: + class ScriptsProfiler; + class ServersProfiler; + class VisualProfiler; + + Ref servers_profiler; + Ref 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 diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index bf8b6379d2e..ed654013ef2 100644 --- a/servers/register_server_types.cpp +++ b/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);