Path: blob/master/modules/objectdb_profiler/editor/data_viewers/refcounted_view.cpp
20920 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);84refs_list->set_theme_type_variation("TreeSecondary");8586if (diff_data) {87refs_list->set_column_title(0, TTRC("Snapshot"));88refs_list->set_column_expand(0, false);89refs_list->set_column_title_tooltip_text(0, "A: " + snapshot_data->name + ", B: " + diff_data->name);90}9192refs_list->set_column_title(offset + 0, TTRC("Class"));93refs_list->set_column_expand(offset + 0, true);94refs_list->set_column_title_tooltip_text(offset + 0, TTRC("Object's class"));9596refs_list->set_column_title(offset + 1, TTRC("Name"));97refs_list->set_column_expand(offset + 1, true);98refs_list->set_column_expand_ratio(offset + 1, 2);99refs_list->set_column_title_tooltip_text(offset + 1, TTRC("Object's name"));100101refs_list->set_column_title(offset + 2, TTRC("Native Refs"));102refs_list->set_column_expand(offset + 2, false);103refs_list->set_column_title_tooltip_text(offset + 2, TTRC("References not owned by the ObjectDB"));104105refs_list->set_column_title(offset + 3, TTRC("ObjectDB Refs"));106refs_list->set_column_expand(offset + 3, false);107refs_list->set_column_title_tooltip_text(offset + 3, TTRC("References owned by the ObjectDB"));108109refs_list->set_column_title(offset + 4, TTRC("Total Refs"));110refs_list->set_column_expand(offset + 4, false);111refs_list->set_column_title_tooltip_text(offset + 4, TTRC("ObjectDB References + Native References"));112113refs_list->set_column_title(offset + 5, TTRC("ObjectDB Cycles"));114refs_list->set_column_expand(offset + 5, false);115refs_list->set_column_title_tooltip_text(offset + 5, TTRC("Cycles detected in the ObjectDB"));116117refs_list->connect(SceneStringName(item_selected), callable_mp(this, &SnapshotRefCountedView::_refcounted_selected));118refs_list->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);119refs_list->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);120121// View of the selected refcounted.122ref_details = memnew(VBoxContainer);123ref_details->set_custom_minimum_size(Size2(200, 0) * EDSCALE);124refs_view->add_child(ref_details);125ref_details->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);126ref_details->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);127128refs_list->create_item();129_insert_data(snapshot_data, TTRC("A"));130if (diff_data) {131_insert_data(diff_data, TTRC("B"));132}133134// Push the split as far right as possible.135filter_bar->select_sort(default_sort.descending);136filter_bar->apply();137refs_list->set_selected(refs_list->get_root()->get_first_child());138139callable_mp(this, &SnapshotRefCountedView::_set_split_to_center).call_deferred();140}141142void SnapshotRefCountedView::_set_split_to_center() {143refs_view->set_split_offset(refs_view->get_size().x * 0.5);144}145146void SnapshotRefCountedView::_insert_data(GameStateSnapshot *p_snapshot, const String &p_name) {147for (const KeyValue<ObjectID, SnapshotDataObject *> &pair : p_snapshot->objects) {148if (!pair.value->is_refcounted()) {149continue;150}151152TreeItem *item = refs_list->create_item(refs_list->get_root());153item_data_map[item] = pair.value;154data_item_map[pair.value] = item;155int total_refs = pair.value->extra_debug_data.has("ref_count") ? (uint64_t)pair.value->extra_debug_data["ref_count"] : 0;156int objectdb_refs = pair.value->get_unique_inbound_references().size();157int native_refs = total_refs - objectdb_refs;158159Array ref_cycles = (Array)pair.value->extra_debug_data["ref_cycles"];160161int offset = 0;162if (diff_data) {163item->set_text(0, p_name);164item->set_tooltip_text(0, p_snapshot->name);165item->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);166offset = 1;167}168169item->set_text(offset + 0, pair.value->type_name);170item->set_auto_translate_mode(offset + 0, AUTO_TRANSLATE_MODE_DISABLED);171item->set_text(offset + 1, pair.value->get_name());172item->set_auto_translate_mode(offset + 1, AUTO_TRANSLATE_MODE_DISABLED);173item->set_text(offset + 2, String::num_uint64(native_refs));174item->set_text(offset + 3, String::num_uint64(objectdb_refs));175item->set_text(offset + 4, String::num_uint64(total_refs));176item->set_text(offset + 5, String::num_uint64(ref_cycles.size())); // Compute cycles and attach it to refcounted object.177178if (total_refs == ref_cycles.size()) {179// Often, references are held by the engine so we can't know if we're stuck in a cycle or not180// But if the full cycle is visible in the ObjectDB,181// tell the user by highlighting the cells in red.182item->set_custom_bg_color(offset + 5, Color(1, 0, 0, 0.1));183}184}185}186187void SnapshotRefCountedView::_refcounted_selected() {188for (int i = 0; i < ref_details->get_child_count(); i++) {189ref_details->get_child(i)->queue_free();190}191192SnapshotDataObject *d = item_data_map[refs_list->get_selected()];193EditorNode::get_singleton()->push_item(static_cast<Object *>(d));194195DarkPanelContainer *refcounted_panel = memnew(DarkPanelContainer);196VBoxContainer *refcounted_panel_content = memnew(VBoxContainer);197refcounted_panel_content->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);198refcounted_panel_content->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);199ref_details->add_child(refcounted_panel);200refcounted_panel->add_child(refcounted_panel_content);201refcounted_panel_content->add_child(memnew(SpanningHeader(d->get_name())));202203ScrollContainer *properties_scroll = memnew(ScrollContainer);204properties_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);205properties_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_AUTO);206properties_scroll->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);207properties_scroll->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);208refcounted_panel_content->add_child(properties_scroll);209210VBoxContainer *properties_container = memnew(VBoxContainer);211properties_container->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);212properties_scroll->add_child(properties_container);213properties_container->add_theme_constant_override("separation", 5);214properties_container->add_theme_constant_override("margin_left", 2);215properties_container->add_theme_constant_override("margin_right", 2);216properties_container->add_theme_constant_override("margin_top", 2);217properties_container->add_theme_constant_override("margin_bottom", 2);218219int total_refs = d->extra_debug_data.has("ref_count") ? (uint64_t)d->extra_debug_data["ref_count"] : 0;220int objectdb_refs = d->get_unique_inbound_references().size();221int native_refs = total_refs - objectdb_refs;222Array ref_cycles = (Array)d->extra_debug_data["ref_cycles"];223224String count_str = "[ul]\n";225count_str += vformat(TTR("Native References: %d"), native_refs) + "\n";226count_str += vformat(TTR("ObjectDB References: %d"), objectdb_refs) + "\n";227count_str += vformat(TTR("Total References: %d"), total_refs) + "\n";228count_str += vformat(TTR("ObjectDB Cycles: %d"), ref_cycles.size()) + "\n";229count_str += "[/ul]\n";230RichTextLabel *counts = memnew(RichTextLabel(count_str));231counts->set_use_bbcode(true);232counts->set_fit_content(true);233counts->add_theme_constant_override("line_separation", 6);234properties_container->add_child(counts);235236if (d->inbound_references.size() > 0) {237RichTextLabel *inbound_lbl = memnew(RichTextLabel(TTRC("[center]ObjectDB References[center]")));238inbound_lbl->set_fit_content(true);239inbound_lbl->set_use_bbcode(true);240properties_container->add_child(inbound_lbl);241Tree *inbound_tree = memnew(Tree);242inbound_tree->set_hide_folding(true);243properties_container->add_child(inbound_tree);244inbound_tree->set_select_mode(Tree::SelectMode::SELECT_ROW);245inbound_tree->set_hide_root(true);246inbound_tree->set_columns(3);247inbound_tree->set_column_titles_visible(true);248inbound_tree->set_column_title(0, TTRC("Source"));249inbound_tree->set_column_expand(0, true);250inbound_tree->set_column_clip_content(0, false);251inbound_tree->set_column_title_tooltip_text(0, TTRC("Other object referencing this object"));252inbound_tree->set_column_title(1, TTRC("Property"));253inbound_tree->set_column_expand(1, true);254inbound_tree->set_column_clip_content(1, true);255inbound_tree->set_column_title_tooltip_text(1, TTRC("Property of other object referencing this object"));256inbound_tree->set_column_title(2, TTRC("Duplicate?"));257inbound_tree->set_column_expand(2, false);258inbound_tree->set_column_title_tooltip_text(2, TTRC("Was the same reference returned by multiple getters on the source object?"));259inbound_tree->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);260inbound_tree->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);261inbound_tree->set_v_scroll_enabled(false);262inbound_tree->connect(SceneStringName(item_selected), callable_mp(this, &SnapshotRefCountedView::_ref_selected).bind(inbound_tree));263264// The same reference can exist as multiple properties of an object (for example, gdscript `@export` properties exist twice).265// We flag for the user if a property is exposed multiple times so it's clearer why there are more references in the list266// than the ObjectDB References count would suggest.267HashMap<ObjectID, int> property_repeat_count;268for (const KeyValue<String, ObjectID> &ob : d->inbound_references) {269if (!property_repeat_count.has(ob.value)) {270property_repeat_count.insert(ob.value, 0);271}272property_repeat_count[ob.value]++;273}274275TreeItem *root = inbound_tree->create_item();276for (const KeyValue<String, ObjectID> &ob : d->inbound_references) {277TreeItem *i = inbound_tree->create_item(root);278SnapshotDataObject *target = d->snapshot->objects[ob.value];279i->set_text(0, target->get_name());280i->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);281i->set_text(1, ob.key);282i->set_auto_translate_mode(1, AUTO_TRANSLATE_MODE_DISABLED);283i->set_text(2, property_repeat_count[ob.value] > 1 ? TTRC("Yes") : TTRC("No"));284reference_item_map[i] = data_item_map[target];285}286}287288if (ref_cycles.size() > 0) {289properties_container->add_child(memnew(SpanningHeader(TTRC("ObjectDB Cycles"))));290Tree *cycles_tree = memnew(Tree);291cycles_tree->set_hide_folding(true);292properties_container->add_child(cycles_tree);293cycles_tree->set_select_mode(Tree::SelectMode::SELECT_ROW);294cycles_tree->set_hide_root(true);295cycles_tree->set_columns(1);296cycles_tree->set_column_titles_visible(false);297cycles_tree->set_column_expand(0, true);298cycles_tree->set_column_clip_content(0, false);299cycles_tree->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);300cycles_tree->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);301cycles_tree->set_v_scroll_enabled(false);302303TreeItem *root = cycles_tree->create_item();304for (const Variant &cycle : ref_cycles) {305TreeItem *i = cycles_tree->create_item(root);306i->set_text(0, cycle);307i->set_text_overrun_behavior(0, TextServer::OverrunBehavior::OVERRUN_NO_TRIMMING);308}309}310}311312void SnapshotRefCountedView::_ref_selected(Tree *p_source_tree) {313TreeItem *target = reference_item_map[p_source_tree->get_selected()];314if (target) {315if (!target->is_visible()) {316// Clear the filter if we can't see the node we just chose.317filter_bar->clear_filter();318}319target->get_tree()->deselect_all();320target->get_tree()->set_selected(target);321target->get_tree()->ensure_cursor_is_visible();322}323}324325326