Merge pull request #75627 from reduz/faster-node-child-management

Optimize Node children management
This commit is contained in:
Rémi Verschelde 2023-04-08 18:01:02 +02:00 committed by GitHub
commit 3683b040ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 242 additions and 218 deletions

View file

@ -166,7 +166,7 @@ void Node::_notification(int p_notification) {
// kill children as cleanly as possible
while (data.children.size()) {
Node *child = data.children[data.children.size() - 1]; //begin from the end because its faster and more consistent with creation
Node *child = data.children.last()->value; // begin from the end because its faster and more consistent with creation
memdelete(child);
}
} break;
@ -176,9 +176,10 @@ void Node::_notification(int p_notification) {
void Node::_propagate_ready() {
data.ready_notified = true;
data.blocked++;
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->_propagate_ready();
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->_propagate_ready();
}
data.blocked--;
notification(NOTIFICATION_POST_ENTER_TREE);
@ -228,9 +229,9 @@ void Node::_propagate_enter_tree() {
data.blocked++;
//block while adding children
for (int i = 0; i < data.children.size(); i++) {
if (!data.children[i]->is_inside_tree()) { // could have been added in enter_tree
data.children[i]->_propagate_enter_tree();
for (KeyValue<StringName, Node *> &K : data.children) {
if (!K.value->is_inside_tree()) { // could have been added in enter_tree
K.value->_propagate_enter_tree();
}
}
@ -267,9 +268,11 @@ void Node::_propagate_after_exit_tree() {
}
data.blocked++;
for (int i = data.children.size() - 1; i >= 0; i--) {
data.children[i]->_propagate_after_exit_tree();
for (HashMap<StringName, Node *>::Iterator I = data.children.last(); I; --I) {
I->value->_propagate_after_exit_tree();
}
data.blocked--;
emit_signal(SceneStringNames::get_singleton()->tree_exited);
@ -286,8 +289,8 @@ void Node::_propagate_exit_tree() {
#endif
data.blocked++;
for (int i = data.children.size() - 1; i >= 0; i--) {
data.children[i]->_propagate_exit_tree();
for (HashMap<StringName, Node *>::Iterator I = data.children.last(); I; --I) {
I->value->_propagate_exit_tree();
}
data.blocked--;
@ -329,25 +332,26 @@ void Node::move_child(Node *p_child, int p_index) {
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
_update_children_cache();
// We need to check whether node is internal and move it only in the relevant node range.
if (p_child->_is_internal_front()) {
if (p_child->data.internal_mode == INTERNAL_MODE_FRONT) {
if (p_index < 0) {
p_index += data.internal_children_front;
p_index += data.internal_children_front_count_cache;
}
ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front, vformat("Invalid new child index: %d. Child is internal.", p_index));
ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front_count_cache, vformat("Invalid new child index: %d. Child is internal.", p_index));
_move_child(p_child, p_index);
} else if (p_child->_is_internal_back()) {
} else if (p_child->data.internal_mode == INTERNAL_MODE_BACK) {
if (p_index < 0) {
p_index += data.internal_children_back;
p_index += data.internal_children_back_count_cache;
}
ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back, vformat("Invalid new child index: %d. Child is internal.", p_index));
_move_child(p_child, data.children.size() - data.internal_children_back + p_index);
ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back_count_cache, vformat("Invalid new child index: %d. Child is internal.", p_index));
_move_child(p_child, (int)data.children_cache.size() - data.internal_children_back_count_cache + p_index);
} else {
if (p_index < 0) {
p_index += get_child_count(false);
}
ERR_FAIL_INDEX_MSG(p_index, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child index: %d.", p_index));
_move_child(p_child, p_index + data.internal_children_front);
ERR_FAIL_INDEX_MSG(p_index, (int)data.children_cache.size() + 1 - data.internal_children_front_count_cache - data.internal_children_back_count_cache, vformat("Invalid new child index: %d.", p_index));
_move_child(p_child, p_index + data.internal_children_front_count_cache);
}
}
@ -357,30 +361,32 @@ void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) {
// Specifying one place beyond the end
// means the same as moving to the last index
if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly.
if (p_child->_is_internal_front()) {
if (p_index == data.internal_children_front) {
if (p_child->data.internal_mode == INTERNAL_MODE_FRONT) {
if (p_index == data.internal_children_front_count_cache) {
p_index--;
}
} else if (p_child->_is_internal_back()) {
if (p_index == data.children.size()) {
} else if (p_child->data.internal_mode == INTERNAL_MODE_BACK) {
if (p_index == (int)data.children_cache.size()) {
p_index--;
}
} else {
if (p_index == data.children.size() - data.internal_children_back) {
if (p_index == (int)data.children_cache.size() - data.internal_children_back_count_cache) {
p_index--;
}
}
}
if (p_child->data.index == p_index) {
int child_index = p_child->get_index();
if (child_index == p_index) {
return; //do nothing
}
int motion_from = MIN(p_index, p_child->data.index);
int motion_to = MAX(p_index, p_child->data.index);
int motion_from = MIN(p_index, child_index);
int motion_to = MAX(p_index, child_index);
data.children.remove_at(p_child->data.index);
data.children.insert(p_index, p_child);
data.children_cache.remove_at(child_index);
data.children_cache.insert(p_index, p_child);
if (data.tree) {
data.tree->tree_changed();
@ -389,13 +395,18 @@ void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) {
data.blocked++;
//new pos first
for (int i = motion_from; i <= motion_to; i++) {
data.children[i]->data.index = i;
if (data.children_cache[i]->data.internal_mode == INTERNAL_MODE_DISABLED) {
data.children_cache[i]->data.index = i - data.internal_children_front_count_cache;
} else if (data.children_cache[i]->data.internal_mode == INTERNAL_MODE_BACK) {
data.children_cache[i]->data.index = i - data.internal_children_front_count_cache - data.external_children_count_cache;
} else {
data.children_cache[i]->data.index = i;
}
}
// notification second
move_child_notify(p_child);
notification(NOTIFICATION_CHILD_ORDER_CHANGED);
emit_signal(SNAME("child_order_changed"));
p_child->_propagate_groups_dirty();
data.blocked--;
@ -408,8 +419,8 @@ void Node::_propagate_groups_dirty() {
}
}
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->_propagate_groups_dirty();
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->_propagate_groups_dirty();
}
}
@ -529,8 +540,8 @@ void Node::_propagate_pause_notification(bool p_enable) {
notification(NOTIFICATION_UNPAUSED);
}
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->_propagate_pause_notification(p_enable);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->_propagate_pause_notification(p_enable);
}
}
@ -549,8 +560,8 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int
notification(p_enabled_notification);
}
for (int i = 0; i < data.children.size(); i++) {
Node *c = data.children[i];
for (KeyValue<StringName, Node *> &K : data.children) {
Node *c = K.value;
if (c->data.process_mode == PROCESS_MODE_INHERIT) {
c->_propagate_process_owner(p_owner, p_pause_notification, p_enabled_notification);
}
@ -561,8 +572,8 @@ void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
data.multiplayer_authority = p_peer_id;
if (p_recursive) {
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->set_multiplayer_authority(p_peer_id, true);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->set_multiplayer_authority(p_peer_id, true);
}
}
}
@ -912,10 +923,13 @@ void Node::set_name(const String &p_name) {
if (data.unique_name_in_owner && data.owner) {
_release_unique_name_in_owner();
}
String old_name = data.name;
data.name = name;
if (data.parent) {
data.parent->data.children.erase(old_name);
data.parent->_validate_child_name(this, true);
data.parent->data.children.insert(data.name, this);
}
if (data.unique_name_in_owner && data.owner) {
@ -977,19 +991,8 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
//new unique name must be assigned
unique = false;
} else {
//check if exists
Node **children = data.children.ptrw();
int cc = data.children.size();
for (int i = 0; i < cc; i++) {
if (children[i] == p_child) {
continue;
}
if (children[i]->data.name == p_child->data.name) {
unique = false;
break;
}
}
const Node *const *existing = data.children.getptr(p_child->data.name);
unique = !existing || *existing == p_child;
}
if (!unique) {
@ -1053,25 +1056,9 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
name = p_child->get_class();
}
//quickly test if proposed name exists
int cc = data.children.size(); //children count
const Node *const *children_ptr = data.children.ptr();
{
bool exists = false;
for (int i = 0; i < cc; i++) {
if (children_ptr[i] == p_child) { //exclude self in renaming if it's already a child
continue;
}
if (children_ptr[i]->data.name == name) {
exists = true;
}
}
if (!exists) {
return; //if it does not exist, it does not need validation
}
const Node *const *existing = data.children.getptr(name);
if (!existing || *existing == p_child) { // Unused, or is current node.
return;
}
// Extract trailing number
@ -1098,16 +1085,9 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
for (;;) {
StringName attempt = name_string + nums;
bool exists = false;
for (int i = 0; i < cc; i++) {
if (children_ptr[i] == p_child) {
continue;
}
if (children_ptr[i]->data.name == attempt) {
exists = true;
}
}
existing = data.children.getptr(attempt);
bool exists = existing != nullptr && *existing != p_child;
if (!exists) {
name = attempt;
@ -1124,17 +1104,34 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
}
}
void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode) {
//add a child node quickly, without name validation
p_child->data.name = p_name;
p_child->data.index = data.children.size();
data.children.push_back(p_child);
data.children.insert(p_name, p_child);
p_child->data.internal_mode = p_internal_mode;
switch (p_internal_mode) {
case INTERNAL_MODE_FRONT: {
p_child->data.index = data.internal_children_front_count_cache++;
} break;
case INTERNAL_MODE_BACK: {
p_child->data.index = data.internal_children_back_count_cache++;
} break;
case INTERNAL_MODE_DISABLED: {
p_child->data.index = data.external_children_count_cache++;
} break;
}
p_child->data.parent = this;
if (data.internal_children_back > 0) {
_move_child(p_child, data.children.size() - data.internal_children_back - 1);
if (!data.children_cache_dirty && p_internal_mode == INTERNAL_MODE_DISABLED && data.internal_children_back_count_cache == 0) {
// Special case, also add to the cached children array since its cheap.
data.children_cache.push_back(p_child);
} else {
data.children_cache_dirty = true;
}
p_child->notification(NOTIFICATION_PARENTED);
if (data.tree) {
@ -1159,17 +1156,7 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_child()` failed. Consider using `add_child.call_deferred(child)` instead.");
_validate_child_name(p_child, p_force_readable_name);
_add_child_nocheck(p_child, p_child->data.name);
if (p_internal == INTERNAL_MODE_FRONT) {
_move_child(p_child, data.internal_children_front);
data.internal_children_front++;
} else if (p_internal == INTERNAL_MODE_BACK) {
if (data.internal_children_back > 0) {
_move_child(p_child, data.children.size() - 1, true);
}
data.internal_children_back++;
}
_add_child_nocheck(p_child, p_child->data.name, p_internal);
}
void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) {
@ -1178,49 +1165,25 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) {
ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself!
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_sibling()` failed. Consider using `add_sibling.call_deferred(sibling)` instead.");
InternalMode internal = INTERNAL_MODE_DISABLED;
if (_is_internal_front()) { // The sibling will have the same internal status.
internal = INTERNAL_MODE_FRONT;
} else if (_is_internal_back()) {
internal = INTERNAL_MODE_BACK;
}
data.parent->add_child(p_sibling, p_force_readable_name, internal);
data.parent->add_child(p_sibling, p_force_readable_name, data.internal_mode);
data.parent->_update_children_cache();
data.parent->_move_child(p_sibling, get_index() + 1);
}
void Node::remove_child(Node *p_child) {
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy adding/removing children, `remove_child()` can't be called at this time. Consider using `remove_child.call_deferred(child)` instead.");
ERR_FAIL_COND(p_child->data.parent != this);
int child_count = data.children.size();
Node **children = data.children.ptrw();
int idx = -1;
if (p_child->data.index >= 0 && p_child->data.index < child_count) {
if (children[p_child->data.index] == p_child) {
idx = p_child->data.index;
}
}
if (idx == -1) { //maybe removed while unparenting or something and index was not updated, so just in case the above fails, try this.
for (int i = 0; i < child_count; i++) {
if (children[i] == p_child) {
idx = i;
break;
}
}
}
ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name()));
//ERR_FAIL_COND( p_child->data.blocked > 0 );
// If internal child, update the counter.
if (p_child->_is_internal_front()) {
data.internal_children_front--;
} else if (p_child->_is_internal_back()) {
data.internal_children_back--;
}
/**
* Do not change the data.internal_children*cache counters here.
* Because if nodes are re-added, the indices can remain
* greater-than-everything indices and children added remain
* properly ordered.
*
* All children indices and counters will be updated next time the
* cache is re-generated.
*/
data.blocked++;
p_child->_set_tree(nullptr);
@ -1228,17 +1191,12 @@ void Node::remove_child(Node *p_child) {
remove_child_notify(p_child);
p_child->notification(NOTIFICATION_UNPARENTED);
data.blocked--;
data.children.remove_at(idx);
//update pointer and size
child_count = data.children.size();
children = data.children.ptrw();
for (int i = idx; i < child_count; i++) {
children[i]->data.index = i;
}
data.children_cache_dirty = true;
bool success = data.children.erase(p_child->data.name);
ERR_FAIL_COND_MSG(!success, "Children name does not match parent name in hashtable, this is a bug.");
notification(NOTIFICATION_CHILD_ORDER_CHANGED);
emit_signal(SNAME("child_order_changed"));
@ -1251,42 +1209,73 @@ void Node::remove_child(Node *p_child) {
}
}
void Node::_update_children_cache_impl() const {
// Assign children
data.children_cache.resize(data.children.size());
int idx = 0;
for (const KeyValue<StringName, Node *> &K : data.children) {
data.children_cache[idx] = K.value;
idx++;
}
// Sort them
data.children_cache.sort_custom<ComparatorByIndex>();
// Update indices
data.external_children_count_cache = 0;
data.internal_children_back_count_cache = 0;
data.internal_children_front_count_cache = 0;
for (uint32_t i = 0; i < data.children_cache.size(); i++) {
switch (data.children_cache[i]->data.internal_mode) {
case INTERNAL_MODE_DISABLED: {
data.children_cache[i]->data.index = data.external_children_count_cache++;
} break;
case INTERNAL_MODE_FRONT: {
data.children_cache[i]->data.index = data.internal_children_front_count_cache++;
} break;
case INTERNAL_MODE_BACK: {
data.children_cache[i]->data.index = data.internal_children_back_count_cache++;
} break;
}
}
data.children_cache_dirty = false;
}
int Node::get_child_count(bool p_include_internal) const {
_update_children_cache();
if (p_include_internal) {
return data.children.size();
return data.children_cache.size();
} else {
return data.children.size() - data.internal_children_front - data.internal_children_back;
return data.children_cache.size() - data.internal_children_front_count_cache - data.internal_children_back_count_cache;
}
}
Node *Node::get_child(int p_index, bool p_include_internal) const {
_update_children_cache();
if (p_include_internal) {
if (p_index < 0) {
p_index += data.children.size();
p_index += data.children_cache.size();
}
ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
return data.children[p_index];
ERR_FAIL_INDEX_V(p_index, (int)data.children_cache.size(), nullptr);
return data.children_cache[p_index];
} else {
if (p_index < 0) {
p_index += data.children.size() - data.internal_children_front - data.internal_children_back;
p_index += (int)data.children_cache.size() - data.internal_children_front_count_cache - data.internal_children_back_count_cache;
}
ERR_FAIL_INDEX_V(p_index, data.children.size() - data.internal_children_front - data.internal_children_back, nullptr);
p_index += data.internal_children_front;
return data.children[p_index];
ERR_FAIL_INDEX_V(p_index, (int)data.children_cache.size() - data.internal_children_front_count_cache - data.internal_children_back_count_cache, nullptr);
p_index += data.internal_children_front_count_cache;
return data.children_cache[p_index];
}
}
Node *Node::_get_child_by_name(const StringName &p_name) const {
int cc = data.children.size();
Node *const *cd = data.children.ptr();
for (int i = 0; i < cc; i++) {
if (cd[i]->data.name == p_name) {
return cd[i];
}
const Node *const *node = data.children.getptr(p_name);
if (node) {
return const_cast<Node *>(*node);
} else {
return nullptr;
}
return nullptr;
}
Node *Node::get_node_or_null(const NodePath &p_path) const {
@ -1348,18 +1337,12 @@ Node *Node::get_node_or_null(const NodePath &p_path) const {
} else {
next = nullptr;
for (int j = 0; j < current->data.children.size(); j++) {
Node *child = current->data.children[j];
if (child->data.name == name) {
next = child;
break;
}
}
if (next == nullptr) {
const Node *const *node = current->data.children.getptr(name);
if (node) {
next = const_cast<Node *>(*node);
} else {
return nullptr;
};
}
}
current = next;
}
@ -1402,9 +1385,9 @@ bool Node::has_node(const NodePath &p_path) const {
// Can be recursive or not, and limited to owned nodes.
Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const {
ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr);
Node *const *cptr = data.children.ptr();
int ccount = data.children.size();
_update_children_cache();
Node *const *cptr = data.children_cache.ptr();
int ccount = data.children_cache.size();
for (int i = 0; i < ccount; i++) {
if (p_owned && !cptr[i]->data.owner) {
continue;
@ -1431,9 +1414,9 @@ Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned)
TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const {
TypedArray<Node> ret;
ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret);
Node *const *cptr = data.children.ptr();
int ccount = data.children.size();
_update_children_cache();
Node *const *cptr = data.children_cache.ptr();
int ccount = data.children_cache.size();
for (int i = 0; i < ccount; i++) {
if (p_owned && !cptr[i]->data.owner) {
continue;
@ -1526,6 +1509,8 @@ bool Node::is_greater_than(const Node *p_node) const {
ERR_FAIL_COND_V(data.depth < 0, false);
ERR_FAIL_COND_V(p_node->data.depth < 0, false);
_update_children_cache();
int *this_stack = (int *)alloca(sizeof(int) * data.depth);
int *that_stack = (int *)alloca(sizeof(int) * p_node->data.depth);
@ -1534,15 +1519,16 @@ bool Node::is_greater_than(const Node *p_node) const {
int idx = data.depth - 1;
while (n) {
ERR_FAIL_INDEX_V(idx, data.depth, false);
this_stack[idx--] = n->data.index;
this_stack[idx--] = n->get_index();
n = n->data.parent;
}
ERR_FAIL_COND_V(idx != -1, false);
n = p_node;
idx = p_node->data.depth - 1;
while (n) {
ERR_FAIL_INDEX_V(idx, p_node->data.depth, false);
that_stack[idx--] = n->data.index;
that_stack[idx--] = n->get_index();
n = n->data.parent;
}
@ -1576,8 +1562,8 @@ void Node::get_owned_by(Node *p_by, List<Node *> *p_owned) {
p_owned->push_back(this);
}
for (int i = 0; i < get_child_count(); i++) {
get_child(i)->get_owned_by(p_by, p_owned);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->get_owned_by(p_by, p_owned);
}
}
@ -1895,9 +1881,10 @@ int Node::get_persistent_group_count() const {
void Node::_print_tree_pretty(const String &prefix, const bool last) {
String new_prefix = last ? String::utf8(" ┖╴") : String::utf8(" ┠╴");
print_line(prefix + new_prefix + String(get_name()));
for (int i = 0; i < data.children.size(); i++) {
_update_children_cache();
for (uint32_t i = 0; i < data.children_cache.size(); i++) {
new_prefix = last ? String::utf8(" ") : String::utf8("");
data.children[i]->_print_tree_pretty(prefix + new_prefix, i == data.children.size() - 1);
data.children_cache[i]->_print_tree_pretty(prefix + new_prefix, i == data.children_cache.size() - 1);
}
}
@ -1911,15 +1898,17 @@ void Node::print_tree() {
void Node::_print_tree(const Node *p_node) {
print_line(String(p_node->get_path_to(this)));
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->_print_tree(p_node);
_update_children_cache();
for (uint32_t i = 0; i < data.children_cache.size(); i++) {
data.children_cache[i]->_print_tree(p_node);
}
}
void Node::_propagate_reverse_notification(int p_notification) {
data.blocked++;
for (int i = data.children.size() - 1; i >= 0; i--) {
data.children[i]->_propagate_reverse_notification(p_notification);
for (HashMap<StringName, Node *>::Iterator I = data.children.last(); I; --I) {
I->value->_propagate_reverse_notification(p_notification);
}
notification(p_notification, true);
@ -1935,8 +1924,8 @@ void Node::_propagate_deferred_notification(int p_notification, bool p_reverse)
MessageQueue::get_singleton()->push_notification(this, p_notification);
}
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->_propagate_deferred_notification(p_notification, p_reverse);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->_propagate_deferred_notification(p_notification, p_reverse);
}
if (p_reverse) {
@ -1950,8 +1939,8 @@ void Node::propagate_notification(int p_notification) {
data.blocked++;
notification(p_notification);
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->propagate_notification(p_notification);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->propagate_notification(p_notification);
}
data.blocked--;
}
@ -1963,8 +1952,8 @@ void Node::propagate_call(const StringName &p_method, const Array &p_args, const
callv(p_method, p_args);
}
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->propagate_call(p_method, p_args, p_parent_first);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->propagate_call(p_method, p_args, p_parent_first);
}
if (!p_parent_first && has_method(p_method)) {
@ -1980,22 +1969,12 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
}
data.blocked++;
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->_propagate_replace_owner(p_owner, p_by_owner);
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->_propagate_replace_owner(p_owner, p_by_owner);
}
data.blocked--;
}
int Node::get_index(bool p_include_internal) const {
// p_include_internal = false doesn't make sense if the node is internal.
ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false.");
if (data.parent && !p_include_internal) {
return data.index - data.parent->data.internal_children_front;
}
return data.index;
}
Ref<Tween> Node::create_tween() {
ERR_FAIL_COND_V_MSG(!data.tree, nullptr, "Can't create Tween when not inside scene tree.");
Ref<Tween> tween = get_tree()->create_tween();
@ -2502,7 +2481,7 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) {
}
Node *parent = data.parent;
int index_in_parent = data.index;
int index_in_parent = get_index();
if (data.parent) {
parent->remove_child(this);
@ -2754,8 +2733,8 @@ void Node::get_argument_options(const StringName &p_function, int p_idx, List<St
void Node::clear_internal_tree_resource_paths() {
clear_internal_resource_paths();
for (int i = 0; i < data.children.size(); i++) {
data.children[i]->clear_internal_tree_resource_paths();
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->clear_internal_tree_resource_paths();
}
}
@ -3102,9 +3081,10 @@ Node::~Node() {
data.grouped.clear();
data.owned.clear();
data.children.clear();
data.children_cache.clear();
ERR_FAIL_COND(data.parent);
ERR_FAIL_COND(data.children.size());
ERR_FAIL_COND(data.children_cache.size());
orphan_node_count--;
}

View file

@ -92,6 +92,18 @@ private:
SceneTree::Group *group = nullptr;
};
struct ComparatorByIndex {
bool operator()(const Node *p_left, const Node *p_right) const {
static const uint32_t order[3] = { 1, 0, 2 };
uint32_t order_left = order[p_left->data.internal_mode];
uint32_t order_right = order[p_right->data.internal_mode];
if (order_left == order_right) {
return p_left->data.index < p_right->data.index;
}
return order_left < order_right;
}
};
// This Data struct is to avoid namespace pollution in derived classes.
struct Data {
String scene_file_path;
@ -100,13 +112,16 @@ private:
Node *parent = nullptr;
Node *owner = nullptr;
Vector<Node *> children;
HashMap<StringName, Node *> children;
mutable bool children_cache_dirty = true;
mutable LocalVector<Node *> children_cache;
HashMap<StringName, Node *> owned_unique_nodes;
bool unique_name_in_owner = false;
int internal_children_front = 0;
int internal_children_back = 0;
int index = -1;
InternalMode internal_mode = INTERNAL_MODE_DISABLED;
mutable int internal_children_front_count_cache = 0;
mutable int internal_children_back_count_cache = 0;
mutable int external_children_count_cache = 0;
mutable int index = -1; // relative to front, normal or back.
int depth = -1;
int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
StringName name;
@ -187,9 +202,6 @@ private:
Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
_FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.index < data.parent->data.internal_children_front; }
_FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.index >= data.parent->data.children.size() - data.parent->data.internal_children_back; }
friend class SceneTree;
void _set_tree(SceneTree *p_tree);
@ -201,6 +213,14 @@ private:
void _release_unique_name_in_owner();
void _acquire_unique_name_in_owner();
_FORCE_INLINE_ void _update_children_cache() const {
if (unlikely(data.children_cache_dirty)) {
_update_children_cache_impl();
}
}
void _update_children_cache_impl() const;
protected:
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
@ -219,7 +239,7 @@ protected:
friend class SceneState;
void _add_child_nocheck(Node *p_child, const StringName &p_name);
void _add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode = INTERNAL_MODE_DISABLED);
void _set_owner_nocheck(Node *p_owner);
void _set_name_nocheck(const StringName &p_name);
@ -361,7 +381,31 @@ public:
void set_unique_name_in_owner(bool p_enabled);
bool is_unique_name_in_owner() const;
int get_index(bool p_include_internal = true) const;
_FORCE_INLINE_ int get_index(bool p_include_internal = true) const {
// p_include_internal = false doesn't make sense if the node is internal.
ERR_FAIL_COND_V_MSG(!p_include_internal && data.internal_mode != INTERNAL_MODE_DISABLED, -1, "Node is internal. Can't get index with 'include_internal' being false.");
if (!data.parent) {
return data.index;
}
data.parent->_update_children_cache();
if (!p_include_internal) {
return data.index;
} else {
switch (data.internal_mode) {
case INTERNAL_MODE_DISABLED: {
return data.parent->data.internal_children_front_count_cache + data.index;
} break;
case INTERNAL_MODE_FRONT: {
return data.index;
} break;
case INTERNAL_MODE_BACK: {
return data.parent->data.internal_children_front_count_cache + data.parent->data.external_children_count_cache + data.index;
} break;
}
return -1;
}
}
Ref<Tween> create_tween();