diff options
Diffstat (limited to 'editor/debugger')
25 files changed, 1798 insertions, 1683 deletions
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp index ff4051fc67..fc806ded5e 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* debug_adapter_parser.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_parser.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "debug_adapter_parser.h" @@ -34,6 +34,8 @@ #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_node.h" #include "editor/editor_run_native.h" +#include "editor/export/editor_export_platform.h" +#include "editor/plugins/script_editor_plugin.h" void DebugAdapterParser::_bind_methods() { // Requests @@ -199,7 +201,7 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const { } } else if (platform_string == "web") { for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { - if (EditorExport::get_singleton()->get_export_platform(i)->get_name() == "HTML5") { + if (EditorExport::get_singleton()->get_export_platform(i)->get_name() == "Web") { idx = i; break; } @@ -211,7 +213,7 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const { } EditorNode *editor = EditorNode::get_singleton(); - Error err = platform_string == "android" ? editor->run_play_native(device, idx) : editor->run_play_native(-1, idx); + Error err = platform_string == "android" ? editor->run_play_native(device * 10000 + idx) : editor->run_play_native(idx); if (err) { if (err == ERR_INVALID_PARAMETER && platform_string == "android") { return prepare_error_response(p_params, DAP::ErrorType::MISSING_DEVICE); @@ -380,12 +382,12 @@ Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const { DAP::StackFrame frame; frame.id = frame_id; - Map<DAP::StackFrame, List<int>>::Element *E = DebugAdapterProtocol::get_singleton()->stackframe_list.find(frame); + HashMap<DAP::StackFrame, List<int>, DAP::StackFrame>::Iterator E = DebugAdapterProtocol::get_singleton()->stackframe_list.find(frame); if (E) { - ERR_FAIL_COND_V(E->value().size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN)); + ERR_FAIL_COND_V(E->value.size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN)); for (int i = 0; i < 3; i++) { DAP::Scope scope; - scope.variablesReference = E->value()[i]; + scope.variablesReference = E->value[i]; switch (i) { case 0: scope.name = "Locals"; @@ -423,16 +425,16 @@ Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const { Dictionary args = p_params["arguments"]; int variable_id = args["variablesReference"]; - Map<int, Array>::Element *E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); + HashMap<int, Array>::Iterator E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); if (E) { if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsVariableType) { - for (int i = 0; i < E->value().size(); i++) { - Dictionary variable = E->value()[i]; + for (int i = 0; i < E->value.size(); i++) { + Dictionary variable = E->value[i]; variable.erase("type"); } } - body["variables"] = E ? E->value() : Array(); + body["variables"] = E ? E->value : Array(); return response; } else { return Dictionary(); diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.h b/editor/debugger/debug_adapter/debug_adapter_parser.h index f458151e17..eb5255dc78 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.h +++ b/editor/debugger/debug_adapter/debug_adapter_parser.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* debug_adapter_parser.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_parser.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DEBUG_ADAPTER_PARSER_H #define DEBUG_ADAPTER_PARSER_H @@ -93,4 +93,4 @@ public: Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const; }; -#endif +#endif // DEBUG_ADAPTER_PARSER_H diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index babe8af8bc..e5f74762f6 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* debug_adapter_protocol.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_protocol.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "debug_adapter_protocol.h" @@ -37,6 +37,7 @@ #include "editor/doc_tools.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" DebugAdapterProtocol *DebugAdapterProtocol::singleton = nullptr; @@ -268,12 +269,12 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) { x.type = type_vec2; y.type = type_vec2; origin.type = type_vec2; - x.value = transform.elements[0]; - y.value = transform.elements[1]; - origin.value = transform.elements[2]; - x.variablesReference = parse_variant(transform.elements[0]); - y.variablesReference = parse_variant(transform.elements[1]); - origin.variablesReference = parse_variant(transform.elements[2]); + x.value = transform.columns[0]; + y.value = transform.columns[1]; + origin.value = transform.columns[2]; + x.variablesReference = parse_variant(transform.columns[0]); + y.variablesReference = parse_variant(transform.columns[1]); + origin.variablesReference = parse_variant(transform.columns[2]); Array arr; arr.push_back(x.to_json()); @@ -349,20 +350,20 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) { case Variant::BASIS: { int id = variable_id++; Basis basis = p_var; - const String type_vec2 = Variant::get_type_name(Variant::VECTOR2); + const String type_vec3 = Variant::get_type_name(Variant::VECTOR3); DAP::Variable x, y, z; x.name = "x"; y.name = "y"; z.name = "z"; - x.type = type_vec2; - y.type = type_vec2; - z.type = type_vec2; - x.value = basis.elements[0]; - y.value = basis.elements[1]; - z.value = basis.elements[2]; - x.variablesReference = parse_variant(basis.elements[0]); - y.variablesReference = parse_variant(basis.elements[1]); - z.variablesReference = parse_variant(basis.elements[2]); + x.type = type_vec3; + y.type = type_vec3; + z.type = type_vec3; + x.value = basis.rows[0]; + y.value = basis.rows[1]; + z.value = basis.rows[2]; + x.variablesReference = parse_variant(basis.rows[0]); + y.variablesReference = parse_variant(basis.rows[1]); + z.variablesReference = parse_variant(basis.rows[2]); Array arr; arr.push_back(x.to_json()); @@ -655,7 +656,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) { bool DebugAdapterProtocol::process_message(const String &p_text) { JSON json; - ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Mal-formed message!"); + ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!"); Dictionary params = json.get_data(); bool completed = true; @@ -918,13 +919,13 @@ void DebugAdapterProtocol::on_debug_stack_frame_vars(const int &p_size) { DAP::StackFrame frame; frame.id = _current_frame; ERR_FAIL_COND(!stackframe_list.has(frame)); - List<int> scope_ids = stackframe_list.find(frame)->value(); + List<int> scope_ids = stackframe_list.find(frame)->value; for (List<int>::Element *E = scope_ids.front(); E; E = E->next()) { - int variable_id = E->get(); - if (variable_list.has(variable_id)) { - variable_list.find(variable_id)->value().clear(); + int var_id = E->get(); + if (variable_list.has(var_id)) { + variable_list.find(var_id)->value.clear(); } else { - variable_list.insert(variable_id, Array()); + variable_list.insert(var_id, Array()); } } } @@ -937,10 +938,10 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) { DAP::StackFrame frame; frame.id = _current_frame; - List<int> scope_ids = stackframe_list.find(frame)->value(); + List<int> scope_ids = stackframe_list.find(frame)->value; ERR_FAIL_COND(scope_ids.size() != 3); ERR_FAIL_INDEX(stack_var.type, 3); - int variable_id = scope_ids[stack_var.type]; + int var_id = scope_ids[stack_var.type]; DAP::Variable variable; @@ -949,7 +950,7 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) { variable.type = Variant::get_type_name(stack_var.value.get_type()); variable.variablesReference = parse_variant(stack_var.value); - variable_list.find(variable_id)->value().push_back(variable.to_json()); + variable_list.find(var_id)->value.push_back(variable.to_json()); _remaining_vars--; } diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.h b/editor/debugger/debug_adapter/debug_adapter_protocol.h index b54a5f1f3f..ddc55816db 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.h +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* debug_adapter_protocol.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_protocol.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DEBUG_ADAPTER_PROTOCOL_H #define DEBUG_ADAPTER_PROTOCOL_H @@ -76,7 +76,7 @@ class DebugAdapterProtocol : public Object { private: static DebugAdapterProtocol *singleton; - DebugAdapterParser *parser; + DebugAdapterParser *parser = nullptr; List<Ref<DAPeer>> clients; Ref<TCPServer> server; @@ -111,12 +111,12 @@ private: String _current_request; Ref<DAPeer> _current_peer; - int breakpoint_id; - int stackframe_id; - int variable_id; + int breakpoint_id = 0; + int stackframe_id = 0; + int variable_id = 0; List<DAP::Breakpoint> breakpoint_list; - Map<DAP::StackFrame, List<int>> stackframe_list; - Map<int, Array> variable_list; + HashMap<DAP::StackFrame, List<int>, DAP::StackFrame> stackframe_list; + HashMap<int, Array> variable_list; public: friend class DebugAdapterServer; @@ -152,4 +152,4 @@ public: ~DebugAdapterProtocol(); }; -#endif +#endif // DEBUG_ADAPTER_PROTOCOL_H diff --git a/editor/debugger/debug_adapter/debug_adapter_server.cpp b/editor/debugger/debug_adapter/debug_adapter_server.cpp index ffbbf66015..9cecc4bb08 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_server.cpp @@ -1,38 +1,39 @@ -/*************************************************************************/ -/* debug_adapter_server.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_server.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "debug_adapter_server.h" #include "core/os/os.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" DebugAdapterServer::DebugAdapterServer() { _EDITOR_DEF("network/debug_adapter/remote_port", remote_port); @@ -42,12 +43,14 @@ DebugAdapterServer::DebugAdapterServer() { void DebugAdapterServer::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_ENTER_TREE: { start(); - break; - case NOTIFICATION_EXIT_TREE: + } break; + + case NOTIFICATION_EXIT_TREE: { stop(); - break; + } break; + case NOTIFICATION_INTERNAL_PROCESS: { // The main loop can be run again during request processing, which modifies internal state of the protocol. // Thus, "polling" is needed to prevent it from parsing other requests while the current one isn't finished. @@ -57,13 +60,14 @@ void DebugAdapterServer::_notification(int p_what) { polling = false; } } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - protocol._request_timeout = EditorSettings::get_singleton()->get("network/debug_adapter/request_timeout"); - protocol._sync_breakpoints = EditorSettings::get_singleton()->get("network/debug_adapter/sync_breakpoints"); - int remote_port = (int)_EDITOR_GET("network/debug_adapter/remote_port"); - if (remote_port != this->remote_port) { - this->stop(); - this->start(); + protocol._request_timeout = EDITOR_GET("network/debug_adapter/request_timeout"); + protocol._sync_breakpoints = EDITOR_GET("network/debug_adapter/sync_breakpoints"); + int port = (int)_EDITOR_GET("network/debug_adapter/remote_port"); + if (port != remote_port) { + stop(); + start(); } } break; } diff --git a/editor/debugger/debug_adapter/debug_adapter_server.h b/editor/debugger/debug_adapter/debug_adapter_server.h index a2b01f92c6..c834ab2182 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.h +++ b/editor/debugger/debug_adapter/debug_adapter_server.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* debug_adapter_server.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_server.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DEBUG_ADAPTER_SERVER_H #define DEBUG_ADAPTER_SERVER_H @@ -54,4 +54,4 @@ public: void stop(); }; -#endif +#endif // DEBUG_ADAPTER_SERVER_H diff --git a/editor/debugger/debug_adapter/debug_adapter_types.h b/editor/debugger/debug_adapter/debug_adapter_types.h index 77b70909b3..c776a92df7 100644 --- a/editor/debugger/debug_adapter/debug_adapter_types.h +++ b/editor/debugger/debug_adapter/debug_adapter_types.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* debug_adapter_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* debug_adapter_types.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DEBUG_ADAPTER_TYPES_H #define DEBUG_ADAPTER_TYPES_H @@ -219,8 +219,11 @@ struct StackFrame { int line; int column; - bool operator<(const StackFrame &p_other) const { - return id < p_other.id; + static uint32_t hash(const StackFrame &p_frame) { + return hash_murmur3_one_32(p_frame.id); + } + bool operator==(const StackFrame &p_other) const { + return id == p_other.id; } _FORCE_INLINE_ void from_json(const Dictionary &p_params) { @@ -275,4 +278,4 @@ struct Variable { } // namespace DAP -#endif +#endif // DEBUG_ADAPTER_TYPES_H diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index addb168e5f..e083e1746d 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* editor_debugger_inspector.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_debugger_inspector.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_debugger_inspector.h" @@ -36,7 +36,7 @@ #include "scene/debugger/scene_debugger.h" bool EditorDebuggerRemoteObject::_set(const StringName &p_name, const Variant &p_value) { - if (!editable || !prop_values.has(p_name) || String(p_name).begins_with("Constants/")) { + if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/")) { return false; } @@ -68,7 +68,7 @@ void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list) String EditorDebuggerRemoteObject::get_title() { if (remote_object_id.is_valid()) { - return TTR("Remote ") + String(type_name) + ": " + itos(remote_object_id); + return vformat(TTR("Remote %s:"), String(type_name)) + " " + itos(remote_object_id); } else { return "<null>"; } @@ -91,7 +91,6 @@ void EditorDebuggerRemoteObject::_bind_methods() { EditorDebuggerInspector::EditorDebuggerInspector() { variables = memnew(EditorDebuggerRemoteObject); - variables->editable = false; } EditorDebuggerInspector::~EditorDebuggerInspector() { @@ -107,14 +106,13 @@ void EditorDebuggerInspector::_bind_methods() { void EditorDebuggerInspector::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_POSTINITIALIZE: + case NOTIFICATION_POSTINITIALIZE: { connect("object_id_selected", callable_mp(this, &EditorDebuggerInspector::_object_selected)); - break; - case NOTIFICATION_ENTER_TREE: + } break; + + case NOTIFICATION_ENTER_TREE: { edit(variables); - break; - default: - break; + } break; } } @@ -127,27 +125,27 @@ void EditorDebuggerInspector::_object_selected(ObjectID p_object) { } ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { - EditorDebuggerRemoteObject *debugObj = nullptr; + EditorDebuggerRemoteObject *debug_obj = nullptr; SceneDebuggerObject obj; obj.deserialize(p_arr); ERR_FAIL_COND_V(obj.id.is_null(), ObjectID()); if (remote_objects.has(obj.id)) { - debugObj = remote_objects[obj.id]; + debug_obj = remote_objects[obj.id]; } else { - debugObj = memnew(EditorDebuggerRemoteObject); - debugObj->remote_object_id = obj.id; - debugObj->type_name = obj.class_name; - remote_objects[obj.id] = debugObj; - debugObj->connect("value_edited", callable_mp(this, &EditorDebuggerInspector::_object_edited)); + debug_obj = memnew(EditorDebuggerRemoteObject); + debug_obj->remote_object_id = obj.id; + debug_obj->type_name = obj.class_name; + remote_objects[obj.id] = debug_obj; + debug_obj->connect("value_edited", callable_mp(this, &EditorDebuggerInspector::_object_edited)); } - int old_prop_size = debugObj->prop_list.size(); + int old_prop_size = debug_obj->prop_list.size(); - debugObj->prop_list.clear(); + debug_obj->prop_list.clear(); int new_props_added = 0; - Set<String> changed; + HashSet<String> changed; for (int i = 0; i < obj.properties.size(); i++) { PropertyInfo &pinfo = obj.properties[i].first; Variant &var = obj.properties[i].second; @@ -155,10 +153,10 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { if (pinfo.type == Variant::OBJECT) { if (var.get_type() == Variant::STRING) { String path = var; - if (path.find("::") != -1) { + if (path.contains("::")) { // built-in resource String base_path = path.get_slice("::", 0); - RES dependency = ResourceLoader::load(base_path); + Ref<Resource> dependency = ResourceLoader::load(base_path); if (dependency.is_valid()) { remote_dependencies.insert(dependency); } @@ -166,12 +164,14 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { var = ResourceLoader::load(path); if (pinfo.hint_string == "Script") { - if (debugObj->get_script() != var) { - debugObj->set_script(REF()); - Ref<Script> script(var); - if (!script.is_null()) { - ScriptInstance *script_instance = script->placeholder_instance_create(debugObj); - debugObj->set_script_and_instance(var, script_instance); + if (debug_obj->get_script() != var) { + debug_obj->set_script(Ref<RefCounted>()); + Ref<Script> scr(var); + if (!scr.is_null()) { + ScriptInstance *scr_instance = scr->placeholder_instance_create(debug_obj); + if (scr_instance) { + debug_obj->set_script_and_instance(var, scr_instance); + } } } } @@ -179,27 +179,27 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { } //always add the property, since props may have been added or removed - debugObj->prop_list.push_back(pinfo); + debug_obj->prop_list.push_back(pinfo); - if (!debugObj->prop_values.has(pinfo.name)) { + if (!debug_obj->prop_values.has(pinfo.name)) { new_props_added++; - debugObj->prop_values[pinfo.name] = var; + debug_obj->prop_values[pinfo.name] = var; } else { - if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) { - debugObj->prop_values[pinfo.name] = var; + if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debug_obj->prop_values[pinfo.name], var))) { + debug_obj->prop_values[pinfo.name] = var; changed.insert(pinfo.name); } } } - if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) { + if (old_prop_size == debug_obj->prop_list.size() && new_props_added == 0) { //only some may have changed, if so, then update those, if exist - for (Set<String>::Element *E = changed.front(); E; E = E->next()) { - emit_signal(SNAME("object_property_updated"), debugObj->remote_object_id, E->get()); + for (const String &E : changed) { + emit_signal(SNAME("object_property_updated"), debug_obj->remote_object_id, E); } } else { //full update, because props were added or removed - debugObj->update(); + debug_obj->update(); } return obj.id; } @@ -207,7 +207,7 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { void EditorDebuggerInspector::clear_cache() { for (const KeyValue<ObjectID, EditorDebuggerRemoteObject *> &E : remote_objects) { EditorNode *editor = EditorNode::get_singleton(); - if (editor->get_editor_history()->get_current() == E.value->get_instance_id()) { + if (editor->get_editor_selection_history()->get_current() == E.value->get_instance_id()) { editor->push_item(nullptr); } memdelete(E.value); @@ -230,7 +230,7 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { Variant v = var.value; PropertyHint h = PROPERTY_HINT_NONE; - String hs = String(); + String hs; if (v.get_type() == Variant::OBJECT) { v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); @@ -262,13 +262,26 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { variables->prop_values[type + n] = v; variables->update(); edit(variables); + + // To prevent constantly resizing when using filtering. + int size_x = get_size().x; + if (size_x > get_custom_minimum_size().x) { + set_custom_minimum_size(Size2(size_x, 0)); + } } void EditorDebuggerInspector::clear_stack_variables() { variables->clear(); variables->update(); + set_custom_minimum_size(Size2(0, 0)); } String EditorDebuggerInspector::get_stack_variable(const String &p_var) { - return variables->get_variant(p_var); + for (KeyValue<StringName, Variant> &E : variables->prop_values) { + String v = E.key.operator String(); + if (v.get_slice("/", 1) == p_var) { + return variables->get_variant(v); + } + } + return String(); } diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index 5cdc4417d0..73dd773750 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -1,35 +1,36 @@ -/*************************************************************************/ -/* editor_debugger_inspector.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_debugger_inspector.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_DEBUGGER_INSPECTOR_H #define EDITOR_DEBUGGER_INSPECTOR_H + #include "editor/editor_inspector.h" class EditorDebuggerRemoteObject : public Object { @@ -42,11 +43,10 @@ protected: static void _bind_methods(); public: - bool editable = false; ObjectID remote_object_id; String type_name; List<PropertyInfo> prop_list; - Map<StringName, Variant> prop_values; + HashMap<StringName, Variant> prop_values; ObjectID get_remote_object_id() { return remote_object_id; }; String get_title(); @@ -68,9 +68,9 @@ class EditorDebuggerInspector : public EditorInspector { private: ObjectID inspected_object_id; - Map<ObjectID, EditorDebuggerRemoteObject *> remote_objects; - Set<RES> remote_dependencies; - EditorDebuggerRemoteObject *variables; + HashMap<ObjectID, EditorDebuggerRemoteObject *> remote_objects; + HashSet<Ref<Resource>> remote_dependencies; + EditorDebuggerRemoteObject *variables = nullptr; void _object_selected(ObjectID p_object); void _object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value); diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index 5bc2be60a7..a368cacf56 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -1,43 +1,49 @@ -/*************************************************************************/ -/* editor_debugger_node.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_debugger_node.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_debugger_node.h" +#include "core/object/undo_redo.h" #include "editor/debugger/editor_debugger_tree.h" #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" +#include "editor/inspector_dock.h" #include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/script_editor_plugin.h" +#include "editor/scene_tree_dock.h" #include "scene/gui/menu_button.h" #include "scene/gui/tab_container.h" +#include "scene/resources/packed_scene.h" template <typename Func> void _for_all(TabContainer *p_node, const Func &p_func) { @@ -59,7 +65,6 @@ EditorDebuggerNode::EditorDebuggerNode() { add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("BottomPanelDebuggerOverride"), SNAME("EditorStyles"))->get_margin(SIDE_RIGHT)); tabs = memnew(TabContainer); - tabs->set_tab_alignment(TabContainer::ALIGNMENT_LEFT); tabs->set_tabs_visible(false); tabs->connect("tab_changed", callable_mp(this, &EditorDebuggerNode::_debugger_changed)); add_child(tabs); @@ -75,32 +80,33 @@ EditorDebuggerNode::EditorDebuggerNode() { remote_scene_tree = memnew(EditorDebuggerTree); remote_scene_tree->connect("object_selected", callable_mp(this, &EditorDebuggerNode::_remote_object_requested)); remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested)); - EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(remote_scene_tree); - EditorNode::get_singleton()->get_scene_tree_dock()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree)); + remote_scene_tree->connect("button_clicked", callable_mp(this, &EditorDebuggerNode::_remote_tree_button_pressed)); + SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree); + SceneTreeDock::get_singleton()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree)); remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0); inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2); EditorNode *editor = EditorNode::get_singleton(); - editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this); - editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this); editor->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused)); } ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() { - ScriptEditorDebugger *node = memnew(ScriptEditorDebugger(EditorNode::get_singleton())); + ScriptEditorDebugger *node = memnew(ScriptEditorDebugger); int id = tabs->get_tab_count(); - node->connect("stop_requested", callable_mp(this, &EditorDebuggerNode::_debugger_wants_stop), varray(id)); - node->connect("stopped", callable_mp(this, &EditorDebuggerNode::_debugger_stopped), varray(id)); - node->connect("stack_frame_selected", callable_mp(this, &EditorDebuggerNode::_stack_frame_selected), varray(id)); - node->connect("error_selected", callable_mp(this, &EditorDebuggerNode::_error_selected), varray(id)); + node->connect("stop_requested", callable_mp(this, &EditorDebuggerNode::_debugger_wants_stop).bind(id)); + node->connect("stopped", callable_mp(this, &EditorDebuggerNode::_debugger_stopped).bind(id)); + node->connect("stack_frame_selected", callable_mp(this, &EditorDebuggerNode::_stack_frame_selected).bind(id)); + node->connect("error_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id)); + node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id)); node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution)); - node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked), varray(id)); - node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated), varray(id)); - node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated), varray(id)); - node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated), varray(id)); - node->connect("remote_object_requested", callable_mp(this, &EditorDebuggerNode::_remote_object_requested), varray(id)); + node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id)); + node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id)); + node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id)); + node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id)); + node->connect("remote_object_requested", callable_mp(this, &EditorDebuggerNode::_remote_object_requested).bind(id)); + node->connect("errors_cleared", callable_mp(this, &EditorDebuggerNode::_update_errors)); if (tabs->get_tab_count() > 0) { get_debugger(0)->clear_style(); @@ -116,8 +122,8 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() { } if (!debugger_plugins.is_empty()) { - for (Set<Ref<Script>>::Element *i = debugger_plugins.front(); i; i = i->next()) { - node->add_debugger_plugin(i->get()); + for (Ref<EditorDebuggerPlugin> plugin : debugger_plugins) { + plugin->create_session(node); } } @@ -139,11 +145,22 @@ 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(); + String file = p_debugger->get_stack_script_file(); if (file.is_empty()) { return; } - stack_script = ResourceLoader::load(file); + if (file.is_resource_file()) { + stack_script = ResourceLoader::load(file); + } else { + // If the script is built-in, it can be opened only if the scene is loaded in memory. + int i = file.find("::"); + int j = file.rfind("(", i); + if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path. + file = file.substr(j + 1, file.find(")", i) - j - 1); + } + Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0)); + stack_script = ResourceLoader::load(file); + } const int line = p_debugger->get_stack_script_line() - 1; emit_signal(SNAME("goto_script_line"), stack_script, line); emit_signal(SNAME("set_execution"), stack_script, line); @@ -153,7 +170,7 @@ void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_d void EditorDebuggerNode::_bind_methods() { // LiveDebug. ClassDB::bind_method("live_debug_create_node", &EditorDebuggerNode::live_debug_create_node); - ClassDB::bind_method("live_debug_instance_node", &EditorDebuggerNode::live_debug_instance_node); + ClassDB::bind_method("live_debug_instantiate_node", &EditorDebuggerNode::live_debug_instantiate_node); ClassDB::bind_method("live_debug_remove_node", &EditorDebuggerNode::live_debug_remove_node); ClassDB::bind_method("live_debug_remove_and_keep_node", &EditorDebuggerNode::live_debug_remove_and_keep_node); ClassDB::bind_method("live_debug_restore_node", &EditorDebuggerNode::live_debug_restore_node); @@ -167,8 +184,13 @@ void EditorDebuggerNode::_bind_methods() { ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); } +void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) { + p_undo_redo->set_method_notify_callback(_method_changeds, this); + p_undo_redo->set_property_notify_callback(_property_changeds, this); +} + EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() { - return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_current())); + return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_current())); } ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const { @@ -188,9 +210,32 @@ String EditorDebuggerNode::get_server_uri() const { return server->get_uri(); } +void EditorDebuggerNode::set_keep_open(bool p_keep_open) { + keep_open = p_keep_open; + if (keep_open) { + if (server.is_null() || !server->is_active()) { + start(); + } + } else { + bool found = false; + _for_all(tabs, [&](ScriptEditorDebugger *p_debugger) { + if (p_debugger->is_session_active()) { + found = true; + } + }); + if (!found) { + stop(); + } + } +} + Error EditorDebuggerNode::start(const String &p_uri) { - stop(); ERR_FAIL_COND_V(p_uri.find("://") < 0, ERR_INVALID_PARAMETER); + if (keep_open && current_uri == p_uri && server.is_valid()) { + return OK; + } + stop(true); + current_uri = p_uri; if (EDITOR_GET("run/output/always_open_output_on_play")) { EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log()); } else { @@ -206,10 +251,20 @@ Error EditorDebuggerNode::start(const String &p_uri) { return OK; } -void EditorDebuggerNode::stop() { +void EditorDebuggerNode::stop(bool p_force) { + if (keep_open && !p_force) { + return; + } + current_uri.clear(); if (server.is_valid()) { server->stop(); EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR); + + if (EditorNode::get_singleton()->is_movie_maker_enabled()) { + // Request attention in case the user was doing something else when movie recording is finished. + DisplayServer::get_singleton()->window_request_attention(); + } + server.unref(); } // Also close all debugging sessions. @@ -219,12 +274,8 @@ void EditorDebuggerNode::stop() { } }); _break_state_changed(); - if (hide_on_stop) { - if (is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } - } breakpoints.clear(); + EditorUndoRedoManager::get_singleton()->clear_history(false, EditorUndoRedoManager::REMOTE_HISTORY); set_process(false); } @@ -238,24 +289,84 @@ void EditorDebuggerNode::_notification(int p_what) { tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("DebuggerPanel"), SNAME("EditorStyles"))); } } break; + case NOTIFICATION_READY: { _update_debug_options(); } break; - default: - break; - } - if (p_what != NOTIFICATION_PROCESS || !server.is_valid()) { - return; - } + case NOTIFICATION_PROCESS: { + if (!server.is_valid()) { + return; + } - if (!server.is_valid() || !server->is_active()) { - stop(); - return; + if (!server->is_active()) { + stop(); + return; + } + server->poll(); + + _update_errors(); + + // Remote scene tree update + remote_scene_tree_timeout -= get_process_delta_time(); + if (remote_scene_tree_timeout < 0) { + remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval"); + if (remote_scene_tree->is_visible_in_tree()) { + get_current_debugger()->request_remote_tree(); + } + } + + // Remote inspector update + inspect_edited_object_timeout -= get_process_delta_time(); + if (inspect_edited_object_timeout < 0) { + inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval"); + if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) { + get_current_debugger()->request_remote_object(obj->remote_object_id); + } + } + + // Take connections. + if (server->is_connection_available()) { + ScriptEditorDebugger *debugger = nullptr; + _for_all(tabs, [&](ScriptEditorDebugger *dbg) { + if (debugger || dbg->is_session_active()) { + return; + } + debugger = dbg; + }); + if (debugger == nullptr) { + if (tabs->get_tab_count() <= 4) { // Max 4 debugging sessions active. + debugger = _add_debugger(); + } else { + // We already have too many sessions, disconnecting new clients to prevent them from hanging. + server->take_connection()->close(); + return; // Can't add, stop here. + } + } + + EditorNode::get_singleton()->get_pause_button()->set_disabled(false); + // Switch to remote tree view if so desired. + auto_switch_remote_scene_tree = (bool)EDITOR_GET("debugger/auto_switch_to_remote_scene_tree"); + if (auto_switch_remote_scene_tree) { + SceneTreeDock::get_singleton()->show_remote_tree(); + } + // Good to go. + SceneTreeDock::get_singleton()->show_tab_buttons(); + debugger->set_editor_remote_tree(remote_scene_tree); + debugger->start(server->take_connection()); + // Send breakpoints. + for (const KeyValue<Breakpoint, bool> &E : breakpoints) { + const Breakpoint &bp = E.key; + debugger->set_breakpoint(bp.source, bp.line, E.value); + } // Will arrive too late, how does the regular run work? + + debugger->update_live_edit_root(); + } + } break; } - server->poll(); +} - // Errors and warnings +void EditorDebuggerNode::_update_errors() { int error_count = 0; int warning_count = 0; _for_all(tabs, [&](ScriptEditorDebugger *dbg) { @@ -289,62 +400,6 @@ void EditorDebuggerNode::_notification(int p_what) { last_error_count = error_count; last_warning_count = warning_count; } - - // Remote scene tree update - remote_scene_tree_timeout -= get_process_delta_time(); - if (remote_scene_tree_timeout < 0) { - remote_scene_tree_timeout = EditorSettings::get_singleton()->get("debugger/remote_scene_tree_refresh_interval"); - if (remote_scene_tree->is_visible_in_tree()) { - get_current_debugger()->request_remote_tree(); - } - } - - // Remote inspector update - inspect_edited_object_timeout -= get_process_delta_time(); - if (inspect_edited_object_timeout < 0) { - inspect_edited_object_timeout = EditorSettings::get_singleton()->get("debugger/remote_inspect_refresh_interval"); - if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) { - get_current_debugger()->request_remote_object(obj->remote_object_id); - } - } - - // Take connections. - if (server->is_connection_available()) { - ScriptEditorDebugger *debugger = nullptr; - _for_all(tabs, [&](ScriptEditorDebugger *dbg) { - if (debugger || dbg->is_session_active()) { - return; - } - debugger = dbg; - }); - if (debugger == nullptr) { - if (tabs->get_tab_count() <= 4) { // Max 4 debugging sessions active. - debugger = _add_debugger(); - } else { - // We already have too many sessions, disconnecting new clients to prevent them from hanging. - server->take_connection()->close(); - return; // Can't add, stop here. - } - } - - EditorNode::get_singleton()->get_pause_button()->set_disabled(false); - // Switch to remote tree view if so desired. - auto_switch_remote_scene_tree = (bool)EditorSettings::get_singleton()->get("debugger/auto_switch_to_remote_scene_tree"); - if (auto_switch_remote_scene_tree) { - EditorNode::get_singleton()->get_scene_tree_dock()->show_remote_tree(); - } - // Good to go. - EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons(); - debugger->set_editor_remote_tree(remote_scene_tree); - debugger->start(server->take_connection()); - // Send breakpoints. - for (const KeyValue<Breakpoint, bool> &E : breakpoints) { - const Breakpoint &bp = E.key; - debugger->set_breakpoint(bp.source, bp.line, E.value); - } // Will arrive too late, how does the regular run work? - - debugger->update_live_edit_root(); - } } void EditorDebuggerNode::_debugger_stopped(int p_id) { @@ -360,8 +415,8 @@ void EditorDebuggerNode::_debugger_stopped(int p_id) { if (!found) { EditorNode::get_singleton()->get_pause_button()->set_pressed(false); EditorNode::get_singleton()->get_pause_button()->set_disabled(true); - EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree(); - EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons(); + SceneTreeDock::get_singleton()->hide_remote_tree(); + SceneTreeDock::get_singleton()->hide_tab_buttons(); EditorNode::get_singleton()->notify_all_debug_sessions_exited(); } } @@ -399,7 +454,6 @@ 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_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)); @@ -439,12 +493,6 @@ void EditorDebuggerNode::_menu_option(int p_id) { case DEBUG_CONTINUE: { debug_continue(); } break; - 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 ischecked = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR)); debug_with_external_editor = !ischecked; @@ -455,13 +503,7 @@ void EditorDebuggerNode::_menu_option(int p_id) { } 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) { + if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "debug_with_external_editor", false).operator bool()) { _menu_option(DEBUG_WITH_EXTERNAL_EDITOR); } } @@ -499,7 +541,7 @@ void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p dbg->set_breakpoint(p_path, p_line, p_enabled); }); - emit_signal("breakpoint_toggled", p_path, p_line, p_enabled); + emit_signal(SNAME("breakpoint_toggled"), p_path, p_line, p_enabled); } void EditorDebuggerNode::set_breakpoints(const String &p_path, Array p_lines) { @@ -554,6 +596,24 @@ void EditorDebuggerNode::_remote_tree_updated(int p_debugger) { remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger); } +void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (p_button != MouseButton::LEFT) { + return; + } + + TreeItem *item = Object::cast_to<TreeItem>(p_item); + ERR_FAIL_COND(!item); + + if (p_id == EditorDebuggerTree::BUTTON_SUBSCENE) { + remote_scene_tree->emit_signal(SNAME("open"), item->get_meta("scene_file_path")); + } else if (p_id == EditorDebuggerTree::BUTTON_VISIBILITY) { + ObjectID obj_id = item->get_metadata(0); + ERR_FAIL_COND(obj_id.is_null()); + get_current_debugger()->update_remote_object(obj_id, "visible", !item->get_meta("visible")); + get_current_debugger()->request_remote_tree(); + } +} + void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) { if (p_debugger != tabs->get_current_tab()) { return; @@ -575,7 +635,7 @@ void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const St if (obj->remote_object_id != p_id) { return; } - EditorNode::get_singleton()->get_inspector()->update_property(p_property); + InspectorDock::get_inspector_singleton()->update_property(p_property); } } @@ -595,12 +655,12 @@ void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_fil } // Remote inspector/edit. -void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) { +void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) { if (!singleton) { return; } _for_all(singleton->tabs, [&](ScriptEditorDebugger *dbg) { - dbg->_method_changed(p_base, p_name, VARIANT_ARG_PASS); + dbg->_method_changed(p_base, p_name, p_args, p_argcount); }); } @@ -632,9 +692,9 @@ void EditorDebuggerNode::live_debug_create_node(const NodePath &p_parent, const }); } -void EditorDebuggerNode::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) { +void EditorDebuggerNode::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) { _for_all(tabs, [&](ScriptEditorDebugger *dbg) { - dbg->live_debug_instance_node(p_parent, p_path, p_name); + dbg->live_debug_instantiate_node(p_parent, p_path, p_name); }); } @@ -679,22 +739,36 @@ EditorDebuggerNode::CameraOverride EditorDebuggerNode::get_camera_override() { return camera_override; } -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); +void EditorDebuggerNode::add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) { + ERR_FAIL_COND_MSG(p_plugin.is_null(), "Debugger plugin is null."); + ERR_FAIL_COND_MSG(debugger_plugins.has(p_plugin), "Debugger plugin already exists."); + debugger_plugins.insert(p_plugin); + + Ref<EditorDebuggerPlugin> plugin = p_plugin; for (int i = 0; get_debugger(i); i++) { - get_debugger(i)->add_debugger_plugin(p_script); + plugin->create_session(get_debugger(i)); } } -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); +void EditorDebuggerNode::remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) { + ERR_FAIL_COND_MSG(p_plugin.is_null(), "Debugger plugin is null."); + ERR_FAIL_COND_MSG(!debugger_plugins.has(p_plugin), "Debugger plugin doesn't exists."); + debugger_plugins.erase(p_plugin); + Ref<EditorDebuggerPlugin>(p_plugin)->clear(); +} + +bool EditorDebuggerNode::plugins_capture(ScriptEditorDebugger *p_debugger, const String &p_message, const Array &p_data) { + int session_index = tabs->get_tab_idx_from_control(p_debugger); + ERR_FAIL_COND_V(session_index < 0, false); + int colon_index = p_message.find_char(':'); + ERR_FAIL_COND_V_MSG(colon_index < 1, false, "Invalid message received."); + + const String cap = p_message.substr(0, colon_index); + bool parsed = false; + for (Ref<EditorDebuggerPlugin> plugin : debugger_plugins) { + if (plugin->has_capture(cap)) { + parsed |= plugin->capture(p_message, p_data, session_index); + } } + return parsed; } diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h index 6fcdbf5f73..e855fa6ee0 100644 --- a/editor/debugger/editor_debugger_node.h +++ b/editor/debugger/editor_debugger_node.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* editor_debugger_node.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_debugger_node.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_DEBUGGER_NODE_H #define EDITOR_DEBUGGER_NODE_H @@ -36,11 +36,13 @@ class Button; class DebugAdapterParser; +class EditorDebuggerPlugin; class EditorDebuggerTree; class EditorDebuggerRemoteObject; class MenuButton; class ScriptEditorDebugger; class TabContainer; +class UndoRedo; class EditorDebuggerNode : public MarginContainer { GDCLASS(EditorDebuggerNode, MarginContainer); @@ -61,7 +63,6 @@ private: DEBUG_STEP, DEBUG_BREAK, DEBUG_CONTINUE, - DEBUG_KEEP_DEBUGGER_OPEN, DEBUG_WITH_EXTERNAL_EDITOR, }; @@ -70,6 +71,14 @@ private: String source; int line = 0; + static uint32_t hash(const Breakpoint &p_val) { + uint32_t h = HashMapHasherDefault::hash(p_val.source); + return hash_murmur3_one_32(p_val.line, h); + } + bool operator==(const Breakpoint &p_b) const { + return (line == p_b.line && source == p_b.source); + } + bool operator<(const Breakpoint &p_b) const { if (line == p_b.line) { return source < p_b.source; @@ -100,14 +109,17 @@ private: float remote_scene_tree_timeout = 0.0; bool auto_switch_remote_scene_tree = false; bool debug_with_external_editor = false; - bool hide_on_stop = true; + bool keep_open = false; + String current_uri; + CameraOverride camera_override = OVERRIDE_NONE; - Map<Breakpoint, bool> breakpoints; + HashMap<Breakpoint, bool, Breakpoint> breakpoints; - Set<Ref<Script>> debugger_plugins; + HashSet<Ref<EditorDebuggerPlugin>> debugger_plugins; ScriptEditorDebugger *_add_debugger(); EditorDebuggerRemoteObject *get_inspected_remote_object(); + void _update_errors(); friend class DebuggerEditorPlugin; friend class DebugAdapterParser; @@ -119,12 +131,13 @@ protected: void _debugger_wants_stop(int p_id); void _debugger_changed(int p_tab); void _remote_tree_updated(int p_debugger); + void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _remote_object_updated(ObjectID p_id, int p_debugger); void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger); void _remote_object_requested(ObjectID p_id, int p_debugger); void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger); - void _clear_execution(REF p_script) { + void _clear_execution(Ref<RefCounted> p_script) { emit_signal(SNAME("clear_execution"), p_script); } @@ -143,6 +156,7 @@ protected: public: static EditorDebuggerNode *get_singleton() { return singleton; } + void register_undo_redo(UndoRedo *p_undo_redo); ScriptEditorDebugger *get_current_debugger() const; ScriptEditorDebugger *get_default_debugger() const; @@ -171,14 +185,14 @@ public: // Remote inspector/edit. void request_remote_tree(); - static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE); + static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); // LiveDebug void set_live_debugging(bool p_enabled); void update_live_edit_root(); void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name); - void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name); + void live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name); void live_debug_remove_node(const NodePath &p_at); void live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id); void live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos); @@ -190,10 +204,13 @@ public: String get_server_uri() const; + void set_keep_open(bool p_keep_open); Error start(const String &p_uri = "tcp://"); - void stop(); + void stop(bool p_force = false); - void add_debugger_plugin(const Ref<Script> &p_script); - void remove_debugger_plugin(const Ref<Script> &p_script); + bool plugins_capture(ScriptEditorDebugger *p_debugger, const String &p_message, const Array &p_data); + void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin); + void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin); }; + #endif // EDITOR_DEBUGGER_NODE_H diff --git a/editor/debugger/editor_debugger_server.cpp b/editor/debugger/editor_debugger_server.cpp index bce131a5fe..c0efc6a1fc 100644 --- a/editor/debugger/editor_debugger_server.cpp +++ b/editor/debugger/editor_debugger_server.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* editor_debugger_server.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_debugger_server.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_debugger_server.h" @@ -72,8 +72,8 @@ String EditorDebuggerServerTCP::get_uri() const { Error EditorDebuggerServerTCP::start(const String &p_uri) { // Default host and port - String bind_host = (String)EditorSettings::get_singleton()->get("network/debug/remote_host"); - int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); + String bind_host = (String)EDITOR_GET("network/debug/remote_host"); + int bind_port = (int)EDITOR_GET("network/debug/remote_port"); // Optionally override if (!p_uri.is_empty() && p_uri != "tcp://") { @@ -122,7 +122,7 @@ Ref<RemoteDebuggerPeer> EditorDebuggerServerTCP::take_connection() { } /// EditorDebuggerServer -Map<StringName, EditorDebuggerServer::CreateServerFunc> EditorDebuggerServer::protocols; +HashMap<StringName, EditorDebuggerServer::CreateServerFunc> EditorDebuggerServer::protocols; EditorDebuggerServer *EditorDebuggerServer::create(const String &p_protocol) { ERR_FAIL_COND_V(!protocols.has(p_protocol), nullptr); diff --git a/editor/debugger/editor_debugger_server.h b/editor/debugger/editor_debugger_server.h index bda4a1ce7d..e171f0fdaf 100644 --- a/editor/debugger/editor_debugger_server.h +++ b/editor/debugger/editor_debugger_server.h @@ -1,35 +1,35 @@ -/*************************************************************************/ -/* editor_debugger_server.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef EDITOR_DEBUGGER_CONNECTION_H -#define EDITOR_DEBUGGER_CONNECTION_H +/**************************************************************************/ +/* editor_debugger_server.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_DEBUGGER_SERVER_H +#define EDITOR_DEBUGGER_SERVER_H #include "core/debugger/remote_debugger_peer.h" #include "core/object/ref_counted.h" @@ -39,7 +39,7 @@ public: typedef EditorDebuggerServer *(*CreateServerFunc)(const String &p_uri); private: - static Map<StringName, CreateServerFunc> protocols; + static HashMap<StringName, CreateServerFunc> protocols; public: static void initialize(); @@ -57,4 +57,4 @@ public: virtual Ref<RemoteDebuggerPeer> take_connection() = 0; }; -#endif // EDITOR_DEBUGGER_CONNECTION_H +#endif // EDITOR_DEBUGGER_SERVER_H diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index 70d64615ae..c67ac69946 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -1,37 +1,40 @@ -/*************************************************************************/ -/* editor_debugger_tree.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_debugger_tree.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_debugger_tree.h" +#include "editor/editor_file_dialog.h" #include "editor/editor_node.h" +#include "editor/scene_tree_dock.h" #include "scene/debugger/scene_debugger.h" +#include "scene/gui/texture_rect.h" #include "scene/resources/packed_scene.h" #include "servers/display_server.h" @@ -51,16 +54,19 @@ EditorDebuggerTree::EditorDebuggerTree() { } void EditorDebuggerTree::_notification(int p_what) { - if (p_what == NOTIFICATION_POSTINITIALIZE) { - connect("cell_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_selected)); - connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded)); - connect("item_rmb_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected)); + switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + connect("cell_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_selected)); + connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded)); + connect("item_mouse_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected)); + } break; } } void EditorDebuggerTree::_bind_methods() { ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger"))); ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger"))); + ADD_SIGNAL(MethodInfo("open")); } void EditorDebuggerTree::_scene_tree_selected() { @@ -96,7 +102,11 @@ void EditorDebuggerTree::_scene_tree_folded(Object *p_obj) { } } -void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) { +void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, MouseButton p_button) { + if (p_button != MouseButton::RIGHT) { + return; + } + TreeItem *item = get_item_at_position(p_position); if (!item) { return; @@ -108,6 +118,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) { item_menu->add_icon_item(get_theme_icon(SNAME("CreateNewSceneFrom"), SNAME("EditorIcons")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE); item_menu->add_icon_item(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH); item_menu->set_position(get_screen_position() + get_local_mouse_position()); + item_menu->reset_size(); item_menu->popup(); } @@ -128,7 +139,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) { void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) { updating_scene_tree = true; const String last_path = get_selected_path(); - const String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter(); + const String filter = SceneTreeDock::get_singleton()->get_filter(); bool filter_changed = filter != last_filter; TreeItem *scroll_item = nullptr; @@ -147,14 +158,18 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int const SceneDebuggerTree::RemoteNode &node = p_tree->nodes[i]; TreeItem *item = create_item(parent); item->set_text(0, node.name); - item->set_tooltip(0, TTR("Type:") + " " + node.type_name); + if (node.scene_file_path.is_empty()) { + item->set_tooltip_text(0, node.name + "\n" + TTR("Type:") + " " + node.type_name); + } else { + item->set_tooltip_text(0, node.name + "\n" + TTR("Instance:") + " " + node.scene_file_path + "\n" + TTR("Type:") + " " + node.type_name); + } Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(node.type_name, ""); if (icon.is_valid()) { item->set_icon(0, icon); } item->set_metadata(0, node.id); - // Set current item as collapsed if necessary (root is never collapsed) + // Set current item as collapsed if necessary (root is never collapsed). if (parent) { if (!unfold_cache.has(node.id)) { item->set_collapsed(true); @@ -170,7 +185,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int } } else { // Must use path if (last_path == _get_path(item)) { - updating_scene_tree = false; // Force emission of new selection + updating_scene_tree = false; // Force emission of new selection. item->select(0); if (filter_changed) { scroll_item = item; @@ -179,6 +194,33 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int } } + // Add buttons. + const Color remote_button_color = Color(1, 1, 1, 0.8); + if (!node.scene_file_path.is_empty()) { + String node_scene_file_path = node.scene_file_path; + Ref<Texture2D> button_icon = get_theme_icon(SNAME("InstanceOptions"), SNAME("EditorIcons")); + String tooltip = vformat(TTR("This node has been instantiated from a PackedScene file:\n%s\nClick to open the original file in the Editor."), node_scene_file_path); + + item->set_meta("scene_file_path", node_scene_file_path); + item->add_button(0, button_icon, BUTTON_SUBSCENE, false, tooltip); + item->set_button_color(0, item->get_button_count(0) - 1, remote_button_color); + } + + if (node.view_flags & SceneDebuggerTree::RemoteNode::VIEW_HAS_VISIBLE_METHOD) { + bool node_visible = node.view_flags & SceneDebuggerTree::RemoteNode::VIEW_VISIBLE; + bool node_visible_in_tree = node.view_flags & SceneDebuggerTree::RemoteNode::VIEW_VISIBLE_IN_TREE; + Ref<Texture2D> button_icon = get_theme_icon(node_visible ? SNAME("GuiVisibilityVisible") : SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")); + String tooltip = TTR("Toggle Visibility"); + + item->set_meta("visible", node_visible); + item->add_button(0, button_icon, BUTTON_VISIBILITY, false, tooltip); + if (ClassDB::is_parent_class(node.type_name, "CanvasItem") || ClassDB::is_parent_class(node.type_name, "Node3D")) { + item->set_button_color(0, item->get_button_count(0) - 1, node_visible_in_tree ? remote_button_color : Color(1, 1, 1, 0.6)); + } else { + item->set_button_color(0, item->get_button_count(0) - 1, remote_button_color); + } + } + // Add in front of the parents stack if children are expected. if (node.child_count) { parents.push_front(Pair<TreeItem *, int>(item, node.child_count)); @@ -186,7 +228,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int // Apply filters. while (parent) { const bool had_siblings = item->get_prev() || item->get_next(); - if (filter.is_subsequence_ofi(item->get_text(0))) { + if (filter.is_subsequence_ofn(item->get_text(0))) { break; // Filter matches, must survive. } parent->remove_child(item); @@ -217,6 +259,39 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int updating_scene_tree = false; } +Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) { + if (get_button_id_at_position(p_point) != -1) { + return Variant(); + } + + TreeItem *selected = get_selected(); + if (!selected) { + return Variant(); + } + + String path = selected->get_text(0); + + HBoxContainer *hb = memnew(HBoxContainer); + TextureRect *tf = memnew(TextureRect); + tf->set_texture(selected->get_icon(0)); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hb->add_child(tf); + Label *label = memnew(Label(path)); + hb->add_child(label); + set_drag_preview(hb); + + if (!selected->get_parent() || !selected->get_parent()->get_parent()) { + path = "."; + } else { + while (selected->get_parent()->get_parent() != get_root()) { + selected = selected->get_parent(); + path = selected->get_text(0) + "/" + path; + } + } + + return vformat("\"%s\"", path); +} + String EditorDebuggerTree::get_selected_path() { if (!get_selected()) { return ""; @@ -250,9 +325,11 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) { ResourceSaver::get_recognized_extensions(sd, &extensions); file_dialog->clear_filters(); for (int i = 0; i < extensions.size(); i++) { - file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper()); } + String filename = get_selected_path().get_file() + "." + extensions.front()->get().to_lower(); + file_dialog->set_current_path(filename); file_dialog->popup_file_dialog(); } break; case ITEM_MENU_COPY_NODE_PATH: { diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h index 58af52b01f..895f33f1a2 100644 --- a/editor/debugger/editor_debugger_tree.h +++ b/editor/debugger/editor_debugger_tree.h @@ -1,38 +1,38 @@ -/*************************************************************************/ -/* editor_debugger_tree.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene/gui/tree.h" +/**************************************************************************/ +/* editor_debugger_tree.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_DEBUGGER_TREE_H #define EDITOR_DEBUGGER_TREE_H +#include "scene/gui/tree.h" + class SceneDebuggerTree; class EditorFileDialog; @@ -48,7 +48,7 @@ private: ObjectID inspected_object_id; int debugger_id = 0; bool updating_scene_tree = false; - Set<ObjectID> unfold_cache; + HashSet<ObjectID> unfold_cache; PopupMenu *item_menu = nullptr; EditorFileDialog *file_dialog = nullptr; String last_filter; @@ -56,7 +56,7 @@ private: String _get_path(TreeItem *p_item); void _scene_tree_folded(Object *p_obj); void _scene_tree_selected(); - void _scene_tree_rmb_selected(const Vector2 &p_position); + void _scene_tree_rmb_selected(const Vector2 &p_position, MouseButton p_button); void _item_menu_id_pressed(int p_option); void _file_selected(const String &p_file); @@ -65,10 +65,18 @@ protected: void _notification(int p_what); public: + enum Button { + BUTTON_SUBSCENE = 0, + BUTTON_VISIBILITY = 1, + }; + + virtual Variant get_drag_data(const Point2 &p_point) override; + String get_selected_path(); ObjectID get_selected_object(); int get_current_debugger(); // Would love to have one tree for every debugger. void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger); EditorDebuggerTree(); }; + #endif // EDITOR_DEBUGGER_TREE_H diff --git a/editor/debugger/editor_network_profiler.cpp b/editor/debugger/editor_network_profiler.cpp deleted file mode 100644 index 698e950f57..0000000000 --- a/editor/debugger/editor_network_profiler.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/*************************************************************************/ -/* editor_network_profiler.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "editor_network_profiler.h" - -#include "core/os/os.h" -#include "editor/editor_scale.h" -#include "editor/editor_settings.h" - -void EditorNetworkProfiler::_bind_methods() { - ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable"))); -} - -void EditorNetworkProfiler::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); - clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); - incoming_bandwidth_text->set_right_icon(get_theme_icon(SNAME("ArrowDown"), SNAME("EditorIcons"))); - outgoing_bandwidth_text->set_right_icon(get_theme_icon(SNAME("ArrowUp"), SNAME("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_uneditable_color", get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.5)); - outgoing_bandwidth_text->add_theme_color_override("font_uneditable_color", get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.5)); - } -} - -void EditorNetworkProfiler::_update_frame() { - counters_display->clear(); - - TreeItem *root = counters_display->create_item(); - - for (const KeyValue<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> &E : nodes_data) { - TreeItem *node = counters_display->create_item(root); - - for (int j = 0; j < counters_display->get_columns(); ++j) { - node->set_text_alignment(j, j > 0 ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT); - } - - node->set_text(0, E.value.node_path); - node->set_text(1, E.value.incoming_rpc == 0 ? "-" : itos(E.value.incoming_rpc)); - node->set_text(2, E.value.incoming_rset == 0 ? "-" : itos(E.value.incoming_rset)); - node->set_text(3, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc)); - node->set_text(4, E.value.outgoing_rset == 0 ? "-" : itos(E.value.outgoing_rset)); - } -} - -void EditorNetworkProfiler::_activate_pressed() { - if (activate->is_pressed()) { - activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); - activate->set_text(TTR("Stop")); - } else { - activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); - activate->set_text(TTR("Start")); - } - emit_signal(SNAME("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(); - } -} - -void EditorNetworkProfiler::add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo 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(vformat(TTR("%s/s"), String::humanize_size(p_incoming))); - outgoing_bandwidth_text->set_text(vformat(TTR("%s/s"), String::humanize_size(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_uneditable_color", - get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, p_incoming > 0 ? 1 : 0.5)); - outgoing_bandwidth_text->add_theme_color_override( - "font_uneditable_color", - get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, p_outgoing > 0 ? 1 : 0.5)); -} - -bool EditorNetworkProfiler::is_profiling() { - return activate->is_pressed(); -} - -EditorNetworkProfiler::EditorNetworkProfiler() { - HBoxContainer *hb = memnew(HBoxContainer); - hb->add_theme_constant_override("separation", 8 * EDSCALE); - add_child(hb); - - activate = memnew(Button); - activate->set_toggle_mode(true); - activate->set_text(TTR("Start")); - activate->connect("pressed", callable_mp(this, &EditorNetworkProfiler::_activate_pressed)); - hb->add_child(activate); - - clear_button = memnew(Button); - clear_button->set_text(TTR("Clear")); - clear_button->connect("pressed", callable_mp(this, &EditorNetworkProfiler::_clear_pressed)); - hb->add_child(clear_button); - - hb->add_spacer(); - - Label *lb = memnew(Label); - lb->set_text(TTR("Down")); - hb->add_child(lb); - - incoming_bandwidth_text = memnew(LineEdit); - incoming_bandwidth_text->set_editable(false); - incoming_bandwidth_text->set_custom_minimum_size(Size2(120, 0) * EDSCALE); - incoming_bandwidth_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); - hb->add_child(incoming_bandwidth_text); - - Control *down_up_spacer = memnew(Control); - down_up_spacer->set_custom_minimum_size(Size2(30, 0) * EDSCALE); - hb->add_child(down_up_spacer); - - lb = memnew(Label); - lb->set_text(TTR("Up")); - hb->add_child(lb); - - outgoing_bandwidth_text = memnew(LineEdit); - outgoing_bandwidth_text->set_editable(false); - outgoing_bandwidth_text->set_custom_minimum_size(Size2(120, 0) * EDSCALE); - outgoing_bandwidth_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); - hb->add_child(outgoing_bandwidth_text); - - // Set initial texts in the incoming/outgoing bandwidth labels - set_bandwidth(0, 0); - - 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_clip_content(0, true); - counters_display->set_column_custom_minimum_width(0, 60 * EDSCALE); - counters_display->set_column_title(1, TTR("Incoming RPC")); - counters_display->set_column_expand(1, false); - counters_display->set_column_clip_content(1, true); - counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE); - counters_display->set_column_title(2, TTR("Incoming RSET")); - counters_display->set_column_expand(2, false); - counters_display->set_column_clip_content(2, true); - counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE); - counters_display->set_column_title(3, TTR("Outgoing RPC")); - counters_display->set_column_expand(3, false); - counters_display->set_column_clip_content(3, true); - counters_display->set_column_custom_minimum_width(3, 120 * EDSCALE); - counters_display->set_column_title(4, TTR("Outgoing RSET")); - counters_display->set_column_expand(4, false); - counters_display->set_column_clip_content(4, true); - counters_display->set_column_custom_minimum_width(4, 120 * EDSCALE); - add_child(counters_display); - - frame_delay = memnew(Timer); - frame_delay->set_wait_time(0.1); - frame_delay->set_one_shot(true); - add_child(frame_delay); - frame_delay->connect("timeout", callable_mp(this, &EditorNetworkProfiler::_update_frame)); -} diff --git a/editor/debugger/editor_network_profiler.h b/editor/debugger/editor_network_profiler.h deleted file mode 100644 index 320dd2a826..0000000000 --- a/editor/debugger/editor_network_profiler.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************/ -/* editor_network_profiler.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef EDITORNETWORKPROFILER_H -#define EDITORNETWORKPROFILER_H - -#include "core/debugger/debugger_marshalls.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, DebuggerMarshalls::MultiplayerNodeInfo> nodes_data; - - void _update_frame(); - - void _activate_pressed(); - void _clear_pressed(); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame); - void set_bandwidth(int p_incoming, int p_outgoing); - bool is_profiling(); - - EditorNetworkProfiler(); -}; - -#endif //EDITORNETWORKPROFILER_H diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp index 56d1e7cee9..e8fd6ef8ef 100644 --- a/editor/debugger/editor_performance_profiler.cpp +++ b/editor/debugger/editor_performance_profiler.cpp @@ -1,35 +1,36 @@ -/*************************************************************************/ -/* editor_performance_profiler.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_performance_profiler.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_property_name_processor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "main/performance.h" @@ -60,7 +61,7 @@ void EditorPerformanceProfiler::Monitor::update_value(float p_value) { } break; } item->set_text(1, label); - item->set_tooltip(1, tooltip); + item->set_tooltip_text(1, tooltip); if (p_value > max) { max = p_value; @@ -72,7 +73,7 @@ void EditorPerformanceProfiler::Monitor::reset() { max = 0.0f; if (item) { item->set_text(1, ""); - item->set_tooltip(1, ""); + item->set_tooltip_text(1, ""); } } @@ -91,14 +92,14 @@ String EditorPerformanceProfiler::_create_label(float p_value, Performance::Moni } void EditorPerformanceProfiler::_monitor_select() { - monitor_draw->update(); + monitor_draw->queue_redraw(); } 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()); + for (const KeyValue<StringName, Monitor> &E : monitors) { + if (E.value.item->is_checked(0)) { + active.push_back(E.key); } } @@ -135,7 +136,7 @@ void EditorPerformanceProfiler::_monitor_draw() { monitor_draw->draw_string(graph_font, rect.position + Point2(0, graph_font->get_ascent(font_size)), current.item->get_text(0), HORIZONTAL_ALIGNMENT_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; + float value_position = rect.size.width - graph_font->get_string_size(current.item->get_text(1), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; if (value_position < 0) { value_position = 0; } @@ -183,7 +184,7 @@ void EditorPerformanceProfiler::_monitor_draw() { 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); + Size2 size = graph_font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, 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; @@ -202,23 +203,23 @@ void EditorPerformanceProfiler::_monitor_draw() { } 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()); + HashSet<StringName> monitor_checked; + for (KeyValue<StringName, Monitor> &E : monitors) { + if (E.value.item && E.value.item->is_checked(0)) { + monitor_checked.insert(E.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()); + for (KeyValue<StringName, Monitor> &E : monitors) { + TreeItem *base = _get_monitor_base(E.value.base); + TreeItem *item = _create_monitor_item(E.value.name, base); + item->set_checked(0, monitor_checked.has(E.key)); + E.value.item = item; + if (!E.value.history.is_empty()) { + E.value.update_value(E.value.history.front()->get()); } } } @@ -233,6 +234,7 @@ TreeItem *EditorPerformanceProfiler::_get_monitor_base(const StringName &p_base_ base->set_editable(0, false); base->set_selectable(0, false); base->set_expand_right(0, true); + base->set_custom_font(0, get_theme_font(SNAME("bold"), SNAME("EditorFonts"))); base_map.insert(p_base_name, base); return base; } @@ -251,9 +253,9 @@ 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() == MouseButton::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()); + for (KeyValue<StringName, Monitor> &E : monitors) { + if (E.value.item->is_checked(0)) { + active.push_back(E.key); } } if (active.size() > 0) { @@ -282,71 +284,81 @@ void EditorPerformanceProfiler::_marker_input(const Ref<InputEvent> &p_event) { float spacing = float(point_sep) / float(columns); marker_frame = (rect.size.x - point.x) / spacing; } - monitor_draw->update(); + monitor_draw->queue_redraw(); return; } } marker_key = ""; - monitor_draw->update(); + monitor_draw->queue_redraw(); } } 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); + HashMap<StringName, Monitor>::Iterator E = monitors.begin(); + while (E != monitors.end()) { + HashMap<StringName, Monitor>::Iterator N = E; + ++N; + if (String(E->key).begins_with("custom:")) { + monitors.remove(E); } else { - i.value().reset(); + E->value.reset(); } + E = N; } _build_monitor_tree(); marker_key = ""; marker_frame = 0; - monitor_draw->update(); + monitor_draw->queue_redraw(); } void EditorPerformanceProfiler::update_monitors(const Vector<StringName> &p_names) { - OrderedHashMap<StringName, int> names; + HashMap<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()); + { + HashMap<StringName, Monitor>::Iterator E = monitors.begin(); + while (E != monitors.end()) { + HashMap<StringName, Monitor>::Iterator N = E; + ++N; + if (String(E->key).begins_with("custom:")) { + if (!names.has(E->key)) { + monitors.remove(E); + } else { + E->value.frame_index = names[E->key]; + names.erase(E->key); + } } + E = N; } } - for (OrderedHashMap<StringName, int>::Element i = names.front(); i; i = i.next()) { - String name = String(i.key()).replace_first("custom:", ""); + for (const KeyValue<StringName, int> &E : names) { + String name = String(E.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)); + monitors.insert(E.key, Monitor(name, base, E.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]; + for (KeyValue<StringName, Monitor> &E : monitors) { + float value = 0.0f; + if (E.value.frame_index >= 0 && E.value.frame_index < p_values.size()) { + value = p_values[E.value.frame_index]; } - i.value().history.push_front(data); - i.value().update_value(data); + E.value.history.push_front(value); + E.value.update_value(value); } marker_frame++; - monitor_draw->update(); + monitor_draw->queue_redraw(); } List<float> *EditorPerformanceProfiler::get_monitor_data(const StringName &p_name) { @@ -380,14 +392,14 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() { info_message->set_text(TTR("Pick one or more items from the list to display the graph.")); info_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - info_message->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); 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); + info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, 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(); + String base = EditorPropertyNameProcessor::get_singleton()->process_name(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)).get_slicec('/', 0), EditorPropertyNameProcessor::STYLE_CAPITALIZED); + String name = EditorPropertyNameProcessor::get_singleton()->process_name(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)).get_slicec('/', 1), EditorPropertyNameProcessor::STYLE_CAPITALIZED); 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)); } diff --git a/editor/debugger/editor_performance_profiler.h b/editor/debugger/editor_performance_profiler.h index 998ecc5bb6..74bb92a95e 100644 --- a/editor/debugger/editor_performance_profiler.h +++ b/editor/debugger/editor_performance_profiler.h @@ -1,38 +1,38 @@ -/*************************************************************************/ -/* editor_performance_profiler.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_performance_profiler.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "core/templates/hash_map.h" +#include "core/templates/rb_map.h" #include "main/performance.h" #include "scene/gui/control.h" #include "scene/gui/label.h" @@ -59,14 +59,14 @@ private: void reset(); }; - OrderedHashMap<StringName, Monitor> monitors; + HashMap<StringName, Monitor> monitors; - Map<StringName, TreeItem *> base_map; - Tree *monitor_tree; - Control *monitor_draw; - Label *info_message; + HashMap<StringName, TreeItem *> base_map; + Tree *monitor_tree = nullptr; + Control *monitor_draw = nullptr; + Label *info_message = nullptr; StringName marker_key; - int marker_frame; + int marker_frame = 0; const int MARGIN = 4; const int POINT_SEPARATION = 5; const int MARKER_MARGIN = 2; diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 8e9b2a9368..e4730faf38 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* editor_profiler.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_profiler.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_profiler.h" @@ -85,8 +85,8 @@ void EditorProfiler::add_frame_metric(const Metric &p_metric, bool p_final) { } void EditorProfiler::clear() { - int metric_size = EditorSettings::get_singleton()->get("debugger/profiler_frame_history_size"); - metric_size = CLAMP(metric_size, 60, 1024); + int metric_size = EDITOR_GET("debugger/profiler_frame_history_size"); + metric_size = CLAMP(metric_size, 60, 10000); frame_metrics.clear(); frame_metrics.resize(metric_size); total_metrics = 0; @@ -104,6 +104,10 @@ void EditorProfiler::clear() { updating_frame = false; hover_metric = -1; seeking = false; + + // Ensure button text (start, stop) is correct + _update_button_text(); + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } static String _get_percent_txt(float p_value, float p_total) { @@ -198,18 +202,18 @@ void EditorProfiler::_update_plot() { for (int i = 0; i < total_metrics; i++) { const Metric &m = _get_frame_metric(i); - for (Set<StringName>::Element *E = plot_sigs.front(); E; E = E->next()) { - const Map<StringName, Metric::Category *>::Element *F = m.category_ptrs.find(E->get()); + for (const StringName &E : plot_sigs) { + HashMap<StringName, Metric::Category *>::ConstIterator F = m.category_ptrs.find(E); if (F) { - highest = MAX(F->get()->total_time, highest); + highest = MAX(F->value->total_time, highest); } - const Map<StringName, Metric::Category::Item *>::Element *G = m.item_ptrs.find(E->get()); + HashMap<StringName, Metric::Category::Item *>::ConstIterator G = m.item_ptrs.find(E); if (G) { if (use_self) { - highest = MAX(G->get()->self, highest); + highest = MAX(G->value->self, highest); } else { - highest = MAX(G->get()->total, highest); + highest = MAX(G->value->total, highest); } } } @@ -225,7 +229,7 @@ void EditorProfiler::_update_plot() { int *column = columnv.ptrw(); - Map<StringName, int> prev_plots; + HashMap<StringName, int> prev_plots; for (int i = 0; i < total_metrics * w / frame_metrics.size() - 1; i++) { for (int j = 0; j < h * 4; j++) { @@ -234,34 +238,34 @@ void EditorProfiler::_update_plot() { int current = i * frame_metrics.size() / w; - for (Set<StringName>::Element *E = plot_sigs.front(); E; E = E->next()) { + for (const StringName &E : plot_sigs) { const Metric &m = _get_frame_metric(current); float value = 0; - const Map<StringName, Metric::Category *>::Element *F = m.category_ptrs.find(E->get()); + HashMap<StringName, Metric::Category *>::ConstIterator F = m.category_ptrs.find(E); if (F) { - value = F->get()->total_time; + value = F->value->total_time; } - const Map<StringName, Metric::Category::Item *>::Element *G = m.item_ptrs.find(E->get()); + HashMap<StringName, Metric::Category::Item *>::ConstIterator G = m.item_ptrs.find(E); if (G) { if (use_self) { - value = G->get()->self; + value = G->value->self; } else { - value = G->get()->total; + value = G->value->total; } } int plot_pos = CLAMP(int(value * h / highest), 0, h - 1); int prev_plot = plot_pos; - Map<StringName, int>::Element *H = prev_plots.find(E->get()); + HashMap<StringName, int>::Iterator H = prev_plots.find(E); if (H) { - prev_plot = H->get(); - H->get() = plot_pos; + prev_plot = H->value; + H->value = plot_pos; } else { - prev_plots[E->get()] = plot_pos; + prev_plots[E] = plot_pos; } plot_pos = h - plot_pos - 1; @@ -271,7 +275,7 @@ void EditorProfiler::_update_plot() { SWAP(prev_plot, plot_pos); } - Color col = _get_color_from_signature(E->get()); + Color col = _get_color_from_signature(E); for (int j = prev_plot; j <= plot_pos; j++) { column[j * 4 + 0] += Math::fast_ftoi(CLAMP(col.r * 255, 0, 255)); @@ -304,21 +308,19 @@ void EditorProfiler::_update_plot() { } } - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, graph_image); + Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image); if (reset_texture) { if (graph_texture.is_null()) { graph_texture.instantiate(); } - graph_texture->create_from_image(img); + graph_texture->set_image(img); } graph_texture->update(img); graph->set_texture(graph_texture); - graph->update(); + graph->queue_redraw(); } void EditorProfiler::_update_frame() { @@ -356,7 +358,7 @@ void EditorProfiler::_update_frame() { item->set_metadata(1, it.script); item->set_metadata(2, it.line); item->set_text_alignment(2, HORIZONTAL_ALIGNMENT_RIGHT); - item->set_tooltip(0, it.name + "\n" + it.script + ":" + itos(it.line)); + item->set_tooltip_text(0, it.name + "\n" + it.script + ":" + itos(it.line)); float time = dtime == DISPLAY_SELF_TIME ? it.self : it.total; @@ -374,15 +376,23 @@ void EditorProfiler::_update_frame() { updating_frame = false; } -void EditorProfiler::_activate_pressed() { +void EditorProfiler::_update_button_text() { if (activate->is_pressed()) { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); - _clear_pressed(); } else { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); activate->set_text(TTR("Start")); } +} + +void EditorProfiler::_activate_pressed() { + _update_button_text(); + + if (activate->is_pressed()) { + _clear_pressed(); + } + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } @@ -393,9 +403,14 @@ void EditorProfiler::_clear_pressed() { } void EditorProfiler::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { - activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); - clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); + clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + } break; } } @@ -416,7 +431,7 @@ void EditorProfiler::_graph_tex_draw() { void EditorProfiler::_graph_tex_mouse_exit() { hover_metric = -1; - graph->update(); + graph->queue_redraw(); } void EditorProfiler::_cursor_metric_changed(double) { @@ -424,7 +439,7 @@ void EditorProfiler::_cursor_metric_changed(double) { return; } - graph->update(); + graph->queue_redraw(); _update_frame(); } @@ -453,11 +468,12 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) { x = frame_metrics.size() - 1; } - if (mb.is_valid() || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { + if (mb.is_valid() || (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { updating_frame = true; - if (x < total_metrics) + if (x < total_metrics) { cursor_metric_edit->set_value(_get_frame_metric(x).frame_number); + } updating_frame = false; if (activate->is_pressed()) { @@ -474,13 +490,13 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) { } } - graph->update(); + graph->queue_redraw(); } } void EditorProfiler::disable_seeking() { seeking = false; - graph->update(); + graph->queue_redraw(); } void EditorProfiler::_combo_changed(int) { @@ -493,8 +509,16 @@ void EditorProfiler::_bind_methods() { ADD_SIGNAL(MethodInfo("break_request")); } -void EditorProfiler::set_enabled(bool p_enable) { +void EditorProfiler::set_enabled(bool p_enable, bool p_clear) { activate->set_disabled(!p_enable); + if (p_clear) { + clear(); + } +} + +void EditorProfiler::set_pressed(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); } bool EditorProfiler::is_profiling() { @@ -509,7 +533,7 @@ Vector<Vector<String>> EditorProfiler::get_data_as_csv() const { } // Different metrics may contain different number of categories. - Set<StringName> possible_signatures; + HashSet<StringName> possible_signatures; for (int i = 0; i < frame_metrics.size(); i++) { const Metric &m = frame_metrics[i]; if (!m.valid) { @@ -524,13 +548,13 @@ Vector<Vector<String>> EditorProfiler::get_data_as_csv() const { } // Generate CSV header and cache indices. - Map<StringName, int> sig_map; + HashMap<StringName, int> sig_map; Vector<String> signatures; signatures.resize(possible_signatures.size()); int sig_index = 0; - for (const Set<StringName>::Element *E = possible_signatures.front(); E; E = E->next()) { - signatures.write[sig_index] = E->get(); - sig_map[E->get()] = sig_index; + for (const StringName &E : possible_signatures) { + signatures.write[sig_index] = E; + sig_map[E] = sig_index; sig_index++; } res.push_back(signatures); @@ -575,6 +599,7 @@ EditorProfiler::EditorProfiler() { add_child(hb); activate = memnew(Button); activate->set_toggle_mode(true); + activate->set_disabled(true); activate->set_text(TTR("Start")); activate->connect("pressed", callable_mp(this, &EditorProfiler::_activate_pressed)); hb->add_child(activate); @@ -601,7 +626,7 @@ EditorProfiler::EditorProfiler() { display_time = memnew(OptionButton); display_time->add_item(TTR("Inclusive")); display_time->add_item(TTR("Self")); - display_time->set_tooltip(TTR("Inclusive: Includes time from other functions called by this function.\nUse this to spot bottlenecks.\n\nSelf: Only count the time spent in the function itself, not in other functions called by that function.\nUse this to find individual functions to optimize.")); + display_time->set_tooltip_text(TTR("Inclusive: Includes time from other functions called by this function.\nUse this to spot bottlenecks.\n\nSelf: Only count the time spent in the function itself, not in other functions called by that function.\nUse this to find individual functions to optimize.")); display_time->connect("item_selected", callable_mp(this, &EditorProfiler::_combo_changed)); hb->add_child(display_time); @@ -645,7 +670,7 @@ EditorProfiler::EditorProfiler() { variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited)); graph = memnew(TextureRect); - graph->set_expand(true); + graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); graph->set_mouse_filter(MOUSE_FILTER_STOP); graph->connect("draw", callable_mp(this, &EditorProfiler::_graph_tex_draw)); graph->connect("gui_input", callable_mp(this, &EditorProfiler::_graph_tex_input)); @@ -654,11 +679,8 @@ EditorProfiler::EditorProfiler() { h_split->add_child(graph); graph->set_h_size_flags(SIZE_EXPAND_FILL); - int metric_size = CLAMP(int(EDITOR_DEF("debugger/profiler_frame_history_size", 600)), 60, 1024); + int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000); frame_metrics.resize(metric_size); - total_metrics = 0; - last_metric = -1; - hover_metric = -1; EDITOR_DEF("debugger/profiler_frame_max_functions", 64); @@ -676,7 +698,4 @@ EditorProfiler::EditorProfiler() { plot_sigs.insert("physics_frame_time"); plot_sigs.insert("category_frame_time"); - - seeking = false; - graph_height = 1; } diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index 45f7ac39c1..eea8ed8365 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -1,35 +1,35 @@ -/*************************************************************************/ -/* editor_profiler.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef EDITORPROFILER_H -#define EDITORPROFILER_H +/**************************************************************************/ +/* editor_profiler.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_PROFILER_H +#define EDITOR_PROFILER_H #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -49,7 +49,7 @@ public: int frame_number = 0; float frame_time = 0; - float idle_time = 0; + float process_time = 0; float physics_time = 0; float physics_frame_time = 0; @@ -73,8 +73,8 @@ public: Vector<Category> categories; - Map<StringName, Category *> category_ptrs; - Map<StringName, Category::Item *> item_ptrs; + HashMap<StringName, Category *> category_ptrs; + HashMap<StringName, Category::Item *> item_ptrs; }; enum DisplayMode { @@ -90,38 +90,39 @@ public: }; private: - Button *activate; - Button *clear_button; - TextureRect *graph; + Button *activate = nullptr; + Button *clear_button = nullptr; + TextureRect *graph = nullptr; Ref<ImageTexture> graph_texture; Vector<uint8_t> graph_image; - Tree *variables; - HSplitContainer *h_split; + Tree *variables = nullptr; + HSplitContainer *h_split = nullptr; - Set<StringName> plot_sigs; + HashSet<StringName> plot_sigs; - OptionButton *display_mode; - OptionButton *display_time; + OptionButton *display_mode = nullptr; + OptionButton *display_time = nullptr; - SpinBox *cursor_metric_edit; + SpinBox *cursor_metric_edit = nullptr; Vector<Metric> frame_metrics; - int total_metrics; - int last_metric; + int total_metrics = 0; + int last_metric = -1; - int max_functions; + int max_functions = 0; - bool updating_frame; + bool updating_frame = false; - int hover_metric; + int hover_metric = -1; - float graph_height; + float graph_height = 1.0f; - bool seeking; + bool seeking = false; - Timer *frame_delay; - Timer *plot_delay; + Timer *frame_delay = nullptr; + Timer *plot_delay = nullptr; + void _update_button_text(); void _update_frame(); void _activate_pressed(); @@ -153,7 +154,8 @@ protected: public: void add_frame_metric(const Metric &p_metric, bool p_final = false); - void set_enabled(bool p_enable); + void set_enabled(bool p_enable, bool p_clear = true); + void set_pressed(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); @@ -165,4 +167,4 @@ public: EditorProfiler(); }; -#endif // EDITORPROFILER_H +#endif // EDITOR_PROFILER_H diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 42b52a3b38..1a06e85f90 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* editor_visual_profiler.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* editor_visual_profiler.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_visual_profiler.h" @@ -41,7 +41,6 @@ void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { } frame_metrics.write[last_metric] = p_metric; - // _make_metric_ptrs(frame_metrics.write[last_metric]); List<String> stack; for (int i = 0; i < frame_metrics[last_metric].areas.size(); i++) { @@ -67,8 +66,9 @@ void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { } updating_frame = true; + clear_button->set_disabled(false); cursor_metric_edit->set_max(frame_metrics[last_metric].frame_number); - cursor_metric_edit->set_min(MAX(frame_metrics[last_metric].frame_number - frame_metrics.size(), 0)); + cursor_metric_edit->set_min(MAX(frame_metrics[last_metric].frame_number - frame_metrics.size(), 0u)); if (!seeking) { cursor_metric_edit->set_value(frame_metrics[last_metric].frame_number); @@ -93,8 +93,8 @@ void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { } void EditorVisualProfiler::clear() { - int metric_size = EditorSettings::get_singleton()->get("debugger/profiler_frame_history_size"); - metric_size = CLAMP(metric_size, 60, 1024); + int metric_size = EDITOR_GET("debugger/profiler_frame_history_size"); + metric_size = CLAMP(metric_size, 60, 10000); frame_metrics.clear(); frame_metrics.resize(metric_size); last_metric = -1; @@ -144,12 +144,12 @@ void EditorVisualProfiler::_item_selected() { } void EditorVisualProfiler::_update_plot() { - int w = graph->get_size().width; - int h = graph->get_size().height; + const int w = graph->get_size().width; + const int h = graph->get_size().height; bool reset_texture = false; - int desired_len = w * h * 4; + const int desired_len = w * h * 4; if (graph_image.size() != desired_len) { reset_texture = true; @@ -157,12 +157,13 @@ void EditorVisualProfiler::_update_plot() { } uint8_t *wr = graph_image.ptrw(); + const Color background_color = get_theme_color("dark_color_2", "Editor"); - //clear + // Clear the previous frame and set the background color. for (int i = 0; i < desired_len; i += 4) { - wr[i + 0] = 0; - wr[i + 1] = 0; - wr[i + 2] = 0; + wr[i + 0] = Math::fast_ftoi(background_color.r * 255); + wr[i + 1] = Math::fast_ftoi(background_color.g * 255); + wr[i + 2] = Math::fast_ftoi(background_color.b * 255); wr[i + 3] = 255; } @@ -260,9 +261,9 @@ void EditorVisualProfiler::_update_plot() { uint8_t r, g, b; if (column_cpu[j].a == 0) { - r = 0; - g = 0; - b = 0; + r = Math::fast_ftoi(background_color.r * 255); + g = Math::fast_ftoi(background_color.g * 255); + b = Math::fast_ftoi(background_color.b * 255); } else { r = CLAMP((column_cpu[j].r / column_cpu[j].a) * 255.0, 0, 255); g = CLAMP((column_cpu[j].g / column_cpu[j].a) * 255.0, 0, 255); @@ -280,9 +281,9 @@ void EditorVisualProfiler::_update_plot() { uint8_t r, g, b; if (column_gpu[j].a == 0) { - r = 0; - g = 0; - b = 0; + r = Math::fast_ftoi(background_color.r * 255); + g = Math::fast_ftoi(background_color.g * 255); + b = Math::fast_ftoi(background_color.b * 255); } else { r = CLAMP((column_gpu[j].r / column_gpu[j].a) * 255.0, 0, 255); g = CLAMP((column_gpu[j].g / column_gpu[j].a) * 255.0, 0, 255); @@ -298,21 +299,19 @@ void EditorVisualProfiler::_update_plot() { } } - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, graph_image); + Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image); if (reset_texture) { if (graph_texture.is_null()) { graph_texture.instantiate(); } - graph_texture->create_from_image(img); + graph_texture->set_image(img); } graph_texture->update(img); graph->set_texture(graph_texture); - graph->update(); + graph->queue_redraw(); } void EditorVisualProfiler::_update_frame(bool p_focus_selected) { @@ -410,6 +409,7 @@ void EditorVisualProfiler::_activate_pressed() { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); _clear_pressed(); //always clear on start + clear_button->set_disabled(false); } else { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); activate->set_text(TTR("Start")); @@ -418,18 +418,24 @@ void EditorVisualProfiler::_activate_pressed() { } void EditorVisualProfiler::_clear_pressed() { + clear_button->set_disabled(true); clear(); _update_plot(); } void EditorVisualProfiler::_notification(int p_what) { - 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(SNAME("PlayBackwards"), SNAME("EditorIcons"))); - } else { - activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); - } - clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + if (is_layout_rtl()) { + activate->set_icon(get_theme_icon(SNAME("PlayBackwards"), SNAME("EditorIcons"))); + } else { + activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); + } + clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + } break; } } @@ -437,8 +443,11 @@ void EditorVisualProfiler::_graph_tex_draw() { if (last_metric < 0) { return; } + Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); + const Color color = get_theme_color(SNAME("font_color"), SNAME("Editor")); + if (seeking) { int max_frames = frame_metrics.size(); int frame = cursor_metric_edit->get_value() - (frame_metrics[last_metric].frame_number - max_frames + 1); @@ -448,10 +457,9 @@ void EditorVisualProfiler::_graph_tex_draw() { int half_width = graph->get_size().x / 2; int cur_x = frame * half_width / max_frames; - //cur_x /= 2.0; - graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.8)); - graph->draw_line(Vector2(cur_x + half_width, 0), Vector2(cur_x + half_width, graph->get_size().y), Color(1, 1, 1, 0.8)); + graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), color * Color(1, 1, 1)); + graph->draw_line(Vector2(cur_x + half_width, 0), Vector2(cur_x + half_width, graph->get_size().y), color * Color(1, 1, 1)); } if (graph_height_cpu > 0) { @@ -459,10 +467,10 @@ void EditorVisualProfiler::_graph_tex_draw() { int half_width = graph->get_size().x / 2; - graph->draw_line(Vector2(0, frame_y), Vector2(half_width, frame_y), Color(1, 1, 1, 0.3)); + graph->draw_line(Vector2(0, frame_y), Vector2(half_width, frame_y), color * Color(1, 1, 1, 0.5)); - String limit_str = String::num(graph_limit, 2); - graph->draw_string(font, Vector2(half_width - font->get_string_size(limit_str, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 0.6)); + const String limit_str = String::num(graph_limit, 2) + " ms"; + graph->draw_string(font, Vector2(half_width - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75)); } if (graph_height_gpu > 0) { @@ -470,32 +478,19 @@ void EditorVisualProfiler::_graph_tex_draw() { int half_width = graph->get_size().x / 2; - graph->draw_line(Vector2(half_width, frame_y), Vector2(graph->get_size().x, frame_y), Color(1, 1, 1, 0.3)); + graph->draw_line(Vector2(half_width, frame_y), Vector2(graph->get_size().x, frame_y), color * Color(1, 1, 1, 0.5)); - String limit_str = String::num(graph_limit, 2); - graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 0.6)); + const String limit_str = String::num(graph_limit, 2) + " ms"; + graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75)); } - graph->draw_string(font, Vector2(font->get_string_size("X", font_size).x, font->get_ascent(font_size) + 2), "CPU:", HORIZONTAL_ALIGNMENT_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:", HORIZONTAL_ALIGNMENT_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) - frame = 0; - - int cur_x = frame * graph->get_size().x / max_frames; - - graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.4)); - } -*/ + graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1)); + graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1)); } void EditorVisualProfiler::_graph_tex_mouse_exit() { hover_metric = -1; - graph->update(); + graph->queue_redraw(); } void EditorVisualProfiler::_cursor_metric_changed(double) { @@ -503,7 +498,7 @@ void EditorVisualProfiler::_cursor_metric_changed(double) { return; } - graph->update(); + graph->queue_redraw(); _update_frame(); } @@ -549,7 +544,7 @@ void EditorVisualProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) { hover_metric = -1; } - if (mb.is_valid() || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { + if (mb.is_valid() || mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) { //cursor_metric=x; updating_frame = true; @@ -619,7 +614,7 @@ void EditorVisualProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) { } } - graph->update(); + graph->queue_redraw(); } } @@ -643,7 +638,7 @@ int EditorVisualProfiler::_get_cursor_index() const { void EditorVisualProfiler::disable_seeking() { seeking = false; - graph->update(); + graph->queue_redraw(); } void EditorVisualProfiler::_combo_changed(int) { @@ -655,10 +650,25 @@ void EditorVisualProfiler::_bind_methods() { ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable"))); } +void EditorVisualProfiler::_update_button_text() { + if (activate->is_pressed()) { + activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); + activate->set_text(TTR("Stop")); + } else { + activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); + activate->set_text(TTR("Start")); + } +} + void EditorVisualProfiler::set_enabled(bool p_enable) { activate->set_disabled(!p_enable); } +void EditorVisualProfiler::set_pressed(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); +} + bool EditorVisualProfiler::is_profiling() { return activate->is_pressed(); } @@ -722,19 +732,21 @@ EditorVisualProfiler::EditorVisualProfiler() { add_child(hb); activate = memnew(Button); activate->set_toggle_mode(true); + activate->set_disabled(true); activate->set_text(TTR("Start")); activate->connect("pressed", callable_mp(this, &EditorVisualProfiler::_activate_pressed)); hb->add_child(activate); clear_button = memnew(Button); clear_button->set_text(TTR("Clear")); + clear_button->set_disabled(true); clear_button->connect("pressed", callable_mp(this, &EditorVisualProfiler::_clear_pressed)); hb->add_child(clear_button); hb->add_child(memnew(Label(TTR("Measure:")))); display_mode = memnew(OptionButton); - display_mode->add_item(TTR("Frame Time (msec)")); + display_mode->add_item(TTR("Frame Time (ms)")); display_mode->add_item(TTR("Frame %")); display_mode->connect("item_selected", callable_mp(this, &EditorVisualProfiler::_combo_changed)); @@ -786,9 +798,8 @@ EditorVisualProfiler::EditorVisualProfiler() { variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected)); graph = memnew(TextureRect); - graph->set_expand(true); + graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); graph->set_mouse_filter(MOUSE_FILTER_STOP); - //graph->set_ignore_mouse(false); graph->connect("draw", callable_mp(this, &EditorVisualProfiler::_graph_tex_draw)); graph->connect("gui_input", callable_mp(this, &EditorVisualProfiler::_graph_tex_input)); graph->connect("mouse_exited", callable_mp(this, &EditorVisualProfiler::_graph_tex_mouse_exit)); @@ -796,31 +807,18 @@ EditorVisualProfiler::EditorVisualProfiler() { h_split->add_child(graph); graph->set_h_size_flags(SIZE_EXPAND_FILL); - int metric_size = CLAMP(int(EDITOR_DEF("debugger/profiler_frame_history_size", 600)), 60, 1024); + int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000); frame_metrics.resize(metric_size); - last_metric = -1; - //cursor_metric=-1; - hover_metric = -1; - - //display_mode=DISPLAY_FRAME_TIME; 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", callable_mp(this, &EditorVisualProfiler::_update_frame), make_binds(false)); + frame_delay->connect("timeout", callable_mp(this, &EditorVisualProfiler::_update_frame).bind(false)); plot_delay = memnew(Timer); plot_delay->set_wait_time(0.1); plot_delay->set_one_shot(true); add_child(plot_delay); plot_delay->connect("timeout", callable_mp(this, &EditorVisualProfiler::_update_plot)); - - seeking = false; - graph_height_cpu = 1; - graph_height_gpu = 1; - - graph_limit = 1000 / 60.0; - - //activate->set_disabled(true); } diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h index 55ba725ae8..5831e3322d 100644 --- a/editor/debugger/editor_visual_profiler.h +++ b/editor/debugger/editor_visual_profiler.h @@ -1,35 +1,35 @@ -/*************************************************************************/ -/* editor_visual_profiler.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef EDITOR_FRAME_PROFILER_H -#define EDITOR_FRAME_PROFILER_H +/**************************************************************************/ +/* editor_visual_profiler.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_VISUAL_PROFILER_H +#define EDITOR_VISUAL_PROFILER_H #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -67,40 +67,41 @@ public: }; private: - Button *activate; - Button *clear_button; + Button *activate = nullptr; + Button *clear_button = nullptr; - TextureRect *graph; + TextureRect *graph = nullptr; Ref<ImageTexture> graph_texture; Vector<uint8_t> graph_image; - Tree *variables; - HSplitContainer *h_split; - CheckBox *frame_relative; - CheckBox *linked; + Tree *variables = nullptr; + HSplitContainer *h_split = nullptr; + CheckBox *frame_relative = nullptr; + CheckBox *linked = nullptr; - OptionButton *display_mode; + OptionButton *display_mode = nullptr; - SpinBox *cursor_metric_edit; + SpinBox *cursor_metric_edit = nullptr; Vector<Metric> frame_metrics; - int last_metric; + int last_metric = -1; + + int hover_metric = -1; StringName selected_area; - bool updating_frame; + bool updating_frame = false; - //int cursor_metric; - int hover_metric; + float graph_height_cpu = 1.0f; + float graph_height_gpu = 1.0f; - float graph_height_cpu; - float graph_height_gpu; + float graph_limit = 1000.0f / 60; - float graph_limit; + bool seeking = false; - bool seeking; + Timer *frame_delay = nullptr; + Timer *plot_delay = nullptr; - Timer *frame_delay; - Timer *plot_delay; + void _update_button_text(); void _update_frame(bool p_focus_selected = false); @@ -134,6 +135,7 @@ protected: public: void add_frame_metric(const Metric &p_metric); void set_enabled(bool p_enable); + void set_pressed(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); @@ -145,4 +147,4 @@ public: EditorVisualProfiler(); }; -#endif // EDITOR_FRAME_PROFILER_H +#endif // EDITOR_VISUAL_PROFILER_H diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index b72a20ee2f..304beec681 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_editor_debugger.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_editor_debugger.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "script_editor_debugger.h" @@ -36,24 +36,25 @@ #include "core/io/marshalls.h" #include "core/string/ustring.h" #include "core/version.h" -#include "core/version_hash.gen.h" #include "editor/debugger/debug_adapter/debug_adapter_protocol.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_file_dialog.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_property_name_processor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/inspector_dock.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" #include "scene/3d/camera_3d.h" #include "scene/debugger/scene_debugger.h" #include "scene/gui/dialogs.h" +#include "scene/gui/grid_container.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" @@ -64,6 +65,7 @@ #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" #include "scene/resources/packed_scene.h" +#include "servers/debugger/servers_debugger.h" #include "servers/display_server.h" using CameraOverride = EditorDebuggerNode::CameraOverride; @@ -128,20 +130,21 @@ void ScriptEditorDebugger::debug_continue() { _clear_execution(); _put_msg("continue", Array()); + _put_msg("servers:foreground", Array()); } void ScriptEditorDebugger::update_tabs() { if (error_count == 0 && warning_count == 0) { errors_tab->set_name(TTR("Errors")); - tabs->set_tab_icon(errors_tab->get_index(), Ref<Texture2D>()); + tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), Ref<Texture2D>()); } else { errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")"); if (error_count >= 1 && warning_count >= 1) { - tabs->set_tab_icon(errors_tab->get_index(), get_theme_icon(SNAME("ErrorWarning"), SNAME("EditorIcons"))); + tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_theme_icon(SNAME("ErrorWarning"), SNAME("EditorIcons"))); } else if (error_count >= 1) { - tabs->set_tab_icon(errors_tab->get_index(), get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); + tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); } else { - tabs->set_tab_icon(errors_tab->get_index(), get_theme_icon(SNAME("Warning"), SNAME("EditorIcons"))); + tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_theme_icon(SNAME("Warning"), SNAME("EditorIcons"))); } } } @@ -161,7 +164,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) { switch (file_dialog_purpose) { case SAVE_MONITORS_CSV: { Error err; - FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); if (err != OK) { ERR_PRINT("Failed to open " + p_file); @@ -206,7 +209,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) { } break; case SAVE_VRAM_CSV: { Error err; - FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); if (err != OK) { ERR_PRINT("Failed to open " + p_file); @@ -278,7 +281,7 @@ void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const } void ScriptEditorDebugger::_video_mem_request() { - _put_msg("core:memory", Array()); + _put_msg("servers:memory", Array()); } void ScriptEditorDebugger::_video_mem_export() { @@ -305,6 +308,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String error = p_data[1]; bool has_stackdump = p_data[2]; breaked = true; + can_request_idle_draw = true; can_debug = can_continue; _update_buttons_state(); _set_reason_text(error, MESSAGE_ERROR); @@ -315,7 +319,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da if (!error.is_empty()) { tabs->set_current_tab(0); } - profiler->set_enabled(false); + profiler->set_enabled(false, false); + visual_profiler->set_enabled(false); inspector->clear_cache(); // Take a chance to force remote objects update. } else if (p_msg == "debug_exit") { @@ -325,8 +330,12 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da _update_buttons_state(); _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS); emit_signal(SNAME("breaked"), false, false, "", false); - profiler->set_enabled(true); + + profiler->set_enabled(true, false); profiler->disable_seeking(); + + visual_profiler->set_enabled(true); + } else if (p_msg == "set_pid") { ERR_FAIL_COND(p_data.size() < 1); remote_pid = p_data[0]; @@ -344,15 +353,15 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da if (id.is_valid()) { emit_signal(SNAME("remote_object_updated"), id); } - } else if (p_msg == "memory:usage") { + } else if (p_msg == "servers:memory_usage") { vmem_tree->clear(); TreeItem *root = vmem_tree->create_item(); - DebuggerMarshalls::ResourceUsage usage; + ServersDebugger::ResourceUsage usage; usage.deserialize(p_data); uint64_t total = 0; - for (const DebuggerMarshalls::ResourceInfo &E : usage.infos) { + for (const ServersDebugger::ResourceInfo &E : usage.infos) { TreeItem *it = vmem_tree->create_item(root); String type = E.type; int bytes = E.vram; @@ -367,9 +376,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } } - vmem_total->set_tooltip(TTR("Bytes:") + " " + itos(total)); + vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total)); vmem_total->set_text(String::humanize_size(total)); + } else if (p_msg == "servers:drawn") { + can_request_idle_draw = true; } else if (p_msg == "stack_dump") { DebuggerMarshalls::ScriptStackDump stack; stack.deserialize(p_data); @@ -390,7 +401,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da stack_dump_info.push_back(d); s->set_metadata(0, d); - String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + d["function"]; + String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + String(d["function"]); s->set_text(0, line); if (i == 0) { @@ -425,6 +436,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da case RemoteDebugger::MESSAGE_TYPE_LOG: { msg_type = EditorLog::MSG_TYPE_STD; } break; + case RemoteDebugger::MESSAGE_TYPE_LOG_RICH: { + msg_type = EditorLog::MSG_TYPE_STD_RICH; + } break; case RemoteDebugger::MESSAGE_TYPE_ERROR: { msg_type = EditorLog::MSG_TYPE_ERROR; } break; @@ -445,7 +459,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da performance_profiler->add_profile_frame(frame_data); } else if (p_msg == "visual:profile_frame") { - DebuggerMarshalls::VisualProfilerFrame frame; + ServersDebugger::VisualProfilerFrame frame; frame.deserialize(p_data); EditorVisualProfiler::Metric metric; @@ -497,7 +511,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da TreeItem *error = error_tree->create_item(r); error->set_collapsed(true); - error->set_icon(0, get_theme_icon(oe.warning ? "Warning" : "Error", "EditorIcons")); + error->set_icon(0, get_theme_icon(oe.warning ? SNAME("Warning") : SNAME("Error"), SNAME("EditorIcons"))); error->set_text(0, time); error->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); @@ -508,7 +522,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String error_title; 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 + ": "; + error_title = _format_frame_text(&oe.callstack[0]) + ": "; } else if (!oe.source_func.is_empty()) { // Otherwise try to use the C++ source function. error_title += oe.source_func + ": "; @@ -519,13 +533,25 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da error->set_text(1, error_title); tooltip += " " + error_title + "\n"; + // Find the language of the error's source file. + String source_language_name = "C++"; // Default value is the old hard-coded one. + const String source_file_extension = oe.source_file.get_extension(); + for (int i = 0; i < ScriptServer::get_language_count(); ++i) { + ScriptLanguage *script_language = ScriptServer::get_language(i); + if (source_file_extension == script_language->get_extension()) { + source_language_name = script_language->get_name(); + break; + } + } + 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") + ">"); + // TRANSLATORS: %s is the name of a language, e.g. C++. + cpp_cond->set_text(0, "<" + vformat(TTR("%s Error"), source_language_name) + ">"); cpp_cond->set_text(1, oe.error); cpp_cond->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); - tooltip += TTR("C++ Error:") + " " + oe.error + "\n"; + tooltip += vformat(TTR("%s Error:"), source_language_name) + " " + oe.error + "\n"; if (source_is_project_file) { cpp_cond->set_metadata(0, source_meta); } @@ -536,14 +562,18 @@ 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.is_empty()) { - source_txt += " @ " + oe.source_func + "()"; + source_txt += " @ " + oe.source_func; + if (!oe.source_func.ends_with(")")) { + source_txt += "()"; + } } TreeItem *cpp_source = error_tree->create_item(error); - cpp_source->set_text(0, "<" + (source_is_project_file ? TTR("Source") : TTR("C++ Source")) + ">"); + // TRANSLATORS: %s is the name of a language, e.g. C++. + cpp_source->set_text(0, "<" + vformat(TTR("%s Source"), source_language_name) + ">"); cpp_source->set_text(1, source_txt); cpp_source->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); - tooltip += (source_is_project_file ? TTR("Source:") : TTR("C++ Source:")) + " " + source_txt + "\n"; + tooltip += vformat(TTR("%s Source:"), source_language_name) + " " + source_txt + "\n"; // Set metadata to highlight error line in scripts. if (source_is_project_file) { @@ -570,13 +600,13 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da tooltip += TTR("Stack Trace:") + "\n"; } - String frame_txt = infos[i].file.get_file() + ":" + itos(infos[i].line) + " @ " + infos[i].func + "()"; + String frame_txt = _format_frame_text(&infos[i]); tooltip += frame_txt + "\n"; stack_trace->set_text(1, frame_txt); } - error->set_tooltip(0, tooltip); - error->set_tooltip(1, tooltip); + error->set_tooltip_text(0, tooltip); + error->set_tooltip_text(1, tooltip); if (warning_count == 0 && error_count == 0) { expand_all_button->set_disabled(false); @@ -592,18 +622,18 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } else if (p_msg == "servers:function_signature") { // Cache a profiler signature. - DebuggerMarshalls::ScriptFunctionSignature sig; + ServersDebugger::ScriptFunctionSignature sig; sig.deserialize(p_data); profiler_signature[sig.id] = sig.name; } else if (p_msg == "servers:profile_frame" || p_msg == "servers:profile_total") { EditorProfiler::Metric metric; - DebuggerMarshalls::ServersProfilerFrame frame; + ServersDebugger::ServersProfilerFrame frame; frame.deserialize(p_data); metric.valid = true; metric.frame_number = frame.frame_number; metric.frame_time = frame.frame_time; - metric.idle_time = frame.idle_time; + metric.process_time = frame.process_time; metric.physics_time = frame.physics_time; metric.physics_frame_time = frame.physics_frame_time; @@ -624,10 +654,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da frame_time.items.push_back(item); - item.name = "Idle Time"; - item.total = metric.idle_time; + item.name = "Process Time"; + item.total = metric.process_time; item.self = item.total; - item.signature = "idle_time"; + item.signature = "process_time"; frame_time.items.push_back(item); @@ -642,10 +672,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } for (int i = 0; i < frame.servers.size(); i++) { - const DebuggerMarshalls::ServerInfo &srv = frame.servers[i]; + const ServersDebugger::ServerInfo &srv = frame.servers[i]; EditorProfiler::Metric::Category c; const String name = srv.name; - c.name = name.capitalize(); + c.name = EditorPropertyNameProcessor::get_singleton()->process_name(name, EditorPropertyNameProcessor::STYLE_CAPITALIZED); c.items.resize(srv.functions.size()); c.total_time = 0; c.signature = "categ::" + name; @@ -657,7 +687,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da item.self = srv.functions[j].time; item.total = item.self; item.signature = "categ::" + name + "::" + item.name; - item.name = item.name.capitalize(); + item.name = EditorPropertyNameProcessor::get_singleton()->process_name(item.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED); c.total_time += item.total; c.items.write[j] = item; } @@ -709,17 +739,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da profiler->add_frame_metric(metric, true); } - } else if (p_msg == "network:profile_frame") { - DebuggerMarshalls::NetworkProfilerFrame frame; - frame.deserialize(p_data); - for (int i = 0; i < frame.infos.size(); i++) { - network_profiler->add_node_frame_data(frame.infos[i]); - } - - } else if (p_msg == "network:bandwidth") { - ERR_FAIL_COND(p_data.size() < 2); - network_profiler->set_bandwidth(p_data[0], p_data[1]); - } else if (p_msg == "request_quit") { emit_signal(SNAME("stop_requested")); _stop_and_notify(); @@ -737,22 +756,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da 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; - } - + bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data); if (!parsed) { WARN_PRINT("unknown message " + p_msg); } @@ -771,43 +775,54 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType reason->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } reason->set_text(p_reason); - reason->set_tooltip(p_reason.word_wrap(80)); + + const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80); + PackedStringArray lines; + for (int i = 0; i < boundaries.size(); i += 2) { + const int start = boundaries[i]; + const int end = boundaries[i + 1]; + lines.append(p_reason.substr(start, end - start + 1)); + } + + reason->set_tooltip_text(String("\n").join(lines)); } void ScriptEditorDebugger::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - skip_breakpoints->set_icon(get_theme_icon(SNAME("DebugSkipBreakpointsOff"), SNAME("EditorIcons"))); + le_set->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_live_edit_set)); + le_clear->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_live_edit_clear)); + error_tree->connect("item_selected", callable_mp(this, &ScriptEditorDebugger::_error_selected)); + error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated)); + breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked)); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + skip_breakpoints->set_icon(get_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff"), SNAME("EditorIcons"))); copy->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); - step->set_icon(get_theme_icon(SNAME("DebugStep"), SNAME("EditorIcons"))); next->set_icon(get_theme_icon(SNAME("DebugNext"), SNAME("EditorIcons"))); dobreak->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons"))); docontinue->set_icon(get_theme_icon(SNAME("DebugContinue"), SNAME("EditorIcons"))); - le_set->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_live_edit_set)); - le_clear->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_live_edit_clear)); - error_tree->connect("item_selected", callable_mp(this, &ScriptEditorDebugger::_error_selected)); - error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated)); vmem_refresh->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); vmem_export->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons"))); + search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); reason->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); - } break; + case NOTIFICATION_PROCESS: { if (is_session_active()) { peer->poll(); if (camera_override == CameraOverride::OVERRIDE_2D) { - CanvasItemEditor *editor = CanvasItemEditor::get_singleton(); - - Dictionary state = editor->get_state(); + Dictionary state = CanvasItemEditor::get_singleton()->get_state(); float zoom = state["zoom"]; Point2 offset = state["ofs"]; Transform2D transform; transform.scale_basis(Size2(zoom, zoom)); - transform.elements[2] = -offset * zoom; + transform.columns[2] = -offset * zoom; Array msg; msg.push_back(transform); @@ -831,6 +846,10 @@ void ScriptEditorDebugger::_notification(int p_what) { msg.push_back(cam->get_far()); _put_msg("scene:override_camera_3D:transform", msg); } + if (breaked && can_request_idle_draw) { + _put_msg("servers:draw", Array()); + can_request_idle_draw = false; + } } const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20; @@ -852,9 +871,10 @@ void ScriptEditorDebugger::_notification(int p_what) { break; }; } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { if (tabs->has_theme_stylebox_override("panel")) { - tabs->add_theme_style_override("panel", editor->get_gui_base()->get_theme_stylebox(SNAME("DebuggerPanel"), SNAME("EditorStyles"))); + tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("DebuggerPanel"), SNAME("EditorStyles"))); } copy->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); @@ -864,6 +884,7 @@ void ScriptEditorDebugger::_notification(int p_what) { docontinue->set_icon(get_theme_icon(SNAME("DebugContinue"), SNAME("EditorIcons"))); vmem_refresh->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); vmem_export->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons"))); + search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); } break; } } @@ -884,19 +905,37 @@ void ScriptEditorDebugger::_clear_execution() { } void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) { - Ref<Script> script = ResourceLoader::load(p_file); - emit_signal("set_breakpoint", script, p_line - 1, p_enabled); - script.unref(); + Ref<Script> scr = ResourceLoader::load(p_file); + emit_signal(SNAME("set_breakpoint"), scr, p_line - 1, p_enabled); + scr.unref(); } void ScriptEditorDebugger::_clear_breakpoints() { - emit_signal("clear_breakpoints"); + emit_signal(SNAME("clear_breakpoints")); +} + +void ScriptEditorDebugger::_breakpoint_tree_clicked() { + TreeItem *selected = breakpoints_tree->get_selected(); + if (selected->has_meta("line")) { + emit_signal(SNAME("breakpoint_selected"), selected->get_parent()->get_text(0), int(selected->get_meta("line"))); + } +} + +String ScriptEditorDebugger::_format_frame_text(const ScriptLanguage::StackInfo *info) { + String text = info->file.get_file() + ":" + itos(info->line) + " @ " + info->func; + if (!text.ends_with(")")) { + text += "()"; + } + return text; } void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { _clear_errors_list(); stop(); + profiler->set_enabled(true, true); + visual_profiler->set_enabled(true); + peer = p_peer; ERR_FAIL_COND(p_peer.is_null()); @@ -945,26 +984,29 @@ void ScriptEditorDebugger::stop() { peer->close(); peer.unref(); reason->set_text(""); - reason->set_tooltip(""); + reason->set_tooltip_text(""); } node_path_cache.clear(); res_path_cache.clear(); profiler_signature.clear(); + profiler->set_enabled(false, false); + profiler->set_pressed(false); + + visual_profiler->set_enabled(false); + visual_profiler->set_pressed(false); + inspector->edit(nullptr); _update_buttons_state(); } void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) { - Array data; - data.push_back(p_enable); + Array msg_data; + msg_data.push_back(p_enable); switch (p_type) { - case PROFILER_NETWORK: - _put_msg("profiler:network", data); - break; case PROFILER_VISUAL: - _put_msg("profiler:visual", data); + _put_msg("profiler:visual", msg_data); break; case PROFILER_SCRIPTS_SERVERS: if (p_enable) { @@ -972,11 +1014,11 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) { profiler_signature.clear(); // Add max funcs options to request. Array opts; - int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions"); + int max_funcs = EDITOR_GET("debugger/profiler_frame_max_functions"); opts.push_back(CLAMP(max_funcs, 16, 512)); - data.push_back(opts); + msg_data.push_back(opts); } - _put_msg("profiler:servers", data); + _put_msg("profiler:servers", msg_data); break; default: ERR_FAIL_MSG("Invalid profiler type"); @@ -1032,10 +1074,10 @@ int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) { } int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) { - Map<String, int>::Element *E = res_path_cache.find(p_path); + HashMap<String, int>::Iterator E = res_path_cache.find(p_path); if (E) { - return E->get(); + return E->value; } last_path_id++; @@ -1049,32 +1091,30 @@ int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) { return last_path_id; } -void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) { - if (!p_base || !live_debug || !is_session_active() || !editor->get_edited_scene()) { +void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) { + if (!p_base || !live_debug || !is_session_active() || !EditorNode::get_singleton()->get_edited_scene()) { return; } Node *node = Object::cast_to<Node>(p_base); - VARIANT_ARGPTRS - - for (int i = 0; i < VARIANT_ARG_MAX; i++) { + for (int i = 0; i < p_argcount; i++) { //no pointers, sorry - if (argptr[i] && (argptr[i]->get_type() == Variant::OBJECT || argptr[i]->get_type() == Variant::RID)) { + if (p_args[i]->get_type() == Variant::OBJECT || p_args[i]->get_type() == Variant::RID) { return; } } if (node) { - NodePath path = editor->get_edited_scene()->get_path_to(node); + NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node); int pathid = _get_node_path_cache(path); Array msg; msg.push_back(pathid); msg.push_back(p_name); - for (int i = 0; i < VARIANT_ARG_MAX; i++) { + for (int i = 0; i < p_argcount; i++) { //no pointers, sorry - msg.push_back(*argptr[i]); + msg.push_back(*p_args[i]); } _put_msg("scene:live_node_call", msg); @@ -1090,9 +1130,9 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n Array msg; msg.push_back(pathid); msg.push_back(p_name); - for (int i = 0; i < VARIANT_ARG_MAX; i++) { + for (int i = 0; i < p_argcount; i++) { //no pointers, sorry - msg.push_back(*argptr[i]); + msg.push_back(*p_args[i]); } _put_msg("scene:live_res_call", msg); @@ -1101,17 +1141,17 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n } void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p_property, const Variant &p_value) { - if (!p_base || !live_debug || !editor->get_edited_scene()) { + if (!p_base || !live_debug || !EditorNode::get_singleton()->get_edited_scene()) { return; } Node *node = Object::cast_to<Node>(p_base); if (node) { - NodePath path = editor->get_edited_scene()->get_path_to(node); + NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node); int pathid = _get_node_path_cache(path); - if (p_value.is_ref()) { + if (p_value.is_ref_counted()) { Ref<Resource> res = p_value; if (res.is_valid() && !res->get_path().is_empty()) { Array msg; @@ -1137,7 +1177,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p String respath = res->get_path(); int pathid = _get_res_path_cache(respath); - if (p_value.is_ref()) { + if (p_value.is_ref_counted()) { Ref<Resource> res2 = p_value; if (res2.is_valid() && !res2->get_path().is_empty()) { Array msg; @@ -1226,25 +1266,25 @@ void ScriptEditorDebugger::_live_edit_set() { NodePath np = path; - editor->get_editor_data().set_edited_scene_live_edit_root(np); + EditorNode::get_singleton()->get_editor_data().set_edited_scene_live_edit_root(np); update_live_edit_root(); } void ScriptEditorDebugger::_live_edit_clear() { NodePath np = NodePath("/root"); - editor->get_editor_data().set_edited_scene_live_edit_root(np); + EditorNode::get_singleton()->get_editor_data().set_edited_scene_live_edit_root(np); update_live_edit_root(); } void ScriptEditorDebugger::update_live_edit_root() { - NodePath np = editor->get_editor_data().get_edited_scene_live_edit_root(); + NodePath np = EditorNode::get_singleton()->get_editor_data().get_edited_scene_live_edit_root(); Array msg; msg.push_back(np); - if (editor->get_edited_scene()) { - msg.push_back(editor->get_edited_scene()->get_scene_file_path()); + if (EditorNode::get_singleton()->get_edited_scene()) { + msg.push_back(EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path()); } else { msg.push_back(""); } @@ -1262,13 +1302,13 @@ void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, cons } } -void ScriptEditorDebugger::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) { +void ScriptEditorDebugger::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) { if (live_debug) { Array msg; msg.push_back(p_parent); msg.push_back(p_path); msg.push_back(p_name); - _put_msg("scene:live_instance_node", msg); + _put_msg("scene:live_instantiate_node", msg); } } @@ -1351,6 +1391,45 @@ void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool msg.push_back(p_line); msg.push_back(p_enabled); _put_msg("breakpoint", msg); + + TreeItem *path_item = breakpoints_tree->search_item_text(p_path); + if (path_item == nullptr) { + if (!p_enabled) { + return; + } + path_item = breakpoints_tree->create_item(); + path_item->set_text(0, p_path); + } + + int idx = 0; + TreeItem *breakpoint_item; + for (breakpoint_item = path_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) { + if ((int)breakpoint_item->get_meta("line") < p_line) { + idx++; + continue; + } + + if ((int)breakpoint_item->get_meta("line") == p_line) { + break; + } + } + + if (breakpoint_item == nullptr) { + if (!p_enabled) { + return; + } + breakpoint_item = breakpoints_tree->create_item(path_item, idx); + breakpoint_item->set_meta("line", p_line); + breakpoint_item->set_text(0, vformat(TTR("Line %d"), p_line)); + return; + } + + if (!p_enabled) { + path_item->remove_child(breakpoint_item); + if (path_item->get_first_child() == nullptr) { + breakpoints_tree->get_root()->remove_child(path_item); + } + } } void ScriptEditorDebugger::reload_scripts() { @@ -1364,6 +1443,10 @@ bool ScriptEditorDebugger::is_skip_breakpoints() { void ScriptEditorDebugger::_error_activated() { TreeItem *selected = error_tree->get_selected(); + if (!selected) { + return; + } + TreeItem *ci = selected->get_first_child(); if (ci) { selected->set_collapsed(!selected->is_collapsed()); @@ -1372,6 +1455,11 @@ void ScriptEditorDebugger::_error_activated() { void ScriptEditorDebugger::_error_selected() { TreeItem *selected = error_tree->get_selected(); + + if (!selected) { + return; + } + Array meta = selected->get_metadata(0); if (meta.size() == 0) { return; @@ -1410,6 +1498,7 @@ void ScriptEditorDebugger::_clear_errors_list() { error_tree->clear(); error_count = 0; warning_count = 0; + emit_signal(SNAME("errors_cleared")); update_tabs(); expand_all_button->set_disabled(true); @@ -1417,14 +1506,39 @@ void ScriptEditorDebugger::_clear_errors_list() { clear_button->set_disabled(true); } +void ScriptEditorDebugger::_breakpoints_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) { + if (p_button != MouseButton::RIGHT) { + return; + } + + breakpoints_menu->clear(); + breakpoints_menu->set_size(Size2(1, 1)); + + const TreeItem *selected = breakpoints_tree->get_selected(); + String file = selected->get_text(0); + if (selected->has_meta("line")) { + breakpoints_menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Breakpoint"), ACTION_DELETE_BREAKPOINT); + file = selected->get_parent()->get_text(0); + } + breakpoints_menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete All Breakpoints in:") + " " + file, ACTION_DELETE_BREAKPOINTS_IN_FILE); + breakpoints_menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete All Breakpoints"), ACTION_DELETE_ALL_BREAKPOINTS); + + breakpoints_menu->set_position(breakpoints_tree->get_global_position() + p_pos); + breakpoints_menu->popup(); +} + // Right click on specific file(s) or folder(s). -void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) { +void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) { + if (p_button != MouseButton::RIGHT) { + return; + } + item_menu->clear(); item_menu->reset_size(); if (error_tree->is_anything_selected()) { item_menu->add_icon_item(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), TTR("Copy Error"), ACTION_COPY_ERROR); - item_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Open C++ Source on GitHub"), ACTION_OPEN_SOURCE); + item_menu->add_icon_item(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), TTR("Open C++ Source on GitHub"), ACTION_OPEN_SOURCE); } if (item_menu->get_item_count() > 0) { @@ -1468,8 +1582,22 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { ti = ti->get_parent(); } - // We only need the first child here (C++ source stack trace). + // Find the child with the "C++ Source". + // It's not at a fixed position as "C++ Error" may come first. TreeItem *ci = ti->get_first_child(); + const String cpp_source = "<" + TTR("C++ Source") + ">"; + while (ci) { + if (ci->get_text(0) == cpp_source) { + break; + } + ci = ci->get_next(); + } + + if (!ci) { + WARN_PRINT_ED("No C++ source reference is available for this error."); + return; + } + // Parse back the `file:line @ method()` string. const Vector<String> file_line_number = ci->get_text(1).split("@")[0].strip_edges().split(":"); ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report)."); @@ -1477,19 +1605,33 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { const int line_number = file_line_number[1].to_int(); // Construct a GitHub repository URL and open it in the user's default web browser. - if (String(VERSION_HASH).length() >= 1) { - // Git commit hash information available; use it for greater accuracy, including for development versions. - OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d", - VERSION_HASH, - file, - line_number)); - } else { - // Git commit hash information unavailable; fall back to tagged releases. - OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s-stable/%s#L%d", - VERSION_NUMBER, - file, - line_number)); + // If the commit hash is available, use it for greater accuracy. Otherwise fall back to tagged release. + String git_ref = String(VERSION_HASH).is_empty() ? String(VERSION_NUMBER) + "-stable" : String(VERSION_HASH); + OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d", + git_ref, file, line_number)); + } break; + case ACTION_DELETE_BREAKPOINT: { + const TreeItem *selected = breakpoints_tree->get_selected(); + _set_breakpoint(selected->get_parent()->get_text(0), selected->get_meta("line"), false); + } break; + case ACTION_DELETE_BREAKPOINTS_IN_FILE: { + TreeItem *file_item = breakpoints_tree->get_selected(); + if (file_item->has_meta("line")) { + file_item = file_item->get_parent(); + } + + // Store first else we will be removing as we loop. + List<int> lines; + for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) { + lines.push_back(breakpoint_item->get_meta("line")); } + + for (const int &line : lines) { + _set_breakpoint(file_item->get_text(0), line, false); + } + } break; + case ACTION_DELETE_ALL_BREAKPOINTS: { + _clear_breakpoints(); } break; } } @@ -1503,7 +1645,7 @@ void ScriptEditorDebugger::_tab_changed(int p_tab) { void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("live_debug_create_node"), &ScriptEditorDebugger::live_debug_create_node); - ClassDB::bind_method(D_METHOD("live_debug_instance_node"), &ScriptEditorDebugger::live_debug_instance_node); + ClassDB::bind_method(D_METHOD("live_debug_instantiate_node"), &ScriptEditorDebugger::live_debug_instantiate_node); ClassDB::bind_method(D_METHOD("live_debug_remove_node"), &ScriptEditorDebugger::live_debug_remove_node); ClassDB::bind_method(D_METHOD("live_debug_remove_and_keep_node"), &ScriptEditorDebugger::live_debug_remove_and_keep_node); ClassDB::bind_method(D_METHOD("live_debug_restore_node"), &ScriptEditorDebugger::live_debug_restore_node); @@ -1517,6 +1659,7 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("stop_requested")); ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame"))); ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error"))); + ADD_SIGNAL(MethodInfo("breakpoint_selected", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script"))); ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"), PropertyInfo(Variant::STRING, "reason"), PropertyInfo(Variant::BOOL, "has_stackdump"))); @@ -1531,55 +1674,39 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data"))); ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); ADD_SIGNAL(MethodInfo("clear_breakpoints")); + ADD_SIGNAL(MethodInfo("errors_cleared")); } -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::add_debugger_tab(Control *p_control) { + tabs->add_child(p_control); } -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::remove_debugger_tab(Control *p_control) { + int idx = tabs->get_tab_idx_from_control(p_control); + ERR_FAIL_COND(idx < 0); + p_control->queue_free(); } 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); +void ScriptEditorDebugger::toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data) { + Array msg_data; + msg_data.push_back(p_enable); + msg_data.append_array(p_data); + _put_msg("profiler:" + p_profiler, msg_data); } -bool ScriptEditorDebugger::has_capture(const StringName &p_name) { - return captures.has(p_name); -} - -ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { - editor = p_editor; - +ScriptEditorDebugger::ScriptEditorDebugger() { tabs = memnew(TabContainer); - tabs->set_tab_alignment(TabContainer::ALIGNMENT_LEFT); - tabs->add_theme_style_override("panel", editor->get_gui_base()->get_theme_stylebox(SNAME("DebuggerPanel"), SNAME("EditorStyles"))); + tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("DebuggerPanel"), SNAME("EditorStyles"))); tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed)); add_child(tabs); + InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected)); + { //debugger VBoxContainer *vbc = memnew(VBoxContainer); vbc->set_name(TTR("Debugger")); @@ -1592,7 +1719,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { reason->set_text(""); hbc->add_child(reason); reason->set_h_size_flags(SIZE_EXPAND_FILL); - reason->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + reason->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); reason->set_max_lines_visible(3); reason->set_mouse_filter(Control::MOUSE_FILTER_PASS); @@ -1601,7 +1728,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { skip_breakpoints = memnew(Button); skip_breakpoints->set_flat(true); hbc->add_child(skip_breakpoints); - skip_breakpoints->set_tooltip(TTR("Skip Breakpoints")); + skip_breakpoints->set_tooltip_text(TTR("Skip Breakpoints")); skip_breakpoints->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints)); hbc->add_child(memnew(VSeparator)); @@ -1609,7 +1736,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { copy = memnew(Button); copy->set_flat(true); hbc->add_child(copy); - copy->set_tooltip(TTR("Copy Error")); + copy->set_tooltip_text(TTR("Copy Error")); copy->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_copy)); hbc->add_child(memnew(VSeparator)); @@ -1617,14 +1744,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { step = memnew(Button); step->set_flat(true); hbc->add_child(step); - step->set_tooltip(TTR("Step Into")); + step->set_tooltip_text(TTR("Step Into")); step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into")); step->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_step)); next = memnew(Button); next->set_flat(true); hbc->add_child(next); - next->set_tooltip(TTR("Step Over")); + next->set_tooltip_text(TTR("Step Over")); next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over")); next->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_next)); @@ -1633,20 +1760,26 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { dobreak = memnew(Button); dobreak->set_flat(true); hbc->add_child(dobreak); - dobreak->set_tooltip(TTR("Break")); + dobreak->set_tooltip_text(TTR("Break")); dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break")); dobreak->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_break)); docontinue = memnew(Button); docontinue->set_flat(true); hbc->add_child(docontinue); - docontinue->set_tooltip(TTR("Continue")); + docontinue->set_tooltip_text(TTR("Continue")); docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue")); docontinue->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_continue)); + HSplitContainer *parent_sc = memnew(HSplitContainer); + vbc->add_child(parent_sc); + parent_sc->set_v_size_flags(SIZE_EXPAND_FILL); + parent_sc->set_split_offset(500 * EDSCALE); + HSplitContainer *sc = memnew(HSplitContainer); - vbc->add_child(sc); sc->set_v_size_flags(SIZE_EXPAND_FILL); + sc->set_h_size_flags(SIZE_EXPAND_FILL); + parent_sc->add_child(sc); stack_dump = memnew(Tree); stack_dump->set_allow_reselect(true); @@ -1658,15 +1791,47 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected)); sc->add_child(stack_dump); + VBoxContainer *inspector_vbox = memnew(VBoxContainer); + inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + sc->add_child(inspector_vbox); + + HBoxContainer *tools_hb = memnew(HBoxContainer); + inspector_vbox->add_child(tools_hb); + + search = memnew(LineEdit); + search->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search->set_placeholder(TTR("Filter Stack Variables")); + search->set_clear_button_enabled(true); + tools_hb->add_child(search); + inspector = memnew(EditorDebuggerInspector); inspector->set_h_size_flags(SIZE_EXPAND_FILL); - inspector->set_enable_capitalize_paths(false); + inspector->set_v_size_flags(SIZE_EXPAND_FILL); + inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW); inspector->set_read_only(true); inspector->connect("object_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected)); inspector->connect("object_edited", callable_mp(this, &ScriptEditorDebugger::_remote_object_edited)); inspector->connect("object_property_updated", callable_mp(this, &ScriptEditorDebugger::_remote_object_property_updated)); - sc->add_child(inspector); + inspector->register_text_enter(search); + inspector->set_use_filter(true); + inspector_vbox->add_child(inspector); + + breakpoints_tree = memnew(Tree); + breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL); + breakpoints_tree->set_column_titles_visible(true); + breakpoints_tree->set_column_title(0, TTR("Breakpoints")); + breakpoints_tree->set_allow_reselect(true); + breakpoints_tree->set_allow_rmb_select(true); + breakpoints_tree->set_hide_root(true); + breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected)); + breakpoints_tree->create_item(); + + parent_sc->add_child(breakpoints_tree); tabs->add_child(dbg); + + breakpoints_menu = memnew(PopupMenu); + breakpoints_menu->connect("id_pressed", callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed)); + breakpoints_tree->add_child(breakpoints_menu); } { //errors @@ -1713,7 +1878,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { error_tree->set_hide_root(true); error_tree->set_v_size_flags(SIZE_EXPAND_FILL); error_tree->set_allow_rmb_select(true); - error_tree->connect("item_rmb_selected", callable_mp(this, &ScriptEditorDebugger::_error_tree_item_rmb_selected)); + error_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_error_tree_item_rmb_selected)); errors_tab->add_child(error_tree); item_menu = memnew(PopupMenu); @@ -1733,7 +1898,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { profiler = memnew(EditorProfiler); profiler->set_name(TTR("Profiler")); tabs->add_child(profiler); - profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_SCRIPTS_SERVERS)); + profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_SCRIPTS_SERVERS)); profiler->connect("break_request", callable_mp(this, &ScriptEditorDebugger::_profiler_seeked)); } @@ -1741,14 +1906,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { visual_profiler = memnew(EditorVisualProfiler); visual_profiler->set_name(TTR("Visual Profiler")); tabs->add_child(visual_profiler); - visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_VISUAL)); - } - - { //network profiler - network_profiler = memnew(EditorNetworkProfiler); - network_profiler->set_name(TTR("Network Profiler")); - tabs->add_child(network_profiler); - network_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_NETWORK)); + visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_VISUAL)); } { //monitors @@ -1774,7 +1932,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmem_hb->add_child(vmem_refresh); vmem_export = memnew(Button); vmem_export->set_flat(true); - vmem_export->set_tooltip(TTR("Export list to a CSV file")); + vmem_export->set_tooltip_text(TTR("Export list to a CSV file")); vmem_hb->add_child(vmem_export); vmem_vb->add_child(vmem_hb); vmem_refresh->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_video_mem_request)); @@ -1816,15 +1974,18 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { info_left->set_columns(2); misc->add_child(info_left); clicked_ctrl = memnew(LineEdit); + clicked_ctrl->set_editable(false); clicked_ctrl->set_h_size_flags(SIZE_EXPAND_FILL); info_left->add_child(memnew(Label(TTR("Clicked Control:")))); info_left->add_child(clicked_ctrl); clicked_ctrl_type = memnew(LineEdit); + clicked_ctrl_type->set_editable(false); info_left->add_child(memnew(Label(TTR("Clicked Control Type:")))); info_left->add_child(clicked_ctrl_type); scene_tree = memnew(SceneDebuggerTree); live_edit_root = memnew(LineEdit); + live_edit_root->set_editable(false); live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL); { diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index ff1a852f26..1659bbee8d 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_editor_debugger.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_editor_debugger.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 SCRIPT_EDITOR_DEBUGGER_H #define SCRIPT_EDITOR_DEBUGGER_H @@ -35,12 +35,10 @@ #include "editor/debugger/editor_debugger_inspector.h" #include "editor/debugger/editor_debugger_node.h" #include "editor/debugger/editor_debugger_server.h" -#include "editor/editor_file_dialog.h" #include "scene/gui/button.h" #include "scene/gui/margin_container.h" class Tree; -class EditorNode; class LineEdit; class TabContainer; class RichTextLabel; @@ -50,8 +48,8 @@ class TreeItem; class HSplitContainer; class ItemList; class EditorProfiler; +class EditorFileDialog; class EditorVisualProfiler; -class EditorNetworkProfiler; class EditorPerformanceProfiler; class SceneDebuggerTree; class EditorDebuggerPlugin; @@ -73,7 +71,6 @@ private: }; enum ProfilerType { - PROFILER_NETWORK, PROFILER_VISUAL, PROFILER_SCRIPTS_SERVERS }; @@ -81,25 +78,31 @@ private: enum Actions { ACTION_COPY_ERROR, ACTION_OPEN_SOURCE, + ACTION_DELETE_BREAKPOINT, + ACTION_DELETE_BREAKPOINTS_IN_FILE, + ACTION_DELETE_ALL_BREAKPOINTS, }; - AcceptDialog *msgdialog; + AcceptDialog *msgdialog = nullptr; - LineEdit *clicked_ctrl; - LineEdit *clicked_ctrl_type; - LineEdit *live_edit_root; - Button *le_set; - Button *le_clear; - Button *export_csv; + LineEdit *clicked_ctrl = nullptr; + LineEdit *clicked_ctrl_type = nullptr; + LineEdit *live_edit_root = nullptr; + Button *le_set = nullptr; + Button *le_clear = nullptr; + Button *export_csv = nullptr; - VBoxContainer *errors_tab; - Tree *error_tree; - Button *expand_all_button; - Button *collapse_all_button; - Button *clear_button; - PopupMenu *item_menu; + VBoxContainer *errors_tab = nullptr; + Tree *error_tree = nullptr; + Button *expand_all_button = nullptr; + Button *collapse_all_button = nullptr; + Button *clear_button = nullptr; + PopupMenu *item_menu = nullptr; - EditorFileDialog *file_dialog; + Tree *breakpoints_tree = nullptr; + PopupMenu *breakpoints_menu = nullptr; + + EditorFileDialog *file_dialog = nullptr; enum FileDialogPurpose { SAVE_MONITORS_CSV, SAVE_VRAM_CSV, @@ -112,57 +115,52 @@ private: bool skip_breakpoints_value = false; Ref<Script> stack_script; - TabContainer *tabs; + TabContainer *tabs = nullptr; - Label *reason; + Label *reason = nullptr; - Button *skip_breakpoints; - Button *copy; - Button *step; - Button *next; - Button *dobreak; - Button *docontinue; + Button *skip_breakpoints = nullptr; + Button *copy = nullptr; + Button *step = nullptr; + Button *next = nullptr; + Button *dobreak = nullptr; + Button *docontinue = nullptr; // Reference to "Remote" tab in scene tree. Needed by _live_edit_set and buttons state. // Each debugger should have it's tree in the future I guess. const Tree *editor_remote_tree = nullptr; - Map<int, String> profiler_signature; + HashMap<int, String> profiler_signature; - Tree *vmem_tree; - Button *vmem_refresh; - Button *vmem_export; - LineEdit *vmem_total; + Tree *vmem_tree = nullptr; + Button *vmem_refresh = nullptr; + Button *vmem_export = nullptr; + LineEdit *vmem_total = nullptr; - Tree *stack_dump; - EditorDebuggerInspector *inspector; - SceneDebuggerTree *scene_tree; + Tree *stack_dump = nullptr; + LineEdit *search = nullptr; + EditorDebuggerInspector *inspector = nullptr; + SceneDebuggerTree *scene_tree = nullptr; Ref<RemoteDebuggerPeer> peer; HashMap<NodePath, int> node_path_cache; int last_path_id; - Map<String, int> res_path_cache; - - EditorProfiler *profiler; - EditorVisualProfiler *visual_profiler; - EditorNetworkProfiler *network_profiler; - EditorPerformanceProfiler *performance_profiler; + HashMap<String, int> res_path_cache; - EditorNode *editor; + EditorProfiler *profiler = nullptr; + EditorVisualProfiler *visual_profiler = nullptr; + EditorPerformanceProfiler *performance_profiler = nullptr; OS::ProcessID remote_pid = 0; bool breaked = false; bool can_debug = false; bool move_to_foreground = true; + bool can_request_idle_draw = false; bool live_debug; EditorDebuggerNode::CameraOverride camera_override; - Map<Ref<Script>, EditorDebuggerPlugin *> debugger_plugins; - - Map<StringName, Callable> captures; - void _stack_dump_frame_selected(); void _file_selected(const String &p_file); @@ -183,7 +181,7 @@ private: void _live_edit_set(); void _live_edit_clear(); - void _method_changed(Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE); + void _method_changed(Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); void _property_changed(Object *p_base, const StringName &p_property, const Variant &p_value); void _error_activated(); @@ -197,7 +195,8 @@ private: void _clear_errors_list(); - void _error_tree_item_rmb_selected(const Vector2 &p_pos); + void _breakpoints_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button); + void _error_tree_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button); void _item_menu_id_pressed(int p_option); void _tab_changed(int p_tab); @@ -210,6 +209,10 @@ private: void _set_breakpoint(const String &p_path, const int &p_line, const bool &p_enabled); void _clear_breakpoints(); + void _breakpoint_tree_clicked(); + + String _format_frame_text(const ScriptLanguage::StackInfo *info); + protected: void _notification(int p_what); static void _bind_methods(); @@ -259,7 +262,7 @@ public: void set_live_debugging(bool p_enable); void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name); - void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name); + void live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name); void live_debug_remove_node(const NodePath &p_at); void live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id); void live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos); @@ -279,16 +282,13 @@ public: 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 add_debugger_tab(Control *p_control); + void remove_debugger_tab(Control *p_control); void send_message(const String &p_message, const Array &p_args); + void toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data); - 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(); ~ScriptEditorDebugger(); }; |