Browse Source

Merge pull request #94748 from aaronp64/tree_perf

Improve `Tree` performance
Rémi Verschelde 11 months ago
parent
commit
9e1c63a051
4 changed files with 182 additions and 34 deletions
  1. 29 34
      scene/gui/tree.cpp
  2. 1 0
      scene/gui/tree.h
  3. 151 0
      tests/scene/test_tree.h
  4. 1 0
      tests/test_main.cpp

+ 29 - 34
scene/gui/tree.cpp

@@ -773,17 +773,21 @@ TreeItem *TreeItem::create_child(int p_index) {
 	TreeItem *item_prev = nullptr;
 	TreeItem *item_prev = nullptr;
 	TreeItem *item_next = first_child;
 	TreeItem *item_next = first_child;
 
 
-	int idx = 0;
-	while (item_next) {
-		if (idx == p_index) {
-			item_next->prev = ti;
-			ti->next = item_next;
-			break;
-		}
+	if (p_index < 0 && last_child) {
+		item_prev = last_child;
+	} else {
+		int idx = 0;
+		while (item_next) {
+			if (idx == p_index) {
+				item_next->prev = ti;
+				ti->next = item_next;
+				break;
+			}
 
 
-		item_prev = item_next;
-		item_next = item_next->next;
-		idx++;
+			item_prev = item_next;
+			item_next = item_next->next;
+			idx++;
+		}
 	}
 	}
 
 
 	if (item_prev) {
 	if (item_prev) {
@@ -804,6 +808,10 @@ TreeItem *TreeItem::create_child(int p_index) {
 		}
 		}
 	}
 	}
 
 
+	if (item_prev == last_child) {
+		last_child = ti;
+	}
+
 	ti->parent = this;
 	ti->parent = this;
 	ti->parent_visible_in_tree = is_visible_in_tree();
 	ti->parent_visible_in_tree = is_visible_in_tree();
 
 
@@ -820,17 +828,13 @@ void TreeItem::add_child(TreeItem *p_item) {
 	p_item->parent_visible_in_tree = is_visible_in_tree();
 	p_item->parent_visible_in_tree = is_visible_in_tree();
 	p_item->_handle_visibility_changed(p_item->parent_visible_in_tree);
 	p_item->_handle_visibility_changed(p_item->parent_visible_in_tree);
 
 
-	TreeItem *item_prev = first_child;
-	while (item_prev && item_prev->next) {
-		item_prev = item_prev->next;
-	}
-
-	if (item_prev) {
-		item_prev->next = p_item;
-		p_item->prev = item_prev;
+	if (last_child) {
+		last_child->next = p_item;
+		p_item->prev = last_child;
 	} else {
 	} else {
 		first_child = p_item;
 		first_child = p_item;
 	}
 	}
+	last_child = p_item;
 
 
 	if (!children_cache.is_empty()) {
 	if (!children_cache.is_empty()) {
 		children_cache.append(p_item);
 		children_cache.append(p_item);
@@ -910,13 +914,8 @@ TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
 		}
 		}
 	} else {
 	} else {
 		current = prev_item;
 		current = prev_item;
-		while ((!current->collapsed || p_include_invisible) && current->first_child) {
-			//go to the very end
-
-			current = current->first_child;
-			while (current->next) {
-				current = current->next;
-			}
+		while ((!current->collapsed || p_include_invisible) && current->last_child) {
+			current = current->last_child;
 		}
 		}
 	}
 	}
 
 
@@ -1037,6 +1036,8 @@ void TreeItem::clear_children() {
 	}
 	}
 
 
 	first_child = nullptr;
 	first_child = nullptr;
+	last_child = nullptr;
+	children_cache.clear();
 };
 };
 
 
 int TreeItem::get_index() {
 int TreeItem::get_index() {
@@ -1141,6 +1142,7 @@ void TreeItem::move_after(TreeItem *p_item) {
 	if (next) {
 	if (next) {
 		parent->children_cache.clear();
 		parent->children_cache.clear();
 	} else {
 	} else {
+		parent->last_child = this;
 		// If the cache is empty, it has not been built but there
 		// If the cache is empty, it has not been built but there
 		// are items in the tree (note p_item != nullptr,) so we cannot update it.
 		// are items in the tree (note p_item != nullptr,) so we cannot update it.
 		if (!parent->children_cache.is_empty()) {
 		if (!parent->children_cache.is_empty()) {
@@ -4468,15 +4470,8 @@ TreeItem *Tree::get_root() const {
 
 
 TreeItem *Tree::get_last_item() const {
 TreeItem *Tree::get_last_item() const {
 	TreeItem *last = root;
 	TreeItem *last = root;
-
-	while (last) {
-		if (last->next) {
-			last = last->next;
-		} else if (last->first_child && !last->collapsed) {
-			last = last->first_child;
-		} else {
-			break;
-		}
+	while (last && last->last_child && !last->collapsed) {
+		last = last->last_child;
 	}
 	}
 
 
 	return last;
 	return last;

+ 1 - 0
scene/gui/tree.h

@@ -136,6 +136,7 @@ private:
 	TreeItem *prev = nullptr; // previous in list
 	TreeItem *prev = nullptr; // previous in list
 	TreeItem *next = nullptr; // next in list
 	TreeItem *next = nullptr; // next in list
 	TreeItem *first_child = nullptr;
 	TreeItem *first_child = nullptr;
+	TreeItem *last_child = nullptr;
 
 
 	Vector<TreeItem *> children_cache;
 	Vector<TreeItem *> children_cache;
 	bool is_root = false; // for tree root
 	bool is_root = false; // for tree root

+ 151 - 0
tests/scene/test_tree.h

@@ -0,0 +1,151 @@
+/**************************************************************************/
+/*  test_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 TEST_TREE_H
+#define TEST_TREE_H
+
+#include "scene/gui/tree.h"
+
+#include "tests/test_macros.h"
+
+namespace TestTree {
+
+TEST_CASE("[SceneTree][Tree]") {
+	SUBCASE("[Tree] Create and remove items.") {
+		Tree *tree = memnew(Tree);
+		TreeItem *root = tree->create_item();
+
+		TreeItem *child1 = tree->create_item();
+		CHECK_EQ(root->get_child_count(), 1);
+
+		TreeItem *child2 = tree->create_item(root);
+		CHECK_EQ(root->get_child_count(), 2);
+
+		TreeItem *child3 = tree->create_item(root, 0);
+		CHECK_EQ(root->get_child_count(), 3);
+
+		CHECK_EQ(root->get_child(0), child3);
+		CHECK_EQ(root->get_child(1), child1);
+		CHECK_EQ(root->get_child(2), child2);
+
+		root->remove_child(child3);
+		CHECK_EQ(root->get_child_count(), 2);
+
+		root->add_child(child3);
+		CHECK_EQ(root->get_child_count(), 3);
+
+		TreeItem *child4 = root->create_child();
+		CHECK_EQ(root->get_child_count(), 4);
+
+		CHECK_EQ(root->get_child(0), child1);
+		CHECK_EQ(root->get_child(1), child2);
+		CHECK_EQ(root->get_child(2), child3);
+		CHECK_EQ(root->get_child(3), child4);
+
+		memdelete(tree);
+	}
+
+	SUBCASE("[Tree] Clear items.") {
+		Tree *tree = memnew(Tree);
+		TreeItem *root = tree->create_item();
+
+		for (int i = 0; i < 10; i++) {
+			tree->create_item();
+		}
+		CHECK_EQ(root->get_child_count(), 10);
+
+		root->clear_children();
+		CHECK_EQ(root->get_child_count(), 0);
+
+		memdelete(tree);
+	}
+
+	SUBCASE("[Tree] Get last item.") {
+		Tree *tree = memnew(Tree);
+		TreeItem *root = tree->create_item();
+
+		TreeItem *last;
+		for (int i = 0; i < 10; i++) {
+			last = tree->create_item();
+		}
+		CHECK_EQ(root->get_child_count(), 10);
+		CHECK_EQ(tree->get_last_item(), last);
+
+		// Check nested.
+		TreeItem *old_last = last;
+		for (int i = 0; i < 10; i++) {
+			last = tree->create_item(old_last);
+		}
+		CHECK_EQ(tree->get_last_item(), last);
+
+		memdelete(tree);
+	}
+
+	SUBCASE("[Tree] Previous and Next items.") {
+		Tree *tree = memnew(Tree);
+		TreeItem *root = tree->create_item();
+
+		TreeItem *child1 = tree->create_item();
+		TreeItem *child2 = tree->create_item();
+		TreeItem *child3 = tree->create_item();
+		CHECK_EQ(child1->get_next(), child2);
+		CHECK_EQ(child1->get_next_in_tree(), child2);
+		CHECK_EQ(child2->get_next(), child3);
+		CHECK_EQ(child2->get_next_in_tree(), child3);
+		CHECK_EQ(child3->get_next(), nullptr);
+		CHECK_EQ(child3->get_next_in_tree(), nullptr);
+
+		CHECK_EQ(child1->get_prev(), nullptr);
+		CHECK_EQ(child1->get_prev_in_tree(), root);
+		CHECK_EQ(child2->get_prev(), child1);
+		CHECK_EQ(child2->get_prev_in_tree(), child1);
+		CHECK_EQ(child3->get_prev(), child2);
+		CHECK_EQ(child3->get_prev_in_tree(), child2);
+
+		TreeItem *nested1 = tree->create_item(child2);
+		TreeItem *nested2 = tree->create_item(child2);
+		TreeItem *nested3 = tree->create_item(child2);
+
+		CHECK_EQ(child1->get_next(), child2);
+		CHECK_EQ(child1->get_next_in_tree(), child2);
+		CHECK_EQ(child2->get_next(), child3);
+		CHECK_EQ(child2->get_next_in_tree(), nested1);
+		CHECK_EQ(child3->get_prev(), child2);
+		CHECK_EQ(child3->get_prev_in_tree(), nested3);
+		CHECK_EQ(nested1->get_prev_in_tree(), child2);
+		CHECK_EQ(nested1->get_next_in_tree(), nested2);
+
+		memdelete(tree);
+	}
+}
+
+} // namespace TestTree
+
+#endif // TEST_TREE_H

+ 1 - 0
tests/test_main.cpp

@@ -133,6 +133,7 @@
 #include "tests/scene/test_color_picker.h"
 #include "tests/scene/test_color_picker.h"
 #include "tests/scene/test_graph_node.h"
 #include "tests/scene/test_graph_node.h"
 #include "tests/scene/test_text_edit.h"
 #include "tests/scene/test_text_edit.h"
+#include "tests/scene/test_tree.h"
 #endif // ADVANCED_GUI_DISABLED
 #endif // ADVANCED_GUI_DISABLED
 
 
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED