diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 8d03f35617f..195292f7e6d 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -2376,21 +2376,18 @@ bool EngineDebugger::is_active() { return ::EngineDebugger::is_active(); } -void EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { - ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name); - profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick)); - ProfilerCallable &p = profilers[p_name]; - ::EngineDebugger::Profiler profiler( - &p, - &EngineDebugger::call_toggle, - &EngineDebugger::call_add, - &EngineDebugger::call_tick); - ::EngineDebugger::register_profiler(p_name, profiler); +void EngineDebugger::register_profiler(const StringName &p_name, Ref p_profiler) { + ERR_FAIL_COND(p_profiler.is_null()); + ERR_FAIL_COND_MSG(p_profiler->is_bound(), "Profiler already registered."); + ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler name already in use: " + p_name); + Error err = p_profiler->bind(p_name); + ERR_FAIL_COND_MSG(err != OK, "Profiler failed to register with error: " + itos(err)); + profilers.insert(p_name, p_profiler); } void EngineDebugger::unregister_profiler(const StringName &p_name) { ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name); - ::EngineDebugger::unregister_profiler(p_name); + profilers[p_name]->unbind(); profilers.erase(p_name); } @@ -2435,45 +2432,6 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) { ::EngineDebugger::get_singleton()->send_message(p_msg, p_data); } -void EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) { - Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle; - if (toggle.is_null()) { - return; - } - Variant enable = p_enable, opts = p_opts; - const Variant *args[2] = { &enable, &opts }; - Variant retval; - Callable::CallError err; - toggle.call(args, 2, retval, err); - ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err)); -} - -void EngineDebugger::call_add(void *p_user, const Array &p_data) { - Callable &add = ((ProfilerCallable *)p_user)->callable_add; - if (add.is_null()) { - return; - } - Variant data = p_data; - const Variant *args[1] = { &data }; - Variant retval; - Callable::CallError err; - add.call(args, 1, retval, err); - ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err)); -} - -void EngineDebugger::call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { - Callable &tick = ((ProfilerCallable *)p_user)->callable_tick; - if (tick.is_null()) { - return; - } - Variant frame_time = p_frame_time, idle_time = p_idle_time, physics_time = p_physics_time, physics_frame_time = p_physics_frame_time; - const Variant *args[4] = { &frame_time, &idle_time, &physics_time, &physics_frame_time }; - Variant retval; - Callable::CallError err; - tick.call(args, 4, retval, err); - ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err)); -} - Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { Callable &capture = *(Callable *)p_user; if (capture.is_null()) { @@ -2495,10 +2453,6 @@ EngineDebugger::~EngineDebugger() { ::EngineDebugger::unregister_message_capture(E.key); } captures.clear(); - for (const KeyValue &E : profilers) { - ::EngineDebugger::unregister_profiler(E.key); - } - profilers.clear(); } EngineDebugger *EngineDebugger::singleton = nullptr; @@ -2506,8 +2460,9 @@ EngineDebugger *EngineDebugger::singleton = nullptr; void EngineDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("is_active"), &EngineDebugger::is_active); - ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &EngineDebugger::register_profiler); + ClassDB::bind_method(D_METHOD("register_profiler", "name", "profiler"), &EngineDebugger::register_profiler); ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &EngineDebugger::unregister_profiler); + ClassDB::bind_method(D_METHOD("is_profiling", "name"), &EngineDebugger::is_profiling); ClassDB::bind_method(D_METHOD("has_profiler", "name"), &EngineDebugger::has_profiler); diff --git a/core/core_bind.h b/core/core_bind.h index ac0e92a87ab..21a1fc20774 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -31,6 +31,7 @@ #ifndef CORE_BIND_H #define CORE_BIND_H +#include "core/debugger/engine_profiler.h" #include "core/io/compression.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -673,25 +674,8 @@ public: class EngineDebugger : public Object { GDCLASS(EngineDebugger, Object); - class ProfilerCallable { - friend class EngineDebugger; - - Callable callable_toggle; - Callable callable_add; - Callable callable_tick; - - public: - ProfilerCallable() {} - - ProfilerCallable(const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { - callable_toggle = p_toggle; - callable_add = p_add; - callable_tick = p_tick; - } - }; - Map captures; - Map profilers; + Map> profilers; protected: static void _bind_methods(); @@ -702,7 +686,7 @@ public: bool is_active(); - void register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick); + void register_profiler(const StringName &p_name, Ref p_profiler); void unregister_profiler(const StringName &p_name); bool is_profiling(const StringName &p_name); bool has_profiler(const StringName &p_name); @@ -715,9 +699,6 @@ public: void send_message(const String &p_msg, const Array &p_data); - static void call_toggle(void *p_user, bool p_enable, const Array &p_opts); - static void call_add(void *p_user, const Array &p_data); - static void call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time); static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); EngineDebugger() { singleton = this; } 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..378c3af8aab 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -32,86 +32,8 @@ #define DEBUGGER_MARSHARLLS_H #include "core/object/script_language.h" -#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 +67,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/engine_profiler.cpp b/core/debugger/engine_profiler.cpp new file mode 100644 index 00000000000..c858b1febd0 --- /dev/null +++ b/core/debugger/engine_profiler.cpp @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* engine_profiler.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 "engine_profiler.h" + +#include "core/debugger/engine_debugger.h" + +void EngineProfiler::_bind_methods() { + GDVIRTUAL_BIND(_toggle, "enable", "options"); + GDVIRTUAL_BIND(_add_frame, "data"); + GDVIRTUAL_BIND(_tick, "frame_time", "idle_time", "physics_time", "physics_frame_time"); +} + +void EngineProfiler::toggle(bool p_enable, const Array &p_array) { + GDVIRTUAL_CALL(_toggle, p_enable, p_array); +} + +void EngineProfiler::add(const Array &p_data) { + GDVIRTUAL_CALL(_add_frame, p_data); +} + +void EngineProfiler::tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { + GDVIRTUAL_CALL(_tick, p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); +} + +Error EngineProfiler::bind(const String &p_name) { + ERR_FAIL_COND_V(is_bound(), ERR_ALREADY_IN_USE); + EngineDebugger::Profiler prof( + this, + [](void *p_user, bool p_enable, const Array &p_opts) { + ((EngineProfiler *)p_user)->toggle(p_enable, p_opts); + }, + [](void *p_user, const Array &p_data) { + ((EngineProfiler *)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) { + ((EngineProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); + }); + registration = p_name; + EngineDebugger::register_profiler(p_name, prof); + return OK; +} + +Error EngineProfiler::unbind() { + ERR_FAIL_COND_V(!is_bound(), ERR_UNCONFIGURED); + EngineDebugger::unregister_profiler(registration); + registration.clear(); + return OK; +} + +EngineProfiler::~EngineProfiler() { + if (is_bound()) { + unbind(); + } +} diff --git a/core/debugger/engine_profiler.h b/core/debugger/engine_profiler.h new file mode 100644 index 00000000000..ade280a7bb3 --- /dev/null +++ b/core/debugger/engine_profiler.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* engine_profiler.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 ENGINE_PROFILER_H +#define ENGINE_PROFILER_H + +#include "core/object/ref_counted.h" + +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" + +class EngineProfiler : public RefCounted { + GDCLASS(EngineProfiler, RefCounted); + +private: + String registration; + +protected: + static void _bind_methods(); + +public: + virtual void toggle(bool p_enable, const Array &p_opts); + virtual void add(const Array &p_data); + virtual void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time); + + Error bind(const String &p_name); + Error unbind(); + bool is_bound() const { return registration.length() > 0; } + + GDVIRTUAL2(_toggle, bool, Array); + GDVIRTUAL1(_add_frame, Array); + GDVIRTUAL4(_tick, double, double, double, double); + + EngineProfiler() {} + virtual ~EngineProfiler(); +}; + +#endif // ENGINE_PROFILER_H diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 339aa9b61f5..2fce23d0036 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,18 +439,12 @@ 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); } - uint64_t loop_begin_usec = 0; - uint64_t loop_time_sec = 0; while (is_peer_connected()) { - loop_begin_usec = OS::get_singleton()->get_ticks_usec(); - flush_output(); peer->poll(); @@ -748,7 +471,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (command == "continue") { script_debugger->set_depth(-1); script_debugger->set_lines_left(-1); - DisplayServer::get_singleton()->window_move_to_foreground(); break; } else if (command == "break") { @@ -824,13 +546,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { OS::get_singleton()->delay_usec(10000); OS::get_singleton()->process_and_drop_events(); } - - // This is for the camera override to stay live even when the game is paused from the editor - loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f; - RenderingServer::get_singleton()->sync(); - if (RenderingServer::get_singleton()->has_changed()) { - RenderingServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale()); - } } send_message("debug_exit", Array()); @@ -897,8 +612,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 +641,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 +678,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/core/register_core_types.cpp b/core/register_core_types.cpp index 388368d1812..a4b6a589f35 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -37,6 +37,7 @@ #include "core/crypto/aes_context.h" #include "core/crypto/crypto.h" #include "core/crypto/hashing_context.h" +#include "core/debugger/engine_profiler.h" #include "core/extension/native_extension.h" #include "core/extension/native_extension_manager.h" #include "core/input/input.h" @@ -237,6 +238,8 @@ void register_core_types() { GDREGISTER_VIRTUAL_CLASS(ResourceUID); + GDREGISTER_CLASS(EngineProfiler); + resource_uid = memnew(ResourceUID); native_extension_manager = memnew(NativeExtensionManager); diff --git a/doc/classes/EngineDebugger.xml b/doc/classes/EngineDebugger.xml index 861053b1c97..0e1bf99afc4 100644 --- a/doc/classes/EngineDebugger.xml +++ b/doc/classes/EngineDebugger.xml @@ -65,14 +65,9 @@ - - - + - Registers a profiler with the given [code]name[/code]. - [code]toggle[/code] callable is called when the profiler is enabled/disabled. It must take an argument array as an argument. - [code]add[/code] callable is called when data is added to profiler using [method EngineDebugger.profiler_add_frame_data]. It must take a data array as argument. - [code]tick[/code] callable is called at every active profiler iteration. It must take frame time, idle time, physics time, and physics idle time as arguments. + Registers a profiler with the given [code]name[/code]. See [EngineProfiler] for more informations. diff --git a/doc/classes/EngineProfiler.xml b/doc/classes/EngineProfiler.xml new file mode 100644 index 00000000000..88780b1a419 --- /dev/null +++ b/doc/classes/EngineProfiler.xml @@ -0,0 +1,39 @@ + + + + Base class for creating custom profilers. + + + This class can be used to implement custom profilers that are able to interact with the engine and editor debugger. + See [EngineDebugger] and [EditorDebuggerPlugin] for more informations. + + + + + + + + + Called when data is added to profiler using [method EngineDebugger.profiler_add_frame_data]. + + + + + + + + + + Called once every engine iteration when the profiler is active with informations about the current frame. + + + + + + + + Called when the profiler is enabled/disabled, along with a set of [code]options[/code]. + + + + 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 6a2cb8ee4a2..28e8edb26e1 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -63,6 +63,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; @@ -127,6 +128,7 @@ void ScriptEditorDebugger::debug_continue() { _clear_execution(); _put_msg("continue", Array()); + _put_msg("servers:foreground", Array()); } void ScriptEditorDebugger::update_tabs() { @@ -277,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() { @@ -343,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; @@ -444,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; @@ -591,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; @@ -641,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(); @@ -708,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]); @@ -832,6 +834,9 @@ void ScriptEditorDebugger::_notification(int p_what) { msg.push_back(cam->get_far()); _put_msg("scene:override_camera_3D:transform", msg); } + if (breaked) { + _put_msg("servers:draw", Array()); + } } const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20; @@ -970,7 +975,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 0bca5bef0ca..1efe3ccd945 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2789,8 +2789,6 @@ void Main::cleanup(bool p_force) { ERR_FAIL_COND(!_start_success); } - EngineDebugger::deinitialize(); - ResourceLoader::remove_custom_loaders(); ResourceSaver::remove_custom_savers(); @@ -2833,6 +2831,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..d1391937d91 --- /dev/null +++ b/servers/debugger/servers_debugger.cpp @@ -0,0 +1,463 @@ +/*************************************************************************/ +/* 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); + r_captured = true; + if (p_cmd == "memory") { + singleton->_send_resource_usage(); + } else if (p_cmd == "draw") { // Forced redraw. + // For camera override to stay live when the game is paused from the editor. + double delta = 0.0; + if (singleton->last_draw_time) { + delta = (OS::get_singleton()->get_ticks_usec() - singleton->last_draw_time) / 1000000.0; + } + singleton->last_draw_time = OS::get_singleton()->get_ticks_usec(); + RenderingServer::get_singleton()->sync(); + if (RenderingServer::get_singleton()->has_changed()) { + RenderingServer::get_singleton()->draw(true, delta); + } + } else if (p_cmd == "foreground") { + singleton->last_draw_time = 0.0; + DisplayServer::get_singleton()->window_move_to_foreground(); + singleton->servers_profiler->skip_frame(); + } 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..d1c55dc6907 --- /dev/null +++ b/servers/debugger/servers_debugger.h @@ -0,0 +1,132 @@ +/*************************************************************************/ +/* 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" + +#include "servers/rendering_server.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; + + double last_draw_time = 0.0; + 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 3726bcde35d..f405dea770b 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);