@@ -27,31 +27,33 @@ pub fn sync_simple_transforms(
27
27
/// to propagate transforms correctly.
28
28
pub fn propagate_transforms (
29
29
mut root_query : Query <
30
- ( Entity , Ref < Children > , Ref < Transform > , & mut GlobalTransform ) ,
30
+ ( Entity , & Children , Ref < Transform > , & mut GlobalTransform ) ,
31
31
Without < Parent > ,
32
32
> ,
33
- transform_query : Query < ( Ref < Transform > , & mut GlobalTransform ) , With < Parent > > ,
34
- parent_query : Query < & Parent > ,
35
- children_query : Query < Ref < Children > , ( With < Parent > , With < GlobalTransform > ) > ,
33
+ transform_query : Query < ( Ref < Transform > , & mut GlobalTransform , Option < & Children > ) , With < Parent > > ,
34
+ parent_query : Query < ( Entity , Ref < Parent > ) > ,
36
35
) {
37
36
root_query. par_for_each_mut (
38
37
// The differing depths and sizes of hierarchy trees causes the work for each root to be
39
38
// different. A batch size of 1 ensures that each tree gets it's own task and multiple
40
39
// large trees are not clumped together.
41
40
1 ,
42
41
|( entity, children, transform, mut global_transform) | {
43
- let mut changed = transform. is_changed ( ) ;
42
+ let changed = transform. is_changed ( ) ;
44
43
if changed {
45
44
* global_transform = GlobalTransform :: from ( * transform) ;
46
45
}
47
46
48
- // If our `Children` has changed, we need to recalculate everything below us
49
- changed |= children. is_changed ( ) ;
50
-
51
- for child in children. iter ( ) {
47
+ for ( child, actual_parent) in parent_query. iter_many ( children) {
48
+ assert_eq ! (
49
+ actual_parent. get( ) , entity,
50
+ "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
51
+ ) ;
52
52
// SAFETY:
53
- // - We may operate as if the hierarchy is consistent, since `propagate_recursive` will panic before continuing
54
- // to propagate if it encounters an entity with inconsistent parentage.
53
+ // - `child` must have consistent parentage, or the above assertion would panic.
54
+ // Since `child` is parented to a root entity, the entire hierarchy leading to it is consistent.
55
+ // - We may operate as if all descendants are consistent, since `propagate_recursive` will panic before
56
+ // continuing to propagate if it encounters an entity with inconsistent parentage.
55
57
// - Since each root entity is unique and the hierarchy is consistent and forest-like,
56
58
// other root entities' `propagate_recursive` calls will not conflict with this one.
57
59
// - Since this is the only place where `transform_query` gets used, there will be no conflicting fetches elsewhere.
@@ -60,10 +62,8 @@ pub fn propagate_transforms(
60
62
& global_transform,
61
63
& transform_query,
62
64
& parent_query,
63
- & children_query,
64
- entity,
65
- * child,
66
- changed,
65
+ child,
66
+ changed || actual_parent. is_changed ( ) ,
67
67
) ;
68
68
}
69
69
}
@@ -75,35 +75,30 @@ pub fn propagate_transforms(
75
75
///
76
76
/// # Panics
77
77
///
78
- /// If `entity` or any of its descendants have a malformed hierarchy.
79
- /// The panic will occur before propagating the transforms of any malformed entities and their descendants.
78
+ /// If `entity`'s descendants have a malformed hierarchy, this function will panic occur before propagating
79
+ /// the transforms of any malformed entities and their descendants.
80
80
///
81
81
/// # Safety
82
82
///
83
- /// While this function is running, `unsafe_transform_query ` must not have any fetches for `entity`,
83
+ /// - While this function is running, `transform_query ` must not have any fetches for `entity`,
84
84
/// nor any of its descendants.
85
+ /// - The caller must ensure that the hierarchy leading to `entity`
86
+ /// is well-formed and must remain as a tree or a forest. Each entity must have at most one parent.
85
87
unsafe fn propagate_recursive (
86
88
parent : & GlobalTransform ,
87
- unsafe_transform_query : & Query < ( Ref < Transform > , & mut GlobalTransform ) , With < Parent > > ,
88
- parent_query : & Query < & Parent > ,
89
- children_query : & Query < Ref < Children > , ( With < Parent > , With < GlobalTransform > ) > ,
90
- expected_parent : Entity ,
89
+ transform_query : & Query <
90
+ ( Ref < Transform > , & mut GlobalTransform , Option < & Children > ) ,
91
+ With < Parent > ,
92
+ > ,
93
+ parent_query : & Query < ( Entity , Ref < Parent > ) > ,
91
94
entity : Entity ,
92
95
mut changed : bool ,
93
96
) {
94
- let Ok ( actual_parent) = parent_query. get ( entity) else {
95
- panic ! ( "Propagated child for {entity:?} has no Parent component!" ) ;
96
- } ;
97
- assert_eq ! (
98
- actual_parent. get( ) , expected_parent,
99
- "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
100
- ) ;
101
-
102
- let global_matrix = {
103
- let Ok ( ( transform, mut global_transform) ) =
97
+ let ( global_matrix, children) = {
98
+ let Ok ( ( transform, mut global_transform, children) ) =
104
99
// SAFETY: This call cannot create aliased mutable references.
105
100
// - The top level iteration parallelizes on the roots of the hierarchy.
106
- // - The above assertion ensures that each child has one and only one unique parent throughout the entire
101
+ // - The caller ensures that each child has one and only one unique parent throughout the entire
107
102
// hierarchy.
108
103
//
109
104
// For example, consider the following malformed hierarchy:
@@ -127,34 +122,35 @@ unsafe fn propagate_recursive(
127
122
//
128
123
// Even if these A and B start two separate tasks running in parallel, one of them will panic before attempting
129
124
// to mutably access E.
130
- ( unsafe { unsafe_transform_query . get_unchecked ( entity) } ) else {
125
+ ( unsafe { transform_query . get_unchecked ( entity) } ) else {
131
126
return ;
132
127
} ;
133
128
134
129
changed |= transform. is_changed ( ) ;
135
130
if changed {
136
131
* global_transform = parent. mul_transform ( * transform) ;
137
132
}
138
- * global_transform
133
+ ( * global_transform, children )
139
134
} ;
140
135
141
- let Ok ( children) = children_query . get ( entity ) else {
142
- return
143
- } ;
144
- // If our `Children` has changed, we need to recalculate everything below us
145
- changed |= children . is_changed ( ) ;
146
- for child in & children {
147
- // SAFETY: The caller guarantees that `unsafe_transform_query ` will not be fetched
136
+ let Some ( children) = children else { return } ;
137
+ for ( child , actual_parent ) in parent_query . iter_many ( children ) {
138
+ assert_eq ! (
139
+ actual_parent . get ( ) , entity ,
140
+ "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
141
+ ) ;
142
+ // SAFETY: The caller guarantees that `transform_query ` will not be fetched
148
143
// for any descendants of `entity`, so it is safe to call `propagate_recursive` for each child.
144
+ //
145
+ // The above assertion ensures that each child has one and only one unique parent throughout the
146
+ // entire hierarchy.
149
147
unsafe {
150
148
propagate_recursive (
151
149
& global_matrix,
152
- unsafe_transform_query ,
150
+ transform_query ,
153
151
parent_query,
154
- children_query,
155
- entity,
156
- * child,
157
- changed,
152
+ child,
153
+ changed || actual_parent. is_changed ( ) ,
158
154
) ;
159
155
}
160
156
}
0 commit comments