[Debugger] Move most profilers to ServersDebugger.
Also splits bandwidth/rpc profiler (RPCProfiler is now in SceneDebugger).
This commit is contained in:
parent
789e648f4d
commit
87f4bbd668
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -35,83 +35,6 @@
|
|||
#include "servers/rendering_server.h"
|
||||
|
||||
struct DebuggerMarshalls {
|
||||
// Memory usage
|
||||
struct ResourceInfo {
|
||||
String path;
|
||||
String format;
|
||||
String type;
|
||||
RID id;
|
||||
int vram = 0;
|
||||
bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
|
||||
};
|
||||
|
||||
struct ResourceUsage {
|
||||
List<ResourceInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
// Network profiler
|
||||
struct MultiplayerNodeInfo {
|
||||
ObjectID node;
|
||||
String node_path;
|
||||
int incoming_rpc = 0;
|
||||
int incoming_rset = 0;
|
||||
int outgoing_rpc = 0;
|
||||
int outgoing_rset = 0;
|
||||
};
|
||||
|
||||
struct NetworkProfilerFrame {
|
||||
Vector<MultiplayerNodeInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
// Script Profiler
|
||||
class ScriptFunctionSignature {
|
||||
public:
|
||||
StringName name;
|
||||
int id = -1;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
struct ScriptFunctionInfo {
|
||||
StringName name;
|
||||
int sig_id = -1;
|
||||
int call_count = 0;
|
||||
double self_time = 0;
|
||||
double total_time = 0;
|
||||
};
|
||||
|
||||
// Servers profiler
|
||||
struct ServerFunctionInfo {
|
||||
StringName name;
|
||||
double time = 0;
|
||||
};
|
||||
|
||||
struct ServerInfo {
|
||||
StringName name;
|
||||
List<ServerFunctionInfo> functions;
|
||||
};
|
||||
|
||||
struct ServersProfilerFrame {
|
||||
int frame_number = 0;
|
||||
double frame_time = 0;
|
||||
double idle_time = 0;
|
||||
double physics_time = 0;
|
||||
double physics_frame_time = 0;
|
||||
double script_time = 0;
|
||||
List<ServerInfo> servers;
|
||||
Vector<ScriptFunctionInfo> script_functions;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
struct ScriptStackVariable {
|
||||
String name;
|
||||
Variant value;
|
||||
|
@ -145,15 +68,6 @@ struct DebuggerMarshalls {
|
|||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
// Visual Profiler
|
||||
struct VisualProfilerFrame {
|
||||
uint64_t frame_number = 0;
|
||||
Vector<RS::FrameProfileArea> areas;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // DEBUGGER_MARSHARLLS_H
|
||||
|
|
|
@ -33,32 +33,13 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/engine_profiler.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
template <typename T>
|
||||
void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) {
|
||||
EngineDebugger::Profiler prof(
|
||||
p_prof,
|
||||
[](void *p_user, bool p_enable, const Array &p_opts) {
|
||||
((T *)p_user)->toggle(p_enable, p_opts);
|
||||
},
|
||||
[](void *p_user, const Array &p_data) {
|
||||
((T *)p_user)->add(p_data);
|
||||
},
|
||||
[](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
|
||||
});
|
||||
EngineDebugger::register_profiler(p_name, prof);
|
||||
}
|
||||
|
||||
struct RemoteDebugger::NetworkProfiler {
|
||||
public:
|
||||
typedef DebuggerMarshalls::MultiplayerNodeInfo NodeInfo;
|
||||
class RemoteDebugger::MultiplayerProfiler : public EngineProfiler {
|
||||
struct BandwidthFrame {
|
||||
uint32_t timestamp;
|
||||
int packet_size;
|
||||
|
@ -70,11 +51,6 @@ public:
|
|||
Vector<BandwidthFrame> bandwidth_out;
|
||||
uint64_t last_bandwidth_time = 0;
|
||||
|
||||
Map<ObjectID, NodeInfo> multiplayer_node_data;
|
||||
uint64_t last_profile_time = 0;
|
||||
|
||||
NetworkProfiler() {}
|
||||
|
||||
int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
|
||||
ERR_FAIL_COND_V(p_buffer.size() == 0, 0);
|
||||
int total_bandwidth = 0;
|
||||
|
@ -96,22 +72,8 @@ public:
|
|||
return total_bandwidth;
|
||||
}
|
||||
|
||||
void init_node(const ObjectID p_node) {
|
||||
if (multiplayer_node_data.has(p_node)) {
|
||||
return;
|
||||
}
|
||||
multiplayer_node_data.insert(p_node, DebuggerMarshalls::MultiplayerNodeInfo());
|
||||
multiplayer_node_data[p_node].node = p_node;
|
||||
multiplayer_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
|
||||
multiplayer_node_data[p_node].incoming_rpc = 0;
|
||||
multiplayer_node_data[p_node].incoming_rset = 0;
|
||||
multiplayer_node_data[p_node].outgoing_rpc = 0;
|
||||
multiplayer_node_data[p_node].outgoing_rset = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
multiplayer_node_data.clear();
|
||||
|
||||
if (!p_enable) {
|
||||
bandwidth_in.clear();
|
||||
bandwidth_out.clear();
|
||||
|
@ -130,37 +92,18 @@ public:
|
|||
}
|
||||
|
||||
void add(const Array &p_data) {
|
||||
ERR_FAIL_COND(p_data.size() < 1);
|
||||
const String type = p_data[0];
|
||||
if (type == "node") {
|
||||
ERR_FAIL_COND(p_data.size() < 3);
|
||||
const ObjectID id = p_data[1];
|
||||
const String what = p_data[2];
|
||||
init_node(id);
|
||||
NodeInfo &info = multiplayer_node_data[id];
|
||||
if (what == "rpc_in") {
|
||||
info.incoming_rpc++;
|
||||
} else if (what == "rpc_out") {
|
||||
info.outgoing_rpc++;
|
||||
} else if (what == "rset_in") {
|
||||
info.incoming_rset = 0;
|
||||
} else if (what == "rset_out") {
|
||||
info.outgoing_rset++;
|
||||
}
|
||||
} else if (type == "bandwidth") {
|
||||
ERR_FAIL_COND(p_data.size() < 4);
|
||||
const String inout = p_data[1];
|
||||
int time = p_data[2];
|
||||
int size = p_data[3];
|
||||
if (inout == "in") {
|
||||
bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
|
||||
bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
|
||||
bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
|
||||
} else if (inout == "out") {
|
||||
bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
|
||||
bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
|
||||
bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
|
||||
}
|
||||
ERR_FAIL_COND(p_data.size() < 3);
|
||||
const String inout = p_data[0];
|
||||
int time = p_data[1];
|
||||
int size = p_data[2];
|
||||
if (inout == "in") {
|
||||
bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
|
||||
bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
|
||||
bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
|
||||
} else if (inout == "out") {
|
||||
bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
|
||||
bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
|
||||
bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,208 +117,17 @@ public:
|
|||
Array arr;
|
||||
arr.push_back(incoming_bandwidth);
|
||||
arr.push_back(outgoing_bandwidth);
|
||||
EngineDebugger::get_singleton()->send_message("network:bandwidth", arr);
|
||||
}
|
||||
if (pt - last_profile_time > 100) {
|
||||
last_profile_time = pt;
|
||||
DebuggerMarshalls::NetworkProfilerFrame frame;
|
||||
for (const KeyValue<ObjectID, NodeInfo> &E : multiplayer_node_data) {
|
||||
frame.infos.push_back(E.value);
|
||||
}
|
||||
multiplayer_node_data.clear();
|
||||
EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize());
|
||||
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<ScriptLanguage::ProfilingInfo> info;
|
||||
Vector<ScriptLanguage::ProfilingInfo *> ptrs;
|
||||
Map<StringName, int> sig_map;
|
||||
int max_frame_functions = 16;
|
||||
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
if (p_enable) {
|
||||
sig_map.clear();
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptServer::get_language(i)->profiling_start();
|
||||
}
|
||||
if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) {
|
||||
max_frame_functions = MAX(0, int(p_opts[0]));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptServer::get_language(i)->profiling_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
|
||||
int ofs = 0;
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
if (p_accumulated) {
|
||||
ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
|
||||
} else {
|
||||
ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ofs; i++) {
|
||||
ptrs.write[i] = &info.write[i];
|
||||
}
|
||||
|
||||
SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
|
||||
sa.sort(ptrs.ptrw(), ofs);
|
||||
|
||||
int to_send = MIN(ofs, max_frame_functions);
|
||||
|
||||
// Check signatures first, and compute total time.
|
||||
r_total = 0;
|
||||
for (int i = 0; i < to_send; i++) {
|
||||
if (!sig_map.has(ptrs[i]->signature)) {
|
||||
int idx = sig_map.size();
|
||||
FunctionSignature sig;
|
||||
sig.name = ptrs[i]->signature;
|
||||
sig.id = idx;
|
||||
EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
|
||||
sig_map[ptrs[i]->signature] = idx;
|
||||
}
|
||||
r_total += ptrs[i]->self_time;
|
||||
}
|
||||
|
||||
// Send frame, script time, functions information then
|
||||
r_funcs.resize(to_send);
|
||||
|
||||
FunctionInfo *w = r_funcs.ptrw();
|
||||
for (int i = 0; i < to_send; i++) {
|
||||
if (sig_map.has(ptrs[i]->signature)) {
|
||||
w[i].sig_id = sig_map[ptrs[i]->signature];
|
||||
}
|
||||
w[i].call_count = ptrs[i]->call_count;
|
||||
w[i].total_time = ptrs[i]->total_time / 1000000.0;
|
||||
w[i].self_time = ptrs[i]->self_time / 1000000.0;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptsProfiler() {
|
||||
info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
|
||||
ptrs.resize(info.size());
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoteDebugger::ServersProfiler {
|
||||
bool skip_profile_frame = false;
|
||||
typedef DebuggerMarshalls::ServerInfo ServerInfo;
|
||||
typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
|
||||
|
||||
Map<StringName, ServerInfo> server_data;
|
||||
ScriptsProfiler scripts_profiler;
|
||||
|
||||
double frame_time = 0;
|
||||
double idle_time = 0;
|
||||
double physics_time = 0;
|
||||
double physics_frame_time = 0;
|
||||
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
skip_profile_frame = false;
|
||||
if (p_enable) {
|
||||
server_data.clear(); // Clear old profiling data.
|
||||
} else {
|
||||
_send_frame_data(true); // Send final frame.
|
||||
}
|
||||
scripts_profiler.toggle(p_enable, p_opts);
|
||||
}
|
||||
|
||||
void add(const Array &p_data) {
|
||||
String name = p_data[0];
|
||||
if (!server_data.has(name)) {
|
||||
ServerInfo info;
|
||||
info.name = name;
|
||||
server_data[name] = info;
|
||||
}
|
||||
ServerInfo &srv = server_data[name];
|
||||
|
||||
ServerFunctionInfo fi;
|
||||
fi.name = p_data[1];
|
||||
fi.time = p_data[2];
|
||||
srv.functions.push_back(fi);
|
||||
}
|
||||
|
||||
void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
frame_time = p_frame_time;
|
||||
idle_time = p_idle_time;
|
||||
physics_time = p_physics_time;
|
||||
physics_frame_time = p_physics_frame_time;
|
||||
_send_frame_data(false);
|
||||
}
|
||||
|
||||
void _send_frame_data(bool p_final) {
|
||||
DebuggerMarshalls::ServersProfilerFrame frame;
|
||||
frame.frame_number = Engine::get_singleton()->get_process_frames();
|
||||
frame.frame_time = frame_time;
|
||||
frame.idle_time = idle_time;
|
||||
frame.physics_time = physics_time;
|
||||
frame.physics_frame_time = physics_frame_time;
|
||||
Map<StringName, ServerInfo>::Element *E = server_data.front();
|
||||
while (E) {
|
||||
if (!p_final) {
|
||||
frame.servers.push_back(E->get());
|
||||
}
|
||||
E->get().functions.clear();
|
||||
E = E->next();
|
||||
}
|
||||
uint64_t time = 0;
|
||||
scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
|
||||
frame.script_time = USEC_TO_SEC(time);
|
||||
if (skip_profile_frame) {
|
||||
skip_profile_frame = false;
|
||||
return;
|
||||
}
|
||||
if (p_final) {
|
||||
EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
|
||||
} else {
|
||||
EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoteDebugger::VisualProfiler {
|
||||
typedef DebuggerMarshalls::ServerInfo ServerInfo;
|
||||
typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
|
||||
|
||||
Map<StringName, ServerInfo> server_data;
|
||||
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
RS::get_singleton()->set_frame_profiling_enabled(p_enable);
|
||||
}
|
||||
|
||||
void add(const Array &p_data) {}
|
||||
|
||||
void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();
|
||||
DebuggerMarshalls::VisualProfilerFrame frame;
|
||||
if (!profile_areas.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame.frame_number = RS::get_singleton()->get_frame_profile_frame();
|
||||
frame.areas.append_array(profile_areas);
|
||||
EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoteDebugger::PerformanceProfiler {
|
||||
class RemoteDebugger::PerformanceProfiler : public EngineProfiler {
|
||||
Object *performance = nullptr;
|
||||
int last_perf_time = 0;
|
||||
uint64_t last_monitor_modification_time = 0;
|
||||
|
||||
public:
|
||||
void toggle(bool p_enable, const Array &p_opts) {}
|
||||
void add(const Array &p_data) {}
|
||||
void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
|
@ -421,29 +173,6 @@ struct RemoteDebugger::PerformanceProfiler {
|
|||
}
|
||||
};
|
||||
|
||||
void RemoteDebugger::_send_resource_usage() {
|
||||
DebuggerMarshalls::ResourceUsage usage;
|
||||
|
||||
List<RS::TextureInfo> tinfo;
|
||||
RS::get_singleton()->texture_debug_usage(&tinfo);
|
||||
|
||||
for (const RS::TextureInfo &E : tinfo) {
|
||||
DebuggerMarshalls::ResourceInfo info;
|
||||
info.path = E.path;
|
||||
info.vram = E.bytes;
|
||||
info.id = E.texture;
|
||||
info.type = "Texture";
|
||||
if (E.depth == 0) {
|
||||
info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
|
||||
} else {
|
||||
info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);
|
||||
}
|
||||
usage.infos.push_back(info);
|
||||
}
|
||||
|
||||
EngineDebugger::get_singleton()->send_message("memory:usage", usage.serialize());
|
||||
}
|
||||
|
||||
Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
|
||||
Array msg;
|
||||
msg.push_back(p_message);
|
||||
|
@ -710,8 +439,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
|
|||
msg.push_back(script_lang->debug_get_stack_level_count() > 0);
|
||||
send_message("debug_enter", msg);
|
||||
|
||||
servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug.
|
||||
|
||||
Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
|
||||
if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
|
||||
|
@ -897,8 +624,6 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo
|
|||
} else if (p_cmd == "set_skip_breakpoints") {
|
||||
ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
|
||||
script_debugger->set_skip_breakpoints(p_data[0]);
|
||||
} else if (p_cmd == "memory") {
|
||||
_send_resource_usage();
|
||||
} else if (p_cmd == "break") {
|
||||
script_debugger->debug(script_debugger->get_break_language());
|
||||
} else {
|
||||
|
@ -928,23 +653,15 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
|
|||
max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second");
|
||||
max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second");
|
||||
|
||||
// Network Profiler
|
||||
network_profiler = memnew(NetworkProfiler);
|
||||
_bind_profiler("network", network_profiler);
|
||||
|
||||
// Servers Profiler (audio/physics/...)
|
||||
servers_profiler = memnew(ServersProfiler);
|
||||
_bind_profiler("servers", servers_profiler);
|
||||
|
||||
// Visual Profiler (cpu/gpu times)
|
||||
visual_profiler = memnew(VisualProfiler);
|
||||
_bind_profiler("visual", visual_profiler);
|
||||
// Multiplayer Profiler
|
||||
multiplayer_profiler.instantiate();
|
||||
multiplayer_profiler->bind("multiplayer");
|
||||
|
||||
// Performance Profiler
|
||||
Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
|
||||
if (perf) {
|
||||
performance_profiler = memnew(PerformanceProfiler(perf));
|
||||
_bind_profiler("performance", performance_profiler);
|
||||
performance_profiler = Ref<PerformanceProfiler>(memnew(PerformanceProfiler(perf)));
|
||||
performance_profiler->bind("performance");
|
||||
profiler_enable("performance", true);
|
||||
}
|
||||
|
||||
|
@ -973,17 +690,4 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
|
|||
RemoteDebugger::~RemoteDebugger() {
|
||||
remove_print_handler(&phl);
|
||||
remove_error_handler(&eh);
|
||||
|
||||
EngineDebugger::get_singleton()->unregister_profiler("servers");
|
||||
EngineDebugger::get_singleton()->unregister_profiler("network");
|
||||
EngineDebugger::get_singleton()->unregister_profiler("visual");
|
||||
if (EngineDebugger::has_profiler("performance")) {
|
||||
EngineDebugger::get_singleton()->unregister_profiler("performance");
|
||||
}
|
||||
memdelete(servers_profiler);
|
||||
memdelete(network_profiler);
|
||||
memdelete(visual_profiler);
|
||||
if (performance_profiler) {
|
||||
memdelete(performance_profiler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,16 +49,11 @@ public:
|
|||
private:
|
||||
typedef DebuggerMarshalls::OutputError ErrorMessage;
|
||||
|
||||
struct NetworkProfiler;
|
||||
struct ServersProfiler;
|
||||
struct ScriptsProfiler;
|
||||
struct VisualProfiler;
|
||||
struct PerformanceProfiler;
|
||||
class MultiplayerProfiler;
|
||||
class PerformanceProfiler;
|
||||
|
||||
NetworkProfiler *network_profiler = nullptr;
|
||||
ServersProfiler *servers_profiler = nullptr;
|
||||
VisualProfiler *visual_profiler = nullptr;
|
||||
PerformanceProfiler *performance_profiler = nullptr;
|
||||
Ref<MultiplayerProfiler> multiplayer_profiler;
|
||||
Ref<PerformanceProfiler> performance_profiler;
|
||||
|
||||
Ref<RemoteDebuggerPeer> peer;
|
||||
|
||||
|
@ -97,7 +92,6 @@ private:
|
|||
bool is_peer_connected() { return peer->is_peer_connected(); }
|
||||
void flush_output();
|
||||
|
||||
void _send_resource_usage();
|
||||
void _send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type);
|
||||
|
||||
Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -56,7 +56,7 @@ void EditorNetworkProfiler::_update_frame() {
|
|||
|
||||
TreeItem *root = counters_display->create_item();
|
||||
|
||||
for (const KeyValue<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> &E : nodes_data) {
|
||||
for (const KeyValue<ObjectID, SceneDebugger::RPCNodeInfo> &E : nodes_data) {
|
||||
TreeItem *node = counters_display->create_item(root);
|
||||
|
||||
for (int j = 0; j < counters_display->get_columns(); ++j) {
|
||||
|
@ -65,9 +65,7 @@ void EditorNetworkProfiler::_update_frame() {
|
|||
|
||||
node->set_text(0, E.value.node_path);
|
||||
node->set_text(1, E.value.incoming_rpc == 0 ? "-" : itos(E.value.incoming_rpc));
|
||||
node->set_text(2, E.value.incoming_rset == 0 ? "-" : itos(E.value.incoming_rset));
|
||||
node->set_text(3, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc));
|
||||
node->set_text(4, E.value.outgoing_rset == 0 ? "-" : itos(E.value.outgoing_rset));
|
||||
node->set_text(2, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,14 +89,12 @@ void EditorNetworkProfiler::_clear_pressed() {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorNetworkProfiler::add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame) {
|
||||
void EditorNetworkProfiler::add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame) {
|
||||
if (!nodes_data.has(p_frame.node)) {
|
||||
nodes_data.insert(p_frame.node, p_frame);
|
||||
} else {
|
||||
nodes_data[p_frame.node].incoming_rpc += p_frame.incoming_rpc;
|
||||
nodes_data[p_frame.node].incoming_rset += p_frame.incoming_rset;
|
||||
nodes_data[p_frame.node].outgoing_rpc += p_frame.outgoing_rpc;
|
||||
nodes_data[p_frame.node].outgoing_rset += p_frame.outgoing_rset;
|
||||
}
|
||||
|
||||
if (frame_delay->is_stopped()) {
|
||||
|
@ -174,7 +170,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
|
|||
counters_display->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
counters_display->set_hide_folding(true);
|
||||
counters_display->set_hide_root(true);
|
||||
counters_display->set_columns(5);
|
||||
counters_display->set_columns(3);
|
||||
counters_display->set_column_titles_visible(true);
|
||||
counters_display->set_column_title(0, TTR("Node"));
|
||||
counters_display->set_column_expand(0, true);
|
||||
|
@ -184,18 +180,10 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
|
|||
counters_display->set_column_expand(1, false);
|
||||
counters_display->set_column_clip_content(1, true);
|
||||
counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE);
|
||||
counters_display->set_column_title(2, TTR("Incoming RSET"));
|
||||
counters_display->set_column_title(2, TTR("Outgoing RPC"));
|
||||
counters_display->set_column_expand(2, false);
|
||||
counters_display->set_column_clip_content(2, true);
|
||||
counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE);
|
||||
counters_display->set_column_title(3, TTR("Outgoing RPC"));
|
||||
counters_display->set_column_expand(3, false);
|
||||
counters_display->set_column_clip_content(3, true);
|
||||
counters_display->set_column_custom_minimum_width(3, 120 * EDSCALE);
|
||||
counters_display->set_column_title(4, TTR("Outgoing RSET"));
|
||||
counters_display->set_column_expand(4, false);
|
||||
counters_display->set_column_clip_content(4, true);
|
||||
counters_display->set_column_custom_minimum_width(4, 120 * EDSCALE);
|
||||
add_child(counters_display);
|
||||
|
||||
frame_delay = memnew(Timer);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#ifndef EDITORNETWORKPROFILER_H
|
||||
#define EDITORNETWORKPROFILER_H
|
||||
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
@ -50,7 +50,7 @@ private:
|
|||
|
||||
Timer *frame_delay;
|
||||
|
||||
Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> nodes_data;
|
||||
Map<ObjectID, SceneDebugger::RPCNodeInfo> nodes_data;
|
||||
|
||||
void _update_frame();
|
||||
|
||||
|
@ -62,7 +62,7 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame);
|
||||
void add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame);
|
||||
void set_bandwidth(int p_incoming, int p_outgoing);
|
||||
bool is_profiling();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -31,30 +31,121 @@
|
|||
#include "scene_debugger.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/engine_profiler.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
void SceneDebugger::initialize() {
|
||||
Array SceneDebugger::RPCProfilerFrame::serialize() {
|
||||
Array arr;
|
||||
arr.push_back(infos.size() * 4);
|
||||
for (int i = 0; i < infos.size(); ++i) {
|
||||
arr.push_back(uint64_t(infos[i].node));
|
||||
arr.push_back(infos[i].node_path);
|
||||
arr.push_back(infos[i].incoming_rpc);
|
||||
arr.push_back(infos[i].outgoing_rpc);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) {
|
||||
ERR_FAIL_COND_V(p_arr.size() < 1, false);
|
||||
uint32_t size = p_arr[0];
|
||||
ERR_FAIL_COND_V(size % 4, false);
|
||||
ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
|
||||
infos.resize(size / 4);
|
||||
int idx = 1;
|
||||
for (uint32_t i = 0; i < size / 4; ++i) {
|
||||
infos.write[i].node = uint64_t(p_arr[idx]);
|
||||
infos.write[i].node_path = p_arr[idx + 1];
|
||||
infos.write[i].incoming_rpc = p_arr[idx + 2];
|
||||
infos.write[i].outgoing_rpc = p_arr[idx + 3];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class SceneDebugger::RPCProfiler : public EngineProfiler {
|
||||
Map<ObjectID, RPCNodeInfo> rpc_node_data;
|
||||
uint64_t last_profile_time = 0;
|
||||
|
||||
void init_node(const ObjectID p_node) {
|
||||
if (rpc_node_data.has(p_node)) {
|
||||
return;
|
||||
}
|
||||
rpc_node_data.insert(p_node, RPCNodeInfo());
|
||||
rpc_node_data[p_node].node = p_node;
|
||||
rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
|
||||
rpc_node_data[p_node].incoming_rpc = 0;
|
||||
rpc_node_data[p_node].outgoing_rpc = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
rpc_node_data.clear();
|
||||
}
|
||||
|
||||
void add(const Array &p_data) {
|
||||
ERR_FAIL_COND(p_data.size() < 2);
|
||||
const ObjectID id = p_data[0];
|
||||
const String what = p_data[1];
|
||||
init_node(id);
|
||||
RPCNodeInfo &info = rpc_node_data[id];
|
||||
if (what == "rpc_in") {
|
||||
info.incoming_rpc++;
|
||||
} else if (what == "rpc_out") {
|
||||
info.outgoing_rpc++;
|
||||
}
|
||||
}
|
||||
|
||||
void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
uint64_t pt = OS::get_singleton()->get_ticks_msec();
|
||||
if (pt - last_profile_time > 100) {
|
||||
last_profile_time = pt;
|
||||
RPCProfilerFrame frame;
|
||||
for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
|
||||
frame.infos.push_back(E.value);
|
||||
}
|
||||
rpc_node_data.clear();
|
||||
EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SceneDebugger *SceneDebugger::singleton = nullptr;
|
||||
|
||||
SceneDebugger::SceneDebugger() {
|
||||
singleton = this;
|
||||
rpc_profiler.instantiate();
|
||||
rpc_profiler->bind("rpc");
|
||||
#ifdef DEBUG_ENABLED
|
||||
LiveEditor::singleton = memnew(LiveEditor);
|
||||
EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneDebugger::deinitialize() {
|
||||
SceneDebugger::~SceneDebugger() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (LiveEditor::singleton) {
|
||||
// Should be removed automatically when deiniting debugger, but just in case
|
||||
if (EngineDebugger::has_capture("scene")) {
|
||||
EngineDebugger::unregister_message_capture("scene");
|
||||
}
|
||||
EngineDebugger::unregister_message_capture("scene");
|
||||
memdelete(LiveEditor::singleton);
|
||||
LiveEditor::singleton = nullptr;
|
||||
}
|
||||
#endif
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
void SceneDebugger::initialize() {
|
||||
if (EngineDebugger::is_active()) {
|
||||
memnew(SceneDebugger);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneDebugger::deinitialize() {
|
||||
if (singleton) {
|
||||
memdelete(singleton);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define SCENE_DEBUGGER_H
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/pair.h"
|
||||
#include "core/variant/array.h"
|
||||
|
@ -40,10 +41,37 @@ class Script;
|
|||
class Node;
|
||||
|
||||
class SceneDebugger {
|
||||
public:
|
||||
// RPC profiler
|
||||
struct RPCNodeInfo {
|
||||
ObjectID node;
|
||||
String node_path;
|
||||
int incoming_rpc = 0;
|
||||
int outgoing_rpc = 0;
|
||||
};
|
||||
|
||||
struct RPCProfilerFrame {
|
||||
Vector<RPCNodeInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
private:
|
||||
class RPCProfiler;
|
||||
|
||||
static SceneDebugger *singleton;
|
||||
|
||||
Ref<RPCProfiler> rpc_profiler;
|
||||
|
||||
SceneDebugger();
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static void deinitialize();
|
||||
|
||||
~SceneDebugger();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
private:
|
||||
static void _save_node(ObjectID id, const String &p_path);
|
||||
|
|
|
@ -107,6 +107,10 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
|
|||
Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
|
||||
ERR_FAIL_COND(multiplayer_peer.is_null());
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
multiplayer->profile_bandwidth("out", packet.size());
|
||||
#endif
|
||||
|
||||
multiplayer_peer->set_transfer_channel(0);
|
||||
multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
|
||||
multiplayer_peer->set_target_peer(p_from);
|
||||
|
@ -188,6 +192,10 @@ bool SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Path
|
|||
Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
|
||||
ERR_FAIL_COND_V(multiplayer_peer.is_null(), false);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
multiplayer->profile_bandwidth("out", packet.size() * peers_to_add.size());
|
||||
#endif
|
||||
|
||||
for (int &E : peers_to_add) {
|
||||
multiplayer_peer->set_target_peer(E); // To all of you.
|
||||
multiplayer_peer->set_transfer_channel(0);
|
||||
|
|
|
@ -147,6 +147,11 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
|
|||
ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
multiplayer->profile_bandwidth("out", p_size);
|
||||
#endif
|
||||
|
||||
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
|
||||
peer->set_target_peer(p_peer);
|
||||
peer->set_transfer_channel(0);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
|
@ -0,0 +1,447 @@
|
|||
/*************************************************************************/
|
||||
/* servers_debugger.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "servers_debugger.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/engine_profiler.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
|
||||
#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
|
||||
|
||||
Array ServersDebugger::ResourceUsage::serialize() {
|
||||
infos.sort();
|
||||
|
||||
Array arr;
|
||||
arr.push_back(infos.size() * 4);
|
||||
for (const ResourceInfo &E : infos) {
|
||||
arr.push_back(E.path);
|
||||
arr.push_back(E.format);
|
||||
arr.push_back(E.type);
|
||||
arr.push_back(E.vram);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
bool ServersDebugger::ResourceUsage::deserialize(const Array &p_arr) {
|
||||
CHECK_SIZE(p_arr, 1, "ResourceUsage");
|
||||
uint32_t size = p_arr[0];
|
||||
CHECK_SIZE(p_arr, size, "ResourceUsage");
|
||||
int idx = 1;
|
||||
for (uint32_t i = 0; i < size / 4; i++) {
|
||||
ResourceInfo info;
|
||||
info.path = p_arr[idx];
|
||||
info.format = p_arr[idx + 1];
|
||||
info.type = p_arr[idx + 2];
|
||||
info.vram = p_arr[idx + 3];
|
||||
infos.push_back(info);
|
||||
}
|
||||
CHECK_END(p_arr, idx, "ResourceUsage");
|
||||
return true;
|
||||
}
|
||||
|
||||
Array ServersDebugger::ScriptFunctionSignature::serialize() {
|
||||
Array arr;
|
||||
arr.push_back(name);
|
||||
arr.push_back(id);
|
||||
return arr;
|
||||
}
|
||||
|
||||
bool ServersDebugger::ScriptFunctionSignature::deserialize(const Array &p_arr) {
|
||||
CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");
|
||||
name = p_arr[0];
|
||||
id = p_arr[1];
|
||||
CHECK_END(p_arr, 2, "ScriptFunctionSignature");
|
||||
return true;
|
||||
}
|
||||
|
||||
Array ServersDebugger::ServersProfilerFrame::serialize() {
|
||||
Array arr;
|
||||
arr.push_back(frame_number);
|
||||
arr.push_back(frame_time);
|
||||
arr.push_back(idle_time);
|
||||
arr.push_back(physics_time);
|
||||
arr.push_back(physics_frame_time);
|
||||
arr.push_back(script_time);
|
||||
|
||||
arr.push_back(servers.size());
|
||||
for (int i = 0; i < servers.size(); i++) {
|
||||
ServerInfo &s = servers[i];
|
||||
arr.push_back(s.name);
|
||||
arr.push_back(s.functions.size() * 2);
|
||||
for (int j = 0; j < s.functions.size(); j++) {
|
||||
ServerFunctionInfo &f = s.functions[j];
|
||||
arr.push_back(f.name);
|
||||
arr.push_back(f.time);
|
||||
}
|
||||
}
|
||||
|
||||
arr.push_back(script_functions.size() * 4);
|
||||
for (int i = 0; i < script_functions.size(); i++) {
|
||||
arr.push_back(script_functions[i].sig_id);
|
||||
arr.push_back(script_functions[i].call_count);
|
||||
arr.push_back(script_functions[i].self_time);
|
||||
arr.push_back(script_functions[i].total_time);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
bool ServersDebugger::ServersProfilerFrame::deserialize(const Array &p_arr) {
|
||||
CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");
|
||||
frame_number = p_arr[0];
|
||||
frame_time = p_arr[1];
|
||||
idle_time = p_arr[2];
|
||||
physics_time = p_arr[3];
|
||||
physics_frame_time = p_arr[4];
|
||||
script_time = p_arr[5];
|
||||
int servers_size = p_arr[6];
|
||||
int idx = 7;
|
||||
while (servers_size) {
|
||||
CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");
|
||||
servers_size--;
|
||||
ServerInfo si;
|
||||
si.name = p_arr[idx];
|
||||
int sub_data_size = p_arr[idx + 1];
|
||||
idx += 2;
|
||||
CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");
|
||||
for (int j = 0; j < sub_data_size / 2; j++) {
|
||||
ServerFunctionInfo sf;
|
||||
sf.name = p_arr[idx];
|
||||
sf.time = p_arr[idx + 1];
|
||||
idx += 2;
|
||||
si.functions.push_back(sf);
|
||||
}
|
||||
servers.push_back(si);
|
||||
}
|
||||
CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame");
|
||||
int func_size = p_arr[idx];
|
||||
idx += 1;
|
||||
CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");
|
||||
for (int i = 0; i < func_size / 4; i++) {
|
||||
ScriptFunctionInfo fi;
|
||||
fi.sig_id = p_arr[idx];
|
||||
fi.call_count = p_arr[idx + 1];
|
||||
fi.self_time = p_arr[idx + 2];
|
||||
fi.total_time = p_arr[idx + 3];
|
||||
script_functions.push_back(fi);
|
||||
idx += 4;
|
||||
}
|
||||
CHECK_END(p_arr, idx, "ServersProfilerFrame");
|
||||
return true;
|
||||
}
|
||||
|
||||
Array ServersDebugger::VisualProfilerFrame::serialize() {
|
||||
Array arr;
|
||||
arr.push_back(frame_number);
|
||||
arr.push_back(areas.size() * 3);
|
||||
for (int i = 0; i < areas.size(); i++) {
|
||||
arr.push_back(areas[i].name);
|
||||
arr.push_back(areas[i].cpu_msec);
|
||||
arr.push_back(areas[i].gpu_msec);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
bool ServersDebugger::VisualProfilerFrame::deserialize(const Array &p_arr) {
|
||||
CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");
|
||||
frame_number = p_arr[0];
|
||||
int size = p_arr[1];
|
||||
CHECK_SIZE(p_arr, size, "VisualProfilerFrame");
|
||||
int idx = 2;
|
||||
areas.resize(size / 3);
|
||||
RS::FrameProfileArea *w = areas.ptrw();
|
||||
for (int i = 0; i < size / 3; i++) {
|
||||
w[i].name = p_arr[idx];
|
||||
w[i].cpu_msec = p_arr[idx + 1];
|
||||
w[i].gpu_msec = p_arr[idx + 2];
|
||||
idx += 3;
|
||||
}
|
||||
CHECK_END(p_arr, idx, "VisualProfilerFrame");
|
||||
return true;
|
||||
}
|
||||
class ServersDebugger::ScriptsProfiler : public EngineProfiler {
|
||||
typedef ServersDebugger::ScriptFunctionSignature FunctionSignature;
|
||||
typedef ServersDebugger::ScriptFunctionInfo FunctionInfo;
|
||||
struct ProfileInfoSort {
|
||||
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
|
||||
return A->total_time < B->total_time;
|
||||
}
|
||||
};
|
||||
Vector<ScriptLanguage::ProfilingInfo> info;
|
||||
Vector<ScriptLanguage::ProfilingInfo *> ptrs;
|
||||
Map<StringName, int> sig_map;
|
||||
int max_frame_functions = 16;
|
||||
|
||||
public:
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
if (p_enable) {
|
||||
sig_map.clear();
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptServer::get_language(i)->profiling_start();
|
||||
}
|
||||
if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) {
|
||||
max_frame_functions = MAX(0, int(p_opts[0]));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptServer::get_language(i)->profiling_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
|
||||
int ofs = 0;
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
if (p_accumulated) {
|
||||
ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
|
||||
} else {
|
||||
ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ofs; i++) {
|
||||
ptrs.write[i] = &info.write[i];
|
||||
}
|
||||
|
||||
SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
|
||||
sa.sort(ptrs.ptrw(), ofs);
|
||||
|
||||
int to_send = MIN(ofs, max_frame_functions);
|
||||
|
||||
// Check signatures first, and compute total time.
|
||||
r_total = 0;
|
||||
for (int i = 0; i < to_send; i++) {
|
||||
if (!sig_map.has(ptrs[i]->signature)) {
|
||||
int idx = sig_map.size();
|
||||
FunctionSignature sig;
|
||||
sig.name = ptrs[i]->signature;
|
||||
sig.id = idx;
|
||||
EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
|
||||
sig_map[ptrs[i]->signature] = idx;
|
||||
}
|
||||
r_total += ptrs[i]->self_time;
|
||||
}
|
||||
|
||||
// Send frame, script time, functions information then
|
||||
r_funcs.resize(to_send);
|
||||
|
||||
FunctionInfo *w = r_funcs.ptrw();
|
||||
for (int i = 0; i < to_send; i++) {
|
||||
if (sig_map.has(ptrs[i]->signature)) {
|
||||
w[i].sig_id = sig_map[ptrs[i]->signature];
|
||||
}
|
||||
w[i].call_count = ptrs[i]->call_count;
|
||||
w[i].total_time = ptrs[i]->total_time / 1000000.0;
|
||||
w[i].self_time = ptrs[i]->self_time / 1000000.0;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptsProfiler() {
|
||||
info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
|
||||
ptrs.resize(info.size());
|
||||
}
|
||||
};
|
||||
|
||||
class ServersDebugger::ServersProfiler : public EngineProfiler {
|
||||
bool skip_profile_frame = false;
|
||||
typedef ServersDebugger::ServerInfo ServerInfo;
|
||||
typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;
|
||||
|
||||
Map<StringName, ServerInfo> server_data;
|
||||
ScriptsProfiler scripts_profiler;
|
||||
|
||||
double frame_time = 0;
|
||||
double idle_time = 0;
|
||||
double physics_time = 0;
|
||||
double physics_frame_time = 0;
|
||||
|
||||
void _send_frame_data(bool p_final) {
|
||||
ServersDebugger::ServersProfilerFrame frame;
|
||||
frame.frame_number = Engine::get_singleton()->get_process_frames();
|
||||
frame.frame_time = frame_time;
|
||||
frame.idle_time = idle_time;
|
||||
frame.physics_time = physics_time;
|
||||
frame.physics_frame_time = physics_frame_time;
|
||||
Map<StringName, ServerInfo>::Element *E = server_data.front();
|
||||
while (E) {
|
||||
if (!p_final) {
|
||||
frame.servers.push_back(E->get());
|
||||
}
|
||||
E->get().functions.clear();
|
||||
E = E->next();
|
||||
}
|
||||
uint64_t time = 0;
|
||||
scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
|
||||
frame.script_time = USEC_TO_SEC(time);
|
||||
if (skip_profile_frame) {
|
||||
skip_profile_frame = false;
|
||||
return;
|
||||
}
|
||||
if (p_final) {
|
||||
EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
|
||||
} else {
|
||||
EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
skip_profile_frame = false;
|
||||
if (p_enable) {
|
||||
server_data.clear(); // Clear old profiling data.
|
||||
} else {
|
||||
_send_frame_data(true); // Send final frame.
|
||||
}
|
||||
scripts_profiler.toggle(p_enable, p_opts);
|
||||
}
|
||||
|
||||
void add(const Array &p_data) {
|
||||
String name = p_data[0];
|
||||
if (!server_data.has(name)) {
|
||||
ServerInfo info;
|
||||
info.name = name;
|
||||
server_data[name] = info;
|
||||
}
|
||||
ServerInfo &srv = server_data[name];
|
||||
|
||||
ServerFunctionInfo fi;
|
||||
fi.name = p_data[1];
|
||||
fi.time = p_data[2];
|
||||
srv.functions.push_back(fi);
|
||||
}
|
||||
|
||||
void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
frame_time = p_frame_time;
|
||||
idle_time = p_idle_time;
|
||||
physics_time = p_physics_time;
|
||||
physics_frame_time = p_physics_frame_time;
|
||||
_send_frame_data(false);
|
||||
}
|
||||
|
||||
void skip_frame() {
|
||||
skip_profile_frame = true;
|
||||
}
|
||||
};
|
||||
|
||||
class ServersDebugger::VisualProfiler : public EngineProfiler {
|
||||
typedef ServersDebugger::ServerInfo ServerInfo;
|
||||
typedef ServersDebugger::ServerFunctionInfo ServerFunctionInfo;
|
||||
|
||||
Map<StringName, ServerInfo> server_data;
|
||||
|
||||
public:
|
||||
void toggle(bool p_enable, const Array &p_opts) {
|
||||
RS::get_singleton()->set_frame_profiling_enabled(p_enable);
|
||||
}
|
||||
|
||||
void add(const Array &p_data) {}
|
||||
|
||||
void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
|
||||
Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();
|
||||
ServersDebugger::VisualProfilerFrame frame;
|
||||
if (!profile_areas.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame.frame_number = RS::get_singleton()->get_frame_profile_frame();
|
||||
frame.areas.append_array(profile_areas);
|
||||
EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
|
||||
}
|
||||
};
|
||||
|
||||
ServersDebugger *ServersDebugger::singleton = nullptr;
|
||||
|
||||
void ServersDebugger::initialize() {
|
||||
if (EngineDebugger::is_active()) {
|
||||
memnew(ServersDebugger);
|
||||
}
|
||||
}
|
||||
|
||||
void ServersDebugger::deinitialize() {
|
||||
if (singleton) {
|
||||
memdelete(singleton);
|
||||
}
|
||||
}
|
||||
|
||||
Error ServersDebugger::_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
|
||||
ERR_FAIL_COND_V(!singleton, ERR_BUG);
|
||||
if (p_cmd == "memory") {
|
||||
singleton->_send_resource_usage();
|
||||
} else {
|
||||
r_captured = false;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void ServersDebugger::_send_resource_usage() {
|
||||
ServersDebugger::ResourceUsage usage;
|
||||
|
||||
List<RS::TextureInfo> tinfo;
|
||||
RS::get_singleton()->texture_debug_usage(&tinfo);
|
||||
|
||||
for (const RS::TextureInfo &E : tinfo) {
|
||||
ServersDebugger::ResourceInfo info;
|
||||
info.path = E.path;
|
||||
info.vram = E.bytes;
|
||||
info.id = E.texture;
|
||||
info.type = "Texture";
|
||||
if (E.depth == 0) {
|
||||
info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
|
||||
} else {
|
||||
info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);
|
||||
}
|
||||
usage.infos.push_back(info);
|
||||
}
|
||||
|
||||
EngineDebugger::get_singleton()->send_message("servers:memory_usage", usage.serialize());
|
||||
}
|
||||
|
||||
ServersDebugger::ServersDebugger() {
|
||||
singleton = this;
|
||||
|
||||
// Generic servers profiler (audio/physics/...)
|
||||
servers_profiler.instantiate();
|
||||
servers_profiler->bind("servers");
|
||||
|
||||
// Visual Profiler (cpu/gpu times)
|
||||
visual_profiler.instantiate();
|
||||
visual_profiler->bind("visual");
|
||||
|
||||
EngineDebugger::Capture servers_cap(nullptr, &_capture);
|
||||
EngineDebugger::register_message_capture("servers", servers_cap);
|
||||
}
|
||||
|
||||
ServersDebugger::~ServersDebugger() {
|
||||
EngineDebugger::unregister_message_capture("servers");
|
||||
singleton = nullptr;
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*************************************************************************/
|
||||
/* servers_debugger.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef SERVER_DEBUGGER_H
|
||||
#define SERVER_DEBUGGER_H
|
||||
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
|
||||
class ServersDebugger {
|
||||
public:
|
||||
// Memory usage
|
||||
struct ResourceInfo {
|
||||
String path;
|
||||
String format;
|
||||
String type;
|
||||
RID id;
|
||||
int vram = 0;
|
||||
bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
|
||||
};
|
||||
|
||||
struct ResourceUsage {
|
||||
List<ResourceInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
// Script Profiler
|
||||
struct ScriptFunctionSignature {
|
||||
StringName name;
|
||||
int id = -1;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
struct ScriptFunctionInfo {
|
||||
StringName name;
|
||||
int sig_id = -1;
|
||||
int call_count = 0;
|
||||
double self_time = 0;
|
||||
double total_time = 0;
|
||||
};
|
||||
|
||||
// Servers profiler
|
||||
struct ServerFunctionInfo {
|
||||
StringName name;
|
||||
double time = 0;
|
||||
};
|
||||
|
||||
struct ServerInfo {
|
||||
StringName name;
|
||||
List<ServerFunctionInfo> functions;
|
||||
};
|
||||
|
||||
struct ServersProfilerFrame {
|
||||
int frame_number = 0;
|
||||
double frame_time = 0;
|
||||
double idle_time = 0;
|
||||
double physics_time = 0;
|
||||
double physics_frame_time = 0;
|
||||
double script_time = 0;
|
||||
List<ServerInfo> servers;
|
||||
Vector<ScriptFunctionInfo> script_functions;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
// Visual Profiler
|
||||
struct VisualProfilerFrame {
|
||||
uint64_t frame_number = 0;
|
||||
Vector<RS::FrameProfileArea> areas;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
private:
|
||||
class ScriptsProfiler;
|
||||
class ServersProfiler;
|
||||
class VisualProfiler;
|
||||
|
||||
Ref<ServersProfiler> servers_profiler;
|
||||
Ref<VisualProfiler> visual_profiler;
|
||||
|
||||
static ServersDebugger *singleton;
|
||||
|
||||
static Error _capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
|
||||
|
||||
void _send_resource_usage();
|
||||
|
||||
ServersDebugger();
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static void deinitialize();
|
||||
|
||||
~ServersDebugger();
|
||||
};
|
||||
|
||||
#endif // SERVERS_DEBUGGER_H
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue