Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_dev_tools/src/schedule_data/serde.rs
30636 views
1
//! Utilities for serializing schedule data for an [`App`](bevy_app::App).
2
//!
3
//! These are mostly around providing types implementing [`Serialize`]/[`Deserialize`] that
4
//! represent schedule data. In addition, there are tools for extracting this data from the
5
//! [`World`](bevy_ecs::world::World).
6
7
use bevy_ecs::{
8
component::{ComponentId, Components},
9
schedule::{
10
ApplyDeferred, ConditionWithAccess, InternedScheduleLabel, NodeId, Schedule,
11
ScheduleBuildMetadata, Schedules,
12
},
13
system::SystemStateFlags,
14
};
15
use bevy_platform::collections::{hash_map::Entry, HashMap};
16
use serde::{Deserialize, Serialize};
17
use thiserror::Error;
18
19
/// The data for the entire app's schedule.
20
#[derive(Serialize, Deserialize, Debug, Clone)]
21
pub struct AppData {
22
/// A list of all schedules in the app.
23
pub schedules: Vec<ScheduleData>,
24
}
25
26
impl AppData {
27
/// Creates the data from the underlying [`Schedules`].
28
///
29
/// Note: we assume all schedules in `schedules` have been initialized through
30
/// [`Schedule::initialize`].
31
pub fn from_schedules(
32
schedules: &Schedules,
33
world_components: &Components,
34
label_to_build_metadata: &HashMap<InternedScheduleLabel, ScheduleBuildMetadata>,
35
) -> Result<Self, ExtractAppDataError> {
36
Ok(Self {
37
schedules: schedules
38
.iter()
39
.map(|(_, schedule)| {
40
ScheduleData::from_schedule(
41
schedule,
42
world_components,
43
label_to_build_metadata.get(&schedule.label()),
44
)
45
})
46
.collect::<Result<_, ExtractAppDataError>>()?,
47
})
48
}
49
}
50
51
/// Data about a particular schedule.
52
#[derive(Serialize, Deserialize, Debug, Clone)]
53
pub struct ScheduleData {
54
/// The name of the schedule.
55
pub name: String,
56
/// The systems in this schedule.
57
pub systems: Vec<SystemData>,
58
/// The system sets in this schedule.
59
pub system_sets: Vec<SystemSetData>,
60
/// A list of relationships indicating that a system/system set is contained in a system set.
61
///
62
/// The order is (parent, child).
63
pub hierarchy: Vec<(SystemSetIndex, ScheduleIndex)>,
64
/// A list of ordering constraints, ensuring that one system/system set runs before another.
65
///
66
/// The order is (first, second).
67
pub dependency: Vec<(ScheduleIndex, ScheduleIndex)>,
68
/// The components that these systems access.
69
pub components: Vec<ComponentData>,
70
/// A list of conflicts between systems.
71
pub conflicts: Vec<SystemConflict>,
72
}
73
74
/// Data about a component type.
75
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
76
pub struct ComponentData {
77
/// The name of the component.
78
pub name: String,
79
}
80
81
/// Data about a particular system.
82
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
83
pub struct SystemData {
84
/// The name of the system.
85
pub name: String,
86
/// Whether this system is a sync point (aka [`ApplyDeferred`]).
87
pub apply_deferred: bool,
88
/// Whether this system is exclusive.
89
pub exclusive: bool,
90
/// Whether this system has deferred buffers to apply.
91
pub deferred: bool,
92
// TODO: Store the conditions specific to this system.
93
}
94
95
/// Data about a particular system set.
96
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
97
pub struct SystemSetData {
98
/// The name of the system set.
99
pub name: String,
100
/// The conditions applied to this system.
101
pub conditions: Vec<ConditionData>,
102
}
103
104
/// Data about a run condition for a system.
105
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
106
pub struct ConditionData {
107
/// The name of the condition.
108
pub name: String,
109
}
110
111
/// An index of an element in a schedule.
112
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
113
pub enum ScheduleIndex {
114
/// The index of a system.
115
System(u32),
116
/// The index of a system set.
117
SystemSet(u32),
118
}
119
120
/// Data about an access conflict between two systems.
121
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
122
pub struct SystemConflict {
123
/// The first system index.
124
pub system_1: u32,
125
/// The second system index.
126
pub system_2: u32,
127
/// The kind of conflict between these systems.
128
pub conflicting_access: AccessConflict,
129
}
130
131
/// Data for describing the kind of access conflict.
132
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
133
pub enum AccessConflict {
134
/// There is a conflict on the **whole world**, since one of the systems requires world access
135
/// and the other needs mutable access to (some of) the world.
136
World,
137
/// There is incompatible accesses to the listed components.
138
Components(Vec<u32>),
139
}
140
141
/// A newtype for the index of a system set.
142
///
143
/// This is the same kind of index as [`ScheduleIndex::SystemSet`], but for cases where we know we
144
/// can't have a system.
145
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
146
pub struct SystemSetIndex(pub u32);
147
148
impl ScheduleData {
149
/// Creates the data from the underlying [`Schedule`].
150
///
151
/// Note: we assume `schedule` has already been initialized.
152
pub fn from_schedule(
153
schedule: &Schedule,
154
world_components: &Components,
155
build_metadata: Option<&ScheduleBuildMetadata>,
156
) -> Result<Self, ExtractAppDataError> {
157
let graph = schedule.graph();
158
159
let mut system_key_to_index = HashMap::new();
160
let mut system_set_key_to_index = HashMap::new();
161
162
fn extract_condition_data(conditions: &[ConditionWithAccess]) -> Vec<ConditionData> {
163
conditions
164
.iter()
165
.map(|condition| ConditionData {
166
name: format!("{}", condition.condition.name()),
167
})
168
.collect()
169
}
170
171
let systems = schedule
172
.systems()
173
.map_err(|_| {
174
ExtractAppDataError::ScheduleNotInitialized(format!("{:?}", schedule.label()))
175
})?
176
.enumerate()
177
.map(|(index, (key, system))| {
178
system_key_to_index.insert(key, index);
179
180
let flags = system.flags();
181
182
SystemData {
183
name: format!("{}", system.name()),
184
apply_deferred: system.system_type()
185
== core::any::TypeId::of::<ApplyDeferred>(),
186
exclusive: flags.contains(SystemStateFlags::EXCLUSIVE),
187
deferred: flags.contains(SystemStateFlags::DEFERRED),
188
}
189
})
190
.collect();
191
192
let system_sets = graph
193
.system_sets
194
.iter()
195
.enumerate()
196
.map(|(index, (key, system_set, conditions))| {
197
system_set_key_to_index.insert(key, index);
198
199
SystemSetData {
200
name: format!("{:?}", system_set),
201
conditions: extract_condition_data(conditions),
202
}
203
})
204
.collect();
205
206
let node_id_to_schedule_index = |node_id: NodeId| match node_id {
207
NodeId::System(key) => ScheduleIndex::System(
208
*system_key_to_index
209
.get(&key)
210
.expect("the system this key refers to should have already been seen")
211
as _,
212
),
213
NodeId::Set(key) => ScheduleIndex::SystemSet(
214
*system_set_key_to_index
215
.get(&key)
216
.expect("the system set this key refers to should have already been seen")
217
as _,
218
),
219
};
220
221
let hierarchy = graph
222
.hierarchy()
223
.graph()
224
.all_edges()
225
.map(|(parent, child)| {
226
let parent = system_set_key_to_index
227
.get(
228
&parent
229
.as_set()
230
.expect("the parent of a system/set is always a set"),
231
)
232
.expect("the system set this key refers to should have already been seen");
233
let child = node_id_to_schedule_index(child);
234
235
(SystemSetIndex(*parent as _), child)
236
})
237
.collect();
238
239
let mut dependency = graph
240
.dependency()
241
.graph()
242
.all_edges()
243
.map(|(a, b)| (node_id_to_schedule_index(a), node_id_to_schedule_index(b)))
244
.collect::<Vec<_>>();
245
246
if let Some(build_metadata) = build_metadata {
247
// Add in all the edges that were created by build passes.
248
dependency.extend(
249
build_metadata
250
.edges_added_by_build_passes
251
.iter()
252
.map(|(a, b)| {
253
(
254
node_id_to_schedule_index(NodeId::System(*a)),
255
node_id_to_schedule_index(NodeId::System(*b)),
256
)
257
}),
258
);
259
}
260
261
let mut component_id_to_index = HashMap::<ComponentId, usize>::new();
262
let mut components = vec![];
263
264
let conflicts = graph
265
.conflicting_systems()
266
.iter()
267
.map(|(system_1, system_2, conflicts)| {
268
let system_1 = system_key_to_index
269
.get(system_1)
270
.expect("the system this key refers to should have already been seen");
271
let system_2 = system_key_to_index
272
.get(system_2)
273
.expect("the system this key refers to should have already been seen");
274
275
SystemConflict {
276
system_1: *system_1 as _,
277
system_2: *system_2 as _,
278
conflicting_access: if conflicts.is_empty() {
279
// The systems conflict on the world if there's no particular component IDs.
280
AccessConflict::World
281
} else {
282
AccessConflict::Components(
283
conflicts
284
.iter()
285
.map(|id| match component_id_to_index.entry(*id) {
286
Entry::Occupied(entry) => *entry.get() as _,
287
Entry::Vacant(entry) => {
288
let component = world_components.get_info(*id).expect(
289
"the component has already been registered by the system",
290
);
291
292
components.push(ComponentData {
293
name: format!("{}", component.name()),
294
});
295
*entry.insert(components.len() - 1) as _
296
}
297
})
298
.collect(),
299
)
300
},
301
}
302
})
303
.collect();
304
305
Ok(Self {
306
name: format!("{:?}", schedule.label()),
307
components,
308
systems,
309
system_sets,
310
hierarchy,
311
dependency,
312
conflicts,
313
})
314
}
315
}
316
317
/// An error occurring while attempting to extract schedule data from an app.
318
#[derive(Error, Debug)]
319
pub enum ExtractAppDataError {
320
/// A schedule has not been initialized through [`Schedule::initialize`].
321
#[error("executable schedule has not been created for label \"{0}\"")]
322
ScheduleNotInitialized(String),
323
}
324
325
#[cfg(test)]
326
/// Tests for extracted schedule data.
327
///
328
/// This is public to allow other test modules in this crate to use its utilities.
329
pub mod tests {
330
use bevy_app::{App, Update};
331
use bevy_ecs::{
332
component::Component,
333
query::{With, Without},
334
schedule::{IntoScheduleConfigs, Schedules, SystemSet},
335
system::{Commands, Query},
336
};
337
use bevy_platform::collections::HashMap;
338
339
use crate::schedule_data::serde::{
340
AccessConflict, AppData, ComponentData, ExtractAppDataError, ScheduleData, ScheduleIndex,
341
SystemConflict, SystemData, SystemSetData, SystemSetIndex,
342
};
343
344
fn app_data_from_app(app: &mut App) -> Result<AppData, ExtractAppDataError> {
345
let schedules = app.world_mut().resource::<Schedules>();
346
// TODO: This is a pain. It would be nice to be able to just hokey-pokey the whole
347
// `Schedules` resource, but initializing a schedule writes to `Schedules`. Also we need to
348
// use interned labels since `Box<dyn ScheduleLabel>` doesn't impl `ScheduleLabel`!
349
let interned_labels = schedules
350
.iter()
351
.map(|(_, schedule)| schedule.label())
352
.collect::<Vec<_>>();
353
354
let mut label_to_build_metadata = HashMap::new();
355
356
for label in interned_labels {
357
let build_metadata = app
358
.world_mut()
359
.schedule_scope(label, |world, schedule| schedule.initialize(world))
360
.unwrap()
361
.unwrap();
362
label_to_build_metadata.insert(label, build_metadata);
363
}
364
365
let mut app_data = AppData::from_schedules(
366
app.world().resource::<Schedules>(),
367
app.world().components(),
368
&label_to_build_metadata,
369
)?;
370
371
remove_module_paths(&mut app_data);
372
sort_app_data(&mut app_data);
373
Ok(app_data)
374
}
375
376
/// Removes the module paths from all items in the [`AppData`], so that moving tests around
377
/// doesn't change the output.
378
pub fn remove_module_paths(app_data: &mut AppData) {
379
for schedule in app_data.schedules.iter_mut() {
380
for system in schedule.systems.iter_mut() {
381
system.name = system.name.rsplit_once(":").unwrap().1.to_string();
382
}
383
for set in schedule.system_sets.iter_mut() {
384
let name_modless = set
385
.name
386
.rsplit_once(":")
387
.map(|(_, suffix)| suffix)
388
.unwrap_or(set.name.as_str())
389
.to_string();
390
if set.name.starts_with("SystemTypeSet") {
391
// This is a set corresponding to a system. Make sure to keep the
392
// `SystemTypeSet` prefix.
393
set.name = format!("SystemTypeSet:{name_modless}");
394
} else {
395
set.name = name_modless;
396
}
397
}
398
for component in schedule.components.iter_mut() {
399
component.name = component.name.rsplit_once(":").unwrap().1.to_string();
400
}
401
}
402
}
403
404
/// Sorts the [`AppData`] so we have a deterministic order when asserting.
405
// Note: we could do this when extracting unconditionally (even in prod), but there's not much
406
// point since schedule order is not guaranteed to be deterministic anyway. So relying on the
407
// same order seems weird.
408
pub fn sort_app_data(app_data: &mut AppData) {
409
// Sort schedules by name.
410
app_data
411
.schedules
412
.sort_by_key(|schedule| schedule.name.clone());
413
// Sort each schedule.
414
app_data.schedules.iter_mut().for_each(sort_schedule);
415
416
/// Sorts a schedule so that systems, system sets, conditions, and components are in name
417
/// order, and other structures are in index order.
418
fn sort_schedule(schedule: &mut ScheduleData) {
419
/// Sorts the slice using `key_fn` and returns a mapping, which maps the original index
420
/// to the new index.
421
fn reorder_slice<T, K: Ord>(
422
slice: &mut [T],
423
key_fn: impl Fn(&T) -> K,
424
) -> HashMap<usize, usize> {
425
let mut mapping = (0..slice.len()).collect::<Vec<_>>();
426
// We assume the two sorts produce the same thing which should be true since we are
427
// using a stable sort.
428
mapping.sort_by_key(|index| key_fn(&slice[*index]));
429
slice.sort_by_key(key_fn);
430
431
mapping
432
.into_iter()
433
// Enumerating produces the new indices.
434
.enumerate()
435
// Flip the order of indices so that we go from old to new.
436
.map(|(new, old)| (old, new))
437
.collect()
438
}
439
440
let system_old_index_to_new_index =
441
reorder_slice(&mut schedule.systems, |system| system.name.clone());
442
let system_set_old_index_to_new_index =
443
reorder_slice(&mut schedule.system_sets, |set| set.name.clone());
444
let component_old_index_to_new_index =
445
reorder_slice(&mut schedule.components, |component| component.name.clone());
446
447
let reindex_system = |index: &mut u32| {
448
*index = *system_old_index_to_new_index
449
.get(&(*index as usize))
450
.unwrap() as u32;
451
};
452
let reindex_system_set = |index: &mut u32| {
453
*index = *system_set_old_index_to_new_index
454
.get(&(*index as usize))
455
.unwrap() as u32;
456
};
457
let reindex_schedule_index = |index: &mut ScheduleIndex| match index {
458
ScheduleIndex::System(system) => reindex_system(system),
459
ScheduleIndex::SystemSet(set) => reindex_system_set(set),
460
};
461
462
let reindex_component = |index: &mut u32| {
463
*index = *component_old_index_to_new_index
464
.get(&(*index as usize))
465
.unwrap() as u32;
466
};
467
468
// Sort the conditions in a system set.
469
for set in schedule.system_sets.iter_mut() {
470
set.conditions
471
.sort_by_key(|condition| condition.name.clone());
472
}
473
474
// Reindex the hierarchy, and sort it.
475
for (parent, child) in schedule.hierarchy.iter_mut() {
476
reindex_system_set(&mut parent.0);
477
reindex_schedule_index(child);
478
}
479
schedule.hierarchy.sort();
480
481
// Reindex the dependencies, and sort it.
482
for (parent, child) in schedule.dependency.iter_mut() {
483
reindex_schedule_index(parent);
484
reindex_schedule_index(child);
485
}
486
schedule.dependency.sort();
487
488
// Reindex the conflicts.
489
for conflict in schedule.conflicts.iter_mut() {
490
reindex_system(&mut conflict.system_1);
491
reindex_system(&mut conflict.system_2);
492
493
// The order of the indices don't matter, so pick the ordering such that `system_1 <
494
// system_2`.
495
if conflict.system_1 > conflict.system_2 {
496
core::mem::swap(&mut conflict.system_1, &mut conflict.system_2);
497
}
498
499
match &mut conflict.conflicting_access {
500
AccessConflict::World => {}
501
AccessConflict::Components(components) => {
502
components.iter_mut().for_each(reindex_component);
503
components.sort();
504
}
505
};
506
}
507
schedule
508
.conflicts
509
.sort_by_key(|conflict| (conflict.system_1, conflict.system_2));
510
}
511
}
512
513
/// Convenience to create a [`SystemData`] for the common case of no flags set.
514
pub fn simple_system(name: &str) -> SystemData {
515
SystemData {
516
name: name.into(),
517
apply_deferred: false,
518
exclusive: false,
519
deferred: false,
520
}
521
}
522
523
/// Convenience to create a [`SystemSetData`] for the common case of being empty.
524
pub fn simple_system_set(name: &str) -> SystemSetData {
525
SystemSetData {
526
name: name.into(),
527
conditions: vec![],
528
}
529
}
530
531
/// Convenience to create a [`ComponentData`] to make test cases shorter.
532
pub fn simple_component(name: &str) -> ComponentData {
533
ComponentData { name: name.into() }
534
}
535
536
/// Convenience to create a [`SystemConflict`] to make test cases shorter.
537
pub fn conflict(
538
system_1: u32,
539
system_2: u32,
540
conflicting_access: AccessConflict,
541
) -> SystemConflict {
542
SystemConflict {
543
system_1,
544
system_2,
545
conflicting_access,
546
}
547
}
548
549
/// A convenience system set that is generic allowing us to make many of these quickly.
550
#[derive(SystemSet, Hash, PartialEq, Eq, Clone)]
551
struct MySet<const NUM: u32>;
552
553
impl<const NUM: u32> core::fmt::Debug for MySet<NUM> {
554
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
555
write!(f, "MySet<{NUM}>")
556
}
557
}
558
559
/// A convenience component that is generic allowing us to make many of these quickly.
560
#[derive(Component)]
561
struct MyComponent<const NUM: u32>;
562
563
#[test]
564
fn linear() {
565
let mut app = App::empty();
566
567
fn a() {}
568
fn b() {}
569
fn c() {}
570
571
app.add_systems(Update, (a, b, c).chain());
572
573
let data = app_data_from_app(&mut app).unwrap();
574
assert_eq!(data.schedules.len(), 1);
575
let schedule = &data.schedules[0];
576
assert_eq!(schedule.name, "Update");
577
assert_eq!(
578
schedule.systems,
579
[simple_system("a"), simple_system("b"), simple_system("c"),]
580
);
581
// Each system is also a system set.
582
assert_eq!(
583
schedule.system_sets,
584
[
585
simple_system_set("SystemTypeSet:a"),
586
simple_system_set("SystemTypeSet:b"),
587
simple_system_set("SystemTypeSet:c"),
588
]
589
);
590
// Every system is in its own system set.
591
assert_eq!(
592
schedule.hierarchy,
593
[
594
(SystemSetIndex(0), ScheduleIndex::System(0)),
595
(SystemSetIndex(1), ScheduleIndex::System(1)),
596
(SystemSetIndex(2), ScheduleIndex::System(2)),
597
]
598
);
599
// There are 2 dependency edges to connect a-b and b-c.
600
assert_eq!(
601
schedule.dependency,
602
[
603
(ScheduleIndex::System(0), ScheduleIndex::System(1)),
604
(ScheduleIndex::System(1), ScheduleIndex::System(2)),
605
]
606
);
607
assert_eq!(schedule.components.len(), 0);
608
assert_eq!(schedule.conflicts.len(), 0);
609
}
610
611
#[test]
612
fn linear_with_system_sets() {
613
let mut app = App::empty();
614
615
app.configure_sets(Update, (MySet::<0>, MySet::<1>, MySet::<2>).chain());
616
617
let data = app_data_from_app(&mut app).unwrap();
618
assert_eq!(data.schedules.len(), 1);
619
let schedule = &data.schedules[0];
620
assert_eq!(schedule.name, "Update");
621
assert_eq!(schedule.systems, []);
622
assert_eq!(
623
schedule.system_sets,
624
[
625
simple_system_set("MySet<0>"),
626
simple_system_set("MySet<1>"),
627
simple_system_set("MySet<2>"),
628
]
629
);
630
assert_eq!(schedule.hierarchy, []);
631
// There are 2 dependency edges to connect 0-1 and 1-2.
632
assert_eq!(
633
schedule.dependency,
634
[
635
(ScheduleIndex::SystemSet(0), ScheduleIndex::SystemSet(1)),
636
(ScheduleIndex::SystemSet(1), ScheduleIndex::SystemSet(2)),
637
]
638
);
639
assert_eq!(schedule.components.len(), 0);
640
assert_eq!(schedule.conflicts.len(), 0);
641
}
642
643
#[test]
644
fn stack_of_system_sets() {
645
let mut app = App::empty();
646
647
fn a() {}
648
649
app.add_systems(Update, a.in_set(MySet::<0>))
650
.configure_sets(Update, MySet::<0>.in_set(MySet::<1>))
651
.configure_sets(Update, MySet::<1>.in_set(MySet::<2>));
652
653
let data = app_data_from_app(&mut app).unwrap();
654
assert_eq!(data.schedules.len(), 1);
655
let schedule = &data.schedules[0];
656
assert_eq!(schedule.name, "Update");
657
assert_eq!(schedule.systems, [simple_system("a")]);
658
assert_eq!(
659
schedule.system_sets,
660
[
661
simple_system_set("MySet<0>"),
662
simple_system_set("MySet<1>"),
663
simple_system_set("MySet<2>"),
664
simple_system_set("SystemTypeSet:a"),
665
]
666
);
667
assert_eq!(
668
schedule.hierarchy,
669
[
670
(SystemSetIndex(0), ScheduleIndex::System(0)),
671
(SystemSetIndex(1), ScheduleIndex::SystemSet(0)),
672
(SystemSetIndex(2), ScheduleIndex::SystemSet(1)),
673
(SystemSetIndex(3), ScheduleIndex::System(0)),
674
]
675
);
676
assert_eq!(schedule.dependency, []);
677
assert_eq!(schedule.components.len(), 0);
678
assert_eq!(schedule.conflicts.len(), 0);
679
}
680
681
#[test]
682
fn records_system_kind_flags() {
683
let mut app = App::empty();
684
685
fn a0(_commands: Commands) {}
686
fn a1(_commands: Commands) {}
687
fn b0() {}
688
fn b1() {}
689
690
fn c0() {}
691
fn c1() {}
692
693
app.add_systems(Update, (((a0, a1), (b0, b1)).chain(), (c0, c1).chain()));
694
695
let data = app_data_from_app(&mut app).unwrap();
696
assert_eq!(data.schedules.len(), 1);
697
let schedule = &data.schedules[0];
698
assert_eq!(schedule.name, "Update");
699
assert_eq!(
700
schedule.systems,
701
[
702
SystemData {
703
name: "a0".into(),
704
apply_deferred: false,
705
exclusive: false,
706
deferred: true,
707
},
708
SystemData {
709
name: "a1".into(),
710
apply_deferred: false,
711
exclusive: false,
712
deferred: true,
713
},
714
SystemData {
715
name: "apply_deferred".into(),
716
apply_deferred: true,
717
exclusive: true,
718
deferred: false,
719
},
720
simple_system("b0"),
721
simple_system("b1"),
722
simple_system("c0"),
723
simple_system("c1"),
724
]
725
);
726
assert_eq!(
727
schedule.system_sets,
728
[
729
simple_system_set("SystemTypeSet:a0"),
730
simple_system_set("SystemTypeSet:a1"),
731
simple_system_set("SystemTypeSet:b0"),
732
simple_system_set("SystemTypeSet:b1"),
733
simple_system_set("SystemTypeSet:c0"),
734
simple_system_set("SystemTypeSet:c1"),
735
]
736
);
737
assert_eq!(
738
schedule.hierarchy,
739
[
740
(SystemSetIndex(0), ScheduleIndex::System(0)),
741
(SystemSetIndex(1), ScheduleIndex::System(1)),
742
(SystemSetIndex(2), ScheduleIndex::System(3)),
743
(SystemSetIndex(3), ScheduleIndex::System(4)),
744
(SystemSetIndex(4), ScheduleIndex::System(5)),
745
(SystemSetIndex(5), ScheduleIndex::System(6)),
746
]
747
);
748
assert_eq!(
749
schedule.dependency,
750
[
751
// a->sync and a->b
752
(ScheduleIndex::System(0), ScheduleIndex::System(2)),
753
(ScheduleIndex::System(0), ScheduleIndex::System(3)),
754
(ScheduleIndex::System(0), ScheduleIndex::System(4)),
755
(ScheduleIndex::System(1), ScheduleIndex::System(2)),
756
(ScheduleIndex::System(1), ScheduleIndex::System(3)),
757
(ScheduleIndex::System(1), ScheduleIndex::System(4)),
758
// sync->b
759
(ScheduleIndex::System(2), ScheduleIndex::System(3)),
760
(ScheduleIndex::System(2), ScheduleIndex::System(4)),
761
// c0->c1
762
(ScheduleIndex::System(5), ScheduleIndex::System(6)),
763
]
764
);
765
assert_eq!(schedule.components.len(), 0);
766
assert_eq!(schedule.conflicts.len(), 0);
767
}
768
769
#[test]
770
fn records_conflicts() {
771
let mut app = App::empty();
772
773
// These two systems don't conflict.
774
fn a0(_: Query<&MyComponent<0>>) {}
775
fn a1(_: Query<&MyComponent<0>>) {}
776
777
// These two systems conflict on one component.
778
fn b0(_: Query<&MyComponent<1>>) {}
779
fn b1(_: Query<&mut MyComponent<1>>) {}
780
781
// These two systems conflict on two components.
782
fn c0(
783
_: Query<(
784
&MyComponent<2>,
785
&mut MyComponent<3>,
786
&MyComponent<4>,
787
&MyComponent<5>,
788
)>,
789
) {
790
}
791
fn c1(
792
_: Query<(
793
&mut MyComponent<2>,
794
&MyComponent<3>,
795
&MyComponent<4>,
796
&MyComponent<6>,
797
)>,
798
) {
799
}
800
801
// These two systems use With/Without to avoid a conflict.
802
fn d0(_: Query<&mut MyComponent<7>, With<MyComponent<8>>>) {}
803
fn d1(_: Query<&mut MyComponent<7>, Without<MyComponent<8>>>) {}
804
805
// These two systems use an ordering to avoid a conflict.
806
fn e0(_: Query<&mut MyComponent<9>>) {}
807
fn e1(_: Query<&mut MyComponent<9>>) {}
808
809
app.add_systems(Update, (a0, a1, b0, b1, c0, c1, d0, d1, (e0, e1).chain()));
810
811
let data = app_data_from_app(&mut app).unwrap();
812
assert_eq!(data.schedules.len(), 1);
813
let schedule = &data.schedules[0];
814
assert_eq!(schedule.name, "Update");
815
assert_eq!(
816
schedule.systems,
817
[
818
simple_system("a0"),
819
simple_system("a1"),
820
simple_system("b0"),
821
simple_system("b1"),
822
simple_system("c0"),
823
simple_system("c1"),
824
simple_system("d0"),
825
simple_system("d1"),
826
simple_system("e0"),
827
simple_system("e1"),
828
]
829
);
830
assert_eq!(
831
schedule.system_sets,
832
[
833
simple_system_set("SystemTypeSet:a0"),
834
simple_system_set("SystemTypeSet:a1"),
835
simple_system_set("SystemTypeSet:b0"),
836
simple_system_set("SystemTypeSet:b1"),
837
simple_system_set("SystemTypeSet:c0"),
838
simple_system_set("SystemTypeSet:c1"),
839
simple_system_set("SystemTypeSet:d0"),
840
simple_system_set("SystemTypeSet:d1"),
841
simple_system_set("SystemTypeSet:e0"),
842
simple_system_set("SystemTypeSet:e1"),
843
]
844
);
845
assert_eq!(
846
schedule.hierarchy,
847
[
848
(SystemSetIndex(0), ScheduleIndex::System(0)),
849
(SystemSetIndex(1), ScheduleIndex::System(1)),
850
(SystemSetIndex(2), ScheduleIndex::System(2)),
851
(SystemSetIndex(3), ScheduleIndex::System(3)),
852
(SystemSetIndex(4), ScheduleIndex::System(4)),
853
(SystemSetIndex(5), ScheduleIndex::System(5)),
854
(SystemSetIndex(6), ScheduleIndex::System(6)),
855
(SystemSetIndex(7), ScheduleIndex::System(7)),
856
(SystemSetIndex(8), ScheduleIndex::System(8)),
857
(SystemSetIndex(9), ScheduleIndex::System(9)),
858
]
859
);
860
assert_eq!(
861
schedule.dependency,
862
[
863
// e0 -> e1
864
(ScheduleIndex::System(8), ScheduleIndex::System(9)),
865
]
866
);
867
assert_eq!(
868
schedule.components,
869
[
870
simple_component("MyComponent<1>"),
871
simple_component("MyComponent<2>"),
872
simple_component("MyComponent<3>"),
873
]
874
);
875
assert_eq!(
876
schedule.conflicts,
877
[
878
conflict(2, 3, AccessConflict::Components(vec![0])),
879
conflict(4, 5, AccessConflict::Components(vec![1, 2]))
880
]
881
);
882
}
883
}
884
885