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