@@ -2,95 +2,140 @@ use crate::components::{GlobalTransform, Transform};
2
2
use bevy_ecs:: prelude:: { Changed , Entity , Query , With , Without } ;
3
3
use bevy_hierarchy:: { Children , Parent } ;
4
4
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
+
5
17
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
6
18
/// [`Transform`] component.
7
- pub fn transform_propagate_system (
19
+ pub fn propagate_transforms (
8
20
mut root_query : Query <
9
21
(
10
- Option < ( & Children , Changed < Children > ) > ,
22
+ Entity ,
23
+ & Children ,
11
24
& Transform ,
12
25
Changed < Transform > ,
26
+ Changed < Children > ,
13
27
& mut GlobalTransform ,
14
- Entity ,
15
28
) ,
16
29
Without < Parent > ,
17
30
> ,
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 > ,
24
33
children_query : Query < ( & Children , Changed < Children > ) , ( With < Parent > , With < GlobalTransform > ) > ,
25
34
) {
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
+ }
33
44
34
- if let Some ( ( children, changed_children) ) = children {
35
45
// 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 (
39
50
& global_transform,
40
- & mut transform_query,
51
+ & transform_query,
52
+ & parent_query,
41
53
& children_query,
42
- * child,
43
54
entity,
55
+ * child,
44
56
changed,
45
57
) ;
46
58
}
47
- }
48
- }
59
+ } ,
60
+ ) ;
49
61
}
50
62
51
63
fn propagate_recursive (
52
64
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 > ,
59
70
children_query : & Query < ( & Children , Changed < Children > ) , ( With < Parent > , With < GlobalTransform > ) > ,
60
- entity : Entity ,
61
71
expected_parent : Entity ,
72
+ entity : Entity ,
62
73
mut changed : bool ,
63
74
// 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
+
65
84
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
+
73
116
changed |= transform_changed;
74
117
if changed {
75
118
* global_transform = parent. mul_transform ( * transform) ;
76
119
}
77
120
* global_transform
78
121
} ;
79
122
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
+ } ;
81
126
// If our `Children` has changed, we need to recalculate everything below us
82
127
changed |= changed_children;
83
128
for child in children {
84
- let _ = propagate_recursive (
129
+ propagate_recursive (
85
130
& global_matrix,
86
- transform_query,
131
+ unsafe_transform_query,
132
+ parent_query,
87
133
children_query,
88
- * child,
89
134
entity,
135
+ * child,
90
136
changed,
91
137
) ;
92
138
}
93
- Ok ( ( ) )
94
139
}
95
140
96
141
#[ cfg( test) ]
@@ -99,9 +144,10 @@ mod test {
99
144
use bevy_ecs:: prelude:: * ;
100
145
use bevy_ecs:: system:: CommandQueue ;
101
146
use bevy_math:: vec3;
147
+ use bevy_tasks:: { ComputeTaskPool , TaskPool } ;
102
148
103
149
use crate :: components:: { GlobalTransform , Transform } ;
104
- use crate :: systems:: transform_propagate_system ;
150
+ use crate :: systems:: * ;
105
151
use crate :: TransformBundle ;
106
152
use bevy_hierarchy:: { BuildChildren , BuildWorldChildren , Children , Parent } ;
107
153
@@ -110,10 +156,12 @@ mod test {
110
156
111
157
#[ test]
112
158
fn did_propagate ( ) {
159
+ ComputeTaskPool :: init ( TaskPool :: default) ;
113
160
let mut world = World :: default ( ) ;
114
161
115
162
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) ;
117
165
118
166
let mut schedule = Schedule :: default ( ) ;
119
167
schedule. add_stage ( Update , update_stage) ;
@@ -152,8 +200,10 @@ mod test {
152
200
#[ test]
153
201
fn did_propagate_command_buffer ( ) {
154
202
let mut world = World :: default ( ) ;
203
+
155
204
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) ;
157
207
158
208
let mut schedule = Schedule :: default ( ) ;
159
209
schedule. add_stage ( Update , update_stage) ;
@@ -192,10 +242,12 @@ mod test {
192
242
193
243
#[ test]
194
244
fn correct_children ( ) {
245
+ ComputeTaskPool :: init ( TaskPool :: default) ;
195
246
let mut world = World :: default ( ) ;
196
247
197
248
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) ;
199
251
200
252
let mut schedule = Schedule :: default ( ) ;
201
253
schedule. add_stage ( Update , update_stage) ;
@@ -272,8 +324,10 @@ mod test {
272
324
#[ test]
273
325
fn correct_transforms_when_no_children ( ) {
274
326
let mut app = App :: new ( ) ;
327
+ ComputeTaskPool :: init ( TaskPool :: default) ;
275
328
276
- app. add_system ( transform_propagate_system) ;
329
+ app. add_system ( sync_simple_transforms) ;
330
+ app. add_system ( propagate_transforms) ;
277
331
278
332
let translation = vec3 ( 1.0 , 0.0 , 0.0 ) ;
279
333
@@ -313,6 +367,7 @@ mod test {
313
367
#[ test]
314
368
#[ should_panic]
315
369
fn panic_when_hierarchy_cycle ( ) {
370
+ ComputeTaskPool :: init ( TaskPool :: default) ;
316
371
// We cannot directly edit Parent and Children, so we use a temp world to break
317
372
// the hierarchy's invariants.
318
373
let mut temp = World :: new ( ) ;
@@ -321,7 +376,8 @@ mod test {
321
376
// FIXME: Parallel executors seem to have some odd interaction with the other
322
377
// tests in this crate. Using single_threaded until a root cause can be found.
323
378
app. add_stage ( "single" , SystemStage :: single_threaded ( ) )
324
- . add_system_to_stage ( "single" , transform_propagate_system) ;
379
+ . add_system_to_stage ( "single" , sync_simple_transforms)
380
+ . add_system_to_stage ( "single" , propagate_transforms) ;
325
381
326
382
fn setup_world ( world : & mut World ) -> ( Entity , Entity ) {
327
383
let mut grandchild = Entity :: from_raw ( 0 ) ;
0 commit comments