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:
commit
d22ac13bc2
@ -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);
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
82
core/debugger/engine_profiler.cpp
Normal file
82
core/debugger/engine_profiler.cpp
Normal 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();
|
||||
}
|
||||
}
|
65
core/debugger/engine_profiler.h
Normal file
65
core/debugger/engine_profiler.h
Normal 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
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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">
|
||||
|
39
doc/classes/EngineProfiler.xml
Normal file
39
doc/classes/EngineProfiler.xml
Normal 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>
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
5
servers/debugger/SCsub
Normal file
5
servers/debugger/SCsub
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
463
servers/debugger/servers_debugger.cpp
Normal file
463
servers/debugger/servers_debugger.cpp
Normal 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;
|
||||
}
|
132
servers/debugger/servers_debugger.h
Normal file
132
servers/debugger/servers_debugger.h
Normal 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
|
@ -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
Block a user