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_ecs_macros::Event;
14use bevy_platform::{
15 collections::{HashMap, HashSet},
16 hash::FixedHasher,
17};
18use bevy_utils::{default, TypeIdMap};
19use core::{
20 any::{Any, TypeId},
21 fmt::{Debug, Write},
22};
23use fixedbitset::FixedBitSet;
24use indexmap::{IndexMap, IndexSet};
25use log::{info, warn};
26use pass::ScheduleBuildPassObj;
27use thiserror::Error;
28#[cfg(feature = "trace")]
29use tracing::info_span;
30
31use crate::{change_detection::CheckChangeTicks, system::System};
32use crate::{
33 component::{ComponentId, Components},
34 prelude::Component,
35 resource::Resource,
36 schedule::*,
37 system::ScheduleSystem,
38 world::World,
39};
40
41pub use stepping::Stepping;
42use Direction::{Incoming, Outgoing};
43
44#[derive(Default, Resource)]
46pub struct Schedules {
47 inner: HashMap<InternedScheduleLabel, Schedule>,
48 pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
50 temporarily_removed: HashSet<InternedScheduleLabel>,
52 empty_labels: HashSet<InternedScheduleLabel>,
55}
56
57impl Schedules {
58 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn insert(&mut self, schedule: Schedule) -> Option<Schedule> {
68 self.temporarily_removed.remove(&schedule.label);
69 self.inner.insert(schedule.label, schedule)
71 }
72
73 pub fn reinsert(&mut self, schedule: Schedule) -> Option<Schedule> {
78 self.temporarily_removed.remove(&schedule.label);
79 self.inner.insert(schedule.label, schedule)
81 }
82
83 pub fn remove(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
85 self.inner.remove(&label.intern())
86 }
87
88 pub fn remove_temporarily(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
90 let label = label.intern();
91 let k = self.inner.remove(&label);
92 if k.is_some() {
93 self.temporarily_removed.insert(label);
94 self.empty_labels.remove(&label);
96 } else {
97 self.empty_labels.insert(label);
98 }
99 k
100 }
101
102 pub fn remove_entry(
104 &mut self,
105 label: impl ScheduleLabel,
106 ) -> Option<(InternedScheduleLabel, Schedule)> {
107 self.inner.remove_entry(&label.intern())
108 }
109
110 pub fn get_temporarily_removed(&self) -> HashSet<InternedScheduleLabel> {
112 self.temporarily_removed.clone()
113 }
114
115 pub fn get_empty_labels(&self) -> HashSet<InternedScheduleLabel> {
117 self.empty_labels.clone()
118 }
119
120 pub fn contains(&self, label: impl ScheduleLabel) -> bool {
122 self.inner.contains_key(&label.intern())
123 }
124
125 pub fn get(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
127 self.inner.get(&label.intern())
128 }
129
130 pub fn get_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
132 self.inner.get_mut(&label.intern())
133 }
134
135 pub fn entry(&mut self, label: impl ScheduleLabel) -> &mut Schedule {
137 self.inner
138 .entry(label.intern())
139 .or_insert_with(|| Schedule::new(label))
140 }
141
142 pub fn iter(&self) -> impl Iterator<Item = (&dyn ScheduleLabel, &Schedule)> {
144 self.inner
145 .iter()
146 .map(|(label, schedule)| (&**label, schedule))
147 }
148 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&dyn ScheduleLabel, &mut Schedule)> {
150 self.inner
151 .iter_mut()
152 .map(|(label, schedule)| (&**label, schedule))
153 }
154
155 pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
159 #[cfg(feature = "trace")]
160 let _all_span = info_span!("check stored schedule ticks").entered();
161 #[cfg_attr(
162 not(feature = "trace"),
163 expect(
164 unused_variables,
165 reason = "The `label` variable goes unused if the `trace` feature isn't active"
166 )
167 )]
168 for (label, schedule) in &mut self.inner {
169 #[cfg(feature = "trace")]
170 let name = format!("{label:?}");
171 #[cfg(feature = "trace")]
172 let _one_span = info_span!("check schedule ticks", name = &name).entered();
173 schedule.check_change_ticks(check);
174 }
175 }
176
177 pub fn configure_schedules(&mut self, schedule_build_settings: ScheduleBuildSettings) {
182 for (_, schedule) in &mut self.inner {
183 schedule.set_build_settings(schedule_build_settings.clone());
184 }
185 }
186
187 pub fn allow_ambiguous_component<T: Component>(&mut self, world: &mut World) {
189 self.ignored_scheduling_ambiguities
190 .insert(world.register_component::<T>());
191 }
192
193 pub fn allow_ambiguous_resource<T: Resource>(&mut self, world: &mut World) {
195 self.ignored_scheduling_ambiguities
196 .insert(world.components_registrator().register_component::<T>());
197 }
198
199 pub fn iter_ignored_ambiguities(&self) -> impl Iterator<Item = &ComponentId> + '_ {
201 self.ignored_scheduling_ambiguities.iter()
202 }
203
204 pub fn print_ignored_ambiguities(&self, components: &Components) {
209 let mut message =
210 "System order ambiguities caused by conflicts on the following types are ignored:\n"
211 .to_string();
212 for id in self.iter_ignored_ambiguities() {
213 writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap();
214 }
215
216 info!("{message}");
217 }
218
219 pub fn add_systems<M>(
221 &mut self,
222 schedule: impl ScheduleLabel,
223 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
224 ) -> &mut Self {
225 self.entry(schedule).add_systems(systems);
226
227 self
228 }
229
230 pub fn remove_systems_in_set<M>(
234 &mut self,
235 schedule: impl ScheduleLabel,
236 set: impl IntoSystemSet<M>,
237 world: &mut World,
238 policy: ScheduleCleanupPolicy,
239 ) -> Result<usize, ScheduleError> {
240 self.get_mut(schedule)
241 .ok_or(ScheduleError::ScheduleNotFound)?
242 .remove_systems_in_set(set, world, policy)
243 }
244
245 #[track_caller]
247 pub fn configure_sets<M>(
248 &mut self,
249 schedule: impl ScheduleLabel,
250 sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
251 ) -> &mut Self {
252 self.entry(schedule).configure_sets(sets);
253
254 self
255 }
256
257 #[track_caller]
264 pub fn ignore_ambiguity<M1, M2, S1, S2>(
265 &mut self,
266 schedule: impl ScheduleLabel,
267 a: S1,
268 b: S2,
269 ) -> &mut Self
270 where
271 S1: IntoSystemSet<M1>,
272 S2: IntoSystemSet<M2>,
273 {
274 self.entry(schedule).ignore_ambiguity(a, b);
275
276 self
277 }
278}
279
280#[derive(Default)]
282pub enum Chain {
283 #[default]
285 Unchained,
286 Chained(TypeIdMap<Box<dyn Any>>),
289}
290
291impl Chain {
292 pub fn set_chained(&mut self) {
294 if matches!(self, Chain::Unchained) {
295 *self = Self::Chained(Default::default());
296 };
297 }
298 pub fn set_chained_with_config<T: 'static>(&mut self, config: T) {
301 self.set_chained();
302 if let Chain::Chained(config_map) = self {
303 config_map.insert(TypeId::of::<T>(), Box::new(config));
304 } else {
305 unreachable!()
306 };
307 }
308}
309
310pub struct Schedule {
383 label: InternedScheduleLabel,
384 graph: ScheduleGraph,
385 executable: SystemSchedule,
386 executor: Box<dyn SystemExecutor>,
387 executor_initialized: bool,
388}
389
390#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
391struct DefaultSchedule;
392
393impl Default for Schedule {
394 fn default() -> Self {
399 Self::new(DefaultSchedule)
400 }
401}
402
403impl Schedule {
404 pub fn new(label: impl ScheduleLabel) -> Self {
406 let mut this = Self {
407 label: label.intern(),
408 graph: ScheduleGraph::new(),
409 executable: SystemSchedule::new(),
410 executor: default_executor(),
411 executor_initialized: false,
412 };
413 this.set_build_settings(Default::default());
415 this
416 }
417
418 pub fn is_changed(&self) -> bool {
420 self.graph.changed
421 }
422
423 pub fn label(&self) -> InternedScheduleLabel {
426 self.label
427 }
428
429 pub fn add_systems<M>(
431 &mut self,
432 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
433 ) -> &mut Self {
434 self.graph.process_configs(systems.into_configs(), false);
435 self
436 }
437
438 pub fn remove_systems_in_set<M>(
462 &mut self,
463 set: impl IntoSystemSet<M>,
464 world: &mut World,
465 policy: ScheduleCleanupPolicy,
466 ) -> Result<usize, ScheduleError> {
467 if self.graph.changed {
468 self.initialize(world)?;
469 }
470 self.graph.remove_systems_in_set(set, policy)
471 }
472
473 #[track_caller]
476 pub fn ignore_ambiguity<M1, M2, S1, S2>(&mut self, a: S1, b: S2) -> &mut Self
477 where
478 S1: IntoSystemSet<M1>,
479 S2: IntoSystemSet<M2>,
480 {
481 let a = a.into_system_set();
482 let b = b.into_system_set();
483
484 let a_id = self.graph.system_sets.get_key_or_insert(a.intern());
485 let b_id = self.graph.system_sets.get_key_or_insert(b.intern());
486
487 self.graph
488 .ambiguous_with
489 .add_edge(NodeId::Set(a_id), NodeId::Set(b_id));
490
491 self
492 }
493
494 #[track_caller]
496 pub fn configure_sets<M>(
497 &mut self,
498 sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
499 ) -> &mut Self {
500 self.graph.configure_sets(sets);
501 self
502 }
503
504 pub fn add_build_pass<T: ScheduleBuildPass>(&mut self, pass: T) -> &mut Self {
506 self.graph.passes.insert(TypeId::of::<T>(), Box::new(pass));
507 self
508 }
509
510 pub fn remove_build_pass<T: ScheduleBuildPass>(&mut self) {
512 self.graph.passes.shift_remove(&TypeId::of::<T>());
513 }
514
515 pub fn set_build_settings(&mut self, settings: ScheduleBuildSettings) -> &mut Self {
523 if settings.auto_insert_apply_deferred {
524 if !self
525 .graph
526 .passes
527 .contains_key(&TypeId::of::<passes::AutoInsertApplyDeferredPass>())
528 {
529 self.add_build_pass(passes::AutoInsertApplyDeferredPass::default());
530 }
531 } else {
532 self.remove_build_pass::<passes::AutoInsertApplyDeferredPass>();
533 }
534 self.graph.settings = settings;
535 self
536 }
537
538 pub fn get_build_settings(&self) -> ScheduleBuildSettings {
540 self.graph.settings.clone()
541 }
542
543 pub fn set_executor(&mut self, executor: impl SystemExecutor + 'static) -> &mut Self {
545 self.executor = Box::new(executor);
546 self.executor_initialized = false;
547 self
548 }
549
550 pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
555 self.executor.set_apply_final_deferred(apply_final_deferred);
556 self
557 }
558
559 pub fn run(&mut self, world: &mut World) {
561 #[cfg(feature = "trace")]
562 let _span = info_span!("schedule", name = ?self.label).entered();
563
564 world.check_change_ticks();
565 self.initialize(world).unwrap_or_else(|e| {
566 panic!(
567 "Error when initializing schedule {:?}: {}",
568 self.label,
569 e.to_string(self.graph(), world)
570 )
571 });
572
573 let error_handler = world.fallback_error_handler();
574
575 #[cfg(not(feature = "bevy_debug_stepping"))]
576 self.executor
577 .run(&mut self.executable, world, None, error_handler);
578
579 #[cfg(feature = "bevy_debug_stepping")]
580 {
581 let skip_systems = match world.get_resource_mut::<Stepping>() {
582 None => None,
583 Some(mut stepping) => stepping.skipped_systems(self),
584 };
585
586 self.executor.run(
587 &mut self.executable,
588 world,
589 skip_systems.as_ref(),
590 error_handler,
591 );
592 }
593 }
594
595 pub fn initialize(
602 &mut self,
603 world: &mut World,
604 ) -> Result<Option<ScheduleBuildMetadata>, ScheduleBuildError> {
605 let mut build_metadata = None;
606 if self.graph.changed {
607 self.graph.initialize(world);
608 let ignored_ambiguities = world
609 .get_resource_or_init::<Schedules>()
610 .ignored_scheduling_ambiguities
611 .clone();
612
613 let mut event = ScheduleBuilt {
614 label: self.label,
615 build_metadata: self.graph.update_schedule(
616 world,
617 &mut self.executable,
618 &ignored_ambiguities,
619 self.label,
620 )?,
621 };
622 self.graph.changed = false;
623 self.executor_initialized = false;
624
625 world.trigger_ref(&mut event);
626 build_metadata = Some(event.build_metadata);
627 }
628
629 if !self.executor_initialized {
630 self.executor.init(&self.executable);
631 self.executor_initialized = true;
632 }
633
634 Ok(build_metadata)
635 }
636
637 pub fn graph(&self) -> &ScheduleGraph {
639 &self.graph
640 }
641
642 pub fn graph_mut(&mut self) -> &mut ScheduleGraph {
644 &mut self.graph
645 }
646
647 pub(crate) fn executable(&self) -> &SystemSchedule {
649 &self.executable
650 }
651
652 pub fn check_change_ticks(&mut self, check: CheckChangeTicks) {
656 for system in &mut self.executable.systems {
657 if !is_apply_deferred(system) {
658 system.check_change_tick(check);
659 }
660 }
661
662 for conditions in &mut self.executable.system_conditions {
663 for condition in conditions {
664 condition.check_change_tick(check);
665 }
666 }
667
668 for conditions in &mut self.executable.set_conditions {
669 for condition in conditions {
670 condition.check_change_tick(check);
671 }
672 }
673 }
674
675 pub fn apply_deferred(&mut self, world: &mut World) {
684 for SystemWithAccess { system, .. } in &mut self.executable.systems {
685 system.apply_deferred(world);
686 }
687 }
688
689 pub fn systems(
694 &self,
695 ) -> Result<impl Iterator<Item = (SystemKey, &ScheduleSystem)> + Sized, ScheduleNotInitialized>
696 {
697 if !self.executor_initialized {
698 return Err(ScheduleNotInitialized);
699 }
700
701 let iter = self
702 .executable
703 .system_ids
704 .iter()
705 .zip(&self.executable.systems)
706 .map(|(&node_id, system)| (node_id, &system.system));
707
708 Ok(iter)
709 }
710
711 pub fn systems_len(&self) -> usize {
713 if !self.executor_initialized {
714 self.graph.systems.len()
715 } else {
716 self.executable.systems.len()
717 }
718 }
719}
720
721#[derive(Default)]
726pub struct ScheduleGraph {
727 pub systems: Systems,
729 pub system_sets: SystemSets,
731 hierarchy: Dag<NodeId>,
733 dependency: Dag<NodeId>,
735 set_systems: DagGroups<SystemSetKey, SystemKey>,
737 ambiguous_with: UnGraph<NodeId>,
738 pub ambiguous_with_all: HashSet<NodeId>,
740 conflicting_systems: ConflictingSystems,
741 anonymous_sets: usize,
742 changed: bool,
743 settings: ScheduleBuildSettings,
744 passes: IndexMap<TypeId, Box<dyn ScheduleBuildPassObj>, FixedHasher>,
745}
746
747impl ScheduleGraph {
748 pub fn new() -> Self {
750 Self {
751 systems: Systems::default(),
752 system_sets: SystemSets::default(),
753 hierarchy: Dag::new(),
754 dependency: Dag::new(),
755 set_systems: DagGroups::default(),
756 ambiguous_with: UnGraph::default(),
757 ambiguous_with_all: HashSet::default(),
758 conflicting_systems: ConflictingSystems::default(),
759 anonymous_sets: 0,
760 changed: false,
761 settings: default(),
762 passes: default(),
763 }
764 }
765
766 pub fn hierarchy(&self) -> &Dag<NodeId> {
771 &self.hierarchy
772 }
773
774 pub fn dependency(&self) -> &Dag<NodeId> {
779 &self.dependency
780 }
781
782 pub fn conflicting_systems(&self) -> &ConflictingSystems {
787 &self.conflicting_systems
788 }
789
790 fn process_config<T: ProcessScheduleConfig + Schedulable>(
791 &mut self,
792 config: ScheduleConfig<T>,
793 collect_nodes: bool,
794 ) -> ProcessConfigsResult {
795 ProcessConfigsResult {
796 densely_chained: true,
797 nodes: collect_nodes
798 .then_some(T::process_config(self, config))
799 .into_iter()
800 .collect(),
801 }
802 }
803
804 fn apply_collective_conditions<
805 T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
806 >(
807 &mut self,
808 configs: &mut [ScheduleConfigs<T>],
809 collective_conditions: Vec<BoxedCondition>,
810 ) {
811 if !collective_conditions.is_empty() {
812 if let [config] = configs {
813 for condition in collective_conditions {
814 config.run_if_dyn(condition);
815 }
816 } else {
817 let set = self.create_anonymous_set();
818 for config in configs.iter_mut() {
819 config.in_set_inner(set.intern());
820 }
821 let mut set_config = InternedSystemSet::into_config(set.intern());
822 set_config.conditions.extend(collective_conditions);
823 self.configure_set_inner(set_config);
824 }
825 }
826 }
827
828 #[track_caller]
837 fn process_configs<
838 T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
839 >(
840 &mut self,
841 configs: ScheduleConfigs<T>,
842 collect_nodes: bool,
843 ) -> ProcessConfigsResult {
844 match configs {
845 ScheduleConfigs::ScheduleConfig(config) => self.process_config(config, collect_nodes),
846 ScheduleConfigs::Configs {
847 metadata,
848 mut configs,
849 collective_conditions,
850 } => {
851 self.apply_collective_conditions(&mut configs, collective_conditions);
852
853 let is_chained = matches!(metadata, Chain::Chained(_));
854
855 let mut densely_chained = is_chained || configs.len() == 1;
859 let mut configs = configs.into_iter();
860 let mut nodes = Vec::new();
861
862 let Some(first) = configs.next() else {
863 return ProcessConfigsResult {
864 nodes: Vec::new(),
865 densely_chained,
866 };
867 };
868 let mut previous_result = self.process_configs(first, collect_nodes || is_chained);
869 densely_chained &= previous_result.densely_chained;
870
871 for current in configs {
872 let current_result = self.process_configs(current, collect_nodes || is_chained);
873 densely_chained &= current_result.densely_chained;
874
875 if let Chain::Chained(chain_options) = &metadata {
876 let current_nodes = if current_result.densely_chained {
878 ¤t_result.nodes[..1]
879 } else {
880 ¤t_result.nodes
881 };
882 let previous_nodes = if previous_result.densely_chained {
884 &previous_result.nodes[previous_result.nodes.len() - 1..]
885 } else {
886 &previous_result.nodes
887 };
888
889 self.dependency
890 .reserve_edges(previous_nodes.len() * current_nodes.len());
891 for previous_node in previous_nodes {
892 for current_node in current_nodes {
893 self.dependency.add_edge(*previous_node, *current_node);
894
895 for pass in self.passes.values_mut() {
896 pass.add_dependency(
897 *previous_node,
898 *current_node,
899 chain_options,
900 );
901 }
902 }
903 }
904 }
905 if collect_nodes {
906 nodes.append(&mut previous_result.nodes);
907 }
908
909 previous_result = current_result;
910 }
911 if collect_nodes {
912 nodes.append(&mut previous_result.nodes);
913 }
914
915 ProcessConfigsResult {
916 nodes,
917 densely_chained,
918 }
919 }
920 }
921 }
922
923 fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
925 let key = self.systems.insert(config.node, config.conditions);
926
927 self.update_graphs(NodeId::System(key), config.metadata);
929
930 key
931 }
932
933 #[track_caller]
934 fn configure_sets<M>(&mut self, sets: impl IntoScheduleConfigs<InternedSystemSet, M>) {
935 self.process_configs(sets.into_configs(), false);
936 }
937
938 fn configure_set_inner(&mut self, config: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
940 let key = self.system_sets.insert(config.node, config.conditions);
941
942 self.update_graphs(NodeId::Set(key), config.metadata);
944
945 key
946 }
947
948 fn create_anonymous_set(&mut self) -> AnonymousSet {
949 let id = self.anonymous_sets;
950 self.anonymous_sets += 1;
951 AnonymousSet::new(id)
952 }
953
954 pub fn systems_in_set(
965 &self,
966 system_set: InternedSystemSet,
967 ) -> Result<&IndexSet<SystemKey, FixedHasher>, ScheduleError> {
968 if self.changed {
969 return Err(ScheduleError::Uninitialized);
970 }
971 let system_set_id = self
972 .system_sets
973 .get_key(system_set)
974 .ok_or(ScheduleError::SetNotFound)?;
975 self.set_systems
976 .get(&system_set_id)
977 .ok_or(ScheduleError::SetNotFound)
978 }
979
980 fn add_edges_for_transitive_dependencies(&mut self, node: NodeId) {
981 let in_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Incoming).collect();
982 let out_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Outgoing).collect();
983
984 self.hierarchy
985 .reserve_edges(in_nodes.len() * out_nodes.len());
986 for &in_node in &in_nodes {
987 for &out_node in &out_nodes {
988 self.hierarchy.add_edge(in_node, out_node);
989 }
990 }
991
992 let in_nodes: Vec<_> = self.dependency.neighbors_directed(node, Incoming).collect();
993 let out_nodes: Vec<_> = self.dependency.neighbors_directed(node, Outgoing).collect();
994
995 self.dependency
996 .reserve_edges(in_nodes.len() * out_nodes.len());
997 for &in_node in &in_nodes {
998 for &out_node in &out_nodes {
999 self.dependency.add_edge(in_node, out_node);
1000 }
1001 }
1002 }
1003
1004 pub fn remove_systems_in_set<M>(
1006 &mut self,
1007 system_set: impl IntoSystemSet<M>,
1008 policy: ScheduleCleanupPolicy,
1009 ) -> Result<usize, ScheduleError> {
1010 let set = system_set.into_system_set();
1011 let interned = set.intern();
1012 let keys = self.systems_in_set(interned)?.clone();
1014
1015 self.changed = true;
1016
1017 match policy {
1018 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages => {
1019 let Some(set_key) = self.system_sets.get_key(interned) else {
1020 return Err(ScheduleError::SetNotFound);
1021 };
1022
1023 self.remove_systems_by_keys(&keys);
1024 self.remove_set_by_key(set_key);
1025
1026 Ok(keys.len())
1027 }
1028 ScheduleCleanupPolicy::RemoveSystemsOnlyAllowBreakages => {
1029 self.remove_systems_by_keys(&keys);
1030
1031 Ok(keys.len())
1032 }
1033 ScheduleCleanupPolicy::RemoveSetAndSystems => {
1034 let Some(set_key) = self.system_sets.get_key(interned) else {
1035 return Err(ScheduleError::SetNotFound);
1036 };
1037
1038 for &key in &keys {
1039 self.add_edges_for_transitive_dependencies(key.into());
1040 }
1041
1042 self.add_edges_for_transitive_dependencies(set_key.into());
1043
1044 self.remove_systems_by_keys(&keys);
1045 self.remove_set_by_key(set_key);
1046
1047 Ok(keys.len())
1048 }
1049 ScheduleCleanupPolicy::RemoveSystemsOnly => {
1050 for &key in &keys {
1051 self.add_edges_for_transitive_dependencies(key.into());
1052 }
1053
1054 self.remove_systems_by_keys(&keys);
1055
1056 Ok(keys.len())
1057 }
1058 }
1059 }
1060
1061 fn remove_systems_by_keys(&mut self, keys: &IndexSet<SystemKey, FixedHasher>) {
1062 for &key in keys {
1063 self.systems.remove(key);
1064
1065 self.hierarchy.remove_node(key.into());
1066 self.dependency.remove_node(key.into());
1067 self.ambiguous_with.remove_node(key.into());
1068 self.ambiguous_with_all.remove(&NodeId::from(key));
1069 }
1070 }
1071
1072 fn remove_set_by_key(&mut self, key: SystemSetKey) {
1073 self.system_sets.remove(key);
1074 self.set_systems.remove(&key);
1075 self.hierarchy.remove_node(key.into());
1076 self.dependency.remove_node(key.into());
1077 self.ambiguous_with.remove_node(key.into());
1078 self.ambiguous_with_all.remove(&NodeId::from(key));
1079 }
1080
1081 fn update_graphs(&mut self, id: NodeId, graph_info: GraphInfo) {
1083 self.changed = true;
1084
1085 let GraphInfo {
1086 hierarchy: sets,
1087 dependencies,
1088 ambiguous_with,
1089 ..
1090 } = graph_info;
1091
1092 self.hierarchy.add_node(id);
1093 self.dependency.add_node(id);
1094
1095 for key in sets
1096 .into_iter()
1097 .map(|set| self.system_sets.get_key_or_insert(set))
1098 {
1099 self.hierarchy.add_edge(NodeId::Set(key), id);
1100
1101 self.dependency.add_node(NodeId::Set(key));
1103 }
1104
1105 for (kind, key, options) in
1106 dependencies
1107 .into_iter()
1108 .map(|Dependency { kind, set, options }| {
1109 (kind, self.system_sets.get_key_or_insert(set), options)
1110 })
1111 {
1112 let (lhs, rhs) = match kind {
1113 DependencyKind::Before => (id, NodeId::Set(key)),
1114 DependencyKind::After => (NodeId::Set(key), id),
1115 };
1116 self.dependency.add_edge(lhs, rhs);
1117 for pass in self.passes.values_mut() {
1118 pass.add_dependency(lhs, rhs, &options);
1119 }
1120
1121 self.hierarchy.add_node(NodeId::Set(key));
1123 }
1124
1125 match ambiguous_with {
1126 Ambiguity::Check => (),
1127 Ambiguity::IgnoreWithSet(ambiguous_with) => {
1128 for key in ambiguous_with
1129 .into_iter()
1130 .map(|set| self.system_sets.get_key_or_insert(set))
1131 {
1132 self.ambiguous_with.add_edge(id, NodeId::Set(key));
1133 }
1134 }
1135 Ambiguity::IgnoreAll => {
1136 self.ambiguous_with_all.insert(id);
1137 }
1138 }
1139 }
1140
1141 pub fn initialize(&mut self, world: &mut World) {
1144 self.systems.initialize(world);
1145 self.system_sets.initialize(world);
1146 }
1147
1148 pub fn build_schedule(
1156 &mut self,
1157 world: &mut World,
1158 ignored_ambiguities: &BTreeSet<ComponentId>,
1159 ) -> Result<(SystemSchedule, ScheduleBuildMetadata), ScheduleBuildError> {
1160 let mut warnings = Vec::new();
1161
1162 let hierarchy_analysis = self
1164 .hierarchy
1165 .analyze()
1166 .map_err(ScheduleBuildError::HierarchySort)?;
1167
1168 if self.settings.hierarchy_detection != LogLevel::Ignore
1171 && let Err(e) = hierarchy_analysis.check_for_redundant_edges()
1172 {
1173 match self.settings.hierarchy_detection {
1174 LogLevel::Error => return Err(ScheduleBuildWarning::HierarchyRedundancy(e).into()),
1175 LogLevel::Warn => warnings.push(ScheduleBuildWarning::HierarchyRedundancy(e)),
1176 LogLevel::Ignore => unreachable!(),
1177 }
1178 }
1179 self.hierarchy.remove_redundant_edges(&hierarchy_analysis);
1181
1182 let dependency_analysis = self
1184 .dependency
1185 .analyze()
1186 .map_err(ScheduleBuildError::DependencySort)?;
1187
1188 dependency_analysis.check_for_cross_dependencies(&hierarchy_analysis)?;
1190
1191 self.set_systems = self
1193 .hierarchy
1194 .group_by_key(self.system_sets.len())
1195 .map_err(ScheduleBuildError::HierarchySort)?;
1196 dependency_analysis.check_for_overlapping_groups(&self.set_systems)?;
1198
1199 self.system_sets.check_type_set_ambiguity(
1201 &self.set_systems,
1202 &self.ambiguous_with,
1203 &self.dependency,
1204 )?;
1205
1206 let mut flat_dependency =
1210 self.set_systems
1211 .flatten(self.dependency.clone(), |set, systems, flattening, temp| {
1212 for pass in self.passes.values_mut() {
1213 pass.collapse_set(set, systems, flattening, temp);
1214 }
1215 });
1216
1217 let mut passes = core::mem::take(&mut self.passes);
1219 let mut added_edges = Default::default();
1220 for pass in passes.values_mut() {
1221 pass.build(
1222 world,
1223 self,
1224 FlattenedDependencies {
1225 dag: &mut flat_dependency,
1226 added_edges: &mut added_edges,
1227 },
1228 )?;
1229 }
1230 self.passes = passes;
1231
1232 let flat_dependency_analysis = flat_dependency
1235 .analyze()
1236 .map_err(ScheduleBuildError::FlatDependencySort)?;
1237 flat_dependency.remove_redundant_edges(&flat_dependency_analysis);
1238
1239 let flat_ambiguous_with = self.set_systems.flatten_undirected(&self.ambiguous_with);
1244
1245 self.conflicting_systems = self.systems.get_conflicting_systems(
1247 &flat_dependency_analysis,
1248 &flat_ambiguous_with,
1249 &self.ambiguous_with_all,
1250 ignored_ambiguities,
1251 );
1252 if self.settings.ambiguity_detection != LogLevel::Ignore
1254 && let Err(e) = self.conflicting_systems.check_if_not_empty()
1255 {
1256 match self.settings.ambiguity_detection {
1257 LogLevel::Error => return Err(ScheduleBuildWarning::Ambiguity(e).into()),
1258 LogLevel::Warn => warnings.push(ScheduleBuildWarning::Ambiguity(e)),
1259 LogLevel::Ignore => unreachable!(),
1260 }
1261 }
1262
1263 Ok((
1265 self.build_schedule_inner(flat_dependency, hierarchy_analysis),
1266 ScheduleBuildMetadata {
1267 warnings,
1268 edges_added_by_build_passes: added_edges,
1269 },
1270 ))
1271 }
1272
1273 fn build_schedule_inner(
1274 &self,
1275 flat_dependency: Dag<SystemKey>,
1276 hierarchy_analysis: DagAnalysis<NodeId>,
1277 ) -> SystemSchedule {
1278 let dg_system_ids = flat_dependency.get_toposort().unwrap().to_vec();
1279 let dg_system_idx_map = dg_system_ids
1280 .iter()
1281 .cloned()
1282 .enumerate()
1283 .map(|(i, id)| (id, i))
1284 .collect::<HashMap<_, _>>();
1285
1286 let hierarchy_toposort = self.hierarchy.get_toposort().unwrap();
1287 let hg_systems = hierarchy_toposort
1288 .iter()
1289 .cloned()
1290 .enumerate()
1291 .filter_map(|(i, id)| Some((i, id.as_system()?)))
1292 .collect::<Vec<_>>();
1293 let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = hierarchy_toposort
1294 .iter()
1295 .cloned()
1296 .enumerate()
1297 .filter_map(|(i, id)| {
1298 let key = id.as_set()?;
1301 self.system_sets.has_conditions(key).then_some((i, key))
1302 })
1303 .unzip();
1304
1305 let sys_count = self.systems.len();
1306 let set_with_conditions_count = hg_set_ids.len();
1307 let hg_node_count = self.hierarchy.node_count();
1308
1309 let mut system_dependencies = Vec::with_capacity(sys_count);
1312 let mut system_dependents = Vec::with_capacity(sys_count);
1313 for &sys_key in &dg_system_ids {
1314 let num_dependencies = flat_dependency
1315 .neighbors_directed(sys_key, Incoming)
1316 .count();
1317
1318 let dependents = flat_dependency
1319 .neighbors_directed(sys_key, Outgoing)
1320 .map(|dep_id| dg_system_idx_map[&dep_id])
1321 .collect::<Vec<_>>();
1322
1323 system_dependencies.push(num_dependencies);
1324 system_dependents.push(dependents);
1325 }
1326
1327 let mut systems_in_sets_with_conditions =
1330 vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
1331 for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
1332 let bitset = &mut systems_in_sets_with_conditions[i];
1333 for &(col, sys_key) in &hg_systems {
1334 let idx = dg_system_idx_map[&sys_key];
1335 let is_descendant = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1336 bitset.set(idx, is_descendant);
1337 }
1338 }
1339
1340 let mut sets_with_conditions_of_systems =
1341 vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
1342 for &(col, sys_key) in &hg_systems {
1343 let i = dg_system_idx_map[&sys_key];
1344 let bitset = &mut sets_with_conditions_of_systems[i];
1345 for (idx, &row) in hg_set_with_conditions_idxs
1346 .iter()
1347 .enumerate()
1348 .take_while(|&(_idx, &row)| row < col)
1349 {
1350 let is_ancestor = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1351 bitset.set(idx, is_ancestor);
1352 }
1353 }
1354
1355 SystemSchedule {
1356 systems: Vec::with_capacity(sys_count),
1357 system_conditions: Vec::with_capacity(sys_count),
1358 set_conditions: Vec::with_capacity(set_with_conditions_count),
1359 system_ids: dg_system_ids,
1360 set_ids: hg_set_ids,
1361 system_dependencies,
1362 system_dependents,
1363 sets_with_conditions_of_systems,
1364 systems_in_sets_with_conditions,
1365 }
1366 }
1367
1368 fn update_schedule(
1370 &mut self,
1371 world: &mut World,
1372 schedule: &mut SystemSchedule,
1373 ignored_ambiguities: &BTreeSet<ComponentId>,
1374 schedule_label: InternedScheduleLabel,
1375 ) -> Result<ScheduleBuildMetadata, ScheduleBuildError> {
1376 if !self.systems.is_initialized() || !self.system_sets.is_initialized() {
1377 return Err(ScheduleBuildError::Uninitialized);
1378 }
1379
1380 for ((key, system), conditions) in schedule
1382 .system_ids
1383 .drain(..)
1384 .zip(schedule.systems.drain(..))
1385 .zip(schedule.system_conditions.drain(..))
1386 {
1387 if let Some(node) = self.systems.node_mut(key) {
1388 node.inner = Some(system);
1389 }
1390
1391 if let Some(node_conditions) = self.systems.get_conditions_mut(key) {
1392 *node_conditions = conditions;
1393 }
1394 }
1395
1396 for (key, conditions) in schedule
1397 .set_ids
1398 .drain(..)
1399 .zip(schedule.set_conditions.drain(..))
1400 {
1401 if let Some(node_conditions) = self.system_sets.get_conditions_mut(key) {
1402 *node_conditions = conditions;
1403 }
1404 }
1405
1406 let (new_schedule, build_metadata) = self.build_schedule(world, ignored_ambiguities)?;
1407 *schedule = new_schedule;
1408
1409 for warning in &build_metadata.warnings {
1410 warn!(
1411 "{:?} schedule built successfully, however: {}",
1412 schedule_label,
1413 warning.to_string(self, world)
1414 );
1415 }
1416
1417 for &key in &schedule.system_ids {
1419 let system = self.systems.node_mut(key).unwrap().inner.take().unwrap();
1420 let conditions = core::mem::take(self.systems.get_conditions_mut(key).unwrap());
1421 schedule.systems.push(system);
1422 schedule.system_conditions.push(conditions);
1423 }
1424
1425 for &key in &schedule.set_ids {
1426 let conditions = core::mem::take(self.system_sets.get_conditions_mut(key).unwrap());
1427 schedule.set_conditions.push(conditions);
1428 }
1429
1430 Ok(build_metadata)
1431 }
1432}
1433
1434struct ProcessConfigsResult {
1436 nodes: Vec<NodeId>,
1439 densely_chained: bool,
1443}
1444
1445trait ProcessScheduleConfig: Schedulable + Sized {
1447 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId;
1449}
1450
1451impl ProcessScheduleConfig for ScheduleSystem {
1452 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1453 NodeId::System(schedule_graph.add_system_inner(config))
1454 }
1455}
1456
1457impl ProcessScheduleConfig for InternedSystemSet {
1458 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1459 NodeId::Set(schedule_graph.configure_set_inner(config))
1460 }
1461}
1462
1463#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
1465pub enum ScheduleCleanupPolicy {
1466 #[default]
1471 RemoveSetAndSystems,
1472 RemoveSystemsOnly,
1476 RemoveSetAndSystemsAllowBreakages,
1481 RemoveSystemsOnlyAllowBreakages,
1485}
1486
1487impl ScheduleGraph {
1489 pub fn get_node_name(&self, id: &NodeId) -> String {
1496 self.get_node_name_inner(id, self.settings.report_sets)
1497 }
1498
1499 #[inline]
1500 fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
1501 match *id {
1502 NodeId::System(key) => {
1503 let name = self.systems[key].name();
1504 let name = if self.settings.use_shortnames {
1505 name.shortname().to_string()
1506 } else {
1507 name.to_string()
1508 };
1509 if report_sets {
1510 let sets = self.names_of_sets_containing_node(id);
1511 if sets.is_empty() {
1512 name
1513 } else if sets.len() == 1 {
1514 format!("{name} (in set {})", sets[0])
1515 } else {
1516 format!("{name} (in sets {})", sets.join(", "))
1517 }
1518 } else {
1519 name
1520 }
1521 }
1522 NodeId::Set(key) => {
1523 let set = &self.system_sets[key];
1524 if set.is_anonymous() {
1525 self.anonymous_set_name(id)
1526 } else {
1527 format!("{set:?}")
1528 }
1529 }
1530 }
1531 }
1532
1533 fn anonymous_set_name(&self, id: &NodeId) -> String {
1534 format!(
1535 "({})",
1536 self.hierarchy
1537 .edges_directed(*id, Outgoing)
1538 .map(|(_, member_id)| self.get_node_name_inner(&member_id, false))
1540 .reduce(|a, b| format!("{a}, {b}"))
1541 .unwrap_or_default()
1542 )
1543 }
1544
1545 fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(SystemSetKey) -> bool) {
1546 for (set_id, _) in self.hierarchy.edges_directed(id, Incoming) {
1547 let NodeId::Set(set_key) = set_id else {
1548 continue;
1549 };
1550 if f(set_key) {
1551 self.traverse_sets_containing_node(NodeId::Set(set_key), f);
1552 }
1553 }
1554 }
1555
1556 fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec<String> {
1557 let mut sets = <HashSet<_>>::default();
1558 self.traverse_sets_containing_node(*id, &mut |key| {
1559 self.system_sets[key].system_type().is_none() && sets.insert(key)
1560 });
1561 let mut sets: Vec<_> = sets
1562 .into_iter()
1563 .map(|key| self.get_node_name(&NodeId::Set(key)))
1564 .collect();
1565 sets.sort();
1566 sets
1567 }
1568}
1569
1570#[derive(Debug, Clone, Copy, PartialEq)]
1572pub enum LogLevel {
1573 Ignore,
1575 Warn,
1577 Error,
1579}
1580
1581#[derive(Clone, Debug)]
1583pub struct ScheduleBuildSettings {
1584 pub ambiguity_detection: LogLevel,
1590 pub hierarchy_detection: LogLevel,
1596 pub auto_insert_apply_deferred: bool,
1606 pub use_shortnames: bool,
1610 pub report_sets: bool,
1614}
1615
1616impl Default for ScheduleBuildSettings {
1617 fn default() -> Self {
1618 Self::new()
1619 }
1620}
1621
1622impl ScheduleBuildSettings {
1623 pub const fn new() -> Self {
1626 Self {
1627 ambiguity_detection: LogLevel::Ignore,
1628 hierarchy_detection: LogLevel::Warn,
1629 auto_insert_apply_deferred: true,
1630 use_shortnames: true,
1631 report_sets: true,
1632 }
1633 }
1634}
1635
1636pub struct ScheduleBuildMetadata {
1638 pub warnings: Vec<ScheduleBuildWarning>,
1640 pub edges_added_by_build_passes: HashSet<(SystemKey, SystemKey)>,
1645}
1646
1647#[derive(Event)]
1653pub struct ScheduleBuilt {
1654 pub label: InternedScheduleLabel,
1656 pub build_metadata: ScheduleBuildMetadata,
1658}
1659
1660#[derive(Error, Debug)]
1663#[error("executable schedule has not been built")]
1664pub struct ScheduleNotInitialized;
1665
1666#[cfg(test)]
1667mod tests {
1668 use alloc::{vec, vec::Vec};
1669 use core::any::TypeId;
1670
1671 use bevy_ecs_macros::ScheduleLabel;
1672
1673 use crate::{
1674 error::{ignore, panic, FallbackErrorHandler, Result},
1675 prelude::{ApplyDeferred, IntoSystemSet, Res, Resource},
1676 schedule::{
1677 passes::AutoInsertApplyDeferredPass, tests::ResMut, FlattenedDependencies,
1678 IntoScheduleConfigs, Schedule, ScheduleBuildPass, ScheduleBuildSettings,
1679 ScheduleCleanupPolicy, SystemSet,
1680 },
1681 system::Commands,
1682 world::World,
1683 };
1684
1685 use super::Schedules;
1686
1687 #[derive(Resource)]
1688 struct Resource1;
1689
1690 #[derive(Resource)]
1691 struct Resource2;
1692
1693 #[test]
1694 fn unchanged_auto_insert_apply_deferred_has_no_effect() {
1695 use alloc::{vec, vec::Vec};
1696
1697 #[derive(PartialEq, Debug)]
1698 enum Entry {
1699 System(usize),
1700 SyncPoint(usize),
1701 }
1702
1703 #[derive(Resource, Default)]
1704 struct Log(Vec<Entry>);
1705
1706 fn system<const N: usize>(mut res: ResMut<Log>, mut commands: Commands) {
1707 res.0.push(Entry::System(N));
1708 commands
1709 .queue(|world: &mut World| world.resource_mut::<Log>().0.push(Entry::SyncPoint(N)));
1710 }
1711
1712 let mut world = World::default();
1713 world.init_resource::<Log>();
1714 let mut schedule = Schedule::default();
1715 schedule.add_systems((system::<1>, system::<2>).chain_ignore_deferred());
1716 schedule.set_build_settings(ScheduleBuildSettings {
1717 auto_insert_apply_deferred: true,
1718 ..Default::default()
1719 });
1720 schedule.run(&mut world);
1721 let actual = world.remove_resource::<Log>().unwrap().0;
1722
1723 let expected = vec![
1724 Entry::System(1),
1725 Entry::System(2),
1726 Entry::SyncPoint(1),
1727 Entry::SyncPoint(2),
1728 ];
1729
1730 assert_eq!(actual, expected);
1731 }
1732
1733 #[test]
1735 fn ambiguous_with_not_breaking_run_conditions() {
1736 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1737 struct Set;
1738
1739 let mut world = World::new();
1740 let mut schedule = Schedule::default();
1741
1742 let system: fn() = || {
1743 panic!("This system must not run");
1744 };
1745
1746 schedule.configure_sets(Set.run_if(|| false));
1747 schedule.add_systems(system.ambiguous_with(|| ()).in_set(Set));
1748 schedule.run(&mut world);
1749 }
1750
1751 #[test]
1752 fn inserts_a_sync_point() {
1753 let mut schedule = Schedule::default();
1754 let mut world = World::default();
1755 schedule.add_systems(
1756 (
1757 |mut commands: Commands| commands.insert_resource(Resource1),
1758 |_: Res<Resource1>| {},
1759 )
1760 .chain(),
1761 );
1762 schedule.run(&mut world);
1763
1764 assert_eq!(schedule.executable.systems.len(), 3);
1766 }
1767
1768 #[test]
1769 fn explicit_sync_point_used_as_auto_sync_point() {
1770 let mut schedule = Schedule::default();
1771 let mut world = World::default();
1772 schedule.add_systems(
1773 (
1774 |mut commands: Commands| commands.insert_resource(Resource1),
1775 |_: Res<Resource1>| {},
1776 )
1777 .chain(),
1778 );
1779 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain());
1780 schedule.run(&mut world);
1781
1782 assert_eq!(schedule.executable.systems.len(), 5);
1784 }
1785
1786 #[test]
1787 fn conditional_explicit_sync_point_not_used_as_auto_sync_point() {
1788 let mut schedule = Schedule::default();
1789 let mut world = World::default();
1790 schedule.add_systems(
1791 (
1792 |mut commands: Commands| commands.insert_resource(Resource1),
1793 |_: Res<Resource1>| {},
1794 )
1795 .chain(),
1796 );
1797 schedule.add_systems((|| {}, ApplyDeferred.run_if(|| false), || {}).chain());
1798 schedule.run(&mut world);
1799
1800 assert_eq!(schedule.executable.systems.len(), 6);
1802 }
1803
1804 #[test]
1805 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_chain() {
1806 let mut schedule = Schedule::default();
1807 let mut world = World::default();
1808 schedule.add_systems(
1809 (
1810 |mut commands: Commands| commands.insert_resource(Resource1),
1811 |_: Res<Resource1>| {},
1812 )
1813 .chain(),
1814 );
1815 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().run_if(|| false));
1816 schedule.run(&mut world);
1817
1818 assert_eq!(schedule.executable.systems.len(), 6);
1820 }
1821
1822 #[test]
1823 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_system_set() {
1824 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1825 struct Set;
1826
1827 let mut schedule = Schedule::default();
1828 let mut world = World::default();
1829 schedule.configure_sets(Set.run_if(|| false));
1830 schedule.add_systems(
1831 (
1832 |mut commands: Commands| commands.insert_resource(Resource1),
1833 |_: Res<Resource1>| {},
1834 )
1835 .chain(),
1836 );
1837 schedule.add_systems((|| {}, ApplyDeferred.in_set(Set), || {}).chain());
1838 schedule.run(&mut world);
1839
1840 assert_eq!(schedule.executable.systems.len(), 6);
1842 }
1843
1844 #[test]
1845 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_nested_system_set()
1846 {
1847 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1848 struct Set1;
1849 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1850 struct Set2;
1851
1852 let mut schedule = Schedule::default();
1853 let mut world = World::default();
1854 schedule.configure_sets(Set2.run_if(|| false));
1855 schedule.configure_sets(Set1.in_set(Set2));
1856 schedule.add_systems(
1857 (
1858 |mut commands: Commands| commands.insert_resource(Resource1),
1859 |_: Res<Resource1>| {},
1860 )
1861 .chain(),
1862 );
1863 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().in_set(Set1));
1864 schedule.run(&mut world);
1865
1866 assert_eq!(schedule.executable.systems.len(), 6);
1868 }
1869
1870 #[test]
1871 fn merges_sync_points_into_one() {
1872 let mut schedule = Schedule::default();
1873 let mut world = World::default();
1874 schedule.add_systems(
1876 (
1877 (
1878 |mut commands: Commands| commands.insert_resource(Resource1),
1879 |mut commands: Commands| commands.insert_resource(Resource2),
1880 ),
1881 |_: Res<Resource1>, _: Res<Resource2>| {},
1882 )
1883 .chain(),
1884 );
1885 schedule.run(&mut world);
1886
1887 assert_eq!(schedule.executable.systems.len(), 4);
1889
1890 schedule.add_systems(((
1892 (
1893 |mut commands: Commands| commands.insert_resource(Resource1),
1894 |mut commands: Commands| commands.insert_resource(Resource2),
1895 ),
1896 |_: Res<Resource1>, _: Res<Resource2>| {},
1897 )
1898 .chain(),));
1899 schedule.run(&mut world);
1900
1901 assert_eq!(schedule.executable.systems.len(), 7);
1902 }
1903
1904 #[test]
1905 fn adds_multiple_consecutive_syncs() {
1906 let mut schedule = Schedule::default();
1907 let mut world = World::default();
1908 schedule.add_systems(
1910 (
1911 |mut commands: Commands| commands.insert_resource(Resource1),
1912 |mut commands: Commands| commands.insert_resource(Resource2),
1913 |_: Res<Resource1>, _: Res<Resource2>| {},
1914 )
1915 .chain(),
1916 );
1917 schedule.run(&mut world);
1918
1919 assert_eq!(schedule.executable.systems.len(), 5);
1920 }
1921
1922 #[test]
1923 fn do_not_consider_ignore_deferred_before_exclusive_system() {
1924 let mut schedule = Schedule::default();
1925 let mut world = World::default();
1926 schedule.add_systems(
1928 (
1929 |_: Commands| {},
1930 |mut commands: Commands| commands.insert_resource(Resource1),
1932 |world: &mut World| assert!(world.contains_resource::<Resource1>()),
1934 |_: &mut World| {},
1936 |_: Commands| {},
1938 )
1939 .chain_ignore_deferred(),
1940 );
1941 schedule.run(&mut world);
1942
1943 assert_eq!(schedule.executable.systems.len(), 6); }
1945
1946 #[test]
1947 fn bubble_sync_point_through_ignore_deferred_node() {
1948 let mut schedule = Schedule::default();
1949 let mut world = World::default();
1950
1951 let insert_resource_config = (
1952 |mut commands: Commands| commands.insert_resource(Resource1),
1954 || {},
1956 )
1957 .chain_ignore_deferred();
1959
1960 schedule.add_systems(
1961 (
1962 insert_resource_config,
1963 |_: Res<Resource1>| {},
1965 )
1966 .chain(),
1968 );
1969
1970 schedule.run(&mut world);
1975
1976 assert_eq!(schedule.executable.systems.len(), 4); }
1978
1979 #[test]
1980 fn disable_auto_sync_points() {
1981 let mut schedule = Schedule::default();
1982 schedule.set_build_settings(ScheduleBuildSettings {
1983 auto_insert_apply_deferred: false,
1984 ..Default::default()
1985 });
1986 let mut world = World::default();
1987 schedule.add_systems(
1988 (
1989 |mut commands: Commands| commands.insert_resource(Resource1),
1990 |res: Option<Res<Resource1>>| assert!(res.is_none()),
1991 )
1992 .chain(),
1993 );
1994 schedule.run(&mut world);
1995
1996 assert_eq!(schedule.executable.systems.len(), 2);
1997 }
1998
1999 mod no_sync_edges {
2000 use super::*;
2001
2002 fn insert_resource(mut commands: Commands) {
2003 commands.insert_resource(Resource1);
2004 }
2005
2006 fn resource_does_not_exist(res: Option<Res<Resource1>>) {
2007 assert!(res.is_none());
2008 }
2009
2010 #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
2011 enum Sets {
2012 A,
2013 B,
2014 }
2015
2016 fn check_no_sync_edges(add_systems: impl FnOnce(&mut Schedule)) {
2017 let mut schedule = Schedule::default();
2018 let mut world = World::default();
2019 add_systems(&mut schedule);
2020
2021 schedule.run(&mut world);
2022
2023 assert_eq!(schedule.executable.systems.len(), 2);
2024 }
2025
2026 #[test]
2027 fn system_to_system_after() {
2028 check_no_sync_edges(|schedule| {
2029 schedule.add_systems((
2030 insert_resource,
2031 resource_does_not_exist.after_ignore_deferred(insert_resource),
2032 ));
2033 });
2034 }
2035
2036 #[test]
2037 fn system_to_system_before() {
2038 check_no_sync_edges(|schedule| {
2039 schedule.add_systems((
2040 insert_resource.before_ignore_deferred(resource_does_not_exist),
2041 resource_does_not_exist,
2042 ));
2043 });
2044 }
2045
2046 #[test]
2047 fn set_to_system_after() {
2048 check_no_sync_edges(|schedule| {
2049 schedule
2050 .add_systems((insert_resource, resource_does_not_exist.in_set(Sets::A)))
2051 .configure_sets(Sets::A.after_ignore_deferred(insert_resource));
2052 });
2053 }
2054
2055 #[test]
2056 fn set_to_system_before() {
2057 check_no_sync_edges(|schedule| {
2058 schedule
2059 .add_systems((insert_resource.in_set(Sets::A), resource_does_not_exist))
2060 .configure_sets(Sets::A.before_ignore_deferred(resource_does_not_exist));
2061 });
2062 }
2063
2064 #[test]
2065 fn set_to_set_after() {
2066 check_no_sync_edges(|schedule| {
2067 schedule
2068 .add_systems((
2069 insert_resource.in_set(Sets::A),
2070 resource_does_not_exist.in_set(Sets::B),
2071 ))
2072 .configure_sets(Sets::B.after_ignore_deferred(Sets::A));
2073 });
2074 }
2075
2076 #[test]
2077 fn set_to_set_before() {
2078 check_no_sync_edges(|schedule| {
2079 schedule
2080 .add_systems((
2081 insert_resource.in_set(Sets::A),
2082 resource_does_not_exist.in_set(Sets::B),
2083 ))
2084 .configure_sets(Sets::A.before_ignore_deferred(Sets::B));
2085 });
2086 }
2087 }
2088
2089 mod no_sync_chain {
2090 use super::*;
2091
2092 #[derive(Resource)]
2093 struct Ra;
2094
2095 #[derive(Resource)]
2096 struct Rb;
2097
2098 #[derive(Resource)]
2099 struct Rc;
2100
2101 fn run_schedule(expected_num_systems: usize, add_systems: impl FnOnce(&mut Schedule)) {
2102 let mut schedule = Schedule::default();
2103 let mut world = World::default();
2104 add_systems(&mut schedule);
2105
2106 schedule.run(&mut world);
2107
2108 assert_eq!(schedule.executable.systems.len(), expected_num_systems);
2109 }
2110
2111 #[test]
2112 fn only_chain_outside() {
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| commands.insert_resource(Rb),
2119 ),
2120 (
2121 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2122 assert!(res_a.is_some());
2123 assert!(res_b.is_some());
2124 },
2125 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2126 assert!(res_a.is_some());
2127 assert!(res_b.is_some());
2128 },
2129 ),
2130 )
2131 .chain(),
2132 );
2133 });
2134
2135 run_schedule(4, |schedule: &mut Schedule| {
2136 schedule.add_systems(
2137 (
2138 (
2139 |mut commands: Commands| commands.insert_resource(Ra),
2140 |mut commands: Commands| commands.insert_resource(Rb),
2141 ),
2142 (
2143 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2144 assert!(res_a.is_none());
2145 assert!(res_b.is_none());
2146 },
2147 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2148 assert!(res_a.is_none());
2149 assert!(res_b.is_none());
2150 },
2151 ),
2152 )
2153 .chain_ignore_deferred(),
2154 );
2155 });
2156 }
2157
2158 #[test]
2159 fn chain_first() {
2160 run_schedule(6, |schedule: &mut Schedule| {
2161 schedule.add_systems(
2162 (
2163 (
2164 |mut commands: Commands| commands.insert_resource(Ra),
2165 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2166 commands.insert_resource(Rb);
2167 assert!(res_a.is_some());
2168 },
2169 )
2170 .chain(),
2171 (
2172 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2173 assert!(res_a.is_some());
2174 assert!(res_b.is_some());
2175 },
2176 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2177 assert!(res_a.is_some());
2178 assert!(res_b.is_some());
2179 },
2180 ),
2181 )
2182 .chain(),
2183 );
2184 });
2185
2186 run_schedule(5, |schedule: &mut Schedule| {
2187 schedule.add_systems(
2188 (
2189 (
2190 |mut commands: Commands| commands.insert_resource(Ra),
2191 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2192 commands.insert_resource(Rb);
2193 assert!(res_a.is_some());
2194 },
2195 )
2196 .chain(),
2197 (
2198 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2199 assert!(res_a.is_some());
2200 assert!(res_b.is_none());
2201 },
2202 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2203 assert!(res_a.is_some());
2204 assert!(res_b.is_none());
2205 },
2206 ),
2207 )
2208 .chain_ignore_deferred(),
2209 );
2210 });
2211 }
2212
2213 #[test]
2214 fn chain_second() {
2215 run_schedule(6, |schedule: &mut Schedule| {
2216 schedule.add_systems(
2217 (
2218 (
2219 |mut commands: Commands| commands.insert_resource(Ra),
2220 |mut commands: Commands| commands.insert_resource(Rb),
2221 ),
2222 (
2223 |mut commands: Commands,
2224 res_a: Option<Res<Ra>>,
2225 res_b: Option<Res<Rb>>| {
2226 commands.insert_resource(Rc);
2227 assert!(res_a.is_some());
2228 assert!(res_b.is_some());
2229 },
2230 |res_a: Option<Res<Ra>>,
2231 res_b: Option<Res<Rb>>,
2232 res_c: Option<Res<Rc>>| {
2233 assert!(res_a.is_some());
2234 assert!(res_b.is_some());
2235 assert!(res_c.is_some());
2236 },
2237 )
2238 .chain(),
2239 )
2240 .chain(),
2241 );
2242 });
2243
2244 run_schedule(5, |schedule: &mut Schedule| {
2245 schedule.add_systems(
2246 (
2247 (
2248 |mut commands: Commands| commands.insert_resource(Ra),
2249 |mut commands: Commands| commands.insert_resource(Rb),
2250 ),
2251 (
2252 |mut commands: Commands,
2253 res_a: Option<Res<Ra>>,
2254 res_b: Option<Res<Rb>>| {
2255 commands.insert_resource(Rc);
2256 assert!(res_a.is_none());
2257 assert!(res_b.is_none());
2258 },
2259 |res_a: Option<Res<Ra>>,
2260 res_b: Option<Res<Rb>>,
2261 res_c: Option<Res<Rc>>| {
2262 assert!(res_a.is_some());
2263 assert!(res_b.is_some());
2264 assert!(res_c.is_some());
2265 },
2266 )
2267 .chain(),
2268 )
2269 .chain_ignore_deferred(),
2270 );
2271 });
2272 }
2273
2274 #[test]
2275 fn chain_all() {
2276 run_schedule(7, |schedule: &mut Schedule| {
2277 schedule.add_systems(
2278 (
2279 (
2280 |mut commands: Commands| commands.insert_resource(Ra),
2281 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2282 commands.insert_resource(Rb);
2283 assert!(res_a.is_some());
2284 },
2285 )
2286 .chain(),
2287 (
2288 |mut commands: Commands,
2289 res_a: Option<Res<Ra>>,
2290 res_b: Option<Res<Rb>>| {
2291 commands.insert_resource(Rc);
2292 assert!(res_a.is_some());
2293 assert!(res_b.is_some());
2294 },
2295 |res_a: Option<Res<Ra>>,
2296 res_b: Option<Res<Rb>>,
2297 res_c: Option<Res<Rc>>| {
2298 assert!(res_a.is_some());
2299 assert!(res_b.is_some());
2300 assert!(res_c.is_some());
2301 },
2302 )
2303 .chain(),
2304 )
2305 .chain(),
2306 );
2307 });
2308
2309 run_schedule(6, |schedule: &mut Schedule| {
2310 schedule.add_systems(
2311 (
2312 (
2313 |mut commands: Commands| commands.insert_resource(Ra),
2314 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2315 commands.insert_resource(Rb);
2316 assert!(res_a.is_some());
2317 },
2318 )
2319 .chain(),
2320 (
2321 |mut commands: Commands,
2322 res_a: Option<Res<Ra>>,
2323 res_b: Option<Res<Rb>>| {
2324 commands.insert_resource(Rc);
2325 assert!(res_a.is_some());
2326 assert!(res_b.is_none());
2327 },
2328 |res_a: Option<Res<Ra>>,
2329 res_b: Option<Res<Rb>>,
2330 res_c: Option<Res<Rc>>| {
2331 assert!(res_a.is_some());
2332 assert!(res_b.is_some());
2333 assert!(res_c.is_some());
2334 },
2335 )
2336 .chain(),
2337 )
2338 .chain_ignore_deferred(),
2339 );
2340 });
2341 }
2342 }
2343
2344 #[derive(ScheduleLabel, Hash, Debug, Clone, PartialEq, Eq)]
2345 struct TestSchedule;
2346
2347 #[derive(Resource)]
2348 struct CheckSystemRan(usize);
2349
2350 #[test]
2351 fn add_systems_to_existing_schedule() {
2352 let mut schedules = Schedules::default();
2353 let schedule = Schedule::new(TestSchedule);
2354
2355 schedules.insert(schedule);
2356 schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2357
2358 let mut world = World::new();
2359
2360 world.insert_resource(CheckSystemRan(0));
2361 world.insert_resource(schedules);
2362 world.run_schedule(TestSchedule);
2363
2364 let value = world
2365 .get_resource::<CheckSystemRan>()
2366 .expect("CheckSystemRan Resource Should Exist");
2367 assert_eq!(value.0, 1);
2368 }
2369
2370 #[test]
2371 fn add_systems_to_non_existing_schedule() {
2372 let mut schedules = Schedules::default();
2373
2374 schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2375
2376 let mut world = World::new();
2377
2378 world.insert_resource(CheckSystemRan(0));
2379 world.insert_resource(schedules);
2380 world.run_schedule(TestSchedule);
2381
2382 let value = world
2383 .get_resource::<CheckSystemRan>()
2384 .expect("CheckSystemRan Resource Should Exist");
2385 assert_eq!(value.0, 1);
2386 }
2387
2388 #[derive(SystemSet, Debug, Hash, Clone, PartialEq, Eq)]
2389 enum TestSet {
2390 First,
2391 Second,
2392 }
2393
2394 #[test]
2395 fn configure_set_on_existing_schedule() {
2396 let mut schedules = Schedules::default();
2397 let schedule = Schedule::new(TestSchedule);
2398
2399 schedules.insert(schedule);
2400
2401 schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2402 schedules.add_systems(
2403 TestSchedule,
2404 (|mut ran: ResMut<CheckSystemRan>| {
2405 assert_eq!(ran.0, 0);
2406 ran.0 += 1;
2407 })
2408 .in_set(TestSet::First),
2409 );
2410
2411 schedules.add_systems(
2412 TestSchedule,
2413 (|mut ran: ResMut<CheckSystemRan>| {
2414 assert_eq!(ran.0, 1);
2415 ran.0 += 1;
2416 })
2417 .in_set(TestSet::Second),
2418 );
2419
2420 let mut world = World::new();
2421
2422 world.insert_resource(CheckSystemRan(0));
2423 world.insert_resource(schedules);
2424 world.run_schedule(TestSchedule);
2425
2426 let value = world
2427 .get_resource::<CheckSystemRan>()
2428 .expect("CheckSystemRan Resource Should Exist");
2429 assert_eq!(value.0, 2);
2430 }
2431
2432 #[test]
2433 fn configure_set_on_new_schedule() {
2434 let mut schedules = Schedules::default();
2435
2436 schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2437 schedules.add_systems(
2438 TestSchedule,
2439 (|mut ran: ResMut<CheckSystemRan>| {
2440 assert_eq!(ran.0, 0);
2441 ran.0 += 1;
2442 })
2443 .in_set(TestSet::First),
2444 );
2445
2446 schedules.add_systems(
2447 TestSchedule,
2448 (|mut ran: ResMut<CheckSystemRan>| {
2449 assert_eq!(ran.0, 1);
2450 ran.0 += 1;
2451 })
2452 .in_set(TestSet::Second),
2453 );
2454
2455 let mut world = World::new();
2456
2457 world.insert_resource(CheckSystemRan(0));
2458 world.insert_resource(schedules);
2459 world.run_schedule(TestSchedule);
2460
2461 let value = world
2462 .get_resource::<CheckSystemRan>()
2463 .expect("CheckSystemRan Resource Should Exist");
2464 assert_eq!(value.0, 2);
2465 }
2466
2467 #[test]
2468 fn test_default_error_handler() {
2469 #[derive(Resource, Default)]
2470 struct Ran(bool);
2471
2472 fn system(mut ran: ResMut<Ran>) -> Result {
2473 ran.0 = true;
2474 Err("I failed!".into())
2475 }
2476
2477 let mut world = World::default();
2479 world.init_resource::<Ran>();
2480 world.insert_resource(FallbackErrorHandler(ignore));
2481 let mut schedule = Schedule::default();
2482 schedule.add_systems(system).run(&mut world);
2483 assert!(world.resource::<Ran>().0);
2484
2485 schedule.add_systems(
2487 (|world: &mut World| {
2488 world.insert_resource(FallbackErrorHandler(panic));
2489 })
2490 .before(system),
2491 );
2492 schedule.run(&mut world);
2493 }
2494
2495 #[test]
2496 fn get_a_system_key() {
2497 fn test_system() {}
2498
2499 let mut schedule = Schedule::default();
2500 schedule.add_systems(test_system);
2501 let mut world = World::default();
2502 let _ = schedule.initialize(&mut world);
2503
2504 let keys = schedule
2505 .graph()
2506 .systems_in_set(test_system.into_system_set().intern())
2507 .unwrap();
2508 assert_eq!(keys.len(), 1);
2509 }
2510
2511 #[test]
2512 fn get_system_keys_in_set() {
2513 fn system_1() {}
2514 fn system_2() {}
2515
2516 let mut schedule = Schedule::default();
2517 schedule.add_systems((system_1, system_2).in_set(TestSet::First));
2518 let mut world = World::default();
2519 let _ = schedule.initialize(&mut world);
2520
2521 let keys = schedule
2522 .graph()
2523 .systems_in_set(TestSet::First.into_system_set().intern())
2524 .unwrap();
2525 assert_eq!(keys.len(), 2);
2526 }
2527
2528 #[test]
2529 fn get_system_keys_with_same_name() {
2530 fn test_system() {}
2531
2532 let mut schedule = Schedule::default();
2533 schedule.add_systems((test_system, test_system));
2534 let mut world = World::default();
2535 let _ = schedule.initialize(&mut world);
2536
2537 let keys = schedule
2538 .graph()
2539 .systems_in_set(test_system.into_system_set().intern())
2540 .unwrap();
2541 assert_eq!(keys.len(), 2);
2542 }
2543
2544 #[test]
2545 fn remove_a_system() {
2546 fn system() {}
2547
2548 let mut schedule = Schedule::default();
2549 schedule.add_systems(system);
2550 let mut world = World::default();
2551
2552 let remove_count = schedule.remove_systems_in_set(
2553 system,
2554 &mut world,
2555 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2556 );
2557 assert_eq!(remove_count.unwrap(), 1);
2558
2559 schedule.initialize(&mut world).unwrap();
2561 assert_eq!(schedule.graph().systems.len(), 0);
2562 }
2563
2564 #[test]
2565 fn remove_multiple_systems() {
2566 fn system() {}
2567
2568 let mut schedule = Schedule::default();
2569 schedule.add_systems((system, system));
2570 let mut world = World::default();
2571
2572 let remove_count = schedule.remove_systems_in_set(
2573 system,
2574 &mut world,
2575 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2576 );
2577 assert_eq!(remove_count.unwrap(), 2);
2578
2579 schedule.initialize(&mut world).unwrap();
2581 assert_eq!(schedule.graph().systems.len(), 0);
2582 }
2583
2584 #[test]
2585 fn remove_a_system_with_dependencies() {
2586 fn system_1() {}
2587 fn system_2() {}
2588
2589 let mut schedule = Schedule::default();
2590 schedule.add_systems((system_1, system_2).chain());
2591 let mut world = World::default();
2592
2593 let remove_count = schedule.remove_systems_in_set(
2594 system_1,
2595 &mut world,
2596 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2597 );
2598 assert_eq!(remove_count.unwrap(), 1);
2599
2600 schedule.initialize(&mut world).unwrap();
2602 assert_eq!(schedule.graph().systems.len(), 1);
2603 }
2604
2605 #[test]
2606 fn remove_a_system_and_still_ordered() {
2607 #[derive(Resource)]
2608 struct A;
2609
2610 fn system_1(_: ResMut<A>) {}
2611 fn system_2() {}
2612 fn system_3(_: ResMut<A>) {}
2613
2614 let mut schedule = Schedule::default();
2615 schedule.add_systems((system_1, system_2, system_3).chain());
2616 let mut world = World::new();
2617
2618 let _ = schedule.remove_systems_in_set(
2619 system_2,
2620 &mut world,
2621 ScheduleCleanupPolicy::RemoveSetAndSystems,
2622 );
2623
2624 let result = schedule.initialize(&mut world);
2625 assert!(result.is_ok());
2626 let conflicts = schedule.graph().conflicting_systems();
2627 assert!(conflicts.is_empty());
2628 }
2629
2630 #[test]
2631 fn remove_a_set_and_still_ordered() {
2632 #[derive(Resource)]
2633 struct A;
2634
2635 #[derive(SystemSet, Hash, PartialEq, Eq, Clone, Debug)]
2636 struct B;
2637
2638 fn system_1(_: ResMut<A>) {}
2639 fn system_2() {}
2640 fn system_3(_: ResMut<A>) {}
2641
2642 let mut schedule = Schedule::default();
2643 schedule.add_systems((system_1.before(B), system_2, system_3.after(B)));
2644 let mut world = World::new();
2645
2646 let _ = schedule.remove_systems_in_set(
2647 B,
2648 &mut world,
2649 ScheduleCleanupPolicy::RemoveSetAndSystems,
2650 );
2651
2652 let result = schedule.initialize(&mut world);
2653 assert!(result.is_ok());
2654 let conflicts = schedule.graph().conflicting_systems();
2655 assert!(conflicts.is_empty());
2656 }
2657
2658 #[test]
2659 fn build_pass_iteration_order() {
2660 #[derive(Debug)]
2661 struct Pass<const N: usize>;
2662
2663 impl<const N: usize> ScheduleBuildPass for Pass<N> {
2664 type EdgeOptions = ();
2665 fn add_dependency(
2666 &mut self,
2667 _from: crate::schedule::NodeId,
2668 _to: crate::schedule::NodeId,
2669 _options: Option<&Self::EdgeOptions>,
2670 ) {
2671 }
2672 fn build(
2673 &mut self,
2674 _world: &mut World,
2675 _graph: &mut super::ScheduleGraph,
2676 _dependency_flattened: FlattenedDependencies<'_>,
2677 ) -> core::result::Result<(), crate::schedule::ScheduleBuildError> {
2678 Ok(())
2679 }
2680 fn collapse_set(
2681 &mut self,
2682 _set: crate::schedule::SystemSetKey,
2683 _systems: &indexmap::IndexSet<
2684 crate::schedule::SystemKey,
2685 bevy_platform::hash::FixedHasher,
2686 >,
2687 _dependency_flattening: &crate::schedule::graph::DiGraph<crate::schedule::NodeId>,
2688 ) -> impl Iterator<Item = (crate::schedule::NodeId, crate::schedule::NodeId)>
2689 {
2690 core::iter::empty()
2691 }
2692 }
2693
2694 let mut schedule = Schedule::default();
2695 schedule.add_build_pass(Pass::<0>);
2696 schedule.add_build_pass(Pass::<1>);
2697 schedule.add_build_pass(Pass::<2>);
2698
2699 let pass_order: Vec<TypeId> = schedule.graph().passes.keys().cloned().collect();
2700
2701 assert_eq!(
2702 pass_order,
2703 vec![
2704 TypeId::of::<AutoInsertApplyDeferredPass>(),
2705 TypeId::of::<Pass<0>>(),
2706 TypeId::of::<Pass<1>>(),
2707 TypeId::of::<Pass<2>>()
2708 ]
2709 );
2710 }
2711}