Browse Source

Merge pull request #107480 from lawnjelly/spat_children_linear_list

[3.x] `Spatial` and `CanvasItem` children change to `LocalVector`
lawnjelly 2 months ago
parent
commit
7172b03b45
4 changed files with 84 additions and 37 deletions
  1. 30 11
      scene/2d/canvas_item.cpp
  2. 7 2
      scene/2d/canvas_item.h
  3. 41 22
      scene/3d/spatial.cpp
  4. 6 2
      scene/3d/spatial.h

+ 30 - 11
scene/2d/canvas_item.cpp

@@ -590,7 +590,11 @@ void CanvasItem::_notification(int p_what) {
 			if (parent) {
 				CanvasItem *ci = Object::cast_to<CanvasItem>(parent);
 				if (ci) {
-					C = ci->children_items.push_back(this);
+					data.index_in_parent = ci->data.canvas_item_children.size();
+					ci->data.canvas_item_children.push_back(this);
+				} else if (data.index_in_parent != UINT32_MAX) {
+					data.index_in_parent = UINT32_MAX;
+					ERR_PRINT("CanvasItem ENTER_TREE detected without EXIT_TREE, recovering.");
 				}
 			}
 			_enter_canvas();
@@ -616,10 +620,28 @@ void CanvasItem::_notification(int p_what) {
 				get_tree()->xform_change_list.remove(&xform_change);
 			}
 			_exit_canvas();
-			if (C) {
-				Object::cast_to<CanvasItem>(get_parent())->children_items.erase(C);
-				C = nullptr;
+
+			CanvasItem *parent = Object::cast_to<CanvasItem>(get_parent());
+			if (parent) {
+				if (data.index_in_parent != UINT32_MAX) {
+					// Aliases
+					uint32_t c = data.index_in_parent;
+					LocalVector<CanvasItem *> &parent_children = parent->data.canvas_item_children;
+
+					parent_children.remove_unordered(c);
+
+					// After unordered remove, we need to inform the moved child
+					// what their new id is in the parent children list.
+					if (parent_children.size() > c) {
+						parent_children[c]->data.index_in_parent = c;
+					}
+
+				} else {
+					ERR_PRINT("CanvasItem index_in_parent unset at EXIT_TREE.");
+				}
 			}
+			data.index_in_parent = UINT32_MAX;
+
 			global_invalid = true;
 		} break;
 		case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
@@ -1006,12 +1028,11 @@ void CanvasItem::_notify_transform(CanvasItem *p_node) {
 		}
 	}
 
-	for (List<CanvasItem *>::Element *E = p_node->children_items.front(); E; E = E->next()) {
-		CanvasItem *ci = E->get();
-		if (ci->toplevel) {
-			continue;
+	for (uint32_t n = 0; n < p_node->data.canvas_item_children.size(); n++) {
+		CanvasItem *ci = p_node->data.canvas_item_children[n];
+		if (!ci->toplevel) {
+			_notify_transform(ci);
 		}
-		_notify_transform(ci);
 	}
 }
 
@@ -1364,8 +1385,6 @@ CanvasItem::CanvasItem() :
 	notify_transform = false;
 	font_sdf_selected = false;
 	light_mask = 1;
-
-	C = nullptr;
 }
 
 CanvasItem::~CanvasItem() {

+ 7 - 2
scene/2d/canvas_item.h

@@ -186,8 +186,13 @@ private:
 	Color modulate;
 	Color self_modulate;
 
-	List<CanvasItem *> children_items;
-	List<CanvasItem *>::Element *C;
+	struct Data {
+		// An unordered vector of `CanvasItem` children only.
+		// This is a subset of the `Node::children`, purely
+		// an optimization for faster traversal.
+		LocalVector<CanvasItem *> canvas_item_children;
+		uint32_t index_in_parent = UINT32_MAX;
+	} data;
 
 	int light_mask;
 

+ 41 - 22
scene/3d/spatial.cpp

@@ -106,12 +106,15 @@ void Spatial::_propagate_transform_changed(Spatial *p_origin) {
 
 	data.children_lock++;
 
-	for (List<Spatial *>::Element *E = data.children.front(); E; E = E->next()) {
-		if (E->get()->data.toplevel_active) {
-			continue; //don't propagate to a toplevel
+	for (uint32_t n = 0; n < data.spatial_children.size(); n++) {
+		Spatial *s = data.spatial_children[n];
+
+		// Don't propagate to a toplevel.
+		if (!s->data.toplevel_active) {
+			s->_propagate_transform_changed(p_origin);
 		}
-		E->get()->_propagate_transform_changed(p_origin);
 	}
+
 #ifdef TOOLS_ENABLED
 	if ((data.gizmo.is_valid() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) {
 #else
@@ -154,10 +157,13 @@ void Spatial::_notification(int p_what) {
 			}
 
 			if (data.parent) {
-				data.C = data.parent->data.children.push_back(this);
-			} else {
-				data.C = nullptr;
+				data.index_in_parent = data.parent->data.spatial_children.size();
+				data.parent->data.spatial_children.push_back(this);
+			} else if (data.index_in_parent != UINT32_MAX) {
+				data.index_in_parent = UINT32_MAX;
+				ERR_PRINT("Spatial ENTER_TREE detected without EXIT_TREE, recovering.");
 			}
+
 			_update_visible_in_tree();
 
 			if (data.toplevel && !Engine::get_singleton()->is_editor_hint()) {
@@ -212,11 +218,28 @@ void Spatial::_notification(int p_what) {
 			if (xform_change.in_list()) {
 				get_tree()->xform_change_list.remove(&xform_change);
 			}
-			if (data.C) {
-				data.parent->data.children.erase(data.C);
+
+			if (data.parent) {
+				if (data.index_in_parent != UINT32_MAX) {
+					// Aliases
+					uint32_t c = data.index_in_parent;
+					LocalVector<Spatial *> &parent_children = data.parent->data.spatial_children;
+
+					parent_children.remove_unordered(c);
+
+					// After unordered remove, we need to inform the moved child
+					// what their new id is in the parent children list.
+					if (parent_children.size() > c) {
+						parent_children[c]->data.index_in_parent = c;
+					}
+
+				} else {
+					ERR_PRINT("CanvasItem index_in_parent unset at EXIT_TREE.");
+				}
 			}
+			data.index_in_parent = UINT32_MAX;
+
 			data.parent = nullptr;
-			data.C = nullptr;
 			data.toplevel_active = false;
 
 			_update_visible_in_tree();
@@ -861,12 +884,12 @@ void Spatial::_propagate_visibility_changed() {
 	}
 #endif
 
-	for (List<Spatial *>::Element *E = data.children.front(); E; E = E->next()) {
-		Spatial *c = E->get();
-		if (!c || !c->data.visible) {
-			continue;
+	for (uint32_t n = 0; n < data.spatial_children.size(); n++) {
+		Spatial *s = data.spatial_children[n];
+
+		if (s->data.visible) {
+			s->_propagate_visibility_changed();
 		}
-		c->_propagate_visibility_changed();
 	}
 }
 
@@ -890,12 +913,9 @@ void Spatial::_propagate_merging_allowed(bool p_merging_allowed) {
 
 	data.merging_allowed = p_merging_allowed;
 
-	for (List<Spatial *>::Element *E = data.children.front(); E; E = E->next()) {
-		Spatial *c = E->get();
-		if (!c) {
-			continue;
-		}
-		c->_propagate_merging_allowed(p_merging_allowed);
+	for (uint32_t n = 0; n < data.spatial_children.size(); n++) {
+		Spatial *s = data.spatial_children[n];
+		s->_propagate_merging_allowed(p_merging_allowed);
 	}
 }
 
@@ -1286,7 +1306,6 @@ Spatial::Spatial() :
 	data.notify_transform_when_fti_off = false;
 
 	data.parent = nullptr;
-	data.C = nullptr;
 }
 
 Spatial::~Spatial() {

+ 6 - 2
scene/3d/spatial.h

@@ -141,8 +141,12 @@ private:
 
 		int children_lock;
 		Spatial *parent;
-		List<Spatial *> children;
-		List<Spatial *>::Element *C;
+
+		// An unordered vector of `Spatial` children only.
+		// This is a subset of the `Node::children`, purely
+		// an optimization for faster traversal.
+		LocalVector<Spatial *> spatial_children;
+		uint32_t index_in_parent = UINT32_MAX;
 
 		float lod_range = 10.0f;
 		ClientPhysicsInterpolationData *client_physics_interpolation_data;