diff --git a/core/templates/iterable.h b/core/templates/iterable.h new file mode 100644 index 00000000000..ad93aeae4ea --- /dev/null +++ b/core/templates/iterable.h @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* iterable.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. */ +/**************************************************************************/ + +#pragma once + +template +class Iterable { + I _begin; + I _end; + +public: + I begin() { return _begin; } + I end() { return _end; } + + Iterable(I &&begin, I &&end) : + _begin(std::move(begin)), _end(std::move(end)) {} + Iterable(const I &begin, const I &end) : + _begin(begin), _end(end) {} +}; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 610a2c61cb7..b0440be3a62 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1781,6 +1781,26 @@ void Node::_update_children_cache_impl() const { data.children_cache_dirty = false; } +template +Iterable Node::iterate_children() const { + // The thread guard is omitted for performance reasons. + // ERR_THREAD_GUARD_V(Iterable(nullptr, nullptr)); + + _update_children_cache(); + const uint32_t size = data.children_cache.size(); + // Might be null, but then size and internal counts are also 0. + Node **ptr = data.children_cache.ptr(); + + if constexpr (p_include_internal) { + return Iterable(ChildrenIterator(ptr), ChildrenIterator(ptr + size)); + } else { + return Iterable(ChildrenIterator(ptr + data.internal_children_front_count_cache), ChildrenIterator(ptr + size - data.internal_children_back_count_cache)); + } +} + +template Iterable Node::iterate_children() const; +template Iterable Node::iterate_children() const; + int Node::get_child_count(bool p_include_internal) const { ERR_THREAD_GUARD_V(0); if (p_include_internal) { diff --git a/scene/main/node.h b/scene/main/node.h index 9e21065b802..71ee09de4c5 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -31,6 +31,7 @@ #pragma once #include "core/string/node_path.h" +#include "core/templates/iterable.h" #include "core/variant/typed_array.h" #include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" @@ -47,8 +48,6 @@ SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) class Node : public Object { GDCLASS(Node, Object); - friend class SceneTreeFTI; - protected: // During group processing, these are thread-safe. // Outside group processing, these avoid the cost of sync by working as plain primitive types. @@ -132,6 +131,29 @@ public: void _update_process(bool p_enable, bool p_for_children); + struct ChildrenIterator { + _FORCE_INLINE_ Node *&operator*() const { return *_ptr; } + _FORCE_INLINE_ Node **operator->() const { return _ptr; } + _FORCE_INLINE_ ChildrenIterator &operator++() { + _ptr++; + return *this; + } + _FORCE_INLINE_ ChildrenIterator &operator--() { + _ptr--; + return *this; + } + + _FORCE_INLINE_ bool operator==(const ChildrenIterator &b) const { return _ptr == b._ptr; } + _FORCE_INLINE_ bool operator!=(const ChildrenIterator &b) const { return _ptr != b._ptr; } + + ChildrenIterator(Node **p_ptr) { _ptr = p_ptr; } + ChildrenIterator() {} + ChildrenIterator(const ChildrenIterator &p_it) { _ptr = p_it._ptr; } + + private: + Node **_ptr = nullptr; + }; + private: struct GroupData { bool persistent = false; @@ -484,6 +506,13 @@ public: void add_sibling(Node *p_sibling, bool p_force_readable_name = false); void remove_child(Node *p_child); + /// Optimal way to iterate the children of this node. + /// The caller is responsible to ensure: + /// - The thread has the rights to access the node (is_accessible_from_caller_thread() == true). + /// - No children are inserted, removed, or have their index changed during iteration. + template + Iterable iterate_children() const; + int get_child_count(bool p_include_internal = true) const; Node *get_child(int p_index, bool p_include_internal = true) const; TypedArray get_children(bool p_include_internal = true) const; diff --git a/scene/main/scene_tree_fti.cpp b/scene/main/scene_tree_fti.cpp index fce3b9fdffe..8e1aecf478f 100644 --- a/scene/main/scene_tree_fti.cpp +++ b/scene/main/scene_tree_fti.cpp @@ -465,19 +465,12 @@ void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_fra return; } - // Temporary direct access to children cache for speed. - // Maybe replaced later by a more generic fast access method - // for children. - p_node->_update_children_cache(); - Span children = p_node->data.children_cache.span(); - uint32_t num_children = children.size(); - // Not a Node3D. // Could be e.g. a viewport or something // so we should still recurse to children. if (!s) { - for (uint32_t n = 0; n < num_children; n++) { - _update_dirty_nodes(children.ptr()[n], p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1); + for (Node *node : p_node->iterate_children()) { + _update_dirty_nodes(node, p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1); } return; } @@ -592,8 +585,8 @@ void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_fra s->_clear_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM); // Recurse to children. - for (uint32_t n = 0; n < num_children; n++) { - _update_dirty_nodes(children.ptr()[n], p_current_half_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1); + for (Node *node : p_node->iterate_children()) { + _update_dirty_nodes(node, p_current_half_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1); } }