Merge pull request #57715 from Faless/debugger/4.x_core_includes_and_servers

[Debugger] Move most profilers to ServersDebugger, fix core includes.
This commit is contained in:
Rémi Verschelde 2022-02-09 21:57:32 +01:00 committed by GitHub
commit d22ac13bc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1007 additions and 741 deletions

View File

@ -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<EngineProfiler> 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<StringName, ProfilerCallable> &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);

View File

@ -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<StringName, Callable> captures;
Map<StringName, ProfilerCallable> profilers;
Map<StringName, Ref<EngineProfiler>> 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<EngineProfiler> 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; }

View File

@ -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;
}

View File

@ -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<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 +67,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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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,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<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 +678,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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -65,14 +65,9 @@
<method name="register_profiler">
<return type="void" />
<argument index="0" name="name" type="StringName" />
<argument index="1" name="toggle" type="Callable" />
<argument index="2" name="add" type="Callable" />
<argument index="3" name="tick" type="Callable" />
<argument index="1" name="profiler" type="EngineProfiler" />
<description>
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.
</description>
</method>
<method name="send_message">

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EngineProfiler" inherits="RefCounted" version="4.0">
<brief_description>
Base class for creating custom profilers.
</brief_description>
<description>
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.
</description>
<tutorials>
</tutorials>
<methods>
<method name="_add_frame" qualifiers="virtual">
<return type="void" />
<argument index="0" name="data" type="Array" />
<description>
Called when data is added to profiler using [method EngineDebugger.profiler_add_frame_data].
</description>
</method>
<method name="_tick" qualifiers="virtual">
<return type="void" />
<argument index="0" name="frame_time" type="float" />
<argument index="1" name="idle_time" type="float" />
<argument index="2" name="physics_time" type="float" />
<argument index="3" name="physics_frame_time" type="float" />
<description>
Called once every engine iteration when the profiler is active with informations about the current frame.
</description>
</method>
<method name="_toggle" qualifiers="virtual">
<return type="void" />
<argument index="0" name="enable" type="bool" />
<argument index="1" name="options" type="Array" />
<description>
Called when the profiler is enabled/disabled, along with a set of [code]options[/code].
</description>
</method>
</methods>
</class>

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

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

5
servers/debugger/SCsub Normal file
View File

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

View File

@ -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<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);
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<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;
}

View File

@ -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<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;
double last_draw_time = 0.0;
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

View File

@ -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);