diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 112816c045e..41aba9ef227 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -108,12 +108,15 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { return; } - for (Node3D *&E : data.children) { - if (E->data.top_level) { - continue; //don't propagate to a top_level + for (uint32_t n = 0; n < data.node3d_children.size(); n++) { + Node3D *s = data.node3d_children[n]; + + // Don't propagate to a toplevel. + if (!s->data.top_level) { + s->_propagate_transform_changed(p_origin); } - E->_propagate_transform_changed(p_origin); } + #ifdef TOOLS_ENABLED if ((!data.gizmos.is_empty() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { #else @@ -148,9 +151,11 @@ void Node3D::_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.node3d_children.size(); + data.parent->data.node3d_children.push_back(this); + } else if (data.index_in_parent != UINT32_MAX) { + data.index_in_parent = UINT32_MAX; + ERR_PRINT("Node3D ENTER_TREE detected without EXIT_TREE, recovering."); } if (data.top_level && !Engine::get_singleton()->is_editor_hint()) { @@ -202,11 +207,27 @@ void Node3D::_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 &parent_children = data.parent->data.node3d_children; + + parent_children.remove_at_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("Node3D index_in_parent unset at EXIT_TREE."); + } } + data.index_in_parent = UINT32_MAX; + data.parent = nullptr; - data.C = nullptr; _update_visibility_parent(true); _disable_client_physics_interpolation(); } break; @@ -1063,11 +1084,12 @@ void Node3D::_propagate_visibility_changed() { } #endif - for (Node3D *c : data.children) { - if (!c || !c->data.visible) { - continue; + for (uint32_t n = 0; n < data.node3d_children.size(); n++) { + Node3D *s = data.node3d_children[n]; + + if (s->data.visible) { + s->_propagate_visibility_changed(); } - c->_propagate_visibility_changed(); } } @@ -1312,7 +1334,7 @@ void Node3D::_update_visibility_parent(bool p_update_root) { RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent); } - for (Node3D *c : data.children) { + for (Node3D *c : data.node3d_children) { c->_update_visibility_parent(false); } } diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 930a174136d..8d09872c91f 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -153,8 +153,12 @@ private: RID visibility_parent; Node3D *parent = nullptr; - List children; - List::Element *C = nullptr; + + // An unordered vector of `Spatial` children only. + // This is a subset of the `Node::children`, purely + // an optimization for faster traversal. + LocalVector node3d_children; + uint32_t index_in_parent = UINT32_MAX; ClientPhysicsInterpolationData *client_physics_interpolation_data = nullptr; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 1aa2d964eda..f5f7e5b036e 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -317,8 +317,15 @@ void CanvasItem::_notification(int p_what) { if (ci) { parent_visible_in_tree = ci->is_visible_in_tree(); - 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."); + } + CanvasLayer *cl = Object::cast_to(parent); if (cl) { @@ -388,10 +395,27 @@ void CanvasItem::_notification(int p_what) { get_tree()->xform_change_list.remove(&xform_change); } _exit_canvas(); - if (C) { - Object::cast_to(get_parent())->children_items.erase(C); - C = nullptr; + + CanvasItem *parent = Object::cast_to(get_parent()); + if (parent) { + if (data.index_in_parent != UINT32_MAX) { + // Aliases + uint32_t c = data.index_in_parent; + LocalVector &parent_children = parent->data.canvas_item_children; + + parent_children.remove_at_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; + if (window) { window->disconnect(SceneStringName(visibility_changed), callable_mp(this, &CanvasItem::_window_visibility_changed)); window = nullptr; @@ -1056,11 +1080,11 @@ void CanvasItem::_notify_transform(CanvasItem *p_node) { } } - for (CanvasItem *ci : p_node->children_items) { - if (ci->top_level) { - 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->top_level) { + _notify_transform(ci); } - _notify_transform(ci); } } @@ -1611,9 +1635,11 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) { _update_self_texture_filter(texture_filter_cache); if (p_propagate) { - for (CanvasItem *E : children_items) { - if (!E->top_level && E->texture_filter == TEXTURE_FILTER_PARENT_NODE) { - E->_update_texture_filter_changed(true); + for (uint32_t n = 0; n < data.canvas_item_children.size(); n++) { + CanvasItem *ci = data.canvas_item_children[n]; + + if (!ci->top_level && ci->texture_filter == TEXTURE_FILTER_PARENT_NODE) { + ci->_update_texture_filter_changed(true); } } } @@ -1665,9 +1691,10 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { _update_self_texture_repeat(texture_repeat_cache); if (p_propagate) { - for (CanvasItem *E : children_items) { - if (!E->top_level && E->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { - E->_update_texture_repeat_changed(true); + for (uint32_t n = 0; n < data.canvas_item_children.size(); n++) { + CanvasItem *ci = data.canvas_item_children[n]; + if (!ci->top_level && ci->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { + ci->_update_texture_repeat_changed(true); } } } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 14e11a97686..9de847b81fc 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -83,8 +83,13 @@ private: Color modulate = Color(1, 1, 1, 1); Color self_modulate = Color(1, 1, 1, 1); - List children_items; - List::Element *C = nullptr; + struct Data { + // An unordered vector of `CanvasItem` children only. + // This is a subset of the `Node::children`, purely + // an optimization for faster traversal. + LocalVector canvas_item_children; + uint32_t index_in_parent = UINT32_MAX; + } data; int light_mask = 1; uint32_t visibility_layer = 1;