@@ -2,95 +2,140 @@ use crate::components::{GlobalTransform, Transform};
22use bevy_ecs:: prelude:: { Changed , Entity , Query , With , Without } ;
33use bevy_hierarchy:: { Children , Parent } ;
44
5+ /// Update [`GlobalTransform`] component of entities that aren't in the hierarchy
6+ pub fn sync_simple_transforms (
7+ mut query : Query <
8+ ( & Transform , & mut GlobalTransform ) ,
9+ ( Changed < Transform > , Without < Parent > , Without < Children > ) ,
10+ > ,
11+ ) {
12+ query. par_for_each_mut ( 1024 , |( transform, mut global_transform) | {
13+ * global_transform = GlobalTransform :: from ( * transform) ;
14+ } ) ;
15+ }
16+
517/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
618/// [`Transform`] component.
7- pub fn transform_propagate_system (
19+ pub fn propagate_transforms (
820 mut root_query : Query <
921 (
10- Option < ( & Children , Changed < Children > ) > ,
22+ Entity ,
23+ & Children ,
1124 & Transform ,
1225 Changed < Transform > ,
26+ Changed < Children > ,
1327 & mut GlobalTransform ,
14- Entity ,
1528 ) ,
1629 Without < Parent > ,
1730 > ,
18- mut transform_query : Query < (
19- & Transform ,
20- Changed < Transform > ,
21- & mut GlobalTransform ,
22- & Parent ,
23- ) > ,
31+ transform_query : Query < ( & Transform , Changed < Transform > , & mut GlobalTransform ) , With < Parent > > ,
32+ parent_query : Query < & Parent > ,
2433 children_query : Query < ( & Children , Changed < Children > ) , ( With < Parent > , With < GlobalTransform > ) > ,
2534) {
26- for ( children, transform, transform_changed, mut global_transform, entity) in
27- root_query. iter_mut ( )
28- {
29- let mut changed = transform_changed;
30- if transform_changed {
31- * global_transform = GlobalTransform :: from ( * transform) ;
32- }
35+ root_query. par_for_each_mut (
36+ // The differing depths and sizes of hierarchy trees causes the work for each root to be
37+ // different. A batch size of 1 ensures that each tree gets it's own task and multiple
38+ // large trees are not clumped together.
39+ 1 ,
40+ |( entity, children, transform, mut changed, children_changed, mut global_transform) | {
41+ if changed {
42+ * global_transform = GlobalTransform :: from ( * transform) ;
43+ }
3344
34- if let Some ( ( children, changed_children) ) = children {
3545 // If our `Children` has changed, we need to recalculate everything below us
36- changed |= changed_children;
37- for child in children {
38- let _ = propagate_recursive (
46+ changed |= children_changed;
47+
48+ for child in children. iter ( ) {
49+ propagate_recursive (
3950 & global_transform,
40- & mut transform_query,
51+ & transform_query,
52+ & parent_query,
4153 & children_query,
42- * child,
4354 entity,
55+ * child,
4456 changed,
4557 ) ;
4658 }
47- }
48- }
59+ } ,
60+ ) ;
4961}
5062
5163fn propagate_recursive (
5264 parent : & GlobalTransform ,
53- transform_query : & mut Query < (
54- & Transform ,
55- Changed < Transform > ,
56- & mut GlobalTransform ,
57- & Parent ,
58- ) > ,
65+ unsafe_transform_query : & Query <
66+ ( & Transform , Changed < Transform > , & mut GlobalTransform ) ,
67+ With < Parent > ,
68+ > ,
69+ parent_query : & Query < & Parent > ,
5970 children_query : & Query < ( & Children , Changed < Children > ) , ( With < Parent > , With < GlobalTransform > ) > ,
60- entity : Entity ,
6171 expected_parent : Entity ,
72+ entity : Entity ,
6273 mut changed : bool ,
6374 // We use a result here to use the `?` operator. Ideally we'd use a try block instead
64- ) -> Result < ( ) , ( ) > {
75+ ) {
76+ let Ok ( actual_parent) = parent_query. get ( entity) else {
77+ panic ! ( "Propagated child for {:?} has no Parent component!" , entity) ;
78+ } ;
79+ assert_eq ! (
80+ actual_parent. get( ) , expected_parent,
81+ "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
82+ ) ;
83+
6584 let global_matrix = {
66- let ( transform, transform_changed, mut global_transform, child_parent) =
67- transform_query. get_mut ( entity) . map_err ( drop) ?;
68- // Note that for parallelising, this check cannot occur here, since there is an `&mut GlobalTransform` (in global_transform)
69- assert_eq ! (
70- child_parent. get( ) , expected_parent,
71- "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
72- ) ;
85+ let Ok ( ( transform, transform_changed, mut global_transform) ) =
86+ // SAFETY: This call cannot create aliased mutable references.
87+ // - The top level iteration parallelizes on the roots of the hierarchy.
88+ // - The above assertion ensures that each child has one and only one unique parent throughout the entire
89+ // hierarchy.
90+ //
91+ // For example, consider the following malformed hierarchy:
92+ //
93+ // A
94+ // / \
95+ // B C
96+ // \ /
97+ // D
98+ //
99+ // D has two parents, B and C. If the propagation passes through C, but the Parent component on D points to B,
100+ // the above check will panic as the origin parent does match the recorded parent.
101+ //
102+ // Also consider the following case, where A and B are roots:
103+ //
104+ // A B
105+ // \ /
106+ // C D
107+ // \ /
108+ // E
109+ //
110+ // Even if these A and B start two separate tasks running in parallel, one of them will panic before attempting
111+ // to mutably access E.
112+ ( unsafe { unsafe_transform_query. get_unchecked ( entity) } ) else {
113+ return ;
114+ } ;
115+
73116 changed |= transform_changed;
74117 if changed {
75118 * global_transform = parent. mul_transform ( * transform) ;
76119 }
77120 * global_transform
78121 } ;
79122
80- let ( children, changed_children) = children_query. get ( entity) . map_err ( drop) ?;
123+ let Ok ( ( children, changed_children) ) = children_query. get ( entity) else {
124+ return
125+ } ;
81126 // If our `Children` has changed, we need to recalculate everything below us
82127 changed |= changed_children;
83128 for child in children {
84- let _ = propagate_recursive (
129+ propagate_recursive (
85130 & global_matrix,
86- transform_query,
131+ unsafe_transform_query,
132+ parent_query,
87133 children_query,
88- * child,
89134 entity,
135+ * child,
90136 changed,
91137 ) ;
92138 }
93- Ok ( ( ) )
94139}
95140
96141#[ cfg( test) ]
@@ -99,9 +144,10 @@ mod test {
99144 use bevy_ecs:: prelude:: * ;
100145 use bevy_ecs:: system:: CommandQueue ;
101146 use bevy_math:: vec3;
147+ use bevy_tasks:: { ComputeTaskPool , TaskPool } ;
102148
103149 use crate :: components:: { GlobalTransform , Transform } ;
104- use crate :: systems:: transform_propagate_system ;
150+ use crate :: systems:: * ;
105151 use crate :: TransformBundle ;
106152 use bevy_hierarchy:: { BuildChildren , BuildWorldChildren , Children , Parent } ;
107153
@@ -110,10 +156,12 @@ mod test {
110156
111157 #[ test]
112158 fn did_propagate ( ) {
159+ ComputeTaskPool :: init ( TaskPool :: default) ;
113160 let mut world = World :: default ( ) ;
114161
115162 let mut update_stage = SystemStage :: parallel ( ) ;
116- update_stage. add_system ( transform_propagate_system) ;
163+ update_stage. add_system ( sync_simple_transforms) ;
164+ update_stage. add_system ( propagate_transforms) ;
117165
118166 let mut schedule = Schedule :: default ( ) ;
119167 schedule. add_stage ( Update , update_stage) ;
@@ -152,8 +200,10 @@ mod test {
152200 #[ test]
153201 fn did_propagate_command_buffer ( ) {
154202 let mut world = World :: default ( ) ;
203+
155204 let mut update_stage = SystemStage :: parallel ( ) ;
156- update_stage. add_system ( transform_propagate_system) ;
205+ update_stage. add_system ( sync_simple_transforms) ;
206+ update_stage. add_system ( propagate_transforms) ;
157207
158208 let mut schedule = Schedule :: default ( ) ;
159209 schedule. add_stage ( Update , update_stage) ;
@@ -192,10 +242,12 @@ mod test {
192242
193243 #[ test]
194244 fn correct_children ( ) {
245+ ComputeTaskPool :: init ( TaskPool :: default) ;
195246 let mut world = World :: default ( ) ;
196247
197248 let mut update_stage = SystemStage :: parallel ( ) ;
198- update_stage. add_system ( transform_propagate_system) ;
249+ update_stage. add_system ( sync_simple_transforms) ;
250+ update_stage. add_system ( propagate_transforms) ;
199251
200252 let mut schedule = Schedule :: default ( ) ;
201253 schedule. add_stage ( Update , update_stage) ;
@@ -272,8 +324,10 @@ mod test {
272324 #[ test]
273325 fn correct_transforms_when_no_children ( ) {
274326 let mut app = App :: new ( ) ;
327+ ComputeTaskPool :: init ( TaskPool :: default) ;
275328
276- app. add_system ( transform_propagate_system) ;
329+ app. add_system ( sync_simple_transforms) ;
330+ app. add_system ( propagate_transforms) ;
277331
278332 let translation = vec3 ( 1.0 , 0.0 , 0.0 ) ;
279333
@@ -313,12 +367,14 @@ mod test {
313367 #[ test]
314368 #[ should_panic]
315369 fn panic_when_hierarchy_cycle ( ) {
370+ ComputeTaskPool :: init ( TaskPool :: default) ;
316371 // We cannot directly edit Parent and Children, so we use a temp world to break
317372 // the hierarchy's invariants.
318373 let mut temp = World :: new ( ) ;
319374 let mut app = App :: new ( ) ;
320375
321- app. add_system ( transform_propagate_system) ;
376+ app. add_system ( propagate_transforms)
377+ . add_system ( sync_simple_transforms) ;
322378
323379 fn setup_world ( world : & mut World ) -> ( Entity , Entity ) {
324380 let mut grandchild = Entity :: from_raw ( 0 ) ;
0 commit comments