mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
Merge pull request #107369 from Ivorforce/node-iter-children
Core: Add `Node::iterate_children` as a fast way to iterate a node's children
This commit is contained in:
commit
e5bf31b170
4 changed files with 101 additions and 13 deletions
46
core/templates/iterable.h
Normal file
46
core/templates/iterable.h
Normal file
|
@ -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 <typename I>
|
||||||
|
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) {}
|
||||||
|
};
|
|
@ -1782,6 +1782,26 @@ void Node::_update_children_cache_impl() const {
|
||||||
data.children_cache_dirty = false;
|
data.children_cache_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool p_include_internal>
|
||||||
|
Iterable<Node::ChildrenIterator> Node::iterate_children() const {
|
||||||
|
// The thread guard is omitted for performance reasons.
|
||||||
|
// ERR_THREAD_GUARD_V(Iterable<ChildrenIterator>(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::ChildrenIterator> Node::iterate_children<true>() const;
|
||||||
|
template Iterable<Node::ChildrenIterator> Node::iterate_children<false>() const;
|
||||||
|
|
||||||
int Node::get_child_count(bool p_include_internal) const {
|
int Node::get_child_count(bool p_include_internal) const {
|
||||||
ERR_THREAD_GUARD_V(0);
|
ERR_THREAD_GUARD_V(0);
|
||||||
if (p_include_internal) {
|
if (p_include_internal) {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/string/node_path.h"
|
#include "core/string/node_path.h"
|
||||||
|
#include "core/templates/iterable.h"
|
||||||
#include "core/variant/typed_array.h"
|
#include "core/variant/typed_array.h"
|
||||||
#include "scene/main/scene_tree.h"
|
#include "scene/main/scene_tree.h"
|
||||||
#include "scene/scene_string_names.h"
|
#include "scene/scene_string_names.h"
|
||||||
|
@ -47,8 +48,6 @@ SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
|
||||||
class Node : public Object {
|
class Node : public Object {
|
||||||
GDCLASS(Node, Object);
|
GDCLASS(Node, Object);
|
||||||
|
|
||||||
friend class SceneTreeFTI;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// During group processing, these are thread-safe.
|
// During group processing, these are thread-safe.
|
||||||
// Outside group processing, these avoid the cost of sync by working as plain primitive types.
|
// Outside group processing, these avoid the cost of sync by working as plain primitive types.
|
||||||
|
@ -136,6 +135,29 @@ public:
|
||||||
|
|
||||||
void _update_process(bool p_enable, bool p_for_children);
|
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:
|
private:
|
||||||
struct GroupData {
|
struct GroupData {
|
||||||
bool persistent = false;
|
bool persistent = false;
|
||||||
|
@ -488,6 +510,13 @@ public:
|
||||||
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
|
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
|
||||||
void remove_child(Node *p_child);
|
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 <bool p_include_internal = true>
|
||||||
|
Iterable<ChildrenIterator> iterate_children() const;
|
||||||
|
|
||||||
int get_child_count(bool p_include_internal = true) const;
|
int get_child_count(bool p_include_internal = true) const;
|
||||||
Node *get_child(int p_index, bool p_include_internal = true) const;
|
Node *get_child(int p_index, bool p_include_internal = true) const;
|
||||||
TypedArray<Node> get_children(bool p_include_internal = true) const;
|
TypedArray<Node> get_children(bool p_include_internal = true) const;
|
||||||
|
|
|
@ -476,19 +476,12 @@ void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_fra
|
||||||
return;
|
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<Node *> children = p_node->data.children_cache.span();
|
|
||||||
uint32_t num_children = children.size();
|
|
||||||
|
|
||||||
// Not a Node3D.
|
// Not a Node3D.
|
||||||
// Could be e.g. a viewport or something
|
// Could be e.g. a viewport or something
|
||||||
// so we should still recurse to children.
|
// so we should still recurse to children.
|
||||||
if (!s) {
|
if (!s) {
|
||||||
for (uint32_t n = 0; n < num_children; n++) {
|
for (Node *node : p_node->iterate_children()) {
|
||||||
_update_dirty_nodes(children.ptr()[n], p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
|
_update_dirty_nodes(node, p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -603,8 +596,8 @@ void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_fra
|
||||||
s->_clear_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM);
|
s->_clear_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM);
|
||||||
|
|
||||||
// Recurse to children.
|
// Recurse to children.
|
||||||
for (uint32_t n = 0; n < num_children; n++) {
|
for (Node *node : p_node->iterate_children()) {
|
||||||
_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);
|
_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue