Path: blob/master/modules/objectdb_profiler/editor/data_viewers/refcounted_view.cpp
11325 views
/**************************************************************************/1/* refcounted_view.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "refcounted_view.h"3132#include "editor/editor_node.h"33#include "editor/themes/editor_scale.h"34#include "scene/gui/rich_text_label.h"35#include "scene/gui/split_container.h"3637SnapshotRefCountedView::SnapshotRefCountedView() {38set_name(TTRC("RefCounted"));39}4041void SnapshotRefCountedView::show_snapshot(GameStateSnapshot *p_data, GameStateSnapshot *p_diff_data) {42SnapshotView::show_snapshot(p_data, p_diff_data);4344item_data_map.clear();45data_item_map.clear();4647set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);48set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);4950refs_view = memnew(HSplitContainer);51add_child(refs_view);52refs_view->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);5354VBoxContainer *refs_column = memnew(VBoxContainer);55refs_column->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);56refs_view->add_child(refs_column);5758// Tree of Refs.59refs_list = memnew(Tree);6061filter_bar = memnew(TreeSortAndFilterBar(refs_list, TTRC("Filter RefCounteds")));62refs_column->add_child(filter_bar);63int offset = diff_data ? 1 : 0;64if (diff_data) {65filter_bar->add_sort_option(TTRC("Snapshot"), TreeSortAndFilterBar::SortType::ALPHA_SORT, 0);66}67filter_bar->add_sort_option(TTRC("Class"), TreeSortAndFilterBar::SortType::ALPHA_SORT, offset + 0);68filter_bar->add_sort_option(TTRC("Name"), TreeSortAndFilterBar::SortType::ALPHA_SORT, offset + 1);69TreeSortAndFilterBar::SortOptionIndexes default_sort = filter_bar->add_sort_option(70TTRC("Native Refs"),71TreeSortAndFilterBar::SortType::NUMERIC_SORT,72offset + 2);73filter_bar->add_sort_option(TTRC("ObjectDB Refs"), TreeSortAndFilterBar::SortType::NUMERIC_SORT, offset + 3);74filter_bar->add_sort_option(TTRC("Total Refs"), TreeSortAndFilterBar::SortType::NUMERIC_SORT, offset + 4);75filter_bar->add_sort_option(TTRC("ObjectDB Cycles"), TreeSortAndFilterBar::SortType::NUMERIC_SORT, offset + 5);7677refs_list->set_select_mode(Tree::SelectMode::SELECT_ROW);78refs_list->set_custom_minimum_size(Size2(200, 0) * EDSCALE);79refs_list->set_hide_folding(false);80refs_column->add_child(refs_list);81refs_list->set_hide_root(true);82refs_list->set_columns(diff_data ? 7 : 6);83refs_list->set_column_titles_visible(true);8485if (diff_data) {86refs_list->set_column_title(0, TTRC("Snapshot"));87refs_list->set_column_expand(0, false);88refs_list->set_column_title_tooltip_text(0, "A: " + snapshot_data->name + ", B: " + diff_data->name);89}9091refs_list->set_column_title(offset + 0, TTRC("Class"));92refs_list->set_column_expand(offset + 0, true);93refs_list->set_column_title_tooltip_text(offset + 0, TTRC("Object's class"));9495refs_list->set_column_title(offset + 1, TTRC("Name"));96refs_list->set_column_expand(offset + 1, true);97refs_list->set_column_expand_ratio(offset + 1, 2);98refs_list->set_column_title_tooltip_text(offset + 1, TTRC("Object's name"));99100refs_list->set_column_title(offset + 2, TTRC("Native Refs"));101refs_list->set_column_expand(offset + 2, false);102refs_list->set_column_title_tooltip_text(offset + 2, TTRC("References not owned by the ObjectDB"));103104refs_list->set_column_title(offset + 3, TTRC("ObjectDB Refs"));105refs_list->set_column_expand(offset + 3, false);106refs_list->set_column_title_tooltip_text(offset + 3, TTRC("References owned by the ObjectDB"));107108refs_list->set_column_title(offset + 4, TTRC("Total Refs"));109refs_list->set_column_expand(offset + 4, false);110refs_list->set_column_title_tooltip_text(offset + 4, TTRC("ObjectDB References + Native References"));111112refs_list->set_column_title(offset + 5, TTRC("ObjectDB Cycles"));113refs_list->set_column_expand(offset + 5, false);114refs_list->set_column_title_tooltip_text(offset + 5, TTRC("Cycles detected in the ObjectDB"));115116refs_list->connect(SceneStringName(item_selected), callable_mp(this, &SnapshotRefCountedView::_refcounted_selected));117refs_list->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);118refs_list->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);119120// View of the selected refcounted.121ref_details = memnew(VBoxContainer);122ref_details->set_custom_minimum_size(Size2(200, 0) * EDSCALE);123refs_view->add_child(ref_details);124ref_details->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);125ref_details->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);126127refs_list->create_item();128_insert_data(snapshot_data, TTRC("A"));129if (diff_data) {130_insert_data(diff_data, TTRC("B"));131}132133// Push the split as far right as possible.134filter_bar->select_sort(default_sort.descending);135filter_bar->apply();136refs_list->set_selected(refs_list->get_root()->get_first_child());137138callable_mp(this, &SnapshotRefCountedView::_set_split_to_center).call_deferred();139}140141void SnapshotRefCountedView::_set_split_to_center() {142refs_view->set_split_offset(refs_view->get_size().x * 0.5);143}144145void SnapshotRefCountedView::_insert_data(GameStateSnapshot *p_snapshot, const String &p_name) {146for (const KeyValue<ObjectID, SnapshotDataObject *> &pair : p_snapshot->objects) {147if (!pair.value->is_refcounted()) {148continue;149}150151TreeItem *item = refs_list->create_item(refs_list->get_root());152item_data_map[item] = pair.value;153data_item_map[pair.value] = item;154int total_refs = pair.value->extra_debug_data.has("ref_count") ? (uint64_t)pair.value->extra_debug_data["ref_count"] : 0;155int objectdb_refs = pair.value->get_unique_inbound_references().size();156int native_refs = total_refs - objectdb_refs;157158Array ref_cycles = (Array)pair.value->extra_debug_data["ref_cycles"];159160int offset = 0;161if (diff_data) {162item->set_text(0, p_name);163item->set_tooltip_text(0, p_snapshot->name);164item->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);165offset = 1;166}167168item->set_text(offset + 0, pair.value->type_name);169item->set_auto_translate_mode(offset + 0, AUTO_TRANSLATE_MODE_DISABLED);170item->set_text(offset + 1, pair.value->get_name());171item->set_auto_translate_mode(offset + 1, AUTO_TRANSLATE_MODE_DISABLED);172item->set_text(offset + 2, String::num_uint64(native_refs));173item->set_text(offset + 3, String::num_uint64(objectdb_refs));174item->set_text(offset + 4, String::num_uint64(total_refs));175item->set_text(offset + 5, String::num_uint64(ref_cycles.size())); // Compute cycles and attach it to refcounted object.176177if (total_refs == ref_cycles.size()) {178// Often, references are held by the engine so we can't know if we're stuck in a cycle or not179// But if the full cycle is visible in the ObjectDB,180// tell the user by highlighting the cells in red.181item->set_custom_bg_color(offset + 5, Color(1, 0, 0, 0.1));182}183}184}185186void SnapshotRefCountedView::_refcounted_selected() {187for (int i = 0; i < ref_details->get_child_count(); i++) {188ref_details->get_child(i)->queue_free();189}190191SnapshotDataObject *d = item_data_map[refs_list->get_selected()];192EditorNode::get_singleton()->push_item(static_cast<Object *>(d));193194DarkPanelContainer *refcounted_panel = memnew(DarkPanelContainer);195VBoxContainer *refcounted_panel_content = memnew(VBoxContainer);196refcounted_panel_content->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);197refcounted_panel_content->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);198ref_details->add_child(refcounted_panel);199refcounted_panel->add_child(refcounted_panel_content);200refcounted_panel_content->add_child(memnew(SpanningHeader(d->get_name())));201202ScrollContainer *properties_scroll = memnew(ScrollContainer);203properties_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);204properties_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_AUTO);205properties_scroll->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);206properties_scroll->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);207refcounted_panel_content->add_child(properties_scroll);208209VBoxContainer *properties_container = memnew(VBoxContainer);210properties_container->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);211properties_scroll->add_child(properties_container);212properties_container->add_theme_constant_override("separation", 5);213properties_container->add_theme_constant_override("margin_left", 2);214properties_container->add_theme_constant_override("margin_right", 2);215properties_container->add_theme_constant_override("margin_top", 2);216properties_container->add_theme_constant_override("margin_bottom", 2);217218int total_refs = d->extra_debug_data.has("ref_count") ? (uint64_t)d->extra_debug_data["ref_count"] : 0;219int objectdb_refs = d->get_unique_inbound_references().size();220int native_refs = total_refs - objectdb_refs;221Array ref_cycles = (Array)d->extra_debug_data["ref_cycles"];222223String count_str = "[ul]\n";224count_str += vformat(TTRC("Native References: %d\n"), native_refs);225count_str += vformat(TTRC("ObjectDB References: %d\n"), objectdb_refs);226count_str += vformat(TTRC("Total References: %d\n"), total_refs);227count_str += vformat(TTRC("ObjectDB Cycles: %d\n"), ref_cycles.size());228count_str += "[/ul]\n";229RichTextLabel *counts = memnew(RichTextLabel(count_str));230counts->set_use_bbcode(true);231counts->set_fit_content(true);232counts->add_theme_constant_override("line_separation", 6);233properties_container->add_child(counts);234235if (d->inbound_references.size() > 0) {236RichTextLabel *inbound_lbl = memnew(RichTextLabel(TTRC("[center]ObjectDB References[center]")));237inbound_lbl->set_fit_content(true);238inbound_lbl->set_use_bbcode(true);239properties_container->add_child(inbound_lbl);240Tree *inbound_tree = memnew(Tree);241inbound_tree->set_hide_folding(true);242properties_container->add_child(inbound_tree);243inbound_tree->set_select_mode(Tree::SelectMode::SELECT_ROW);244inbound_tree->set_hide_root(true);245inbound_tree->set_columns(3);246inbound_tree->set_column_titles_visible(true);247inbound_tree->set_column_title(0, TTRC("Source"));248inbound_tree->set_column_expand(0, true);249inbound_tree->set_column_clip_content(0, false);250inbound_tree->set_column_title_tooltip_text(0, TTRC("Other object referencing this object"));251inbound_tree->set_column_title(1, TTRC("Property"));252inbound_tree->set_column_expand(1, true);253inbound_tree->set_column_clip_content(1, true);254inbound_tree->set_column_title_tooltip_text(1, TTRC("Property of other object referencing this object"));255inbound_tree->set_column_title(2, TTRC("Duplicate?"));256inbound_tree->set_column_expand(2, false);257inbound_tree->set_column_title_tooltip_text(2, TTRC("Was the same reference returned by multiple getters on the source object?"));258inbound_tree->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);259inbound_tree->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);260inbound_tree->set_v_scroll_enabled(false);261inbound_tree->connect(SceneStringName(item_selected), callable_mp(this, &SnapshotRefCountedView::_ref_selected).bind(inbound_tree));262263// The same reference can exist as multiple properties of an object (for example, gdscript `@export` properties exist twice).264// We flag for the user if a property is exposed multiple times so it's clearer why there are more references in the list265// than the ObjectDB References count would suggest.266HashMap<ObjectID, int> property_repeat_count;267for (const KeyValue<String, ObjectID> &ob : d->inbound_references) {268if (!property_repeat_count.has(ob.value)) {269property_repeat_count.insert(ob.value, 0);270}271property_repeat_count[ob.value]++;272}273274TreeItem *root = inbound_tree->create_item();275for (const KeyValue<String, ObjectID> &ob : d->inbound_references) {276TreeItem *i = inbound_tree->create_item(root);277SnapshotDataObject *target = d->snapshot->objects[ob.value];278i->set_text(0, target->get_name());279i->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);280i->set_text(1, ob.key);281i->set_auto_translate_mode(1, AUTO_TRANSLATE_MODE_DISABLED);282i->set_text(2, property_repeat_count[ob.value] > 1 ? TTRC("Yes") : TTRC("No"));283reference_item_map[i] = data_item_map[target];284}285}286287if (ref_cycles.size() > 0) {288properties_container->add_child(memnew(SpanningHeader(TTRC("ObjectDB Cycles"))));289Tree *cycles_tree = memnew(Tree);290cycles_tree->set_hide_folding(true);291properties_container->add_child(cycles_tree);292cycles_tree->set_select_mode(Tree::SelectMode::SELECT_ROW);293cycles_tree->set_hide_root(true);294cycles_tree->set_columns(1);295cycles_tree->set_column_titles_visible(false);296cycles_tree->set_column_expand(0, true);297cycles_tree->set_column_clip_content(0, false);298cycles_tree->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);299cycles_tree->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);300cycles_tree->set_v_scroll_enabled(false);301302TreeItem *root = cycles_tree->create_item();303for (const Variant &cycle : ref_cycles) {304TreeItem *i = cycles_tree->create_item(root);305i->set_text(0, cycle);306i->set_text_overrun_behavior(0, TextServer::OverrunBehavior::OVERRUN_NO_TRIMMING);307}308}309}310311void SnapshotRefCountedView::_ref_selected(Tree *p_source_tree) {312TreeItem *target = reference_item_map[p_source_tree->get_selected()];313if (target) {314if (!target->is_visible()) {315// Clear the filter if we can't see the node we just chose.316filter_bar->clear_filter();317}318target->get_tree()->deselect_all();319target->get_tree()->set_selected(target);320target->get_tree()->ensure_cursor_is_visible();321}322}323324325