diff options
Diffstat (limited to 'editor/debugger')
-rw-r--r-- | editor/debugger/editor_debugger_inspector.cpp | 14 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_inspector.h | 7 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_node.cpp | 75 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_node.h | 12 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_server.cpp | 4 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_server.h | 6 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_tree.cpp | 23 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_tree.h | 5 | ||||
-rw-r--r-- | editor/debugger/editor_network_profiler.cpp | 12 | ||||
-rw-r--r-- | editor/debugger/editor_network_profiler.h | 4 | ||||
-rw-r--r-- | editor/debugger/editor_performance_profiler.cpp | 395 | ||||
-rw-r--r-- | editor/debugger/editor_performance_profiler.h | 90 | ||||
-rw-r--r-- | editor/debugger/editor_profiler.cpp | 16 | ||||
-rw-r--r-- | editor/debugger/editor_profiler.h | 31 | ||||
-rw-r--r-- | editor/debugger/editor_visual_profiler.cpp | 31 | ||||
-rw-r--r-- | editor/debugger/editor_visual_profiler.h | 12 | ||||
-rw-r--r-- | editor/debugger/script_editor_debugger.cpp | 334 | ||||
-rw-r--r-- | editor/debugger/script_editor_debugger.h | 33 |
18 files changed, 796 insertions, 308 deletions
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 125439d09b..6035cc072e 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -153,12 +153,9 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { if (path.find("::") != -1) { // built-in resource String base_path = path.get_slice("::", 0); - if (ResourceLoader::get_resource_type(base_path) == "PackedScene") { - if (!EditorNode::get_singleton()->is_scene_open(base_path)) { - EditorNode::get_singleton()->load_scene(base_path); - } - } else { - EditorNode::get_singleton()->load_resource(base_path); + RES dependency = ResourceLoader::load(base_path); + if (dependency.is_valid()) { + remote_dependencies.insert(dependency); } } var = ResourceLoader::load(path); @@ -211,6 +208,7 @@ void EditorDebuggerInspector::clear_cache() { memdelete(E->value()); } remote_objects.clear(); + remote_dependencies.clear(); } Object *EditorDebuggerInspector::get_object(ObjectID p_id) { diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index 638dee3c3f..6648c99c03 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -58,7 +58,7 @@ public: prop_values.clear(); } - void update() { _change_notify(); } + void update() { notify_property_list_changed(); } EditorDebuggerRemoteObject() {} }; @@ -69,6 +69,7 @@ class EditorDebuggerInspector : public EditorInspector { private: ObjectID inspected_object_id; Map<ObjectID, EditorDebuggerRemoteObject *> remote_objects; + Set<RES> remote_dependencies; EditorDebuggerRemoteObject *variables; void _object_selected(ObjectID p_object); diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index a9c18138d8..3ef9548727 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -34,6 +34,7 @@ #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "scene/gui/menu_button.h" #include "scene/gui/tab_container.h" @@ -54,8 +55,8 @@ EditorDebuggerNode::EditorDebuggerNode() { singleton = this; } - add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_LEFT)); - add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_RIGHT)); + add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(SIDE_LEFT)); + add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(SIDE_RIGHT)); tabs = memnew(TabContainer); tabs->set_tab_align(TabContainer::ALIGN_LEFT); @@ -114,6 +115,12 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() { tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("DebuggerPanel", "EditorStyles")); } + if (!debugger_plugins.is_empty()) { + for (Set<Ref<Script>>::Element *i = debugger_plugins.front(); i; i = i->next()) { + node->add_debugger_plugin(i->get()); + } + } + return node; } @@ -133,7 +140,7 @@ void EditorDebuggerNode::_error_selected(const String &p_file, int p_line, int p void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_debugger) { const String file = p_debugger->get_stack_script_file(); - if (file.empty()) { + if (file.is_empty()) { return; } stack_script = ResourceLoader::load(file); @@ -219,12 +226,15 @@ void EditorDebuggerNode::_notification(int p_what) { switch (p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { if (tabs->get_tab_count() > 1) { - add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_LEFT)); - add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_RIGHT)); + add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(SIDE_LEFT)); + add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(SIDE_RIGHT)); tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("DebuggerPanel", "EditorStyles")); } } break; + case NOTIFICATION_READY: { + _update_debug_options(); + } break; default: break; } @@ -378,7 +388,7 @@ void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) { p->add_shortcut(ED_GET_SHORTCUT("debugger/break"), DEBUG_BREAK); p->add_shortcut(ED_GET_SHORTCUT("debugger/continue"), DEBUG_CONTINUE); p->add_separator(); - p->add_check_shortcut(ED_GET_SHORTCUT("debugger/keep_debugger_open"), DEBUG_SHOW_KEEP_OPEN); + p->add_check_shortcut(ED_GET_SHORTCUT("debugger/keep_debugger_open"), DEBUG_KEEP_DEBUGGER_OPEN); p->add_check_shortcut(ED_GET_SHORTCUT("debugger/debug_with_external_editor"), DEBUG_WITH_EXTERNAL_EDITOR); p->connect("id_pressed", callable_mp(this, &EditorDebuggerNode::_menu_option)); @@ -418,20 +428,33 @@ void EditorDebuggerNode::_menu_option(int p_id) { case DEBUG_CONTINUE: { debug_continue(); } break; - - case DEBUG_SHOW_KEEP_OPEN: { - bool visible = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN)); - hide_on_stop = visible; - script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible); + case DEBUG_KEEP_DEBUGGER_OPEN: { + bool ischecked = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_KEEP_DEBUGGER_OPEN)); + hide_on_stop = ischecked; + script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_KEEP_DEBUGGER_OPEN), !ischecked); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "keep_debugger_open", !ischecked); } break; case DEBUG_WITH_EXTERNAL_EDITOR: { - bool checked = !script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR)); - debug_with_external_editor = checked; - script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), checked); + bool ischecked = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR)); + debug_with_external_editor = !ischecked; + script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), !ischecked); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "debug_with_external_editor", !ischecked); } break; } } +void EditorDebuggerNode::_update_debug_options() { + bool keep_debugger_open = EditorSettings::get_singleton()->get_project_metadata("debug_options", "keep_debugger_open", false); + bool debug_with_external_editor = EditorSettings::get_singleton()->get_project_metadata("debug_options", "debug_with_external_editor", false); + + if (keep_debugger_open) { + _menu_option(DEBUG_KEEP_DEBUGGER_OPEN); + } + if (debug_with_external_editor) { + _menu_option(DEBUG_WITH_EXTERNAL_EDITOR); + } +} + void EditorDebuggerNode::_paused() { const bool paused = EditorNode::get_singleton()->get_pause_button()->is_pressed(); _for_all(tabs, [&](ScriptEditorDebugger *dbg) { @@ -618,3 +641,23 @@ void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const No dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos); }); } + +void EditorDebuggerNode::add_debugger_plugin(const Ref<Script> &p_script) { + ERR_FAIL_COND_MSG(debugger_plugins.has(p_script), "Debugger plugin already exists."); + ERR_FAIL_COND_MSG(p_script.is_null(), "Debugger plugin script is null"); + ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) == "", "Debugger plugin script has error."); + ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) != "EditorDebuggerPlugin", "Base type of debugger plugin is not 'EditorDebuggerPlugin'."); + ERR_FAIL_COND_MSG(!p_script->is_tool(), "Debugger plugin script is not in tool mode."); + debugger_plugins.insert(p_script); + for (int i = 0; get_debugger(i); i++) { + get_debugger(i)->add_debugger_plugin(p_script); + } +} + +void EditorDebuggerNode::remove_debugger_plugin(const Ref<Script> &p_script) { + ERR_FAIL_COND_MSG(!debugger_plugins.has(p_script), "Debugger plugin doesn't exists."); + debugger_plugins.erase(p_script); + for (int i = 0; get_debugger(i); i++) { + get_debugger(i)->remove_debugger_plugin(p_script); + } +} diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h index ff9601c026..3510ac0726 100644 --- a/editor/debugger/editor_debugger_node.h +++ b/editor/debugger/editor_debugger_node.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -60,7 +60,7 @@ private: DEBUG_STEP, DEBUG_BREAK, DEBUG_CONTINUE, - DEBUG_SHOW_KEEP_OPEN, + DEBUG_KEEP_DEBUGGER_OPEN, DEBUG_WITH_EXTERNAL_EDITOR, }; @@ -103,6 +103,8 @@ private: CameraOverride camera_override = OVERRIDE_NONE; Map<Breakpoint, bool> breakpoints; + Set<Ref<Script>> debugger_plugins; + ScriptEditorDebugger *_add_debugger(); EditorDebuggerRemoteObject *get_inspected_remote_object(); @@ -131,6 +133,7 @@ protected: void _paused(); void _break_state_changed(); void _menu_option(int p_id); + void _update_debug_options(); protected: void _notification(int p_what); @@ -186,5 +189,8 @@ public: Error start(const String &p_protocol = "tcp://"); void stop(); + + void add_debugger_plugin(const Ref<Script> &p_script); + void remove_debugger_plugin(const Ref<Script> &p_script); }; #endif // EDITOR_DEBUGGER_NODE_H diff --git a/editor/debugger/editor_debugger_server.cpp b/editor/debugger/editor_debugger_server.cpp index 0b655044a8..4add891bcb 100644 --- a/editor/debugger/editor_debugger_server.cpp +++ b/editor/debugger/editor_debugger_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ diff --git a/editor/debugger/editor_debugger_server.h b/editor/debugger/editor_debugger_server.h index 10a9a232ab..6458421e7a 100644 --- a/editor/debugger/editor_debugger_server.h +++ b/editor/debugger/editor_debugger_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -32,7 +32,7 @@ #define EDITOR_DEBUGGER_CONNECTION_H #include "core/debugger/remote_debugger_peer.h" -#include "core/reference.h" +#include "core/object/reference.h" class EditorDebuggerServer : public Reference { public: diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index 0b5f865a98..ec92edc795 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -129,6 +129,8 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int updating_scene_tree = true; const String last_path = get_selected_path(); const String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter(); + bool filter_changed = filter != last_filter; + TreeItem *scroll_item = nullptr; // Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion. List<Pair<TreeItem *, int>> parents; @@ -162,11 +164,17 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int if (debugger_id == p_debugger) { // Can use remote id. if (node.id == inspected_object_id) { item->select(0); + if (filter_changed) { + scroll_item = item; + } } } else { // Must use path if (last_path == _get_path(item)) { updating_scene_tree = false; // Force emission of new selection item->select(0); + if (filter_changed) { + scroll_item = item; + } updating_scene_tree = true; } } @@ -183,6 +191,9 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int } parent->remove_child(item); memdelete(item); + if (scroll_item == item) { + scroll_item = nullptr; + } if (had_siblings) { break; // Parent must survive. } @@ -199,6 +210,10 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int } } debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree + if (scroll_item) { + call_deferred("scroll_to_item", scroll_item); + } + last_filter = filter; updating_scene_tree = false; } @@ -238,11 +253,11 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) { file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); } - file_dialog->popup_centered_ratio(); + file_dialog->popup_file_dialog(); } break; case ITEM_MENU_COPY_NODE_PATH: { String text = get_selected_path(); - if (text.empty()) { + if (text.is_empty()) { return; } else if (text == "/root") { text = "."; diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h index 5ec1423c07..13193344f1 100644 --- a/editor/debugger/editor_debugger_tree.h +++ b/editor/debugger/editor_debugger_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -51,6 +51,7 @@ private: Set<ObjectID> unfold_cache; PopupMenu *item_menu = nullptr; EditorFileDialog *file_dialog = nullptr; + String last_filter; String _get_path(TreeItem *p_item); void _scene_tree_folded(Object *p_obj); diff --git a/editor/debugger/editor_network_profiler.cpp b/editor/debugger/editor_network_profiler.cpp index baa88bcdbc..2d57dff69d 100644 --- a/editor/debugger/editor_network_profiler.cpp +++ b/editor/debugger/editor_network_profiler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -46,8 +46,8 @@ void EditorNetworkProfiler::_notification(int p_what) { outgoing_bandwidth_text->set_right_icon(get_theme_icon("ArrowUp", "EditorIcons")); // This needs to be done here to set the faded color when the profiler is first opened - incoming_bandwidth_text->add_theme_color_override("font_color_uneditable", get_theme_color("font_color", "Editor") * Color(1, 1, 1, 0.5)); - outgoing_bandwidth_text->add_theme_color_override("font_color_uneditable", get_theme_color("font_color", "Editor") * Color(1, 1, 1, 0.5)); + incoming_bandwidth_text->add_theme_color_override("font_uneditable_color", get_theme_color("font_color", "Editor") * Color(1, 1, 1, 0.5)); + outgoing_bandwidth_text->add_theme_color_override("font_uneditable_color", get_theme_color("font_color", "Editor") * Color(1, 1, 1, 0.5)); } } @@ -113,10 +113,10 @@ void EditorNetworkProfiler::set_bandwidth(int p_incoming, int p_outgoing) { // Make labels more prominent when the bandwidth is greater than 0 to attract user attention incoming_bandwidth_text->add_theme_color_override( - "font_color_uneditable", + "font_uneditable_color", get_theme_color("font_color", "Editor") * Color(1, 1, 1, p_incoming > 0 ? 1 : 0.5)); outgoing_bandwidth_text->add_theme_color_override( - "font_color_uneditable", + "font_uneditable_color", get_theme_color("font_color", "Editor") * Color(1, 1, 1, p_outgoing > 0 ? 1 : 0.5)); } diff --git a/editor/debugger/editor_network_profiler.h b/editor/debugger/editor_network_profiler.h index cf65fb5316..8c1da1cb2d 100644 --- a/editor/debugger/editor_network_profiler.h +++ b/editor/debugger/editor_network_profiler.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp new file mode 100644 index 0000000000..33d08a2f6b --- /dev/null +++ b/editor/debugger/editor_performance_profiler.cpp @@ -0,0 +1,395 @@ +/*************************************************************************/ +/* editor_performance_profiler.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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_performance_profiler.h" + +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "main/performance.h" + +EditorPerformanceProfiler::Monitor::Monitor() {} + +EditorPerformanceProfiler::Monitor::Monitor(String p_name, String p_base, int p_frame_index, Performance::MonitorType p_type, TreeItem *p_item) { + type = p_type; + item = p_item; + frame_index = p_frame_index; + name = p_name; + base = p_base; +} + +void EditorPerformanceProfiler::Monitor::update_value(float p_value) { + ERR_FAIL_COND(!item); + String label = EditorPerformanceProfiler::_create_label(p_value, type); + String tooltip = label; + switch (type) { + case Performance::MONITOR_TYPE_MEMORY: { + tooltip = label; + } break; + case Performance::MONITOR_TYPE_TIME: { + tooltip = label; + } break; + default: { + tooltip += " " + item->get_text(0); + } break; + } + item->set_text(1, label); + item->set_tooltip(1, tooltip); + + if (p_value > max) { + max = p_value; + } +} + +void EditorPerformanceProfiler::Monitor::reset() { + history.clear(); + max = 0.0f; + if (item) { + item->set_text(1, ""); + item->set_tooltip(1, ""); + } +} + +String EditorPerformanceProfiler::_create_label(float p_value, Performance::MonitorType p_type) { + switch (p_type) { + case Performance::MONITOR_TYPE_MEMORY: { + return String::humanize_size(p_value); + } + case Performance::MONITOR_TYPE_TIME: { + return TS->format_number(rtos(p_value * 1000).pad_decimals(2)) + " " + RTR("ms"); + } + default: { + return TS->format_number(rtos(p_value)); + } + } +} + +void EditorPerformanceProfiler::_monitor_select() { + monitor_draw->update(); +} + +void EditorPerformanceProfiler::_monitor_draw() { + Vector<StringName> active; + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + if (i.value().item->is_checked(0)) { + active.push_back(i.key()); + } + } + + if (active.is_empty()) { + info_message->show(); + return; + } + + info_message->hide(); + + Ref<StyleBox> graph_style_box = get_theme_stylebox("normal", "TextEdit"); + Ref<Font> graph_font = get_theme_font("font", "TextEdit"); + int font_size = get_theme_font_size("font_size", "TextEdit"); + + int columns = int(Math::ceil(Math::sqrt(float(active.size())))); + int rows = int(Math::ceil(float(active.size()) / float(columns))); + if (active.size() == 1) { + rows = 1; + } + Size2i cell_size = Size2i(monitor_draw->get_size()) / Size2i(columns, rows); + float spacing = float(POINT_SEPARATION) / float(columns); + float value_multiplier = EditorSettings::get_singleton()->is_dark_theme() ? 1.4f : 0.55f; + float hue_shift = 1.0f / float(monitors.size()); + + for (int i = 0; i < active.size(); i++) { + Monitor ¤t = monitors[active[i]]; + Rect2i rect(Point2i(i % columns, i / columns) * cell_size + Point2i(MARGIN, MARGIN), cell_size - Point2i(MARGIN, MARGIN) * 2); + monitor_draw->draw_style_box(graph_style_box, rect); + + rect.position += graph_style_box->get_offset(); + rect.size -= graph_style_box->get_minimum_size(); + Color draw_color = get_theme_color("accent_color", "Editor"); + draw_color.set_hsv(Math::fmod(hue_shift * float(current.frame_index), 0.9f), draw_color.get_s() * 0.9f, draw_color.get_v() * value_multiplier, 0.6f); + monitor_draw->draw_string(graph_font, rect.position + Point2(0, graph_font->get_ascent(font_size)), current.item->get_text(0), HALIGN_LEFT, rect.size.x, font_size, draw_color); + + draw_color.a = 0.9f; + float value_position = rect.size.width - graph_font->get_string_size(current.item->get_text(1), font_size).width; + if (value_position < 0) { + value_position = 0; + } + monitor_draw->draw_string(graph_font, rect.position + Point2(value_position, graph_font->get_ascent(font_size)), current.item->get_text(1), HALIGN_LEFT, rect.size.x, font_size, draw_color); + + rect.position.y += graph_font->get_height(font_size); + rect.size.height -= graph_font->get_height(font_size); + + int line_count = rect.size.height / (graph_font->get_height(font_size) * 2); + if (line_count > 5) { + line_count = 5; + } + if (line_count > 0) { + Color horizontal_line_color; + horizontal_line_color.set_hsv(draw_color.get_h(), draw_color.get_s() * 0.5f, draw_color.get_v() * 0.5f, 0.3f); + monitor_draw->draw_line(rect.position, rect.position + Vector2(rect.size.width, 0), horizontal_line_color, Math::round(EDSCALE)); + monitor_draw->draw_string(graph_font, rect.position + Vector2(0, graph_font->get_ascent(font_size)), _create_label(current.max, current.type), HALIGN_LEFT, rect.size.width, font_size, horizontal_line_color); + + for (int j = 0; j < line_count; j++) { + Vector2 y_offset = Vector2(0, rect.size.height * (1.0f - float(j) / float(line_count))); + monitor_draw->draw_line(rect.position + y_offset, rect.position + Vector2(rect.size.width, 0) + y_offset, horizontal_line_color, Math::round(EDSCALE)); + monitor_draw->draw_string(graph_font, rect.position - Vector2(0, graph_font->get_descent(font_size)) + y_offset, _create_label(current.max * float(j) / float(line_count), current.type), HALIGN_LEFT, rect.size.width, font_size, horizontal_line_color); + } + } + + float from = rect.size.width; + float prev = -1.0f; + int count = 0; + List<float>::Element *e = current.history.front(); + + while (from >= 0 && e) { + float m = current.max; + float h2 = 0; + if (m != 0) { + h2 = (e->get() / m); + } + h2 = (1.0f - h2) * float(rect.size.y); + if (e != current.history.front()) { + monitor_draw->draw_line(rect.position + Point2(from, h2), rect.position + Point2(from + spacing, prev), draw_color, Math::round(EDSCALE)); + } + + if (marker_key == active[i] && count == marker_frame) { + Color line_color; + line_color.set_hsv(draw_color.get_h(), draw_color.get_s() * 0.8f, draw_color.get_v(), 0.5f); + monitor_draw->draw_line(rect.position + Point2(from, 0), rect.position + Point2(from, rect.size.y), line_color, Math::round(EDSCALE)); + + String label = _create_label(e->get(), current.type); + Size2 size = graph_font->get_string_size(label, font_size); + Vector2 text_top_left_position = Vector2(from, h2) - (size + Vector2(MARKER_MARGIN, MARKER_MARGIN)); + if (text_top_left_position.x < 0) { + text_top_left_position.x = from + MARKER_MARGIN; + } + if (text_top_left_position.y < 0) { + text_top_left_position.y = h2 + MARKER_MARGIN; + } + monitor_draw->draw_string(graph_font, rect.position + text_top_left_position + Point2(0, graph_font->get_ascent(font_size)), label, HALIGN_LEFT, rect.size.x, font_size, line_color); + } + prev = h2; + e = e->next(); + from -= spacing; + count++; + } + } +} + +void EditorPerformanceProfiler::_build_monitor_tree() { + Set<StringName> monitor_checked; + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + if (i.value().item && i.value().item->is_checked(0)) { + monitor_checked.insert(i.key()); + } + } + + base_map.clear(); + monitor_tree->get_root()->clear_children(); + + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + TreeItem *base = _get_monitor_base(i.value().base); + TreeItem *item = _create_monitor_item(i.value().name, base); + item->set_checked(0, monitor_checked.has(i.key())); + i.value().item = item; + if (!i.value().history.is_empty()) { + i.value().update_value(i.value().history.front()->get()); + } + } +} + +TreeItem *EditorPerformanceProfiler::_get_monitor_base(const StringName &p_base_name) { + if (base_map.has(p_base_name)) { + return base_map[p_base_name]; + } + + TreeItem *base = monitor_tree->create_item(monitor_tree->get_root()); + base->set_text(0, p_base_name); + base->set_editable(0, false); + base->set_selectable(0, false); + base->set_expand_right(0, true); + base_map.insert(p_base_name, base); + return base; +} + +TreeItem *EditorPerformanceProfiler::_create_monitor_item(const StringName &p_monitor_name, TreeItem *p_base) { + TreeItem *item = monitor_tree->create_item(p_base); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, true); + item->set_selectable(0, false); + item->set_selectable(1, false); + item->set_text(0, p_monitor_name); + return item; +} + +void EditorPerformanceProfiler::_marker_input(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + Vector<StringName> active; + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + if (i.value().item->is_checked(0)) { + active.push_back(i.key()); + } + } + if (active.size() > 0) { + int columns = int(Math::ceil(Math::sqrt(float(active.size())))); + int rows = int(Math::ceil(float(active.size()) / float(columns))); + if (active.size() == 1) { + rows = 1; + } + Size2i cell_size = Size2i(monitor_draw->get_size()) / Size2i(columns, rows); + Vector2i index = mb->get_position() / cell_size; + Rect2i rect(index * cell_size + Point2i(MARGIN, MARGIN), cell_size - Point2i(MARGIN, MARGIN) * 2); + if (rect.has_point(mb->get_position())) { + if (index.x + index.y * columns < active.size()) { + marker_key = active[index.x + index.y * columns]; + } else { + marker_key = ""; + } + Ref<StyleBox> graph_style_box = get_theme_stylebox("normal", "TextEdit"); + rect.position += graph_style_box->get_offset(); + rect.size -= graph_style_box->get_minimum_size(); + Vector2 point = mb->get_position() - rect.position; + if (point.x >= rect.size.x) { + marker_frame = 0; + } else { + int point_sep = 5; + float spacing = float(point_sep) / float(columns); + marker_frame = (rect.size.x - point.x) / spacing; + } + monitor_draw->update(); + return; + } + } + marker_key = ""; + monitor_draw->update(); + } +} + +void EditorPerformanceProfiler::reset() { + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + if (String(i.key()).begins_with("custom:")) { + monitors.erase(i); + } else { + i.value().reset(); + } + } + + _build_monitor_tree(); + marker_key = ""; + marker_frame = 0; + monitor_draw->update(); +} + +void EditorPerformanceProfiler::update_monitors(const Vector<StringName> &p_names) { + OrderedHashMap<StringName, int> names; + for (int i = 0; i < p_names.size(); i++) { + names.insert("custom:" + p_names[i], Performance::MONITOR_MAX + i); + } + + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + if (String(i.key()).begins_with("custom:")) { + if (!names.has(i.key())) { + monitors.erase(i); + } else { + i.value().frame_index = names[i.key()]; + names.erase(i.key()); + } + } + } + + for (OrderedHashMap<StringName, int>::Element i = names.front(); i; i = i.next()) { + String name = String(i.key()).replace_first("custom:", ""); + String base = "Custom"; + if (name.get_slice_count("/") == 2) { + base = name.get_slicec('/', 0); + name = name.get_slicec('/', 1); + } + monitors.insert(i.key(), Monitor(name, base, i.value(), Performance::MONITOR_TYPE_QUANTITY, nullptr)); + } + + _build_monitor_tree(); +} + +void EditorPerformanceProfiler::add_profile_frame(const Vector<float> &p_values) { + for (OrderedHashMap<StringName, Monitor>::Element i = monitors.front(); i; i = i.next()) { + float data = 0.0f; + if (i.value().frame_index >= 0 && i.value().frame_index < p_values.size()) { + data = p_values[i.value().frame_index]; + } + i.value().history.push_front(data); + i.value().update_value(data); + } + marker_frame++; + monitor_draw->update(); +} + +List<float> *EditorPerformanceProfiler::get_monitor_data(const StringName &p_name) { + if (monitors.has(p_name)) { + return &monitors[p_name].history; + } + return nullptr; +} + +EditorPerformanceProfiler::EditorPerformanceProfiler() { + set_name(TTR("Monitors")); + set_split_offset(340 * EDSCALE); + + monitor_tree = memnew(Tree); + monitor_tree->set_columns(2); + monitor_tree->set_column_title(0, TTR("Monitor")); + monitor_tree->set_column_title(1, TTR("Value")); + monitor_tree->set_column_titles_visible(true); + monitor_tree->connect("item_edited", callable_mp(this, &EditorPerformanceProfiler::_monitor_select)); + monitor_tree->create_item(); + monitor_tree->set_hide_root(true); + add_child(monitor_tree); + + monitor_draw = memnew(Control); + monitor_draw->set_clip_contents(true); + monitor_draw->connect("draw", callable_mp(this, &EditorPerformanceProfiler::_monitor_draw)); + monitor_draw->connect("gui_input", callable_mp(this, &EditorPerformanceProfiler::_marker_input)); + add_child(monitor_draw); + + info_message = memnew(Label); + info_message->set_text(TTR("Pick one or more items from the list to display the graph.")); + info_message->set_valign(Label::VALIGN_CENTER); + info_message->set_align(Label::ALIGN_CENTER); + info_message->set_autowrap(true); + info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); + monitor_draw->add_child(info_message); + + for (int i = 0; i < Performance::MONITOR_MAX; i++) { + String base = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)).get_slicec('/', 0).capitalize(); + String name = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)).get_slicec('/', 1).capitalize(); + monitors.insert(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)), Monitor(name, base, i, Performance::get_singleton()->get_monitor_type(Performance::Monitor(i)), nullptr)); + } + + _build_monitor_tree(); +} diff --git a/editor/debugger/editor_performance_profiler.h b/editor/debugger/editor_performance_profiler.h new file mode 100644 index 0000000000..ea3404b208 --- /dev/null +++ b/editor/debugger/editor_performance_profiler.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* editor_performance_profiler.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 EDITOR_PERFORMANCE_PROFILER_H +#define EDITOR_PERFORMANCE_PROFILER_H + +#include "core/templates/map.h" +#include "core/templates/ordered_hash_map.h" +#include "main/performance.h" +#include "scene/gui/control.h" +#include "scene/gui/label.h" +#include "scene/gui/split_container.h" +#include "scene/gui/tree.h" + +class EditorPerformanceProfiler : public HSplitContainer { + GDCLASS(EditorPerformanceProfiler, HSplitContainer); + +private: + class Monitor { + public: + String name; + String base; + List<float> history; + float max = 0.0f; + TreeItem *item = nullptr; + Performance::MonitorType type = Performance::MONITOR_TYPE_QUANTITY; + int frame_index = 0; + + Monitor(); + Monitor(String p_name, String p_base, int p_frame_index, Performance::MonitorType p_type, TreeItem *p_item); + void update_value(float p_value); + void reset(); + }; + + OrderedHashMap<StringName, Monitor> monitors; + + Map<StringName, TreeItem *> base_map; + Tree *monitor_tree; + Control *monitor_draw; + Label *info_message; + StringName marker_key; + int marker_frame; + const int MARGIN = 4; + const int POINT_SEPARATION = 5; + const int MARKER_MARGIN = 2; + + static String _create_label(float p_value, Performance::MonitorType p_type); + void _monitor_select(); + void _monitor_draw(); + void _build_monitor_tree(); + TreeItem *_get_monitor_base(const StringName &p_base_name); + TreeItem *_create_monitor_item(const StringName &p_monitor_name, TreeItem *p_base); + void _marker_input(const Ref<InputEvent> &p_event); + +public: + void reset(); + void update_monitors(const Vector<StringName> &p_names); + void add_profile_frame(const Vector<float> &p_values); + List<float> *get_monitor_data(const StringName &p_name); + EditorPerformanceProfiler(); +}; + +#endif // EDITOR_PERFORMANCE_PROFILER_H diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 8bd21fff5c..9304b116d0 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -103,19 +103,19 @@ static String _get_percent_txt(float p_value, float p_total) { p_total = 0.00001; } - return String::num((p_value / p_total) * 100, 1) + "%"; + return TS->format_number(String::num((p_value / p_total) * 100, 1)) + TS->percent_sign(); } String EditorProfiler::_get_time_as_text(const Metric &m, float p_time, int p_calls) { const int dmode = display_mode->get_selected(); if (dmode == DISPLAY_FRAME_TIME) { - return rtos(p_time * 1000).pad_decimals(2) + " ms"; + return TS->format_number(rtos(p_time * 1000).pad_decimals(2)) + " " + RTR("ms"); } else if (dmode == DISPLAY_AVERAGE_TIME) { if (p_calls == 0) { - return "0.00 ms"; + return TS->format_number("0.00") + " " + RTR("ms"); } else { - return rtos((p_time / p_calls) * 1000).pad_decimals(2) + " ms"; + return TS->format_number(rtos((p_time / p_calls) * 1000).pad_decimals(2)) + " " + RTR("ms"); } } else if (dmode == DISPLAY_FRAME_PERCENT) { return _get_percent_txt(p_time, m.frame_time); @@ -423,7 +423,7 @@ void EditorProfiler::_clear_pressed() { } void EditorProfiler::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { activate->set_icon(get_theme_icon("Play", "EditorIcons")); clear_button->set_icon(get_theme_icon("Clear", "EditorIcons")); } @@ -596,7 +596,7 @@ bool EditorProfiler::is_profiling() { Vector<Vector<String>> EditorProfiler::get_data_as_csv() const { Vector<Vector<String>> res; - if (frame_metrics.empty()) { + if (frame_metrics.is_empty()) { return res; } diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index aa2ef58db4..e16bde41f6 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -45,27 +45,27 @@ class EditorProfiler : public VBoxContainer { public: struct Metric { - bool valid; + bool valid = false; - int frame_number; - float frame_time; - float idle_time; - float physics_time; - float physics_frame_time; + int frame_number = 0; + float frame_time = 0; + float idle_time = 0; + float physics_time = 0; + float physics_frame_time = 0; struct Category { StringName signature; String name; - float total_time; //total for category + float total_time = 0; //total for category struct Item { StringName signature; String name; String script; - int line; - float self; - float total; - int calls; + int line = 0; + float self = 0; + float total = 0; + int calls = 0; }; Vector<Item> items; @@ -75,11 +75,6 @@ public: Map<StringName, Category *> category_ptrs; Map<StringName, Category::Item *> item_ptrs; - - Metric() { - valid = false; - frame_number = 0; - } }; enum DisplayMode { diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 81b42da08e..d825a980c7 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -114,9 +114,9 @@ String EditorVisualProfiler::_get_time_as_text(float p_time) { int dmode = display_mode->get_selected(); if (dmode == DISPLAY_FRAME_TIME) { - return rtos(p_time) + "ms"; + return TS->format_number(rtos(p_time)) + " " + RTR("ms"); } else if (dmode == DISPLAY_FRAME_PERCENT) { - return String::num(p_time * 100 / graph_limit, 2) + "%"; + return TS->format_number(String::num(p_time * 100 / graph_limit, 2)) + " " + TS->percent_sign(); } return "err"; @@ -423,8 +423,12 @@ void EditorVisualProfiler::_clear_pressed() { } void EditorVisualProfiler::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - activate->set_icon(get_theme_icon("Play", "EditorIcons")); + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { + if (is_layout_rtl()) { + activate->set_icon(get_theme_icon("PlayBackwards", "EditorIcons")); + } else { + activate->set_icon(get_theme_icon("Play", "EditorIcons")); + } clear_button->set_icon(get_theme_icon("Clear", "EditorIcons")); } } @@ -434,6 +438,7 @@ void EditorVisualProfiler::_graph_tex_draw() { return; } Ref<Font> font = get_theme_font("font", "Label"); + int font_size = get_theme_font_size("font_size", "Label"); if (seeking) { int max_frames = frame_metrics.size(); int frame = cursor_metric_edit->get_value() - (frame_metrics[last_metric].frame_number - max_frames + 1); @@ -457,7 +462,7 @@ void EditorVisualProfiler::_graph_tex_draw() { graph->draw_line(Vector2(0, frame_y), Vector2(half_width, frame_y), Color(1, 1, 1, 0.3)); String limit_str = String::num(graph_limit, 2); - graph->draw_string(font, Vector2(half_width - font->get_string_size(limit_str).x - 2, frame_y - 2), limit_str, Color(1, 1, 1, 0.6)); + graph->draw_string(font, Vector2(half_width - font->get_string_size(limit_str, font_size).x - 2, frame_y - 2), limit_str, HALIGN_LEFT, -1, font_size, Color(1, 1, 1, 0.6)); } if (graph_height_gpu > 0) { @@ -468,15 +473,14 @@ void EditorVisualProfiler::_graph_tex_draw() { graph->draw_line(Vector2(half_width, frame_y), Vector2(graph->get_size().x, frame_y), Color(1, 1, 1, 0.3)); String limit_str = String::num(graph_limit, 2); - graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str).x - 2, frame_y - 2), limit_str, Color(1, 1, 1, 0.6)); + graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, font_size).x - 2, frame_y - 2), limit_str, HALIGN_LEFT, -1, font_size, Color(1, 1, 1, 0.6)); } - graph->draw_string(font, Vector2(font->get_string_size("X").x, font->get_ascent() + 2), "CPU:", Color(1, 1, 1, 0.8)); - graph->draw_string(font, Vector2(font->get_string_size("X").x + graph->get_size().width / 2, font->get_ascent() + 2), "GPU:", Color(1, 1, 1, 0.8)); + graph->draw_string(font, Vector2(font->get_string_size("X", font_size).x, font->get_ascent(font_size) + 2), "CPU:", HALIGN_LEFT, -1, font_size, Color(1, 1, 1, 0.8)); + graph->draw_string(font, Vector2(font->get_string_size("X", font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU:", HALIGN_LEFT, -1, font_size, Color(1, 1, 1, 0.8)); /* if (hover_metric != -1 && frame_metrics[hover_metric].valid) { - int max_frames = frame_metrics.size(); int frame = frame_metrics[hover_metric].frame_number - (frame_metrics[last_metric].frame_number - max_frames + 1); if (frame < 0) @@ -662,7 +666,7 @@ bool EditorVisualProfiler::is_profiling() { Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const { Vector<Vector<String>> res; #if 0 - if (frame_metrics.empty()) { + if (frame_metrics.is_empty()) { return res; } @@ -671,7 +675,6 @@ Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const { const Vector<EditorFrameProfiler::Metric::Category> &categories = frame_metrics[0].categories; for (int j = 0; j < categories.size(); j++) { - const EditorFrameProfiler::Metric::Category &c = categories[j]; signatures.push_back(c.signature); @@ -688,7 +691,6 @@ Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const { int index = last_metric; for (int i = 0; i < frame_metrics.size(); i++) { - ++index; if (index >= frame_metrics.size()) { @@ -702,7 +704,6 @@ Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const { const Vector<EditorFrameProfiler::Metric::Category> &frame_cat = frame_metrics[index].categories; for (int j = 0; j < frame_cat.size(); j++) { - const EditorFrameProfiler::Metric::Category &c = frame_cat[j]; values.write[it++] = String::num_real(c.total_time); diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h index 3c1a55dc38..6b04fdbafc 100644 --- a/editor/debugger/editor_visual_profiler.h +++ b/editor/debugger/editor_visual_profiler.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -46,9 +46,9 @@ class EditorVisualProfiler : public VBoxContainer { public: struct Metric { - bool valid; + bool valid = false; - uint64_t frame_number; + uint64_t frame_number = 0; struct Area { String name; @@ -59,10 +59,6 @@ public: }; Vector<Area> areas; - - Metric() { - valid = false; - } }; enum DisplayTimeMode { diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 49137f76fa..be2b98bf1a 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -30,12 +30,13 @@ #include "script_editor_debugger.h" +#include "core/config/project_settings.h" #include "core/debugger/debugger_marshalls.h" #include "core/debugger/remote_debugger.h" #include "core/io/marshalls.h" -#include "core/project_settings.h" -#include "core/ustring.h" +#include "core/string/ustring.h" #include "editor/debugger/editor_network_profiler.h" +#include "editor/debugger/editor_performance_profiler.h" #include "editor/debugger/editor_profiler.h" #include "editor/debugger/editor_visual_profiler.h" #include "editor/editor_log.h" @@ -43,6 +44,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/property_editor.h" #include "main/performance.h" @@ -172,14 +174,25 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) { file->store_csv_line(line); // values - List<Vector<float>>::Element *E = perf_history.back(); - while (E) { - Vector<float> &perf_data = E->get(); - for (int i = 0; i < perf_data.size(); i++) { - line.write[i] = String::num_real(perf_data[i]); + Vector<List<float>::Element *> iterators; + iterators.resize(Performance::MONITOR_MAX); + bool continue_iteration = false; + for (int i = 0; i < Performance::MONITOR_MAX; i++) { + iterators.write[i] = performance_profiler->get_monitor_data(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)))->back(); + continue_iteration = continue_iteration || iterators[i]; + } + while (continue_iteration) { + continue_iteration = false; + for (int i = 0; i < Performance::MONITOR_MAX; i++) { + if (iterators[i]) { + line.write[i] = String::num_real(iterators[i]->get()); + iterators.write[i] = iterators[i]->prev(); + } else { + line.write[i] = ""; + } + continue_iteration = continue_iteration || iterators[i]; } file->store_csv_line(line); - E = E->prev(); } file->store_string("\n"); @@ -270,7 +283,7 @@ void ScriptEditorDebugger::_video_mem_export() { file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); file_dialog->clear_filters(); file_dialog_purpose = SAVE_VRAM_CSV; - file_dialog->popup_centered_ratio(); + file_dialog->popup_file_dialog(); } Size2 ScriptEditorDebugger::get_minimum_size() const { @@ -409,37 +422,12 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da EditorNode::get_log()->add_message(output_strings[i], msg_type); } } else if (p_msg == "performance:profile_frame") { - Vector<float> p; - p.resize(p_data.size()); + Vector<float> frame_data; + frame_data.resize(p_data.size()); for (int i = 0; i < p_data.size(); i++) { - p.write[i] = p_data[i]; - if (i < perf_items.size()) { - const float value = p[i]; - String label = rtos(value); - String tooltip = label; - switch (Performance::MonitorType((int)perf_items[i]->get_metadata(1))) { - case Performance::MONITOR_TYPE_MEMORY: { - label = String::humanize_size(value); - tooltip = label; - } break; - case Performance::MONITOR_TYPE_TIME: { - label = rtos(value * 1000).pad_decimals(2) + " ms"; - tooltip = label; - } break; - default: { - tooltip += " " + perf_items[i]->get_text(0); - } break; - } - - perf_items[i]->set_text(1, label); - perf_items[i]->set_tooltip(1, tooltip); - if (p[i] > perf_max[i]) { - perf_max.write[i] = p[i]; - } - } + frame_data.write[i] = p_data[i]; } - perf_history.push_front(p); - perf_draw->update(); + performance_profiler->add_profile_frame(frame_data); } else if (p_msg == "visual:profile_frame") { DebuggerMarshalls::VisualProfilerFrame frame; @@ -499,17 +487,20 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da error->set_text_align(0, TreeItem::ALIGN_LEFT); String error_title; - // Include method name, when given, in error title. - if (!oe.source_func.empty()) { + if (oe.callstack.size() > 0) { + // If available, use the script's stack in the error title. + error_title = oe.callstack[oe.callstack.size() - 1].func + ": "; + } else if (!oe.source_func.is_empty()) { + // Otherwise try to use the C++ source function. error_title += oe.source_func + ": "; } // If we have a (custom) error message, use it as title, and add a C++ Error // item with the original error condition. - error_title += oe.error_descr.empty() ? oe.error : oe.error_descr; + error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr; error->set_text(1, error_title); tooltip += " " + error_title + "\n"; - if (!oe.error_descr.empty()) { + if (!oe.error_descr.is_empty()) { // Add item for C++ error condition. TreeItem *cpp_cond = error_tree->create_item(error); cpp_cond->set_text(0, "<" + TTR("C++ Error") + ">"); @@ -525,7 +516,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da // Source of the error. String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line); - if (!oe.source_func.empty()) { + if (!oe.source_func.is_empty()) { source_txt += " @ " + oe.source_func + "()"; } @@ -541,9 +532,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da cpp_source->set_metadata(0, source_meta); } - error->set_tooltip(0, tooltip); - error->set_tooltip(1, tooltip); - // Format stack trace. // stack_items_count is the number of elements to parse, with 3 items per frame // of the stack trace (script, method, line). @@ -560,10 +548,17 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">"); stack_trace->set_text_align(0, TreeItem::ALIGN_LEFT); error->set_metadata(0, meta); + tooltip += TTR("Stack Trace:") + "\n"; } - stack_trace->set_text(1, infos[i].file.get_file() + ":" + itos(infos[i].line) + " @ " + infos[i].func + "()"); + + String frame_txt = infos[i].file.get_file() + ":" + itos(infos[i].line) + " @ " + infos[i].func + "()"; + tooltip += frame_txt + "\n"; + stack_trace->set_text(1, frame_txt); } + error->set_tooltip(0, tooltip); + error->set_tooltip(1, tooltip); + if (oe.warning) { warning_count++; } else { @@ -704,8 +699,38 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da emit_signal("stop_requested"); _stop_and_notify(); + } else if (p_msg == "performance:profile_names") { + Vector<StringName> monitors; + monitors.resize(p_data.size()); + for (int i = 0; i < p_data.size(); i++) { + ERR_FAIL_COND(p_data[i].get_type() != Variant::STRING_NAME); + monitors.set(i, p_data[i]); + } + performance_profiler->update_monitors(monitors); + } else { - WARN_PRINT("unknown message " + p_msg); + int colon_index = p_msg.find_char(':'); + ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received"); + + bool parsed = false; + const String cap = p_msg.substr(0, colon_index); + Map<StringName, Callable>::Element *element = captures.find(cap); + if (element) { + Callable &c = element->value(); + ERR_FAIL_COND_MSG(c.is_null(), "Invalid callable registered: " + cap); + Variant cmd = p_msg.substr(colon_index + 1), data = p_data; + const Variant *args[2] = { &cmd, &data }; + Variant retval; + Callable::CallError err; + c.call(args, 2, retval, err); + ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(c, args, 2, err)); + ERR_FAIL_COND_MSG(retval.get_type() != Variant::BOOL, "Error calling 'capture' to callable: " + String(c) + ". Return type is not bool."); + parsed = retval; + } + + if (!parsed) { + WARN_PRINT("unknown message " + p_msg); + } } } @@ -724,80 +749,6 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType reason->set_tooltip(p_reason.word_wrap(80)); } -void ScriptEditorDebugger::_performance_select() { - perf_draw->update(); -} - -void ScriptEditorDebugger::_performance_draw() { - Vector<int> which; - for (int i = 0; i < perf_items.size(); i++) { - if (perf_items[i]->is_checked(0)) { - which.push_back(i); - } - } - - if (which.empty()) { - info_message->show(); - return; - } - - info_message->hide(); - - Ref<StyleBox> graph_sb = get_theme_stylebox("normal", "TextEdit"); - Ref<Font> graph_font = get_theme_font("font", "TextEdit"); - - int cols = Math::ceil(Math::sqrt((float)which.size())); - int rows = Math::ceil((float)which.size() / cols); - if (which.size() == 1) { - rows = 1; - } - - int margin = 3; - int point_sep = 5; - Size2i s = Size2i(perf_draw->get_size()) / Size2i(cols, rows); - for (int i = 0; i < which.size(); i++) { - Point2i p(i % cols, i / cols); - Rect2i r(p * s, s); - r.position += Point2(margin, margin); - r.size -= Point2(margin, margin) * 2.0; - perf_draw->draw_style_box(graph_sb, r); - r.position += graph_sb->get_offset(); - r.size -= graph_sb->get_minimum_size(); - int pi = which[i]; - Color c = get_theme_color("accent_color", "Editor"); - float h = (float)which[i] / (float)(perf_items.size()); - // Use a darker color on light backgrounds for better visibility - float value_multiplier = EditorSettings::get_singleton()->is_dark_theme() ? 1.4 : 0.55; - c.set_hsv(Math::fmod(h + 0.4, 0.9), c.get_s() * 0.9, c.get_v() * value_multiplier); - - c.a = 0.6; - perf_draw->draw_string(graph_font, r.position + Point2(0, graph_font->get_ascent()), perf_items[pi]->get_text(0), c, r.size.x); - c.a = 0.9; - perf_draw->draw_string(graph_font, r.position + Point2(0, graph_font->get_ascent() + graph_font->get_height()), perf_items[pi]->get_text(1), c, r.size.y); - - float spacing = point_sep / float(cols); - float from = r.size.width; - - List<Vector<float>>::Element *E = perf_history.front(); - float prev = -1; - while (from >= 0 && E) { - float m = perf_max[pi]; - if (m == 0) { - m = 0.00001; - } - float h2 = E->get()[pi] / m; - h2 = (1.0 - h2) * r.size.y; - - if (E != perf_history.front()) { - perf_draw->draw_line(r.position + Point2(from, h2), r.position + Point2(from + spacing, prev), c, Math::round(EDSCALE)); - } - prev = h2; - E = E->next(); - from -= spacing; - } - } -} - void ScriptEditorDebugger::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -851,8 +802,8 @@ void ScriptEditorDebugger::_notification(int p_what) { msg.push_back(true); msg.push_back(cam->get_fov()); } - msg.push_back(cam->get_znear()); - msg.push_back(cam->get_zfar()); + msg.push_back(cam->get_near()); + msg.push_back(cam->get_far()); _put_msg("scene:override_camera_3D:transform", msg); } } @@ -915,10 +866,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { peer = p_peer; ERR_FAIL_COND(p_peer.is_null()); - perf_history.clear(); - for (int i = 0; i < Performance::MONITOR_MAX; i++) { - perf_max.write[i] = 0; - } + performance_profiler->reset(); set_process(true); breaked = false; @@ -928,6 +876,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { tabs->set_current_tab(0); _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS); _update_buttons_state(); + emit_signal("started"); } void ScriptEditorDebugger::_update_buttons_state() { @@ -1025,7 +974,7 @@ void ScriptEditorDebugger::_export_csv() { file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); file_dialog_purpose = SAVE_MONITORS_CSV; - file_dialog->popup_centered_ratio(); + file_dialog->popup_file_dialog(); } String ScriptEditorDebugger::get_var_value(const String &p_var) const { @@ -1081,7 +1030,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n for (int i = 0; i < VARIANT_ARG_MAX; i++) { //no pointers, sorry - if (argptr[i] && (argptr[i]->get_type() == Variant::OBJECT || argptr[i]->get_type() == Variant::_RID)) { + if (argptr[i] && (argptr[i]->get_type() == Variant::OBJECT || argptr[i]->get_type() == Variant::RID)) { return; } } @@ -1476,6 +1425,7 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("request_remote_object", "id"), &ScriptEditorDebugger::request_remote_object); ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value"), &ScriptEditorDebugger::update_remote_object); + ADD_SIGNAL(MethodInfo("started")); ADD_SIGNAL(MethodInfo("stopped")); ADD_SIGNAL(MethodInfo("stop_requested")); ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame"))); @@ -1489,6 +1439,43 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("remote_tree_updated")); } +void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) { + if (!debugger_plugins.has(p_script)) { + EditorDebuggerPlugin *plugin = memnew(EditorDebuggerPlugin()); + plugin->attach_debugger(this); + plugin->set_script(p_script); + tabs->add_child(plugin); + debugger_plugins.insert(p_script, plugin); + } +} + +void ScriptEditorDebugger::remove_debugger_plugin(const Ref<Script> &p_script) { + if (debugger_plugins.has(p_script)) { + tabs->remove_child(debugger_plugins[p_script]); + debugger_plugins[p_script]->detach_debugger(false); + memdelete(debugger_plugins[p_script]); + debugger_plugins.erase(p_script); + } +} + +void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) { + _put_msg(p_message, p_args); +} + +void ScriptEditorDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) { + ERR_FAIL_COND_MSG(has_capture(p_name), "Capture already registered: " + p_name); + captures.insert(p_name, p_callable); +} + +void ScriptEditorDebugger::unregister_message_capture(const StringName &p_name) { + ERR_FAIL_COND_MSG(!has_capture(p_name), "Capture not registered: " + p_name); + captures.erase(p_name); +} + +bool ScriptEditorDebugger::has_capture(const StringName &p_name) { + return captures.has(p_name); +} + ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { editor = p_editor; @@ -1517,27 +1504,31 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { hbc->add_child(memnew(VSeparator)); - skip_breakpoints = memnew(ToolButton); + skip_breakpoints = memnew(Button); + skip_breakpoints->set_flat(true); hbc->add_child(skip_breakpoints); skip_breakpoints->set_tooltip(TTR("Skip Breakpoints")); skip_breakpoints->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints)); hbc->add_child(memnew(VSeparator)); - copy = memnew(ToolButton); + copy = memnew(Button); + copy->set_flat(true); hbc->add_child(copy); copy->set_tooltip(TTR("Copy Error")); copy->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_copy)); hbc->add_child(memnew(VSeparator)); - step = memnew(ToolButton); + step = memnew(Button); + step->set_flat(true); hbc->add_child(step); step->set_tooltip(TTR("Step Into")); step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into")); step->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_step)); - next = memnew(ToolButton); + next = memnew(Button); + next->set_flat(true); hbc->add_child(next); next->set_tooltip(TTR("Step Over")); next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over")); @@ -1545,13 +1536,15 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { hbc->add_child(memnew(VSeparator)); - dobreak = memnew(ToolButton); + dobreak = memnew(Button); + dobreak->set_flat(true); hbc->add_child(dobreak); dobreak->set_tooltip(TTR("Break")); dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break")); dobreak->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_break)); - docontinue = memnew(ToolButton); + docontinue = memnew(Button); + docontinue->set_flat(true); hbc->add_child(docontinue); docontinue->set_tooltip(TTR("Continue")); docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue")); @@ -1660,63 +1653,8 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { } { //monitors - - HSplitContainer *hsp = memnew(HSplitContainer); - - perf_monitors = memnew(Tree); - perf_monitors->set_columns(2); - perf_monitors->set_column_title(0, TTR("Monitor")); - perf_monitors->set_column_title(1, TTR("Value")); - perf_monitors->set_column_titles_visible(true); - perf_monitors->connect("item_edited", callable_mp(this, &ScriptEditorDebugger::_performance_select)); - hsp->add_child(perf_monitors); - - perf_draw = memnew(Control); - perf_draw->set_clip_contents(true); - perf_draw->connect("draw", callable_mp(this, &ScriptEditorDebugger::_performance_draw)); - hsp->add_child(perf_draw); - - hsp->set_name(TTR("Monitors")); - hsp->set_split_offset(340 * EDSCALE); - tabs->add_child(hsp); - perf_max.resize(Performance::MONITOR_MAX); - - Map<String, TreeItem *> bases; - TreeItem *root = perf_monitors->create_item(); - perf_monitors->set_hide_root(true); - for (int i = 0; i < Performance::MONITOR_MAX; i++) { - String n = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); - Performance::MonitorType mtype = Performance::get_singleton()->get_monitor_type(Performance::Monitor(i)); - String base = n.get_slice("/", 0); - String name = n.get_slice("/", 1); - if (!bases.has(base)) { - TreeItem *b = perf_monitors->create_item(root); - b->set_text(0, base.capitalize()); - b->set_editable(0, false); - b->set_selectable(0, false); - b->set_expand_right(0, true); - bases[base] = b; - } - - TreeItem *it = perf_monitors->create_item(bases[base]); - it->set_metadata(1, mtype); - it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - it->set_editable(0, true); - it->set_selectable(0, false); - it->set_selectable(1, false); - it->set_text(0, name.capitalize()); - perf_items.push_back(it); - perf_max.write[i] = 0; - } - - info_message = memnew(Label); - info_message->set_text(TTR("Pick one or more items from the list to display the graph.")); - info_message->set_valign(Label::VALIGN_CENTER); - info_message->set_align(Label::ALIGN_CENTER); - info_message->set_autowrap(true); - info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - info_message->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); - perf_draw->add_child(info_message); + performance_profiler = memnew(EditorPerformanceProfiler); + tabs->add_child(performance_profiler); } { //vmem inspect @@ -1730,9 +1668,11 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmem_total->set_editable(false); vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE); vmem_hb->add_child(vmem_total); - vmem_refresh = memnew(ToolButton); + vmem_refresh = memnew(Button); + vmem_refresh->set_flat(true); vmem_hb->add_child(vmem_refresh); - vmem_export = memnew(ToolButton); + vmem_export = memnew(Button); + vmem_export->set_flat(true); vmem_export->set_tooltip(TTR("Export list to a CSV file")); vmem_hb->add_child(vmem_export); vmem_vb->add_child(vmem_hb); diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 2984051aa1..e5fb3c35a9 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -52,7 +52,9 @@ class ItemList; class EditorProfiler; class EditorVisualProfiler; class EditorNetworkProfiler; +class EditorPerformanceProfiler; class SceneDebuggerTree; +class EditorDebuggerPlugin; class ScriptEditorDebugger : public MarginContainer { GDCLASS(ScriptEditorDebugger, MarginContainer); @@ -113,16 +115,8 @@ private: // Each debugger should have it's tree in the future I guess. const Tree *editor_remote_tree = nullptr; - List<Vector<float>> perf_history; - Vector<float> perf_max; - Vector<TreeItem *> perf_items; - Map<int, String> profiler_signature; - Tree *perf_monitors; - Control *perf_draw; - Label *info_message; - Tree *vmem_tree; Button *vmem_refresh; Button *vmem_export; @@ -141,6 +135,7 @@ private: EditorProfiler *profiler; EditorVisualProfiler *visual_profiler; EditorNetworkProfiler *network_profiler; + EditorPerformanceProfiler *performance_profiler; EditorNode *editor; @@ -152,8 +147,10 @@ private: EditorDebuggerNode::CameraOverride camera_override; - void _performance_draw(); - void _performance_select(); + Map<Ref<Script>, EditorDebuggerPlugin *> debugger_plugins; + + Map<StringName, Callable> captures; + void _stack_dump_frame_selected(); void _file_selected(const String &p_file); @@ -260,7 +257,17 @@ public: bool is_skip_breakpoints(); - virtual Size2 get_minimum_size() const; + virtual Size2 get_minimum_size() const override; + + void add_debugger_plugin(const Ref<Script> &p_script); + void remove_debugger_plugin(const Ref<Script> &p_script); + + void send_message(const String &p_message, const Array &p_args); + + void register_message_capture(const StringName &p_name, const Callable &p_callable); + void unregister_message_capture(const StringName &p_name); + bool has_capture(const StringName &p_name); + ScriptEditorDebugger(EditorNode *p_editor = nullptr); ~ScriptEditorDebugger(); }; |