@@ -27,31 +27,33 @@ pub fn sync_simple_transforms(
2727/// to propagate transforms correctly.
2828pub fn propagate_transforms (
2929 mut root_query : Query <
30- ( Entity , Ref < Children > , Ref < Transform > , & mut GlobalTransform ) ,
30+ ( Entity , & Children , Ref < Transform > , & mut GlobalTransform ) ,
3131 Without < Parent > ,
3232 > ,
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 > ) > ,
3635) {
3736 root_query. par_for_each_mut (
3837 // The differing depths and sizes of hierarchy trees causes the work for each root to be
3938 // different. A batch size of 1 ensures that each tree gets it's own task and multiple
4039 // large trees are not clumped together.
4140 1 ,
4241 |( entity, children, transform, mut global_transform) | {
43- let mut changed = transform. is_changed ( ) ;
42+ let changed = transform. is_changed ( ) ;
4443 if changed {
4544 * global_transform = GlobalTransform :: from ( * transform) ;
4645 }
4746
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+ ) ;
5252 // 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.
5557 // - Since each root entity is unique and the hierarchy is consistent and forest-like,
5658 // other root entities' `propagate_recursive` calls will not conflict with this one.
5759 // - 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(
6062 & global_transform,
6163 & transform_query,
6264 & parent_query,
63- & children_query,
64- entity,
65- * child,
66- changed,
65+ child,
66+ changed || actual_parent. is_changed ( ) ,
6767 ) ;
6868 }
6969 }
@@ -75,35 +75,30 @@ pub fn propagate_transforms(
7575///
7676/// # Panics
7777///
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.
8080///
8181/// # Safety
8282///
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`,
8484/// 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.
8587unsafe fn propagate_recursive (
8688 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 > ) > ,
9194 entity : Entity ,
9295 mut changed : bool ,
9396) {
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) ) =
10499 // SAFETY: This call cannot create aliased mutable references.
105100 // - 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
107102 // hierarchy.
108103 //
109104 // For example, consider the following malformed hierarchy:
@@ -127,34 +122,35 @@ unsafe fn propagate_recursive(
127122 //
128123 // Even if these A and B start two separate tasks running in parallel, one of them will panic before attempting
129124 // to mutably access E.
130- ( unsafe { unsafe_transform_query . get_unchecked ( entity) } ) else {
125+ ( unsafe { transform_query . get_unchecked ( entity) } ) else {
131126 return ;
132127 } ;
133128
134129 changed |= transform. is_changed ( ) ;
135130 if changed {
136131 * global_transform = parent. mul_transform ( * transform) ;
137132 }
138- * global_transform
133+ ( * global_transform, children )
139134 } ;
140135
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
148143 // 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.
149147 unsafe {
150148 propagate_recursive (
151149 & global_matrix,
152- unsafe_transform_query ,
150+ transform_query ,
153151 parent_query,
154- children_query,
155- entity,
156- * child,
157- changed,
152+ child,
153+ changed || actual_parent. is_changed ( ) ,
158154 ) ;
159155 }
160156 }
0 commit comments