Merge pull request #31870 from JFonS/add_network_profiler

Add network profiler
This commit is contained in:
Fabio Alessandrelli 2019-09-05 16:34:36 +02:00 committed by GitHub
commit 768d637a1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 579 additions and 1 deletions

View file

@ -33,6 +33,10 @@
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "scene/main/node.h" #include "scene/main/node.h"
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
#endif
_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) {
switch (mode) { switch (mode) {
@ -166,6 +170,14 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
#ifdef DEBUG_ENABLED
if (profiling) {
bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len;
bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size();
}
#endif
uint8_t packet_type = p_packet[0]; uint8_t packet_type = p_packet[0];
switch (packet_type) { switch (packet_type) {
@ -284,6 +296,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
p_offset++; p_offset++;
#ifdef DEBUG_ENABLED
if (profiling) {
ObjectID id = p_node->get_instance_id();
_init_node_profile(id);
profiler_frame_data[id].incoming_rpc += 1;
}
#endif
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
@ -322,6 +342,14 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p
bool can_call = _can_call_mode(p_node, rset_mode, p_from); bool can_call = _can_call_mode(p_node, rset_mode, p_from);
ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
#ifdef DEBUG_ENABLED
if (profiling) {
ObjectID id = p_node->get_instance_id();
_init_node_profile(id);
profiler_frame_data[id].incoming_rset += 1;
}
#endif
Variant value; Variant value;
Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed()); Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed());
@ -512,6 +540,14 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
} }
} }
#ifdef DEBUG_ENABLED
if (profiling) {
bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs;
bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size();
}
#endif
// See if all peers have cached path (is so, call can be fast). // See if all peers have cached path (is so, call can be fast).
bool has_all_peers = _send_confirm_path(from_path, psc, p_to); bool has_all_peers = _send_confirm_path(from_path, psc, p_to);
@ -615,6 +651,15 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
} }
if (!skip_rpc) { if (!skip_rpc) {
#ifdef DEBUG_ENABLED
if (profiling) {
ObjectID id = p_node->get_instance_id();
_init_node_profile(id);
profiler_frame_data[id].outgoing_rpc += 1;
}
#endif
_send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
} }
@ -709,6 +754,14 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
return; return;
} }
#ifdef DEBUG_ENABLED
if (profiling) {
ObjectID id = p_node->get_instance_id();
_init_node_profile(id);
profiler_frame_data[id].outgoing_rset += 1;
}
#endif
const Variant *vptr = &p_value; const Variant *vptr = &p_value;
_send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
@ -792,6 +845,96 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
return allow_object_decoding; return allow_object_decoding;
} }
void MultiplayerAPI::profiling_start() {
#ifdef DEBUG_ENABLED
profiling = true;
profiler_frame_data.clear();
bandwidth_incoming_pointer = 0;
bandwidth_incoming_data.resize(16384); // ~128kB
for (int i = 0; i < bandwidth_incoming_data.size(); ++i) {
bandwidth_incoming_data.write[i].packet_size = -1;
}
bandwidth_outgoing_pointer = 0;
bandwidth_outgoing_data.resize(16384); // ~128kB
for (int i = 0; i < bandwidth_outgoing_data.size(); ++i) {
bandwidth_outgoing_data.write[i].packet_size = -1;
}
#endif
}
void MultiplayerAPI::profiling_end() {
#ifdef DEBUG_ENABLED
profiling = false;
bandwidth_incoming_data.clear();
bandwidth_outgoing_data.clear();
#endif
}
int MultiplayerAPI::get_profiling_frame(ProfilingInfo *r_info) {
int i = 0;
#ifdef DEBUG_ENABLED
for (Map<ObjectID, ProfilingInfo>::Element *E = profiler_frame_data.front(); E; E = E->next()) {
r_info[i] = E->get();
++i;
}
profiler_frame_data.clear();
#endif
return i;
}
int MultiplayerAPI::get_incoming_bandwidth_usage() {
#ifdef DEBUG_ENABLED
return _get_bandwidth_usage(bandwidth_incoming_data, bandwidth_incoming_pointer);
#else
return 0;
#endif
}
int MultiplayerAPI::get_outgoing_bandwidth_usage() {
#ifdef DEBUG_ENABLED
return _get_bandwidth_usage(bandwidth_outgoing_data, bandwidth_outgoing_pointer);
#else
return 0;
#endif
}
#ifdef DEBUG_ENABLED
int MultiplayerAPI::_get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
int total_bandwidth = 0;
uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
uint32_t final_timestamp = timestamp - 1000;
int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
while (i != p_pointer && p_buffer[i].packet_size > 0) {
if (p_buffer[i].timestamp < final_timestamp) {
return total_bandwidth;
}
total_bandwidth += p_buffer[i].packet_size;
i = (i + p_buffer.size() - 1) % p_buffer.size();
}
ERR_EXPLAIN("Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
ERR_FAIL_COND_V(i == p_pointer, total_bandwidth);
return total_bandwidth;
}
void MultiplayerAPI::_init_node_profile(ObjectID p_node) {
if (profiler_frame_data.has(p_node))
return;
profiler_frame_data.insert(p_node, ProfilingInfo());
profiler_frame_data[p_node].node = p_node;
profiler_frame_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
profiler_frame_data[p_node].incoming_rpc = 0;
profiler_frame_data[p_node].incoming_rset = 0;
profiler_frame_data[p_node].outgoing_rpc = 0;
profiler_frame_data[p_node].outgoing_rset = 0;
}
#endif
void MultiplayerAPI::_bind_methods() { void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE));
@ -842,6 +985,9 @@ MultiplayerAPI::MultiplayerAPI() :
allow_object_decoding(false) { allow_object_decoding(false) {
rpc_sender_id = 0; rpc_sender_id = 0;
root_node = NULL; root_node = NULL;
#ifdef DEBUG_ENABLED
profiling = false;
#endif
clear(); clear();
} }

View file

@ -38,6 +38,16 @@ class MultiplayerAPI : public Reference {
GDCLASS(MultiplayerAPI, Reference); GDCLASS(MultiplayerAPI, Reference);
public:
struct ProfilingInfo {
ObjectID node;
String node_path;
int incoming_rpc;
int incoming_rset;
int outgoing_rpc;
int outgoing_rset;
};
private: private:
//path sent caches //path sent caches
struct PathSentCache { struct PathSentCache {
@ -55,6 +65,23 @@ private:
Map<int, NodeInfo> nodes; Map<int, NodeInfo> nodes;
}; };
#ifdef DEBUG_ENABLED
struct BandwidthFrame {
uint32_t timestamp;
int packet_size;
};
int bandwidth_incoming_pointer;
Vector<BandwidthFrame> bandwidth_incoming_data;
int bandwidth_outgoing_pointer;
Vector<BandwidthFrame> bandwidth_outgoing_data;
Map<ObjectID, ProfilingInfo> profiler_frame_data;
bool profiling;
void _init_node_profile(ObjectID p_node);
int _get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer);
#endif
Ref<NetworkedMultiplayerPeer> network_peer; Ref<NetworkedMultiplayerPeer> network_peer;
int rpc_sender_id; int rpc_sender_id;
Set<int> connected_peers; Set<int> connected_peers;
@ -130,6 +157,13 @@ public:
void set_allow_object_decoding(bool p_enable); void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const; bool is_object_decoding_allowed() const;
void profiling_start();
void profiling_end();
int get_profiling_frame(ProfilingInfo *r_info);
int get_incoming_bandwidth_usage();
int get_outgoing_bandwidth_usage();
MultiplayerAPI(); MultiplayerAPI();
~MultiplayerAPI(); ~MultiplayerAPI();
}; };

View file

@ -769,6 +769,14 @@ void ScriptDebuggerRemote::_poll_events() {
profiling = false; profiling = false;
_send_profiling_data(false); _send_profiling_data(false);
print_line("PROFILING END!"); print_line("PROFILING END!");
} else if (command == "start_network_profiling") {
multiplayer->profiling_start();
profiling_network = true;
} else if (command == "stop_network_profiling") {
multiplayer->profiling_end();
profiling_network = false;
} else if (command == "reload_scripts") { } else if (command == "reload_scripts") {
reload_all_scripts = true; reload_all_scripts = true;
} else if (command == "breakpoint") { } else if (command == "breakpoint") {
@ -918,6 +926,18 @@ void ScriptDebuggerRemote::idle_poll() {
} }
} }
if (profiling_network) {
uint64_t pt = OS::get_singleton()->get_ticks_msec();
if (pt - last_net_bandwidth_time > 200) {
last_net_bandwidth_time = pt;
_send_network_bandwidth_usage();
}
if (pt - last_net_prof_time > 100) {
last_net_prof_time = pt;
_send_network_profiling_data();
}
}
if (reload_all_scripts) { if (reload_all_scripts) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) { for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@ -929,6 +949,35 @@ void ScriptDebuggerRemote::idle_poll() {
_poll_events(); _poll_events();
} }
void ScriptDebuggerRemote::_send_network_profiling_data() {
ERR_FAIL_COND(multiplayer.is_null());
int n_nodes = multiplayer->get_profiling_frame(&network_profile_info.write[0]);
packet_peer_stream->put_var("network_profile");
packet_peer_stream->put_var(n_nodes * 6);
for (int i = 0; i < n_nodes; ++i) {
packet_peer_stream->put_var(network_profile_info[i].node);
packet_peer_stream->put_var(network_profile_info[i].node_path);
packet_peer_stream->put_var(network_profile_info[i].incoming_rpc);
packet_peer_stream->put_var(network_profile_info[i].incoming_rset);
packet_peer_stream->put_var(network_profile_info[i].outgoing_rpc);
packet_peer_stream->put_var(network_profile_info[i].outgoing_rset);
}
}
void ScriptDebuggerRemote::_send_network_bandwidth_usage() {
ERR_FAIL_COND(multiplayer.is_null());
int incoming_bandwidth = multiplayer->get_incoming_bandwidth_usage();
int outgoing_bandwidth = multiplayer->get_outgoing_bandwidth_usage();
packet_peer_stream->put_var("network_bandwidth");
packet_peer_stream->put_var(2);
packet_peer_stream->put_var(incoming_bandwidth);
packet_peer_stream->put_var(outgoing_bandwidth);
}
void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) { void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) {
mutex->lock(); mutex->lock();
@ -1068,6 +1117,10 @@ void ScriptDebuggerRemote::set_live_edit_funcs(LiveEditFuncs *p_funcs) {
live_edit_funcs = p_funcs; live_edit_funcs = p_funcs;
} }
void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
multiplayer = p_multiplayer;
}
bool ScriptDebuggerRemote::is_profiling() const { bool ScriptDebuggerRemote::is_profiling() const {
return profiling; return profiling;
@ -1117,12 +1170,15 @@ ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_fun
ScriptDebuggerRemote::ScriptDebuggerRemote() : ScriptDebuggerRemote::ScriptDebuggerRemote() :
profiling(false), profiling(false),
profiling_network(false),
max_frame_functions(16), max_frame_functions(16),
skip_profile_frame(false), skip_profile_frame(false),
reload_all_scripts(false), reload_all_scripts(false),
tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))), tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))), packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
last_perf_time(0), last_perf_time(0),
last_net_prof_time(0),
last_net_bandwidth_time(0),
performance(Engine::get_singleton()->get_singleton_object("Performance")), performance(Engine::get_singleton()->get_singleton_object("Performance")),
requested_quit(false), requested_quit(false),
mutex(Mutex::create()), mutex(Mutex::create()),
@ -1154,6 +1210,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
add_error_handler(&eh); add_error_handler(&eh);
profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions")); profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
network_profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
profile_info_ptrs.resize(profile_info.size()); profile_info_ptrs.resize(profile_info.size());
} }

View file

@ -54,11 +54,13 @@ class ScriptDebuggerRemote : public ScriptDebugger {
Vector<ScriptLanguage::ProfilingInfo> profile_info; Vector<ScriptLanguage::ProfilingInfo> profile_info;
Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs; Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
Map<StringName, int> profiler_function_signature_map; Map<StringName, int> profiler_function_signature_map;
float frame_time, idle_time, physics_time, physics_frame_time; float frame_time, idle_time, physics_time, physics_frame_time;
bool profiling; bool profiling;
bool profiling_network;
int max_frame_functions; int max_frame_functions;
bool skip_profile_frame; bool skip_profile_frame;
bool reload_all_scripts; bool reload_all_scripts;
@ -67,6 +69,8 @@ class ScriptDebuggerRemote : public ScriptDebugger {
Ref<PacketPeerStream> packet_peer_stream; Ref<PacketPeerStream> packet_peer_stream;
uint64_t last_perf_time; uint64_t last_perf_time;
uint64_t last_net_prof_time;
uint64_t last_net_bandwidth_time;
Object *performance; Object *performance;
bool requested_quit; bool requested_quit;
Mutex *mutex; Mutex *mutex;
@ -123,10 +127,14 @@ class ScriptDebuggerRemote : public ScriptDebugger {
void _send_video_memory(); void _send_video_memory();
LiveEditFuncs *live_edit_funcs; LiveEditFuncs *live_edit_funcs;
Ref<MultiplayerAPI> multiplayer;
ErrorHandlerList eh; ErrorHandlerList eh;
static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
void _send_profiling_data(bool p_for_frame); void _send_profiling_data(bool p_for_frame);
void _send_network_profiling_data();
void _send_network_bandwidth_usage();
struct FrameData { struct FrameData {
@ -170,6 +178,7 @@ public:
virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata); virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata);
virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs); virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs);
virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
virtual bool is_profiling() const; virtual bool is_profiling() const;
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data); virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);

View file

@ -482,6 +482,7 @@ public:
virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata) {} virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata) {}
virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs) {} virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs) {}
virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {}
virtual bool is_profiling() const = 0; virtual bool is_profiling() const = 0;
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) = 0; virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) = 0;

View file

@ -0,0 +1,208 @@
/*************************************************************************/
/* editor_network_profiler.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 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 "editor_network_profiler.h"
#include "core/os/os.h"
#include "editor_scale.h"
#include "editor_settings.h"
void EditorNetworkProfiler::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_frame"), &EditorNetworkProfiler::_update_frame);
ClassDB::bind_method(D_METHOD("_activate_pressed"), &EditorNetworkProfiler::_activate_pressed);
ClassDB::bind_method(D_METHOD("_clear_pressed"), &EditorNetworkProfiler::_clear_pressed);
ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
}
void EditorNetworkProfiler::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
activate->set_icon(get_icon("Play", "EditorIcons"));
clear_button->set_icon(get_icon("Clear", "EditorIcons"));
}
}
void EditorNetworkProfiler::_update_frame() {
counters_display->clear();
TreeItem *root = counters_display->create_item();
for (Map<ObjectID, MultiplayerAPI::ProfilingInfo>::Element *E = nodes_data.front(); E; E = E->next()) {
TreeItem *node = counters_display->create_item(root);
for (int j = 0; j < counters_display->get_columns(); ++j) {
node->set_text_align(j, j > 0 ? TreeItem::ALIGN_RIGHT : TreeItem::ALIGN_LEFT);
}
node->set_text(0, E->get().node_path);
node->set_text(1, E->get().incoming_rpc == 0 ? "-" : itos(E->get().incoming_rpc));
node->set_text(2, E->get().incoming_rset == 0 ? "-" : itos(E->get().incoming_rset));
node->set_text(3, E->get().outgoing_rpc == 0 ? "-" : itos(E->get().outgoing_rpc));
node->set_text(4, E->get().outgoing_rset == 0 ? "-" : itos(E->get().outgoing_rset));
}
}
void EditorNetworkProfiler::_activate_pressed() {
if (activate->is_pressed()) {
activate->set_icon(get_icon("Stop", "EditorIcons"));
activate->set_text(TTR("Stop"));
} else {
activate->set_icon(get_icon("Play", "EditorIcons"));
activate->set_text(TTR("Start"));
}
emit_signal("enable_profiling", activate->is_pressed());
}
void EditorNetworkProfiler::_clear_pressed() {
nodes_data.clear();
set_bandwidth(0, 0);
if (frame_delay->is_stopped()) {
frame_delay->set_wait_time(0.1);
frame_delay->start();
}
}
String EditorNetworkProfiler::_format_bandwidth(int p_value) {
String unit = "B";
float v = p_value;
if (v > 1073741824.0) {
unit = "GiB";
v /= 1073741824.0;
} else if (v > 1048576.0) {
unit = "MiB";
v /= 1048576.0;
} else if (v > 1024.0) {
unit = "KiB";
v /= 1024.0;
}
return vformat("%.1f %s/s", v, unit);
}
void EditorNetworkProfiler::add_node_frame_data(const MultiplayerAPI::ProfilingInfo 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()) {
frame_delay->set_wait_time(0.1);
frame_delay->start();
}
}
void EditorNetworkProfiler::set_bandwidth(int p_incoming, int p_outgoing) {
incoming_bandwidth_text->set_text(_format_bandwidth(p_incoming));
outgoing_bandwidth_text->set_text(_format_bandwidth(p_outgoing));
}
bool EditorNetworkProfiler::is_profiling() {
return activate->is_pressed();
}
EditorNetworkProfiler::EditorNetworkProfiler() {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_constant_override("separation", 8 * EDSCALE);
add_child(hb);
activate = memnew(Button);
activate->set_toggle_mode(true);
activate->set_text(TTR("Start"));
activate->connect("pressed", this, "_activate_pressed");
hb->add_child(activate);
clear_button = memnew(Button);
clear_button->set_text(TTR("Clear"));
clear_button->connect("pressed", this, "_clear_pressed");
hb->add_child(clear_button);
hb->add_spacer();
Label *lb = memnew(Label);
lb->set_text("Down ");
hb->add_child(lb);
incoming_bandwidth_text = memnew(LineEdit);
incoming_bandwidth_text->set_editable(false);
incoming_bandwidth_text->set_custom_minimum_size(Size2(100, 0));
incoming_bandwidth_text->set_align(LineEdit::Align::ALIGN_RIGHT);
incoming_bandwidth_text->set_text("0.0 B/s");
hb->add_child(incoming_bandwidth_text);
lb = memnew(Label);
lb->set_text("Up ");
hb->add_child(lb);
outgoing_bandwidth_text = memnew(LineEdit);
outgoing_bandwidth_text->set_editable(false);
outgoing_bandwidth_text->set_custom_minimum_size(Size2(100, 0));
outgoing_bandwidth_text->set_align(LineEdit::Align::ALIGN_RIGHT);
outgoing_bandwidth_text->set_text("0.0 B/s");
hb->add_child(outgoing_bandwidth_text);
counters_display = memnew(Tree);
counters_display->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
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_column_titles_visible(true);
counters_display->set_column_title(0, TTR("Node"));
counters_display->set_column_expand(0, true);
counters_display->set_column_min_width(0, 60);
counters_display->set_column_title(1, TTR("Incoming RPC"));
counters_display->set_column_expand(1, false);
counters_display->set_column_min_width(1, 120 * EDSCALE);
counters_display->set_column_title(2, TTR("Incoming RSET"));
counters_display->set_column_expand(2, false);
counters_display->set_column_min_width(2, 120 * EDSCALE);
counters_display->set_column_title(3, TTR("Outgoing RPC"));
counters_display->set_column_expand(3, false);
counters_display->set_column_min_width(3, 120 * EDSCALE);
counters_display->set_column_title(4, TTR("Outgoing RSET"));
counters_display->set_column_expand(4, false);
counters_display->set_column_min_width(4, 120 * EDSCALE);
add_child(counters_display);
frame_delay = memnew(Timer);
frame_delay->set_wait_time(0.1);
frame_delay->set_one_shot(true);
add_child(frame_delay);
frame_delay->connect("timeout", this, "_update_frame");
}

View file

@ -0,0 +1,73 @@
/*************************************************************************/
/* editor_network_profiler.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 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 EDITORNETWORKPROFILER_H
#define EDITORNETWORKPROFILER_H
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tree.h"
class EditorNetworkProfiler : public VBoxContainer {
GDCLASS(EditorNetworkProfiler, VBoxContainer)
private:
Button *activate;
Button *clear_button;
Tree *counters_display;
LineEdit *incoming_bandwidth_text;
LineEdit *outgoing_bandwidth_text;
Timer *frame_delay;
Map<ObjectID, MultiplayerAPI::ProfilingInfo> nodes_data;
void _update_frame();
void _activate_pressed();
void _clear_pressed();
String _format_bandwidth(int p_value);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void add_node_frame_data(const MultiplayerAPI::ProfilingInfo p_frame);
void set_bandwidth(int p_incoming, int p_outgoing);
bool is_profiling();
EditorNetworkProfiler();
};
#endif //EDITORNETWORKPROFILER_H

View file

@ -33,6 +33,7 @@
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/project_settings.h" #include "core/project_settings.h"
#include "core/ustring.h" #include "core/ustring.h"
#include "editor_network_profiler.h"
#include "editor_node.h" #include "editor_node.h"
#include "editor_profiler.h" #include "editor_profiler.h"
#include "editor_settings.h" #include "editor_settings.h"
@ -992,7 +993,20 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
profiler->add_frame_metric(metric, false); profiler->add_frame_metric(metric, false);
else else
profiler->add_frame_metric(metric, true); profiler->add_frame_metric(metric, true);
} else if (p_msg == "network_profile") {
int frame_size = 6;
for (int i = 0; i < p_data.size(); i += frame_size) {
MultiplayerAPI::ProfilingInfo pi;
pi.node = p_data[i + 0];
pi.node_path = p_data[i + 1];
pi.incoming_rpc = p_data[i + 2];
pi.incoming_rset = p_data[i + 3];
pi.outgoing_rpc = p_data[i + 4];
pi.outgoing_rset = p_data[i + 5];
network_profiler->add_node_frame_data(pi);
}
} else if (p_msg == "network_bandwidth") {
network_profiler->set_bandwidth(p_data[0], p_data[1]);
} else if (p_msg == "kill_me") { } else if (p_msg == "kill_me") {
editor->call_deferred("stop_child_process"); editor->call_deferred("stop_child_process");
@ -1208,6 +1222,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
if (profiler->is_profiling()) { if (profiler->is_profiling()) {
_profiler_activate(true); _profiler_activate(true);
} }
if (network_profiler->is_profiling()) {
_network_profiler_activate(true);
}
} }
} }
@ -1427,6 +1445,25 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable) {
} }
} }
void ScriptEditorDebugger::_network_profiler_activate(bool p_enable) {
if (!connection.is_valid())
return;
if (p_enable) {
Array msg;
msg.push_back("start_network_profiling");
ppeer->put_var(msg);
print_verbose("Starting network profiling.");
} else {
Array msg;
msg.push_back("stop_network_profiling");
ppeer->put_var(msg);
print_verbose("Ending network profiling.");
}
}
void ScriptEditorDebugger::_profiler_seeked() { void ScriptEditorDebugger::_profiler_seeked() {
if (!connection.is_valid() || !connection->is_connected_to_host()) if (!connection.is_valid() || !connection->is_connected_to_host())
@ -2020,6 +2057,7 @@ void ScriptEditorDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("_expand_errors_list"), &ScriptEditorDebugger::_expand_errors_list); ClassDB::bind_method(D_METHOD("_expand_errors_list"), &ScriptEditorDebugger::_expand_errors_list);
ClassDB::bind_method(D_METHOD("_collapse_errors_list"), &ScriptEditorDebugger::_collapse_errors_list); ClassDB::bind_method(D_METHOD("_collapse_errors_list"), &ScriptEditorDebugger::_collapse_errors_list);
ClassDB::bind_method(D_METHOD("_profiler_activate"), &ScriptEditorDebugger::_profiler_activate); ClassDB::bind_method(D_METHOD("_profiler_activate"), &ScriptEditorDebugger::_profiler_activate);
ClassDB::bind_method(D_METHOD("_network_profiler_activate"), &ScriptEditorDebugger::_network_profiler_activate);
ClassDB::bind_method(D_METHOD("_profiler_seeked"), &ScriptEditorDebugger::_profiler_seeked); ClassDB::bind_method(D_METHOD("_profiler_seeked"), &ScriptEditorDebugger::_profiler_seeked);
ClassDB::bind_method(D_METHOD("_clear_errors_list"), &ScriptEditorDebugger::_clear_errors_list); ClassDB::bind_method(D_METHOD("_clear_errors_list"), &ScriptEditorDebugger::_clear_errors_list);
@ -2245,6 +2283,13 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
profiler->connect("break_request", this, "_profiler_seeked"); profiler->connect("break_request", this, "_profiler_seeked");
} }
{ //network profiler
network_profiler = memnew(EditorNetworkProfiler);
network_profiler->set_name(TTR("Network Profiler"));
tabs->add_child(network_profiler);
network_profiler->connect("enable_profiling", this, "_network_profiler_activate");
}
{ //monitors { //monitors
HSplitContainer *hsp = memnew(HSplitContainer); HSplitContainer *hsp = memnew(HSplitContainer);

View file

@ -50,6 +50,7 @@ class TreeItem;
class HSplitContainer; class HSplitContainer;
class ItemList; class ItemList;
class EditorProfiler; class EditorProfiler;
class EditorNetworkProfiler;
class ScriptEditorDebuggerInspectedObject; class ScriptEditorDebuggerInspectedObject;
@ -155,6 +156,7 @@ class ScriptEditorDebugger : public Control {
Map<String, int> res_path_cache; Map<String, int> res_path_cache;
EditorProfiler *profiler; EditorProfiler *profiler;
EditorNetworkProfiler *network_profiler;
EditorNode *editor; EditorNode *editor;
@ -199,6 +201,8 @@ class ScriptEditorDebugger : public Control {
void _profiler_activate(bool p_enable); void _profiler_activate(bool p_enable);
void _profiler_seeked(); void _profiler_seeked();
void _network_profiler_activate(bool p_enable);
void _paused(); void _paused();
void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj); void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj);

View file

@ -2096,6 +2096,7 @@ SceneTree::SceneTree() {
if (ScriptDebugger::get_singleton()) { if (ScriptDebugger::get_singleton()) {
ScriptDebugger::get_singleton()->set_request_scene_tree_message_func(_debugger_request_tree, this); ScriptDebugger::get_singleton()->set_request_scene_tree_message_func(_debugger_request_tree, this);
ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
} }
root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true)); root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true));