Skip to content

Commit 90c61dd

Browse files
committed
Core: Add Node::iter_children as a fast way to iterate a node's children, without needing allocations or get_child.
Adds `Iterable` class to templates.
1 parent ca1e478 commit 90c61dd

File tree

4 files changed

+96
-13
lines changed

4 files changed

+96
-13
lines changed

core/templates/iterable.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**************************************************************************/
2+
/* iterable.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
template <typename I>
34+
class Iterable {
35+
I _begin;
36+
I _end;
37+
38+
public:
39+
I begin() { return _begin; }
40+
I end() { return _end; }
41+
42+
Iterable(I &&begin, I &&end) :
43+
_begin(std::move(begin)), _end(std::move(end)) {}
44+
Iterable(const I &begin, const I &end) :
45+
_begin(begin), _end(end) {}
46+
};

scene/main/node.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,6 +1781,25 @@ void Node::_update_children_cache_impl() const {
17811781
data.children_cache_dirty = false;
17821782
}
17831783

1784+
template <bool p_include_internal>
1785+
Iterable<Node::ChildrenIterator> Node::iter_children() const {
1786+
ERR_THREAD_GUARD_V(Iterable<ChildrenIterator>(nullptr, nullptr));
1787+
1788+
_update_children_cache();
1789+
const uint32_t size = data.children_cache.size();
1790+
// Might be null, but then size and internal counts are also 0.
1791+
Node **ptr = data.children_cache.ptr();
1792+
1793+
if constexpr (p_include_internal) {
1794+
return Iterable(ChildrenIterator(ptr), ChildrenIterator(ptr + size));
1795+
} else {
1796+
return Iterable(ChildrenIterator(ptr + data.internal_children_front_count_cache), ChildrenIterator(ptr + size - data.internal_children_front_count_cache - data.internal_children_back_count_cache));
1797+
}
1798+
}
1799+
1800+
template Iterable<Node::ChildrenIterator> Node::iter_children<true>() const;
1801+
template Iterable<Node::ChildrenIterator> Node::iter_children<false>() const;
1802+
17841803
int Node::get_child_count(bool p_include_internal) const {
17851804
ERR_THREAD_GUARD_V(0);
17861805
if (p_include_internal) {

scene/main/node.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#pragma once
3232

3333
#include "core/string/node_path.h"
34+
#include "core/templates/iterable.h"
3435
#include "core/variant/typed_array.h"
3536
#include "scene/main/scene_tree.h"
3637
#include "scene/scene_string_names.h"
@@ -47,8 +48,6 @@ SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
4748
class Node : public Object {
4849
GDCLASS(Node, Object);
4950

50-
friend class SceneTreeFTI;
51-
5251
protected:
5352
// During group processing, these are thread-safe.
5453
// Outside group processing, these avoid the cost of sync by working as plain primitive types.
@@ -132,6 +131,29 @@ class Node : public Object {
132131

133132
void _update_process(bool p_enable, bool p_for_children);
134133

134+
struct ChildrenIterator {
135+
_FORCE_INLINE_ Node *&operator*() const { return *_ptr; }
136+
_FORCE_INLINE_ Node **operator->() const { return _ptr; }
137+
_FORCE_INLINE_ ChildrenIterator &operator++() {
138+
_ptr++;
139+
return *this;
140+
}
141+
_FORCE_INLINE_ ChildrenIterator &operator--() {
142+
_ptr--;
143+
return *this;
144+
}
145+
146+
_FORCE_INLINE_ bool operator==(const ChildrenIterator &b) const { return _ptr == b._ptr; }
147+
_FORCE_INLINE_ bool operator!=(const ChildrenIterator &b) const { return _ptr != b._ptr; }
148+
149+
ChildrenIterator(Node **p_ptr) { _ptr = p_ptr; }
150+
ChildrenIterator() {}
151+
ChildrenIterator(const ChildrenIterator &p_it) { _ptr = p_it._ptr; }
152+
153+
private:
154+
Node **_ptr = nullptr;
155+
};
156+
135157
private:
136158
struct GroupData {
137159
bool persistent = false;
@@ -484,6 +506,9 @@ class Node : public Object {
484506
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
485507
void remove_child(Node *p_child);
486508

509+
template <bool p_include_internal = true>
510+
Iterable<ChildrenIterator> iter_children() const;
511+
487512
int get_child_count(bool p_include_internal = true) const;
488513
Node *get_child(int p_index, bool p_include_internal = true) const;
489514
TypedArray<Node> get_children(bool p_include_internal = true) const;

scene/main/scene_tree_fti.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -460,19 +460,12 @@ void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_fra
460460
return;
461461
}
462462

463-
// Temporary direct access to children cache for speed.
464-
// Maybe replaced later by a more generic fast access method
465-
// for children.
466-
p_node->_update_children_cache();
467-
Span<Node *> children = p_node->data.children_cache.span();
468-
uint32_t num_children = children.size();
469-
470463
// Not a Node3D.
471464
// Could be e.g. a viewport or something
472465
// so we should still recurse to children.
473466
if (!s) {
474-
for (uint32_t n = 0; n < num_children; n++) {
475-
_update_dirty_nodes(children.ptr()[n], p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
467+
for (Node *node : p_node->iter_children()) {
468+
_update_dirty_nodes(node, p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
476469
}
477470
return;
478471
}
@@ -592,8 +585,8 @@ void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_fra
592585
s->_clear_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM);
593586

594587
// Recurse to children.
595-
for (uint32_t n = 0; n < num_children; n++) {
596-
_update_dirty_nodes(p_node->get_child(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);
588+
for (Node *node : p_node->iter_children()) {
589+
_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);
597590
}
598591
}
599592

0 commit comments

Comments
 (0)