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