1#![expect(
2 clippy::module_inception,
3 reason = "This instance of module inception is being discussed; see #17344."
4)]
5use alloc::{
6 boxed::Box,
7 collections::BTreeSet,
8 format,
9 string::{String, ToString},
10 vec,
11 vec::Vec,
12};
13use bevy_platform::{
14 collections::{HashMap, HashSet},
15 hash::FixedHasher,
16};
17use bevy_utils::{default, TypeIdMap};
18use core::{
19 any::{Any, TypeId},
20 fmt::{Debug, Write},
21};
22use fixedbitset::FixedBitSet;
23use indexmap::{IndexMap, IndexSet};
24use log::{info, warn};
25use pass::ScheduleBuildPassObj;
26use thiserror::Error;
27#[cfg(feature = "trace")]
28use tracing::info_span;
29
30use crate::{change_detection::CheckChangeTicks, system::System};
31use crate::{
32 component::{ComponentId, Components},
33 prelude::Component,
34 resource::Resource,
35 schedule::*,
36 system::ScheduleSystem,
37 world::World,
38};
39
40pub use stepping::Stepping;
41use Direction::{Incoming, Outgoing};
42
43#[derive(Default, Resource)]
45pub struct Schedules {
46 inner: HashMap<InternedScheduleLabel, Schedule>,
47 pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
49}
50
51impl Schedules {
52 pub fn new() -> Self {
54 Self::default()
55 }
56
57 pub fn insert(&mut self, schedule: Schedule) -> Option<Schedule> {
62 self.inner.insert(schedule.label, schedule)
63 }
64
65 pub fn remove(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
67 self.inner.remove(&label.intern())
68 }
69
70 pub fn remove_entry(
72 &mut self,
73 label: impl ScheduleLabel,
74 ) -> Option<(InternedScheduleLabel, Schedule)> {
75 self.inner.remove_entry(&label.intern())
76 }
77
78 pub fn contains(&self, label: impl ScheduleLabel) -> bool {
80 self.inner.contains_key(&label.intern())
81 }
82
83 pub fn get(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
85 self.inner.get(&label.intern())
86 }
87
88 pub fn get_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
90 self.inner.get_mut(&label.intern())
91 }
92
93 pub fn entry(&mut self, label: impl ScheduleLabel) -> &mut Schedule {
95 self.inner
96 .entry(label.intern())
97 .or_insert_with(|| Schedule::new(label))
98 }
99
100 pub fn iter(&self) -> impl Iterator<Item = (&dyn ScheduleLabel, &Schedule)> {
102 self.inner
103 .iter()
104 .map(|(label, schedule)| (&**label, schedule))
105 }
106 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&dyn ScheduleLabel, &mut Schedule)> {
108 self.inner
109 .iter_mut()
110 .map(|(label, schedule)| (&**label, schedule))
111 }
112
113 pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
117 #[cfg(feature = "trace")]
118 let _all_span = info_span!("check stored schedule ticks").entered();
119 #[cfg_attr(
120 not(feature = "trace"),
121 expect(
122 unused_variables,
123 reason = "The `label` variable goes unused if the `trace` feature isn't active"
124 )
125 )]
126 for (label, schedule) in &mut self.inner {
127 #[cfg(feature = "trace")]
128 let name = format!("{label:?}");
129 #[cfg(feature = "trace")]
130 let _one_span = info_span!("check schedule ticks", name = &name).entered();
131 schedule.check_change_ticks(check);
132 }
133 }
134
135 pub fn configure_schedules(&mut self, schedule_build_settings: ScheduleBuildSettings) {
137 for (_, schedule) in &mut self.inner {
138 schedule.set_build_settings(schedule_build_settings.clone());
139 }
140 }
141
142 pub fn allow_ambiguous_component<T: Component>(&mut self, world: &mut World) {
144 self.ignored_scheduling_ambiguities
145 .insert(world.register_component::<T>());
146 }
147
148 pub fn allow_ambiguous_resource<T: Resource>(&mut self, world: &mut World) {
150 self.ignored_scheduling_ambiguities
151 .insert(world.components_registrator().register_resource::<T>());
152 }
153
154 pub fn iter_ignored_ambiguities(&self) -> impl Iterator<Item = &ComponentId> + '_ {
156 self.ignored_scheduling_ambiguities.iter()
157 }
158
159 pub fn print_ignored_ambiguities(&self, components: &Components) {
164 let mut message =
165 "System order ambiguities caused by conflicts on the following types are ignored:\n"
166 .to_string();
167 for id in self.iter_ignored_ambiguities() {
168 writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap();
169 }
170
171 info!("{message}");
172 }
173
174 pub fn add_systems<M>(
176 &mut self,
177 schedule: impl ScheduleLabel,
178 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
179 ) -> &mut Self {
180 self.entry(schedule).add_systems(systems);
181
182 self
183 }
184
185 pub fn remove_systems_in_set<M>(
189 &mut self,
190 schedule: impl ScheduleLabel,
191 set: impl IntoSystemSet<M>,
192 world: &mut World,
193 policy: ScheduleCleanupPolicy,
194 ) -> Result<usize, ScheduleError> {
195 self.get_mut(schedule)
196 .ok_or(ScheduleError::ScheduleNotFound)?
197 .remove_systems_in_set(set, world, policy)
198 }
199
200 #[track_caller]
202 pub fn configure_sets<M>(
203 &mut self,
204 schedule: impl ScheduleLabel,
205 sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
206 ) -> &mut Self {
207 self.entry(schedule).configure_sets(sets);
208
209 self
210 }
211
212 #[track_caller]
219 pub fn ignore_ambiguity<M1, M2, S1, S2>(
220 &mut self,
221 schedule: impl ScheduleLabel,
222 a: S1,
223 b: S2,
224 ) -> &mut Self
225 where
226 S1: IntoSystemSet<M1>,
227 S2: IntoSystemSet<M2>,
228 {
229 self.entry(schedule).ignore_ambiguity(a, b);
230
231 self
232 }
233}
234
235fn make_executor(kind: ExecutorKind) -> Box<dyn SystemExecutor> {
236 match kind {
237 ExecutorKind::SingleThreaded => Box::new(SingleThreadedExecutor::new()),
238 #[cfg(feature = "std")]
239 ExecutorKind::MultiThreaded => Box::new(MultiThreadedExecutor::new()),
240 }
241}
242
243#[derive(Default)]
245pub enum Chain {
246 #[default]
248 Unchained,
249 Chained(TypeIdMap<Box<dyn Any>>),
252}
253
254impl Chain {
255 pub fn set_chained(&mut self) {
257 if matches!(self, Chain::Unchained) {
258 *self = Self::Chained(Default::default());
259 };
260 }
261 pub fn set_chained_with_config<T: 'static>(&mut self, config: T) {
264 self.set_chained();
265 if let Chain::Chained(config_map) = self {
266 config_map.insert(TypeId::of::<T>(), Box::new(config));
267 } else {
268 unreachable!()
269 };
270 }
271}
272
273pub struct Schedule {
346 label: InternedScheduleLabel,
347 graph: ScheduleGraph,
348 executable: SystemSchedule,
349 executor: Box<dyn SystemExecutor>,
350 executor_initialized: bool,
351 warnings: Vec<ScheduleBuildWarning>,
352}
353
354#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
355struct DefaultSchedule;
356
357impl Default for Schedule {
358 fn default() -> Self {
363 Self::new(DefaultSchedule)
364 }
365}
366
367impl Schedule {
368 pub fn new(label: impl ScheduleLabel) -> Self {
370 let mut this = Self {
371 label: label.intern(),
372 graph: ScheduleGraph::new(),
373 executable: SystemSchedule::new(),
374 executor: make_executor(ExecutorKind::default()),
375 executor_initialized: false,
376 warnings: Vec::new(),
377 };
378 this.set_build_settings(Default::default());
380 this
381 }
382
383 pub fn label(&self) -> InternedScheduleLabel {
386 self.label
387 }
388
389 pub fn add_systems<M>(
391 &mut self,
392 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
393 ) -> &mut Self {
394 self.graph.process_configs(systems.into_configs(), false);
395 self
396 }
397
398 pub fn remove_systems_in_set<M>(
422 &mut self,
423 set: impl IntoSystemSet<M>,
424 world: &mut World,
425 policy: ScheduleCleanupPolicy,
426 ) -> Result<usize, ScheduleError> {
427 if self.graph.changed {
428 self.initialize(world)?;
429 }
430 self.graph.remove_systems_in_set(set, policy)
431 }
432
433 #[track_caller]
436 pub fn ignore_ambiguity<M1, M2, S1, S2>(&mut self, a: S1, b: S2) -> &mut Self
437 where
438 S1: IntoSystemSet<M1>,
439 S2: IntoSystemSet<M2>,
440 {
441 let a = a.into_system_set();
442 let b = b.into_system_set();
443
444 let a_id = self.graph.system_sets.get_key_or_insert(a.intern());
445 let b_id = self.graph.system_sets.get_key_or_insert(b.intern());
446
447 self.graph
448 .ambiguous_with
449 .add_edge(NodeId::Set(a_id), NodeId::Set(b_id));
450
451 self
452 }
453
454 #[track_caller]
456 pub fn configure_sets<M>(
457 &mut self,
458 sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
459 ) -> &mut Self {
460 self.graph.configure_sets(sets);
461 self
462 }
463
464 pub fn add_build_pass<T: ScheduleBuildPass>(&mut self, pass: T) -> &mut Self {
466 self.graph.passes.insert(TypeId::of::<T>(), Box::new(pass));
467 self
468 }
469
470 pub fn remove_build_pass<T: ScheduleBuildPass>(&mut self) {
472 self.graph.passes.shift_remove(&TypeId::of::<T>());
473 }
474
475 pub fn set_build_settings(&mut self, settings: ScheduleBuildSettings) -> &mut Self {
483 if settings.auto_insert_apply_deferred {
484 if !self
485 .graph
486 .passes
487 .contains_key(&TypeId::of::<passes::AutoInsertApplyDeferredPass>())
488 {
489 self.add_build_pass(passes::AutoInsertApplyDeferredPass::default());
490 }
491 } else {
492 self.remove_build_pass::<passes::AutoInsertApplyDeferredPass>();
493 }
494 self.graph.settings = settings;
495 self
496 }
497
498 pub fn get_build_settings(&self) -> ScheduleBuildSettings {
500 self.graph.settings.clone()
501 }
502
503 pub fn get_executor_kind(&self) -> ExecutorKind {
505 self.executor.kind()
506 }
507
508 pub fn set_executor_kind(&mut self, executor: ExecutorKind) -> &mut Self {
510 if executor != self.executor.kind() {
511 self.executor = make_executor(executor);
512 self.executor_initialized = false;
513 }
514 self
515 }
516
517 pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
522 self.executor.set_apply_final_deferred(apply_final_deferred);
523 self
524 }
525
526 pub fn run(&mut self, world: &mut World) {
528 #[cfg(feature = "trace")]
529 let _span = info_span!("schedule", name = ?self.label).entered();
530
531 world.check_change_ticks();
532 self.initialize(world).unwrap_or_else(|e| {
533 panic!(
534 "Error when initializing schedule {:?}: {}",
535 self.label,
536 e.to_string(self.graph(), world)
537 )
538 });
539
540 let error_handler = world.default_error_handler();
541
542 #[cfg(not(feature = "bevy_debug_stepping"))]
543 self.executor
544 .run(&mut self.executable, world, None, error_handler);
545
546 #[cfg(feature = "bevy_debug_stepping")]
547 {
548 let skip_systems = match world.get_resource_mut::<Stepping>() {
549 None => None,
550 Some(mut stepping) => stepping.skipped_systems(self),
551 };
552
553 self.executor.run(
554 &mut self.executable,
555 world,
556 skip_systems.as_ref(),
557 error_handler,
558 );
559 }
560 }
561
562 pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> {
567 if self.graph.changed {
568 self.graph.initialize(world);
569 let ignored_ambiguities = world
570 .get_resource_or_init::<Schedules>()
571 .ignored_scheduling_ambiguities
572 .clone();
573 self.warnings = self.graph.update_schedule(
574 world,
575 &mut self.executable,
576 &ignored_ambiguities,
577 self.label,
578 )?;
579 self.graph.changed = false;
580 self.executor_initialized = false;
581 }
582
583 if !self.executor_initialized {
584 self.executor.init(&self.executable);
585 self.executor_initialized = true;
586 }
587
588 Ok(())
589 }
590
591 pub fn graph(&self) -> &ScheduleGraph {
593 &self.graph
594 }
595
596 pub fn graph_mut(&mut self) -> &mut ScheduleGraph {
598 &mut self.graph
599 }
600
601 pub(crate) fn executable(&self) -> &SystemSchedule {
603 &self.executable
604 }
605
606 pub fn check_change_ticks(&mut self, check: CheckChangeTicks) {
610 for system in &mut self.executable.systems {
611 if !is_apply_deferred(system) {
612 system.check_change_tick(check);
613 }
614 }
615
616 for conditions in &mut self.executable.system_conditions {
617 for condition in conditions {
618 condition.check_change_tick(check);
619 }
620 }
621
622 for conditions in &mut self.executable.set_conditions {
623 for condition in conditions {
624 condition.check_change_tick(check);
625 }
626 }
627 }
628
629 pub fn apply_deferred(&mut self, world: &mut World) {
638 for SystemWithAccess { system, .. } in &mut self.executable.systems {
639 system.apply_deferred(world);
640 }
641 }
642
643 pub fn systems(
648 &self,
649 ) -> Result<impl Iterator<Item = (SystemKey, &ScheduleSystem)> + Sized, ScheduleNotInitialized>
650 {
651 if !self.executor_initialized {
652 return Err(ScheduleNotInitialized);
653 }
654
655 let iter = self
656 .executable
657 .system_ids
658 .iter()
659 .zip(&self.executable.systems)
660 .map(|(&node_id, system)| (node_id, &system.system));
661
662 Ok(iter)
663 }
664
665 pub fn systems_len(&self) -> usize {
667 if !self.executor_initialized {
668 self.graph.systems.len()
669 } else {
670 self.executable.systems.len()
671 }
672 }
673
674 pub fn warnings(&self) -> &[ScheduleBuildWarning] {
677 &self.warnings
678 }
679}
680
681#[derive(Default)]
686pub struct ScheduleGraph {
687 pub systems: Systems,
689 pub system_sets: SystemSets,
691 hierarchy: Dag<NodeId>,
693 dependency: Dag<NodeId>,
695 set_systems: DagGroups<SystemSetKey, SystemKey>,
697 ambiguous_with: UnGraph<NodeId>,
698 pub ambiguous_with_all: HashSet<NodeId>,
700 conflicting_systems: ConflictingSystems,
701 anonymous_sets: usize,
702 changed: bool,
703 settings: ScheduleBuildSettings,
704 passes: IndexMap<TypeId, Box<dyn ScheduleBuildPassObj>, FixedHasher>,
705}
706
707impl ScheduleGraph {
708 pub fn new() -> Self {
710 Self {
711 systems: Systems::default(),
712 system_sets: SystemSets::default(),
713 hierarchy: Dag::new(),
714 dependency: Dag::new(),
715 set_systems: DagGroups::default(),
716 ambiguous_with: UnGraph::default(),
717 ambiguous_with_all: HashSet::default(),
718 conflicting_systems: ConflictingSystems::default(),
719 anonymous_sets: 0,
720 changed: false,
721 settings: default(),
722 passes: default(),
723 }
724 }
725
726 pub fn hierarchy(&self) -> &Dag<NodeId> {
731 &self.hierarchy
732 }
733
734 pub fn dependency(&self) -> &Dag<NodeId> {
739 &self.dependency
740 }
741
742 pub fn conflicting_systems(&self) -> &ConflictingSystems {
747 &self.conflicting_systems
748 }
749
750 fn process_config<T: ProcessScheduleConfig + Schedulable>(
751 &mut self,
752 config: ScheduleConfig<T>,
753 collect_nodes: bool,
754 ) -> ProcessConfigsResult {
755 ProcessConfigsResult {
756 densely_chained: true,
757 nodes: collect_nodes
758 .then_some(T::process_config(self, config))
759 .into_iter()
760 .collect(),
761 }
762 }
763
764 fn apply_collective_conditions<
765 T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
766 >(
767 &mut self,
768 configs: &mut [ScheduleConfigs<T>],
769 collective_conditions: Vec<BoxedCondition>,
770 ) {
771 if !collective_conditions.is_empty() {
772 if let [config] = configs {
773 for condition in collective_conditions {
774 config.run_if_dyn(condition);
775 }
776 } else {
777 let set = self.create_anonymous_set();
778 for config in configs.iter_mut() {
779 config.in_set_inner(set.intern());
780 }
781 let mut set_config = InternedSystemSet::into_config(set.intern());
782 set_config.conditions.extend(collective_conditions);
783 self.configure_set_inner(set_config);
784 }
785 }
786 }
787
788 #[track_caller]
797 fn process_configs<
798 T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
799 >(
800 &mut self,
801 configs: ScheduleConfigs<T>,
802 collect_nodes: bool,
803 ) -> ProcessConfigsResult {
804 match configs {
805 ScheduleConfigs::ScheduleConfig(config) => self.process_config(config, collect_nodes),
806 ScheduleConfigs::Configs {
807 metadata,
808 mut configs,
809 collective_conditions,
810 } => {
811 self.apply_collective_conditions(&mut configs, collective_conditions);
812
813 let is_chained = matches!(metadata, Chain::Chained(_));
814
815 let mut densely_chained = is_chained || configs.len() == 1;
819 let mut configs = configs.into_iter();
820 let mut nodes = Vec::new();
821
822 let Some(first) = configs.next() else {
823 return ProcessConfigsResult {
824 nodes: Vec::new(),
825 densely_chained,
826 };
827 };
828 let mut previous_result = self.process_configs(first, collect_nodes || is_chained);
829 densely_chained &= previous_result.densely_chained;
830
831 for current in configs {
832 let current_result = self.process_configs(current, collect_nodes || is_chained);
833 densely_chained &= current_result.densely_chained;
834
835 if let Chain::Chained(chain_options) = &metadata {
836 let current_nodes = if current_result.densely_chained {
838 ¤t_result.nodes[..1]
839 } else {
840 ¤t_result.nodes
841 };
842 let previous_nodes = if previous_result.densely_chained {
844 &previous_result.nodes[previous_result.nodes.len() - 1..]
845 } else {
846 &previous_result.nodes
847 };
848
849 self.dependency
850 .reserve_edges(previous_nodes.len() * current_nodes.len());
851 for previous_node in previous_nodes {
852 for current_node in current_nodes {
853 self.dependency.add_edge(*previous_node, *current_node);
854
855 for pass in self.passes.values_mut() {
856 pass.add_dependency(
857 *previous_node,
858 *current_node,
859 chain_options,
860 );
861 }
862 }
863 }
864 }
865 if collect_nodes {
866 nodes.append(&mut previous_result.nodes);
867 }
868
869 previous_result = current_result;
870 }
871 if collect_nodes {
872 nodes.append(&mut previous_result.nodes);
873 }
874
875 ProcessConfigsResult {
876 nodes,
877 densely_chained,
878 }
879 }
880 }
881 }
882
883 fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
885 let key = self.systems.insert(config.node, config.conditions);
886
887 self.update_graphs(NodeId::System(key), config.metadata);
889
890 key
891 }
892
893 #[track_caller]
894 fn configure_sets<M>(&mut self, sets: impl IntoScheduleConfigs<InternedSystemSet, M>) {
895 self.process_configs(sets.into_configs(), false);
896 }
897
898 fn configure_set_inner(&mut self, config: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
900 let key = self.system_sets.insert(config.node, config.conditions);
901
902 self.update_graphs(NodeId::Set(key), config.metadata);
904
905 key
906 }
907
908 fn create_anonymous_set(&mut self) -> AnonymousSet {
909 let id = self.anonymous_sets;
910 self.anonymous_sets += 1;
911 AnonymousSet::new(id)
912 }
913
914 pub fn systems_in_set(
925 &self,
926 system_set: InternedSystemSet,
927 ) -> Result<&IndexSet<SystemKey, FixedHasher>, ScheduleError> {
928 if self.changed {
929 return Err(ScheduleError::Uninitialized);
930 }
931 let system_set_id = self
932 .system_sets
933 .get_key(system_set)
934 .ok_or(ScheduleError::SetNotFound)?;
935 self.set_systems
936 .get(&system_set_id)
937 .ok_or(ScheduleError::SetNotFound)
938 }
939
940 fn add_edges_for_transitive_dependencies(&mut self, node: NodeId) {
941 let in_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Incoming).collect();
942 let out_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Outgoing).collect();
943
944 self.hierarchy
945 .reserve_edges(in_nodes.len() * out_nodes.len());
946 for &in_node in &in_nodes {
947 for &out_node in &out_nodes {
948 self.hierarchy.add_edge(in_node, out_node);
949 }
950 }
951
952 let in_nodes: Vec<_> = self.dependency.neighbors_directed(node, Incoming).collect();
953 let out_nodes: Vec<_> = self.dependency.neighbors_directed(node, Outgoing).collect();
954
955 self.dependency
956 .reserve_edges(in_nodes.len() * out_nodes.len());
957 for &in_node in &in_nodes {
958 for &out_node in &out_nodes {
959 self.dependency.add_edge(in_node, out_node);
960 }
961 }
962 }
963
964 pub fn remove_systems_in_set<M>(
966 &mut self,
967 system_set: impl IntoSystemSet<M>,
968 policy: ScheduleCleanupPolicy,
969 ) -> Result<usize, ScheduleError> {
970 let set = system_set.into_system_set();
971 let interned = set.intern();
972 let keys = self.systems_in_set(interned)?.clone();
974
975 self.changed = true;
976
977 match policy {
978 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages => {
979 let Some(set_key) = self.system_sets.get_key(interned) else {
980 return Err(ScheduleError::SetNotFound);
981 };
982
983 self.remove_systems_by_keys(&keys);
984 self.remove_set_by_key(set_key);
985
986 Ok(keys.len())
987 }
988 ScheduleCleanupPolicy::RemoveSystemsOnlyAllowBreakages => {
989 self.remove_systems_by_keys(&keys);
990
991 Ok(keys.len())
992 }
993 ScheduleCleanupPolicy::RemoveSetAndSystems => {
994 let Some(set_key) = self.system_sets.get_key(interned) else {
995 return Err(ScheduleError::SetNotFound);
996 };
997
998 for &key in &keys {
999 self.add_edges_for_transitive_dependencies(key.into());
1000 }
1001
1002 self.add_edges_for_transitive_dependencies(set_key.into());
1003
1004 self.remove_systems_by_keys(&keys);
1005 self.remove_set_by_key(set_key);
1006
1007 Ok(keys.len())
1008 }
1009 ScheduleCleanupPolicy::RemoveSystemsOnly => {
1010 for &key in &keys {
1011 self.add_edges_for_transitive_dependencies(key.into());
1012 }
1013
1014 self.remove_systems_by_keys(&keys);
1015
1016 Ok(keys.len())
1017 }
1018 }
1019 }
1020
1021 fn remove_systems_by_keys(&mut self, keys: &IndexSet<SystemKey, FixedHasher>) {
1022 for &key in keys {
1023 self.systems.remove(key);
1024
1025 self.hierarchy.remove_node(key.into());
1026 self.dependency.remove_node(key.into());
1027 self.ambiguous_with.remove_node(key.into());
1028 self.ambiguous_with_all.remove(&NodeId::from(key));
1029 }
1030 }
1031
1032 fn remove_set_by_key(&mut self, key: SystemSetKey) {
1033 self.system_sets.remove(key);
1034 self.set_systems.remove(&key);
1035 self.hierarchy.remove_node(key.into());
1036 self.dependency.remove_node(key.into());
1037 self.ambiguous_with.remove_node(key.into());
1038 self.ambiguous_with_all.remove(&NodeId::from(key));
1039 }
1040
1041 fn update_graphs(&mut self, id: NodeId, graph_info: GraphInfo) {
1043 self.changed = true;
1044
1045 let GraphInfo {
1046 hierarchy: sets,
1047 dependencies,
1048 ambiguous_with,
1049 ..
1050 } = graph_info;
1051
1052 self.hierarchy.add_node(id);
1053 self.dependency.add_node(id);
1054
1055 for key in sets
1056 .into_iter()
1057 .map(|set| self.system_sets.get_key_or_insert(set))
1058 {
1059 self.hierarchy.add_edge(NodeId::Set(key), id);
1060
1061 self.dependency.add_node(NodeId::Set(key));
1063 }
1064
1065 for (kind, key, options) in
1066 dependencies
1067 .into_iter()
1068 .map(|Dependency { kind, set, options }| {
1069 (kind, self.system_sets.get_key_or_insert(set), options)
1070 })
1071 {
1072 let (lhs, rhs) = match kind {
1073 DependencyKind::Before => (id, NodeId::Set(key)),
1074 DependencyKind::After => (NodeId::Set(key), id),
1075 };
1076 self.dependency.add_edge(lhs, rhs);
1077 for pass in self.passes.values_mut() {
1078 pass.add_dependency(lhs, rhs, &options);
1079 }
1080
1081 self.hierarchy.add_node(NodeId::Set(key));
1083 }
1084
1085 match ambiguous_with {
1086 Ambiguity::Check => (),
1087 Ambiguity::IgnoreWithSet(ambiguous_with) => {
1088 for key in ambiguous_with
1089 .into_iter()
1090 .map(|set| self.system_sets.get_key_or_insert(set))
1091 {
1092 self.ambiguous_with.add_edge(id, NodeId::Set(key));
1093 }
1094 }
1095 Ambiguity::IgnoreAll => {
1096 self.ambiguous_with_all.insert(id);
1097 }
1098 }
1099 }
1100
1101 pub fn initialize(&mut self, world: &mut World) {
1104 self.systems.initialize(world);
1105 self.system_sets.initialize(world);
1106 }
1107
1108 pub fn build_schedule(
1116 &mut self,
1117 world: &mut World,
1118 ignored_ambiguities: &BTreeSet<ComponentId>,
1119 ) -> Result<(SystemSchedule, Vec<ScheduleBuildWarning>), ScheduleBuildError> {
1120 let mut warnings = Vec::new();
1121
1122 let hierarchy_analysis = self
1124 .hierarchy
1125 .analyze()
1126 .map_err(ScheduleBuildError::HierarchySort)?;
1127
1128 if self.settings.hierarchy_detection != LogLevel::Ignore
1131 && let Err(e) = hierarchy_analysis.check_for_redundant_edges()
1132 {
1133 match self.settings.hierarchy_detection {
1134 LogLevel::Error => return Err(ScheduleBuildWarning::HierarchyRedundancy(e).into()),
1135 LogLevel::Warn => warnings.push(ScheduleBuildWarning::HierarchyRedundancy(e)),
1136 LogLevel::Ignore => unreachable!(),
1137 }
1138 }
1139 self.hierarchy.remove_redundant_edges(&hierarchy_analysis);
1141
1142 let dependency_analysis = self
1144 .dependency
1145 .analyze()
1146 .map_err(ScheduleBuildError::DependencySort)?;
1147
1148 dependency_analysis.check_for_cross_dependencies(&hierarchy_analysis)?;
1150
1151 self.set_systems = self
1153 .hierarchy
1154 .group_by_key(self.system_sets.len())
1155 .map_err(ScheduleBuildError::HierarchySort)?;
1156 dependency_analysis.check_for_overlapping_groups(&self.set_systems)?;
1158
1159 self.system_sets.check_type_set_ambiguity(
1161 &self.set_systems,
1162 &self.ambiguous_with,
1163 &self.dependency,
1164 )?;
1165
1166 let mut flat_dependency =
1170 self.set_systems
1171 .flatten(self.dependency.clone(), |set, systems, flattening, temp| {
1172 for pass in self.passes.values_mut() {
1173 pass.collapse_set(set, systems, flattening, temp);
1174 }
1175 });
1176
1177 let mut passes = core::mem::take(&mut self.passes);
1179 for pass in passes.values_mut() {
1180 pass.build(world, self, &mut flat_dependency)?;
1181 }
1182 self.passes = passes;
1183
1184 let flat_dependency_analysis = flat_dependency
1187 .analyze()
1188 .map_err(ScheduleBuildError::FlatDependencySort)?;
1189 flat_dependency.remove_redundant_edges(&flat_dependency_analysis);
1190
1191 let flat_ambiguous_with = self.set_systems.flatten_undirected(&self.ambiguous_with);
1196
1197 self.conflicting_systems = self.systems.get_conflicting_systems(
1199 &flat_dependency_analysis,
1200 &flat_ambiguous_with,
1201 &self.ambiguous_with_all,
1202 ignored_ambiguities,
1203 );
1204 if self.settings.ambiguity_detection != LogLevel::Ignore
1206 && let Err(e) = self.conflicting_systems.check_if_not_empty()
1207 {
1208 match self.settings.ambiguity_detection {
1209 LogLevel::Error => return Err(ScheduleBuildWarning::Ambiguity(e).into()),
1210 LogLevel::Warn => warnings.push(ScheduleBuildWarning::Ambiguity(e)),
1211 LogLevel::Ignore => unreachable!(),
1212 }
1213 }
1214
1215 Ok((
1217 self.build_schedule_inner(flat_dependency, hierarchy_analysis),
1218 warnings,
1219 ))
1220 }
1221
1222 fn build_schedule_inner(
1223 &self,
1224 flat_dependency: Dag<SystemKey>,
1225 hierarchy_analysis: DagAnalysis<NodeId>,
1226 ) -> SystemSchedule {
1227 let dg_system_ids = flat_dependency.get_toposort().unwrap().to_vec();
1228 let dg_system_idx_map = dg_system_ids
1229 .iter()
1230 .cloned()
1231 .enumerate()
1232 .map(|(i, id)| (id, i))
1233 .collect::<HashMap<_, _>>();
1234
1235 let hierarchy_toposort = self.hierarchy.get_toposort().unwrap();
1236 let hg_systems = hierarchy_toposort
1237 .iter()
1238 .cloned()
1239 .enumerate()
1240 .filter_map(|(i, id)| Some((i, id.as_system()?)))
1241 .collect::<Vec<_>>();
1242 let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = hierarchy_toposort
1243 .iter()
1244 .cloned()
1245 .enumerate()
1246 .filter_map(|(i, id)| {
1247 let key = id.as_set()?;
1250 self.system_sets.has_conditions(key).then_some((i, key))
1251 })
1252 .unzip();
1253
1254 let sys_count = self.systems.len();
1255 let set_with_conditions_count = hg_set_ids.len();
1256 let hg_node_count = self.hierarchy.node_count();
1257
1258 let mut system_dependencies = Vec::with_capacity(sys_count);
1261 let mut system_dependents = Vec::with_capacity(sys_count);
1262 for &sys_key in &dg_system_ids {
1263 let num_dependencies = flat_dependency
1264 .neighbors_directed(sys_key, Incoming)
1265 .count();
1266
1267 let dependents = flat_dependency
1268 .neighbors_directed(sys_key, Outgoing)
1269 .map(|dep_id| dg_system_idx_map[&dep_id])
1270 .collect::<Vec<_>>();
1271
1272 system_dependencies.push(num_dependencies);
1273 system_dependents.push(dependents);
1274 }
1275
1276 let mut systems_in_sets_with_conditions =
1279 vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
1280 for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
1281 let bitset = &mut systems_in_sets_with_conditions[i];
1282 for &(col, sys_key) in &hg_systems {
1283 let idx = dg_system_idx_map[&sys_key];
1284 let is_descendant = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1285 bitset.set(idx, is_descendant);
1286 }
1287 }
1288
1289 let mut sets_with_conditions_of_systems =
1290 vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
1291 for &(col, sys_key) in &hg_systems {
1292 let i = dg_system_idx_map[&sys_key];
1293 let bitset = &mut sets_with_conditions_of_systems[i];
1294 for (idx, &row) in hg_set_with_conditions_idxs
1295 .iter()
1296 .enumerate()
1297 .take_while(|&(_idx, &row)| row < col)
1298 {
1299 let is_ancestor = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1300 bitset.set(idx, is_ancestor);
1301 }
1302 }
1303
1304 SystemSchedule {
1305 systems: Vec::with_capacity(sys_count),
1306 system_conditions: Vec::with_capacity(sys_count),
1307 set_conditions: Vec::with_capacity(set_with_conditions_count),
1308 system_ids: dg_system_ids,
1309 set_ids: hg_set_ids,
1310 system_dependencies,
1311 system_dependents,
1312 sets_with_conditions_of_systems,
1313 systems_in_sets_with_conditions,
1314 }
1315 }
1316
1317 fn update_schedule(
1319 &mut self,
1320 world: &mut World,
1321 schedule: &mut SystemSchedule,
1322 ignored_ambiguities: &BTreeSet<ComponentId>,
1323 schedule_label: InternedScheduleLabel,
1324 ) -> Result<Vec<ScheduleBuildWarning>, ScheduleBuildError> {
1325 if !self.systems.is_initialized() || !self.system_sets.is_initialized() {
1326 return Err(ScheduleBuildError::Uninitialized);
1327 }
1328
1329 for ((key, system), conditions) in schedule
1331 .system_ids
1332 .drain(..)
1333 .zip(schedule.systems.drain(..))
1334 .zip(schedule.system_conditions.drain(..))
1335 {
1336 if let Some(node) = self.systems.node_mut(key) {
1337 node.inner = Some(system);
1338 }
1339
1340 if let Some(node_conditions) = self.systems.get_conditions_mut(key) {
1341 *node_conditions = conditions;
1342 }
1343 }
1344
1345 for (key, conditions) in schedule
1346 .set_ids
1347 .drain(..)
1348 .zip(schedule.set_conditions.drain(..))
1349 {
1350 if let Some(node_conditions) = self.system_sets.get_conditions_mut(key) {
1351 *node_conditions = conditions;
1352 }
1353 }
1354
1355 let (new_schedule, warnings) = self.build_schedule(world, ignored_ambiguities)?;
1356 *schedule = new_schedule;
1357
1358 for warning in &warnings {
1359 warn!(
1360 "{:?} schedule built successfully, however: {}",
1361 schedule_label,
1362 warning.to_string(self, world)
1363 );
1364 }
1365
1366 for &key in &schedule.system_ids {
1368 let system = self.systems.node_mut(key).unwrap().inner.take().unwrap();
1369 let conditions = core::mem::take(self.systems.get_conditions_mut(key).unwrap());
1370 schedule.systems.push(system);
1371 schedule.system_conditions.push(conditions);
1372 }
1373
1374 for &key in &schedule.set_ids {
1375 let conditions = core::mem::take(self.system_sets.get_conditions_mut(key).unwrap());
1376 schedule.set_conditions.push(conditions);
1377 }
1378
1379 Ok(warnings)
1380 }
1381}
1382
1383struct ProcessConfigsResult {
1385 nodes: Vec<NodeId>,
1388 densely_chained: bool,
1392}
1393
1394trait ProcessScheduleConfig: Schedulable + Sized {
1396 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId;
1398}
1399
1400impl ProcessScheduleConfig for ScheduleSystem {
1401 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1402 NodeId::System(schedule_graph.add_system_inner(config))
1403 }
1404}
1405
1406impl ProcessScheduleConfig for InternedSystemSet {
1407 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1408 NodeId::Set(schedule_graph.configure_set_inner(config))
1409 }
1410}
1411
1412#[derive(Default)]
1414pub enum ScheduleCleanupPolicy {
1415 #[default]
1420 RemoveSetAndSystems,
1421 RemoveSystemsOnly,
1425 RemoveSetAndSystemsAllowBreakages,
1430 RemoveSystemsOnlyAllowBreakages,
1434}
1435
1436impl ScheduleGraph {
1438 pub fn get_node_name(&self, id: &NodeId) -> String {
1445 self.get_node_name_inner(id, self.settings.report_sets)
1446 }
1447
1448 #[inline]
1449 fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
1450 match *id {
1451 NodeId::System(key) => {
1452 let name = self.systems[key].name();
1453 let name = if self.settings.use_shortnames {
1454 name.shortname().to_string()
1455 } else {
1456 name.to_string()
1457 };
1458 if report_sets {
1459 let sets = self.names_of_sets_containing_node(id);
1460 if sets.is_empty() {
1461 name
1462 } else if sets.len() == 1 {
1463 format!("{name} (in set {})", sets[0])
1464 } else {
1465 format!("{name} (in sets {})", sets.join(", "))
1466 }
1467 } else {
1468 name
1469 }
1470 }
1471 NodeId::Set(key) => {
1472 let set = &self.system_sets[key];
1473 if set.is_anonymous() {
1474 self.anonymous_set_name(id)
1475 } else {
1476 format!("{set:?}")
1477 }
1478 }
1479 }
1480 }
1481
1482 fn anonymous_set_name(&self, id: &NodeId) -> String {
1483 format!(
1484 "({})",
1485 self.hierarchy
1486 .edges_directed(*id, Outgoing)
1487 .map(|(_, member_id)| self.get_node_name_inner(&member_id, false))
1489 .reduce(|a, b| format!("{a}, {b}"))
1490 .unwrap_or_default()
1491 )
1492 }
1493
1494 fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(SystemSetKey) -> bool) {
1495 for (set_id, _) in self.hierarchy.edges_directed(id, Incoming) {
1496 let NodeId::Set(set_key) = set_id else {
1497 continue;
1498 };
1499 if f(set_key) {
1500 self.traverse_sets_containing_node(NodeId::Set(set_key), f);
1501 }
1502 }
1503 }
1504
1505 fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec<String> {
1506 let mut sets = <HashSet<_>>::default();
1507 self.traverse_sets_containing_node(*id, &mut |key| {
1508 self.system_sets[key].system_type().is_none() && sets.insert(key)
1509 });
1510 let mut sets: Vec<_> = sets
1511 .into_iter()
1512 .map(|key| self.get_node_name(&NodeId::Set(key)))
1513 .collect();
1514 sets.sort();
1515 sets
1516 }
1517}
1518
1519#[derive(Debug, Clone, Copy, PartialEq)]
1521pub enum LogLevel {
1522 Ignore,
1524 Warn,
1526 Error,
1528}
1529
1530#[derive(Clone, Debug)]
1532pub struct ScheduleBuildSettings {
1533 pub ambiguity_detection: LogLevel,
1539 pub hierarchy_detection: LogLevel,
1545 pub auto_insert_apply_deferred: bool,
1555 pub use_shortnames: bool,
1559 pub report_sets: bool,
1563}
1564
1565impl Default for ScheduleBuildSettings {
1566 fn default() -> Self {
1567 Self::new()
1568 }
1569}
1570
1571impl ScheduleBuildSettings {
1572 pub const fn new() -> Self {
1575 Self {
1576 ambiguity_detection: LogLevel::Ignore,
1577 hierarchy_detection: LogLevel::Warn,
1578 auto_insert_apply_deferred: true,
1579 use_shortnames: true,
1580 report_sets: true,
1581 }
1582 }
1583}
1584
1585#[derive(Error, Debug)]
1588#[error("executable schedule has not been built")]
1589pub struct ScheduleNotInitialized;
1590
1591#[cfg(test)]
1592mod tests {
1593 use alloc::{vec, vec::Vec};
1594 use core::any::TypeId;
1595
1596 use bevy_ecs_macros::ScheduleLabel;
1597
1598 use crate::{
1599 error::{ignore, panic, DefaultErrorHandler, Result},
1600 prelude::{ApplyDeferred, IntoSystemSet, Res, Resource},
1601 schedule::{
1602 passes::AutoInsertApplyDeferredPass, tests::ResMut, IntoScheduleConfigs, Schedule,
1603 ScheduleBuildPass, ScheduleBuildSettings, ScheduleCleanupPolicy, SystemSet,
1604 },
1605 system::Commands,
1606 world::World,
1607 };
1608
1609 use super::Schedules;
1610
1611 #[derive(Resource)]
1612 struct Resource1;
1613
1614 #[derive(Resource)]
1615 struct Resource2;
1616
1617 #[test]
1618 fn unchanged_auto_insert_apply_deferred_has_no_effect() {
1619 use alloc::{vec, vec::Vec};
1620
1621 #[derive(PartialEq, Debug)]
1622 enum Entry {
1623 System(usize),
1624 SyncPoint(usize),
1625 }
1626
1627 #[derive(Resource, Default)]
1628 struct Log(Vec<Entry>);
1629
1630 fn system<const N: usize>(mut res: ResMut<Log>, mut commands: Commands) {
1631 res.0.push(Entry::System(N));
1632 commands
1633 .queue(|world: &mut World| world.resource_mut::<Log>().0.push(Entry::SyncPoint(N)));
1634 }
1635
1636 let mut world = World::default();
1637 world.init_resource::<Log>();
1638 let mut schedule = Schedule::default();
1639 schedule.add_systems((system::<1>, system::<2>).chain_ignore_deferred());
1640 schedule.set_build_settings(ScheduleBuildSettings {
1641 auto_insert_apply_deferred: true,
1642 ..Default::default()
1643 });
1644 schedule.run(&mut world);
1645 let actual = world.remove_resource::<Log>().unwrap().0;
1646
1647 let expected = vec![
1648 Entry::System(1),
1649 Entry::System(2),
1650 Entry::SyncPoint(1),
1651 Entry::SyncPoint(2),
1652 ];
1653
1654 assert_eq!(actual, expected);
1655 }
1656
1657 #[test]
1659 fn ambiguous_with_not_breaking_run_conditions() {
1660 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1661 struct Set;
1662
1663 let mut world = World::new();
1664 let mut schedule = Schedule::default();
1665
1666 let system: fn() = || {
1667 panic!("This system must not run");
1668 };
1669
1670 schedule.configure_sets(Set.run_if(|| false));
1671 schedule.add_systems(system.ambiguous_with(|| ()).in_set(Set));
1672 schedule.run(&mut world);
1673 }
1674
1675 #[test]
1676 fn inserts_a_sync_point() {
1677 let mut schedule = Schedule::default();
1678 let mut world = World::default();
1679 schedule.add_systems(
1680 (
1681 |mut commands: Commands| commands.insert_resource(Resource1),
1682 |_: Res<Resource1>| {},
1683 )
1684 .chain(),
1685 );
1686 schedule.run(&mut world);
1687
1688 assert_eq!(schedule.executable.systems.len(), 3);
1690 }
1691
1692 #[test]
1693 fn explicit_sync_point_used_as_auto_sync_point() {
1694 let mut schedule = Schedule::default();
1695 let mut world = World::default();
1696 schedule.add_systems(
1697 (
1698 |mut commands: Commands| commands.insert_resource(Resource1),
1699 |_: Res<Resource1>| {},
1700 )
1701 .chain(),
1702 );
1703 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain());
1704 schedule.run(&mut world);
1705
1706 assert_eq!(schedule.executable.systems.len(), 5);
1708 }
1709
1710 #[test]
1711 fn conditional_explicit_sync_point_not_used_as_auto_sync_point() {
1712 let mut schedule = Schedule::default();
1713 let mut world = World::default();
1714 schedule.add_systems(
1715 (
1716 |mut commands: Commands| commands.insert_resource(Resource1),
1717 |_: Res<Resource1>| {},
1718 )
1719 .chain(),
1720 );
1721 schedule.add_systems((|| {}, ApplyDeferred.run_if(|| false), || {}).chain());
1722 schedule.run(&mut world);
1723
1724 assert_eq!(schedule.executable.systems.len(), 6);
1726 }
1727
1728 #[test]
1729 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_chain() {
1730 let mut schedule = Schedule::default();
1731 let mut world = World::default();
1732 schedule.add_systems(
1733 (
1734 |mut commands: Commands| commands.insert_resource(Resource1),
1735 |_: Res<Resource1>| {},
1736 )
1737 .chain(),
1738 );
1739 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().run_if(|| false));
1740 schedule.run(&mut world);
1741
1742 assert_eq!(schedule.executable.systems.len(), 6);
1744 }
1745
1746 #[test]
1747 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_system_set() {
1748 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1749 struct Set;
1750
1751 let mut schedule = Schedule::default();
1752 let mut world = World::default();
1753 schedule.configure_sets(Set.run_if(|| false));
1754 schedule.add_systems(
1755 (
1756 |mut commands: Commands| commands.insert_resource(Resource1),
1757 |_: Res<Resource1>| {},
1758 )
1759 .chain(),
1760 );
1761 schedule.add_systems((|| {}, ApplyDeferred.in_set(Set), || {}).chain());
1762 schedule.run(&mut world);
1763
1764 assert_eq!(schedule.executable.systems.len(), 6);
1766 }
1767
1768 #[test]
1769 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_nested_system_set()
1770 {
1771 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1772 struct Set1;
1773 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1774 struct Set2;
1775
1776 let mut schedule = Schedule::default();
1777 let mut world = World::default();
1778 schedule.configure_sets(Set2.run_if(|| false));
1779 schedule.configure_sets(Set1.in_set(Set2));
1780 schedule.add_systems(
1781 (
1782 |mut commands: Commands| commands.insert_resource(Resource1),
1783 |_: Res<Resource1>| {},
1784 )
1785 .chain(),
1786 );
1787 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().in_set(Set1));
1788 schedule.run(&mut world);
1789
1790 assert_eq!(schedule.executable.systems.len(), 6);
1792 }
1793
1794 #[test]
1795 fn merges_sync_points_into_one() {
1796 let mut schedule = Schedule::default();
1797 let mut world = World::default();
1798 schedule.add_systems(
1800 (
1801 (
1802 |mut commands: Commands| commands.insert_resource(Resource1),
1803 |mut commands: Commands| commands.insert_resource(Resource2),
1804 ),
1805 |_: Res<Resource1>, _: Res<Resource2>| {},
1806 )
1807 .chain(),
1808 );
1809 schedule.run(&mut world);
1810
1811 assert_eq!(schedule.executable.systems.len(), 4);
1813
1814 schedule.add_systems(((
1816 (
1817 |mut commands: Commands| commands.insert_resource(Resource1),
1818 |mut commands: Commands| commands.insert_resource(Resource2),
1819 ),
1820 |_: Res<Resource1>, _: Res<Resource2>| {},
1821 )
1822 .chain(),));
1823 schedule.run(&mut world);
1824
1825 assert_eq!(schedule.executable.systems.len(), 7);
1826 }
1827
1828 #[test]
1829 fn adds_multiple_consecutive_syncs() {
1830 let mut schedule = Schedule::default();
1831 let mut world = World::default();
1832 schedule.add_systems(
1834 (
1835 |mut commands: Commands| commands.insert_resource(Resource1),
1836 |mut commands: Commands| commands.insert_resource(Resource2),
1837 |_: Res<Resource1>, _: Res<Resource2>| {},
1838 )
1839 .chain(),
1840 );
1841 schedule.run(&mut world);
1842
1843 assert_eq!(schedule.executable.systems.len(), 5);
1844 }
1845
1846 #[test]
1847 fn do_not_consider_ignore_deferred_before_exclusive_system() {
1848 let mut schedule = Schedule::default();
1849 let mut world = World::default();
1850 schedule.add_systems(
1852 (
1853 |_: Commands| {},
1854 |mut commands: Commands| commands.insert_resource(Resource1),
1856 |world: &mut World| assert!(world.contains_resource::<Resource1>()),
1858 |_: &mut World| {},
1860 |_: Commands| {},
1862 )
1863 .chain_ignore_deferred(),
1864 );
1865 schedule.run(&mut world);
1866
1867 assert_eq!(schedule.executable.systems.len(), 6); }
1869
1870 #[test]
1871 fn bubble_sync_point_through_ignore_deferred_node() {
1872 let mut schedule = Schedule::default();
1873 let mut world = World::default();
1874
1875 let insert_resource_config = (
1876 |mut commands: Commands| commands.insert_resource(Resource1),
1878 || {},
1880 )
1881 .chain_ignore_deferred();
1883
1884 schedule.add_systems(
1885 (
1886 insert_resource_config,
1887 |_: Res<Resource1>| {},
1889 )
1890 .chain(),
1892 );
1893
1894 schedule.run(&mut world);
1899
1900 assert_eq!(schedule.executable.systems.len(), 4); }
1902
1903 #[test]
1904 fn disable_auto_sync_points() {
1905 let mut schedule = Schedule::default();
1906 schedule.set_build_settings(ScheduleBuildSettings {
1907 auto_insert_apply_deferred: false,
1908 ..Default::default()
1909 });
1910 let mut world = World::default();
1911 schedule.add_systems(
1912 (
1913 |mut commands: Commands| commands.insert_resource(Resource1),
1914 |res: Option<Res<Resource1>>| assert!(res.is_none()),
1915 )
1916 .chain(),
1917 );
1918 schedule.run(&mut world);
1919
1920 assert_eq!(schedule.executable.systems.len(), 2);
1921 }
1922
1923 mod no_sync_edges {
1924 use super::*;
1925
1926 fn insert_resource(mut commands: Commands) {
1927 commands.insert_resource(Resource1);
1928 }
1929
1930 fn resource_does_not_exist(res: Option<Res<Resource1>>) {
1931 assert!(res.is_none());
1932 }
1933
1934 #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
1935 enum Sets {
1936 A,
1937 B,
1938 }
1939
1940 fn check_no_sync_edges(add_systems: impl FnOnce(&mut Schedule)) {
1941 let mut schedule = Schedule::default();
1942 let mut world = World::default();
1943 add_systems(&mut schedule);
1944
1945 schedule.run(&mut world);
1946
1947 assert_eq!(schedule.executable.systems.len(), 2);
1948 }
1949
1950 #[test]
1951 fn system_to_system_after() {
1952 check_no_sync_edges(|schedule| {
1953 schedule.add_systems((
1954 insert_resource,
1955 resource_does_not_exist.after_ignore_deferred(insert_resource),
1956 ));
1957 });
1958 }
1959
1960 #[test]
1961 fn system_to_system_before() {
1962 check_no_sync_edges(|schedule| {
1963 schedule.add_systems((
1964 insert_resource.before_ignore_deferred(resource_does_not_exist),
1965 resource_does_not_exist,
1966 ));
1967 });
1968 }
1969
1970 #[test]
1971 fn set_to_system_after() {
1972 check_no_sync_edges(|schedule| {
1973 schedule
1974 .add_systems((insert_resource, resource_does_not_exist.in_set(Sets::A)))
1975 .configure_sets(Sets::A.after_ignore_deferred(insert_resource));
1976 });
1977 }
1978
1979 #[test]
1980 fn set_to_system_before() {
1981 check_no_sync_edges(|schedule| {
1982 schedule
1983 .add_systems((insert_resource.in_set(Sets::A), resource_does_not_exist))
1984 .configure_sets(Sets::A.before_ignore_deferred(resource_does_not_exist));
1985 });
1986 }
1987
1988 #[test]
1989 fn set_to_set_after() {
1990 check_no_sync_edges(|schedule| {
1991 schedule
1992 .add_systems((
1993 insert_resource.in_set(Sets::A),
1994 resource_does_not_exist.in_set(Sets::B),
1995 ))
1996 .configure_sets(Sets::B.after_ignore_deferred(Sets::A));
1997 });
1998 }
1999
2000 #[test]
2001 fn set_to_set_before() {
2002 check_no_sync_edges(|schedule| {
2003 schedule
2004 .add_systems((
2005 insert_resource.in_set(Sets::A),
2006 resource_does_not_exist.in_set(Sets::B),
2007 ))
2008 .configure_sets(Sets::A.before_ignore_deferred(Sets::B));
2009 });
2010 }
2011 }
2012
2013 mod no_sync_chain {
2014 use super::*;
2015
2016 #[derive(Resource)]
2017 struct Ra;
2018
2019 #[derive(Resource)]
2020 struct Rb;
2021
2022 #[derive(Resource)]
2023 struct Rc;
2024
2025 fn run_schedule(expected_num_systems: usize, add_systems: impl FnOnce(&mut Schedule)) {
2026 let mut schedule = Schedule::default();
2027 let mut world = World::default();
2028 add_systems(&mut schedule);
2029
2030 schedule.run(&mut world);
2031
2032 assert_eq!(schedule.executable.systems.len(), expected_num_systems);
2033 }
2034
2035 #[test]
2036 fn only_chain_outside() {
2037 run_schedule(5, |schedule: &mut Schedule| {
2038 schedule.add_systems(
2039 (
2040 (
2041 |mut commands: Commands| commands.insert_resource(Ra),
2042 |mut commands: Commands| commands.insert_resource(Rb),
2043 ),
2044 (
2045 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2046 assert!(res_a.is_some());
2047 assert!(res_b.is_some());
2048 },
2049 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2050 assert!(res_a.is_some());
2051 assert!(res_b.is_some());
2052 },
2053 ),
2054 )
2055 .chain(),
2056 );
2057 });
2058
2059 run_schedule(4, |schedule: &mut Schedule| {
2060 schedule.add_systems(
2061 (
2062 (
2063 |mut commands: Commands| commands.insert_resource(Ra),
2064 |mut commands: Commands| commands.insert_resource(Rb),
2065 ),
2066 (
2067 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2068 assert!(res_a.is_none());
2069 assert!(res_b.is_none());
2070 },
2071 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2072 assert!(res_a.is_none());
2073 assert!(res_b.is_none());
2074 },
2075 ),
2076 )
2077 .chain_ignore_deferred(),
2078 );
2079 });
2080 }
2081
2082 #[test]
2083 fn chain_first() {
2084 run_schedule(6, |schedule: &mut Schedule| {
2085 schedule.add_systems(
2086 (
2087 (
2088 |mut commands: Commands| commands.insert_resource(Ra),
2089 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2090 commands.insert_resource(Rb);
2091 assert!(res_a.is_some());
2092 },
2093 )
2094 .chain(),
2095 (
2096 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2097 assert!(res_a.is_some());
2098 assert!(res_b.is_some());
2099 },
2100 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2101 assert!(res_a.is_some());
2102 assert!(res_b.is_some());
2103 },
2104 ),
2105 )
2106 .chain(),
2107 );
2108 });
2109
2110 run_schedule(5, |schedule: &mut Schedule| {
2111 schedule.add_systems(
2112 (
2113 (
2114 |mut commands: Commands| commands.insert_resource(Ra),
2115 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2116 commands.insert_resource(Rb);
2117 assert!(res_a.is_some());
2118 },
2119 )
2120 .chain(),
2121 (
2122 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2123 assert!(res_a.is_some());
2124 assert!(res_b.is_none());
2125 },
2126 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2127 assert!(res_a.is_some());
2128 assert!(res_b.is_none());
2129 },
2130 ),
2131 )
2132 .chain_ignore_deferred(),
2133 );
2134 });
2135 }
2136
2137 #[test]
2138 fn chain_second() {
2139 run_schedule(6, |schedule: &mut Schedule| {
2140 schedule.add_systems(
2141 (
2142 (
2143 |mut commands: Commands| commands.insert_resource(Ra),
2144 |mut commands: Commands| commands.insert_resource(Rb),
2145 ),
2146 (
2147 |mut commands: Commands,
2148 res_a: Option<Res<Ra>>,
2149 res_b: Option<Res<Rb>>| {
2150 commands.insert_resource(Rc);
2151 assert!(res_a.is_some());
2152 assert!(res_b.is_some());
2153 },
2154 |res_a: Option<Res<Ra>>,
2155 res_b: Option<Res<Rb>>,
2156 res_c: Option<Res<Rc>>| {
2157 assert!(res_a.is_some());
2158 assert!(res_b.is_some());
2159 assert!(res_c.is_some());
2160 },
2161 )
2162 .chain(),
2163 )
2164 .chain(),
2165 );
2166 });
2167
2168 run_schedule(5, |schedule: &mut Schedule| {
2169 schedule.add_systems(
2170 (
2171 (
2172 |mut commands: Commands| commands.insert_resource(Ra),
2173 |mut commands: Commands| commands.insert_resource(Rb),
2174 ),
2175 (
2176 |mut commands: Commands,
2177 res_a: Option<Res<Ra>>,
2178 res_b: Option<Res<Rb>>| {
2179 commands.insert_resource(Rc);
2180 assert!(res_a.is_none());
2181 assert!(res_b.is_none());
2182 },
2183 |res_a: Option<Res<Ra>>,
2184 res_b: Option<Res<Rb>>,
2185 res_c: Option<Res<Rc>>| {
2186 assert!(res_a.is_some());
2187 assert!(res_b.is_some());
2188 assert!(res_c.is_some());
2189 },
2190 )
2191 .chain(),
2192 )
2193 .chain_ignore_deferred(),
2194 );
2195 });
2196 }
2197
2198 #[test]
2199 fn chain_all() {
2200 run_schedule(7, |schedule: &mut Schedule| {
2201 schedule.add_systems(
2202 (
2203 (
2204 |mut commands: Commands| commands.insert_resource(Ra),
2205 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2206 commands.insert_resource(Rb);
2207 assert!(res_a.is_some());
2208 },
2209 )
2210 .chain(),
2211 (
2212 |mut commands: Commands,
2213 res_a: Option<Res<Ra>>,
2214 res_b: Option<Res<Rb>>| {
2215 commands.insert_resource(Rc);
2216 assert!(res_a.is_some());
2217 assert!(res_b.is_some());
2218 },
2219 |res_a: Option<Res<Ra>>,
2220 res_b: Option<Res<Rb>>,
2221 res_c: Option<Res<Rc>>| {
2222 assert!(res_a.is_some());
2223 assert!(res_b.is_some());
2224 assert!(res_c.is_some());
2225 },
2226 )
2227 .chain(),
2228 )
2229 .chain(),
2230 );
2231 });
2232
2233 run_schedule(6, |schedule: &mut Schedule| {
2234 schedule.add_systems(
2235 (
2236 (
2237 |mut commands: Commands| commands.insert_resource(Ra),
2238 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2239 commands.insert_resource(Rb);
2240 assert!(res_a.is_some());
2241 },
2242 )
2243 .chain(),
2244 (
2245 |mut commands: Commands,
2246 res_a: Option<Res<Ra>>,
2247 res_b: Option<Res<Rb>>| {
2248 commands.insert_resource(Rc);
2249 assert!(res_a.is_some());
2250 assert!(res_b.is_none());
2251 },
2252 |res_a: Option<Res<Ra>>,
2253 res_b: Option<Res<Rb>>,
2254 res_c: Option<Res<Rc>>| {
2255 assert!(res_a.is_some());
2256 assert!(res_b.is_some());
2257 assert!(res_c.is_some());
2258 },
2259 )
2260 .chain(),
2261 )
2262 .chain_ignore_deferred(),
2263 );
2264 });
2265 }
2266 }
2267
2268 #[derive(ScheduleLabel, Hash, Debug, Clone, PartialEq, Eq)]
2269 struct TestSchedule;
2270
2271 #[derive(Resource)]
2272 struct CheckSystemRan(usize);
2273
2274 #[test]
2275 fn add_systems_to_existing_schedule() {
2276 let mut schedules = Schedules::default();
2277 let schedule = Schedule::new(TestSchedule);
2278
2279 schedules.insert(schedule);
2280 schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2281
2282 let mut world = World::new();
2283
2284 world.insert_resource(CheckSystemRan(0));
2285 world.insert_resource(schedules);
2286 world.run_schedule(TestSchedule);
2287
2288 let value = world
2289 .get_resource::<CheckSystemRan>()
2290 .expect("CheckSystemRan Resource Should Exist");
2291 assert_eq!(value.0, 1);
2292 }
2293
2294 #[test]
2295 fn add_systems_to_non_existing_schedule() {
2296 let mut schedules = Schedules::default();
2297
2298 schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2299
2300 let mut world = World::new();
2301
2302 world.insert_resource(CheckSystemRan(0));
2303 world.insert_resource(schedules);
2304 world.run_schedule(TestSchedule);
2305
2306 let value = world
2307 .get_resource::<CheckSystemRan>()
2308 .expect("CheckSystemRan Resource Should Exist");
2309 assert_eq!(value.0, 1);
2310 }
2311
2312 #[derive(SystemSet, Debug, Hash, Clone, PartialEq, Eq)]
2313 enum TestSet {
2314 First,
2315 Second,
2316 }
2317
2318 #[test]
2319 fn configure_set_on_existing_schedule() {
2320 let mut schedules = Schedules::default();
2321 let schedule = Schedule::new(TestSchedule);
2322
2323 schedules.insert(schedule);
2324
2325 schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2326 schedules.add_systems(
2327 TestSchedule,
2328 (|mut ran: ResMut<CheckSystemRan>| {
2329 assert_eq!(ran.0, 0);
2330 ran.0 += 1;
2331 })
2332 .in_set(TestSet::First),
2333 );
2334
2335 schedules.add_systems(
2336 TestSchedule,
2337 (|mut ran: ResMut<CheckSystemRan>| {
2338 assert_eq!(ran.0, 1);
2339 ran.0 += 1;
2340 })
2341 .in_set(TestSet::Second),
2342 );
2343
2344 let mut world = World::new();
2345
2346 world.insert_resource(CheckSystemRan(0));
2347 world.insert_resource(schedules);
2348 world.run_schedule(TestSchedule);
2349
2350 let value = world
2351 .get_resource::<CheckSystemRan>()
2352 .expect("CheckSystemRan Resource Should Exist");
2353 assert_eq!(value.0, 2);
2354 }
2355
2356 #[test]
2357 fn configure_set_on_new_schedule() {
2358 let mut schedules = Schedules::default();
2359
2360 schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2361 schedules.add_systems(
2362 TestSchedule,
2363 (|mut ran: ResMut<CheckSystemRan>| {
2364 assert_eq!(ran.0, 0);
2365 ran.0 += 1;
2366 })
2367 .in_set(TestSet::First),
2368 );
2369
2370 schedules.add_systems(
2371 TestSchedule,
2372 (|mut ran: ResMut<CheckSystemRan>| {
2373 assert_eq!(ran.0, 1);
2374 ran.0 += 1;
2375 })
2376 .in_set(TestSet::Second),
2377 );
2378
2379 let mut world = World::new();
2380
2381 world.insert_resource(CheckSystemRan(0));
2382 world.insert_resource(schedules);
2383 world.run_schedule(TestSchedule);
2384
2385 let value = world
2386 .get_resource::<CheckSystemRan>()
2387 .expect("CheckSystemRan Resource Should Exist");
2388 assert_eq!(value.0, 2);
2389 }
2390
2391 #[test]
2392 fn test_default_error_handler() {
2393 #[derive(Resource, Default)]
2394 struct Ran(bool);
2395
2396 fn system(mut ran: ResMut<Ran>) -> Result {
2397 ran.0 = true;
2398 Err("I failed!".into())
2399 }
2400
2401 let mut world = World::default();
2403 world.init_resource::<Ran>();
2404 world.insert_resource(DefaultErrorHandler(ignore));
2405 let mut schedule = Schedule::default();
2406 schedule.add_systems(system).run(&mut world);
2407 assert!(world.resource::<Ran>().0);
2408
2409 schedule.add_systems(
2411 (|world: &mut World| {
2412 world.insert_resource(DefaultErrorHandler(panic));
2413 })
2414 .before(system),
2415 );
2416 schedule.run(&mut world);
2417 }
2418
2419 #[test]
2420 fn get_a_system_key() {
2421 fn test_system() {}
2422
2423 let mut schedule = Schedule::default();
2424 schedule.add_systems(test_system);
2425 let mut world = World::default();
2426 let _ = schedule.initialize(&mut world);
2427
2428 let keys = schedule
2429 .graph()
2430 .systems_in_set(test_system.into_system_set().intern())
2431 .unwrap();
2432 assert_eq!(keys.len(), 1);
2433 }
2434
2435 #[test]
2436 fn get_system_keys_in_set() {
2437 fn system_1() {}
2438 fn system_2() {}
2439
2440 let mut schedule = Schedule::default();
2441 schedule.add_systems((system_1, system_2).in_set(TestSet::First));
2442 let mut world = World::default();
2443 let _ = schedule.initialize(&mut world);
2444
2445 let keys = schedule
2446 .graph()
2447 .systems_in_set(TestSet::First.into_system_set().intern())
2448 .unwrap();
2449 assert_eq!(keys.len(), 2);
2450 }
2451
2452 #[test]
2453 fn get_system_keys_with_same_name() {
2454 fn test_system() {}
2455
2456 let mut schedule = Schedule::default();
2457 schedule.add_systems((test_system, test_system));
2458 let mut world = World::default();
2459 let _ = schedule.initialize(&mut world);
2460
2461 let keys = schedule
2462 .graph()
2463 .systems_in_set(test_system.into_system_set().intern())
2464 .unwrap();
2465 assert_eq!(keys.len(), 2);
2466 }
2467
2468 #[test]
2469 fn remove_a_system() {
2470 fn system() {}
2471
2472 let mut schedule = Schedule::default();
2473 schedule.add_systems(system);
2474 let mut world = World::default();
2475
2476 let remove_count = schedule.remove_systems_in_set(
2477 system,
2478 &mut world,
2479 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2480 );
2481 assert_eq!(remove_count.unwrap(), 1);
2482
2483 schedule.initialize(&mut world).unwrap();
2485 assert_eq!(schedule.graph().systems.len(), 0);
2486 }
2487
2488 #[test]
2489 fn remove_multiple_systems() {
2490 fn system() {}
2491
2492 let mut schedule = Schedule::default();
2493 schedule.add_systems((system, system));
2494 let mut world = World::default();
2495
2496 let remove_count = schedule.remove_systems_in_set(
2497 system,
2498 &mut world,
2499 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2500 );
2501 assert_eq!(remove_count.unwrap(), 2);
2502
2503 schedule.initialize(&mut world).unwrap();
2505 assert_eq!(schedule.graph().systems.len(), 0);
2506 }
2507
2508 #[test]
2509 fn remove_a_system_with_dependencies() {
2510 fn system_1() {}
2511 fn system_2() {}
2512
2513 let mut schedule = Schedule::default();
2514 schedule.add_systems((system_1, system_2).chain());
2515 let mut world = World::default();
2516
2517 let remove_count = schedule.remove_systems_in_set(
2518 system_1,
2519 &mut world,
2520 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2521 );
2522 assert_eq!(remove_count.unwrap(), 1);
2523
2524 schedule.initialize(&mut world).unwrap();
2526 assert_eq!(schedule.graph().systems.len(), 1);
2527 }
2528
2529 #[test]
2530 fn remove_a_system_and_still_ordered() {
2531 #[derive(Resource)]
2532 struct A;
2533
2534 fn system_1(_: ResMut<A>) {}
2535 fn system_2() {}
2536 fn system_3(_: ResMut<A>) {}
2537
2538 let mut schedule = Schedule::default();
2539 schedule.add_systems((system_1, system_2, system_3).chain());
2540 let mut world = World::new();
2541
2542 let _ = schedule.remove_systems_in_set(
2543 system_2,
2544 &mut world,
2545 ScheduleCleanupPolicy::RemoveSetAndSystems,
2546 );
2547
2548 let result = schedule.initialize(&mut world);
2549 assert!(result.is_ok());
2550 let conflicts = schedule.graph().conflicting_systems();
2551 assert!(conflicts.is_empty());
2552 }
2553
2554 #[test]
2555 fn remove_a_set_and_still_ordered() {
2556 #[derive(Resource)]
2557 struct A;
2558
2559 #[derive(SystemSet, Hash, PartialEq, Eq, Clone, Debug)]
2560 struct B;
2561
2562 fn system_1(_: ResMut<A>) {}
2563 fn system_2() {}
2564 fn system_3(_: ResMut<A>) {}
2565
2566 let mut schedule = Schedule::default();
2567 schedule.add_systems((system_1.before(B), system_2, system_3.after(B)));
2568 let mut world = World::new();
2569
2570 let _ = schedule.remove_systems_in_set(
2571 B,
2572 &mut world,
2573 ScheduleCleanupPolicy::RemoveSetAndSystems,
2574 );
2575
2576 let result = schedule.initialize(&mut world);
2577 assert!(result.is_ok());
2578 let conflicts = schedule.graph().conflicting_systems();
2579 assert!(conflicts.is_empty());
2580 }
2581
2582 #[test]
2583 fn build_pass_iteration_order() {
2584 #[derive(Debug)]
2585 struct Pass<const N: usize>;
2586
2587 impl<const N: usize> ScheduleBuildPass for Pass<N> {
2588 type EdgeOptions = ();
2589 fn add_dependency(
2590 &mut self,
2591 _from: crate::schedule::NodeId,
2592 _to: crate::schedule::NodeId,
2593 _options: Option<&Self::EdgeOptions>,
2594 ) {
2595 }
2596 fn build(
2597 &mut self,
2598 _world: &mut World,
2599 _graph: &mut super::ScheduleGraph,
2600 _dependency_flattened: &mut crate::schedule::graph::Dag<crate::schedule::SystemKey>,
2601 ) -> core::result::Result<(), crate::schedule::ScheduleBuildError> {
2602 Ok(())
2603 }
2604 fn collapse_set(
2605 &mut self,
2606 _set: crate::schedule::SystemSetKey,
2607 _systems: &indexmap::IndexSet<
2608 crate::schedule::SystemKey,
2609 bevy_platform::hash::FixedHasher,
2610 >,
2611 _dependency_flattening: &crate::schedule::graph::DiGraph<crate::schedule::NodeId>,
2612 ) -> impl Iterator<Item = (crate::schedule::NodeId, crate::schedule::NodeId)>
2613 {
2614 core::iter::empty()
2615 }
2616 }
2617
2618 let mut schedule = Schedule::default();
2619 schedule.add_build_pass(Pass::<0>);
2620 schedule.add_build_pass(Pass::<1>);
2621 schedule.add_build_pass(Pass::<2>);
2622
2623 let pass_order: Vec<TypeId> = schedule.graph().passes.keys().cloned().collect();
2624
2625 assert_eq!(
2626 pass_order,
2627 vec![
2628 TypeId::of::<AutoInsertApplyDeferredPass>(),
2629 TypeId::of::<Pass<0>>(),
2630 TypeId::of::<Pass<1>>(),
2631 TypeId::of::<Pass<2>>()
2632 ]
2633 );
2634 }
2635}