Add network profiler
This commit is contained in:
parent
979e772947
commit
8244f535cd
|
@ -33,6 +33,10 @@
|
|||
#include "core/io/marshalls.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) {
|
||||
|
||||
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(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];
|
||||
|
||||
switch (packet_type) {
|
||||
|
@ -284,6 +296,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
|
|||
|
||||
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++) {
|
||||
|
||||
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);
|
||||
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;
|
||||
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).
|
||||
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) {
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
@ -709,6 +754,14 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
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;
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
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() {
|
||||
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));
|
||||
|
@ -842,6 +985,9 @@ MultiplayerAPI::MultiplayerAPI() :
|
|||
allow_object_decoding(false) {
|
||||
rpc_sender_id = 0;
|
||||
root_node = NULL;
|
||||
#ifdef DEBUG_ENABLED
|
||||
profiling = false;
|
||||
#endif
|
||||
clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,16 @@ class MultiplayerAPI : public 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:
|
||||
//path sent caches
|
||||
struct PathSentCache {
|
||||
|
@ -55,6 +65,23 @@ private:
|
|||
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;
|
||||
int rpc_sender_id;
|
||||
Set<int> connected_peers;
|
||||
|
@ -130,6 +157,13 @@ public:
|
|||
void set_allow_object_decoding(bool p_enable);
|
||||
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();
|
||||
};
|
||||
|
|
|
@ -764,6 +764,14 @@ void ScriptDebuggerRemote::_poll_events() {
|
|||
profiling = false;
|
||||
_send_profiling_data(false);
|
||||
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") {
|
||||
reload_all_scripts = true;
|
||||
} else if (command == "breakpoint") {
|
||||
|
@ -911,6 +919,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) {
|
||||
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
|
@ -922,6 +942,35 @@ void ScriptDebuggerRemote::idle_poll() {
|
|||
_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) {
|
||||
|
||||
mutex->lock();
|
||||
|
@ -1061,6 +1110,10 @@ void ScriptDebuggerRemote::set_live_edit_funcs(LiveEditFuncs *p_funcs) {
|
|||
live_edit_funcs = p_funcs;
|
||||
}
|
||||
|
||||
void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
|
||||
multiplayer = p_multiplayer;
|
||||
}
|
||||
|
||||
bool ScriptDebuggerRemote::is_profiling() const {
|
||||
|
||||
return profiling;
|
||||
|
@ -1106,12 +1159,15 @@ ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_fun
|
|||
|
||||
ScriptDebuggerRemote::ScriptDebuggerRemote() :
|
||||
profiling(false),
|
||||
profiling_network(false),
|
||||
max_frame_functions(16),
|
||||
skip_profile_frame(false),
|
||||
reload_all_scripts(false),
|
||||
tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
|
||||
packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
|
||||
last_perf_time(0),
|
||||
last_net_prof_time(0),
|
||||
last_net_bandwidth_time(0),
|
||||
performance(Engine::get_singleton()->get_singleton_object("Performance")),
|
||||
requested_quit(false),
|
||||
mutex(Mutex::create()),
|
||||
|
@ -1143,6 +1199,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
|
|||
add_error_handler(&eh);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -54,11 +54,13 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
|||
|
||||
Vector<ScriptLanguage::ProfilingInfo> profile_info;
|
||||
Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
|
||||
Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
|
||||
|
||||
Map<StringName, int> profiler_function_signature_map;
|
||||
float frame_time, idle_time, physics_time, physics_frame_time;
|
||||
|
||||
bool profiling;
|
||||
bool profiling_network;
|
||||
int max_frame_functions;
|
||||
bool skip_profile_frame;
|
||||
bool reload_all_scripts;
|
||||
|
@ -67,6 +69,8 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
|||
Ref<PacketPeerStream> packet_peer_stream;
|
||||
|
||||
uint64_t last_perf_time;
|
||||
uint64_t last_net_prof_time;
|
||||
uint64_t last_net_bandwidth_time;
|
||||
Object *performance;
|
||||
bool requested_quit;
|
||||
Mutex *mutex;
|
||||
|
@ -123,10 +127,14 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
|||
void _send_video_memory();
|
||||
LiveEditFuncs *live_edit_funcs;
|
||||
|
||||
Ref<MultiplayerAPI> multiplayer;
|
||||
|
||||
ErrorHandlerList eh;
|
||||
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_network_profiling_data();
|
||||
void _send_network_bandwidth_usage();
|
||||
|
||||
struct FrameData {
|
||||
|
||||
|
@ -168,6 +176,7 @@ public:
|
|||
|
||||
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_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
|
||||
|
||||
virtual bool is_profiling() const;
|
||||
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);
|
||||
|
|
|
@ -476,6 +476,7 @@ public:
|
|||
|
||||
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_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {}
|
||||
|
||||
virtual bool is_profiling() const = 0;
|
||||
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) = 0;
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/io/marshalls.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/ustring.h"
|
||||
#include "editor_network_profiler.h"
|
||||
#include "editor_node.h"
|
||||
#include "editor_profiler.h"
|
||||
#include "editor_settings.h"
|
||||
|
@ -977,7 +978,20 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
profiler->add_frame_metric(metric, false);
|
||||
else
|
||||
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") {
|
||||
|
||||
editor->call_deferred("stop_child_process");
|
||||
|
@ -1193,6 +1207,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
if (profiler->is_profiling()) {
|
||||
_profiler_activate(true);
|
||||
}
|
||||
|
||||
if (network_profiler->is_profiling()) {
|
||||
_network_profiler_activate(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1412,6 +1430,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() {
|
||||
|
||||
if (!connection.is_valid() || !connection->is_connected_to_host())
|
||||
|
@ -2000,6 +2037,7 @@ void ScriptEditorDebugger::_bind_methods() {
|
|||
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("_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("_clear_errors_list"), &ScriptEditorDebugger::_clear_errors_list);
|
||||
|
||||
|
@ -2218,6 +2256,13 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
|||
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
|
||||
|
||||
HSplitContainer *hsp = memnew(HSplitContainer);
|
||||
|
|
|
@ -50,6 +50,7 @@ class TreeItem;
|
|||
class HSplitContainer;
|
||||
class ItemList;
|
||||
class EditorProfiler;
|
||||
class EditorNetworkProfiler;
|
||||
|
||||
class ScriptEditorDebuggerInspectedObject;
|
||||
|
||||
|
@ -152,6 +153,7 @@ class ScriptEditorDebugger : public Control {
|
|||
Map<String, int> res_path_cache;
|
||||
|
||||
EditorProfiler *profiler;
|
||||
EditorNetworkProfiler *network_profiler;
|
||||
|
||||
EditorNode *editor;
|
||||
|
||||
|
@ -196,6 +198,8 @@ class ScriptEditorDebugger : public Control {
|
|||
void _profiler_activate(bool p_enable);
|
||||
void _profiler_seeked();
|
||||
|
||||
void _network_profiler_activate(bool p_enable);
|
||||
|
||||
void _paused();
|
||||
|
||||
void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj);
|
||||
|
|
|
@ -2063,6 +2063,7 @@ SceneTree::SceneTree() {
|
|||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
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));
|
||||
|
|
Loading…
Reference in New Issue