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, TypeIdHashMap};
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;
27#[cfg(feature = "debug")]
28use rand::{seq::SliceRandom, SeedableRng};
29use thiserror::Error;
30#[cfg(feature = "trace")]
31use tracing::info_span;
32
33use crate::{
34 change_detection::CheckChangeTicks,
35 system::{System, SystemAccess},
36};
37use crate::{
38 component::{ComponentId, Components},
39 prelude::Component,
40 resource::Resource,
41 schedule::*,
42 system::ScheduleSystem,
43 world::World,
44};
45
46pub use stepping::Stepping;
47use Direction::{Incoming, Outgoing};
48
49#[derive(Default, Resource)]
51pub struct Schedules {
52 inner: HashMap<InternedScheduleLabel, Schedule>,
53 pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
55 temporarily_removed: HashSet<InternedScheduleLabel>,
57 empty_labels: HashSet<InternedScheduleLabel>,
60}
61
62impl Schedules {
63 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn insert(&mut self, schedule: Schedule) -> Option<Schedule> {
73 self.temporarily_removed.remove(&schedule.label);
74 self.inner.insert(schedule.label, schedule)
76 }
77
78 pub fn reinsert(&mut self, schedule: Schedule) -> Option<Schedule> {
83 self.temporarily_removed.remove(&schedule.label);
84 self.inner.insert(schedule.label, schedule)
86 }
87
88 pub fn remove(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
90 self.inner.remove(&label.intern())
91 }
92
93 pub fn remove_temporarily(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
95 let label = label.intern();
96 let k = self.inner.remove(&label);
97 if k.is_some() {
98 self.temporarily_removed.insert(label);
99 self.empty_labels.remove(&label);
101 } else {
102 self.empty_labels.insert(label);
103 }
104 k
105 }
106
107 pub fn remove_entry(
109 &mut self,
110 label: impl ScheduleLabel,
111 ) -> Option<(InternedScheduleLabel, Schedule)> {
112 self.inner.remove_entry(&label.intern())
113 }
114
115 pub fn get_temporarily_removed(&self) -> HashSet<InternedScheduleLabel> {
117 self.temporarily_removed.clone()
118 }
119
120 pub fn get_empty_labels(&self) -> HashSet<InternedScheduleLabel> {
122 self.empty_labels.clone()
123 }
124
125 pub fn contains(&self, label: impl ScheduleLabel) -> bool {
127 self.inner.contains_key(&label.intern())
128 }
129
130 pub fn get(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
132 self.inner.get(&label.intern())
133 }
134
135 pub fn get_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
137 self.inner.get_mut(&label.intern())
138 }
139
140 pub fn entry(&mut self, label: impl ScheduleLabel) -> &mut Schedule {
142 self.inner
143 .entry(label.intern())
144 .or_insert_with(|| Schedule::new(label))
145 }
146
147 pub fn iter(&self) -> impl Iterator<Item = (&dyn ScheduleLabel, &Schedule)> {
149 self.inner
150 .iter()
151 .map(|(label, schedule)| (&**label, schedule))
152 }
153 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&dyn ScheduleLabel, &mut Schedule)> {
155 self.inner
156 .iter_mut()
157 .map(|(label, schedule)| (&**label, schedule))
158 }
159
160 pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
164 #[cfg(feature = "trace")]
165 let _all_span = info_span!("check stored schedule ticks").entered();
166 #[cfg_attr(
167 not(feature = "trace"),
168 expect(
169 unused_variables,
170 reason = "The `label` variable goes unused if the `trace` feature isn't active"
171 )
172 )]
173 for (label, schedule) in &mut self.inner {
174 #[cfg(feature = "trace")]
175 let name = format!("{label:?}");
176 #[cfg(feature = "trace")]
177 let _one_span = info_span!("check schedule ticks", name = &name).entered();
178 schedule.check_change_ticks(check);
179 }
180 }
181
182 pub fn configure_schedules(&mut self, schedule_build_settings: ScheduleBuildSettings) {
187 for (_, schedule) in &mut self.inner {
188 schedule.set_build_settings(schedule_build_settings.clone());
189 }
190 }
191
192 pub fn allow_ambiguous_component<T: Component>(&mut self, world: &mut World) {
194 self.ignored_scheduling_ambiguities
195 .insert(world.register_component::<T>());
196 }
197
198 pub fn allow_ambiguous_resource<T: Resource>(&mut self, world: &mut World) {
200 self.ignored_scheduling_ambiguities
201 .insert(world.components_registrator().register_component::<T>());
202 }
203
204 pub fn iter_ignored_ambiguities(&self) -> impl Iterator<Item = &ComponentId> + '_ {
206 self.ignored_scheduling_ambiguities.iter()
207 }
208
209 pub fn print_ignored_ambiguities(&self, components: &Components) {
214 let mut message =
215 "System order ambiguities caused by conflicts on the following types are ignored:\n"
216 .to_string();
217 for id in self.iter_ignored_ambiguities() {
218 writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap();
219 }
220
221 info!("{message}");
222 }
223
224 pub fn add_systems<M>(
226 &mut self,
227 schedule: impl ScheduleLabel,
228 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
229 ) -> &mut Self {
230 self.entry(schedule).add_systems(systems);
231
232 self
233 }
234
235 pub fn remove_systems_in_set<M>(
239 &mut self,
240 schedule: impl ScheduleLabel,
241 set: impl IntoSystemSet<M>,
242 world: &mut World,
243 policy: ScheduleCleanupPolicy,
244 ) -> Result<usize, ScheduleError> {
245 self.get_mut(schedule)
246 .ok_or(ScheduleError::ScheduleNotFound)?
247 .remove_systems_in_set(set, world, policy)
248 }
249
250 #[track_caller]
252 pub fn configure_sets<M>(
253 &mut self,
254 schedule: impl ScheduleLabel,
255 sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
256 ) -> &mut Self {
257 self.entry(schedule).configure_sets(sets);
258
259 self
260 }
261
262 #[track_caller]
269 pub fn ignore_ambiguity<M1, M2, S1, S2>(
270 &mut self,
271 schedule: impl ScheduleLabel,
272 a: S1,
273 b: S2,
274 ) -> &mut Self
275 where
276 S1: IntoSystemSet<M1>,
277 S2: IntoSystemSet<M2>,
278 {
279 self.entry(schedule).ignore_ambiguity(a, b);
280
281 self
282 }
283}
284
285pub(crate) struct Weak;
290
291#[derive(Default)]
293pub enum Chain {
294 #[default]
296 Unchained,
297 Chained(TypeIdHashMap<Box<dyn Any>>),
300}
301
302impl Chain {
303 pub fn set_chained(&mut self) {
305 if matches!(self, Chain::Unchained) {
306 *self = Self::Chained(Default::default());
307 };
308 }
309 pub fn set_chained_with_config<T: 'static>(&mut self, config: T) {
312 self.set_chained();
313 if let Chain::Chained(config_map) = self {
314 config_map.insert(TypeId::of::<T>(), Box::new(config));
315 } else {
316 unreachable!()
317 };
318 }
319}
320
321pub struct Schedule {
394 label: InternedScheduleLabel,
395 graph: ScheduleGraph,
396 executable: SystemSchedule,
397 executor: Box<dyn SystemExecutor>,
398 executor_initialized: bool,
399}
400
401#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
402struct DefaultSchedule;
403
404impl Default for Schedule {
405 fn default() -> Self {
410 Self::new(DefaultSchedule)
411 }
412}
413
414impl Schedule {
415 pub fn new(label: impl ScheduleLabel) -> Self {
417 let mut this = Self {
418 label: label.intern(),
419 graph: ScheduleGraph::new(),
420 executable: SystemSchedule::new(),
421 executor: default_executor(),
422 executor_initialized: false,
423 };
424 this.set_build_settings(Default::default());
426 this
427 }
428
429 pub fn is_changed(&self) -> bool {
431 self.graph.changed
432 }
433
434 pub fn label(&self) -> InternedScheduleLabel {
437 self.label
438 }
439
440 pub fn add_systems<M>(
442 &mut self,
443 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
444 ) -> &mut Self {
445 self.graph.process_configs(systems.into_configs(), false);
446 self
447 }
448
449 pub fn remove_systems_in_set<M>(
473 &mut self,
474 set: impl IntoSystemSet<M>,
475 world: &mut World,
476 policy: ScheduleCleanupPolicy,
477 ) -> Result<usize, ScheduleError> {
478 if self.graph.changed {
479 self.initialize(world)?;
480 }
481 self.graph.remove_systems_in_set(set, policy)
482 }
483
484 #[track_caller]
487 pub fn ignore_ambiguity<M1, M2, S1, S2>(&mut self, a: S1, b: S2) -> &mut Self
488 where
489 S1: IntoSystemSet<M1>,
490 S2: IntoSystemSet<M2>,
491 {
492 let a = a.into_system_set();
493 let b = b.into_system_set();
494
495 let a_id = self.graph.system_sets.get_key_or_insert(a.intern());
496 let b_id = self.graph.system_sets.get_key_or_insert(b.intern());
497
498 self.graph
499 .ambiguous_with
500 .add_edge(NodeId::Set(a_id), NodeId::Set(b_id));
501
502 self
503 }
504
505 #[track_caller]
507 pub fn configure_sets<M>(
508 &mut self,
509 sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
510 ) -> &mut Self {
511 self.graph.configure_sets(sets);
512 self
513 }
514
515 pub fn add_build_pass<T: ScheduleBuildPass>(&mut self, pass: T) -> &mut Self {
517 self.graph.passes.insert(TypeId::of::<T>(), Box::new(pass));
518 self
519 }
520
521 pub fn remove_build_pass<T: ScheduleBuildPass>(&mut self) {
523 self.graph.passes.shift_remove(&TypeId::of::<T>());
524 }
525
526 pub fn set_build_settings(&mut self, settings: ScheduleBuildSettings) -> &mut Self {
534 if settings.auto_insert_apply_deferred {
535 if !self
536 .graph
537 .passes
538 .contains_key(&TypeId::of::<passes::AutoInsertApplyDeferredPass>())
539 {
540 self.add_build_pass(passes::AutoInsertApplyDeferredPass::default());
541 }
542 } else {
543 self.remove_build_pass::<passes::AutoInsertApplyDeferredPass>();
544 }
545 self.graph.settings = settings;
546 self
547 }
548
549 pub fn get_build_settings(&self) -> ScheduleBuildSettings {
551 self.graph.settings.clone()
552 }
553
554 pub fn set_executor(&mut self, executor: impl SystemExecutor + 'static) -> &mut Self {
556 self.executor = Box::new(executor);
557 self.executor_initialized = false;
558 self
559 }
560
561 pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
566 self.executor.set_apply_final_deferred(apply_final_deferred);
567 self
568 }
569
570 pub fn run(&mut self, world: &mut World) {
572 #[cfg(feature = "trace")]
573 let _span = info_span!("schedule", name = ?self.label).entered();
574
575 world.check_change_ticks();
576 self.initialize(world).unwrap_or_else(|e| {
577 panic!(
578 "Error when initializing schedule {:?}: {}",
579 self.label,
580 e.to_string(self.graph(), world)
581 )
582 });
583
584 let error_handler = world.fallback_error_handler();
585
586 #[cfg(not(feature = "bevy_debug_stepping"))]
587 self.executor
588 .run(&mut self.executable, world, None, error_handler);
589
590 #[cfg(feature = "bevy_debug_stepping")]
591 {
592 let skip_systems = match world.get_resource_mut::<Stepping>() {
593 None => None,
594 Some(mut stepping) => stepping.skipped_systems(self),
595 };
596
597 self.executor.run(
598 &mut self.executable,
599 world,
600 skip_systems.as_ref(),
601 error_handler,
602 );
603 }
604 }
605
606 pub fn initialize(
613 &mut self,
614 world: &mut World,
615 ) -> Result<Option<ScheduleBuildMetadata>, ScheduleBuildError> {
616 let mut build_metadata = None;
617 if self.graph.changed {
618 self.graph.initialize(world);
619 let ignored_ambiguities = world
620 .get_resource_or_init::<Schedules>()
621 .ignored_scheduling_ambiguities
622 .clone();
623
624 let mut event = ScheduleBuilt {
625 label: self.label,
626 build_metadata: self.graph.update_schedule(
627 world,
628 &mut self.executable,
629 &ignored_ambiguities,
630 self.label,
631 )?,
632 };
633 self.graph.changed = false;
634 self.executor_initialized = false;
635
636 world.trigger_ref(&mut event);
637 build_metadata = Some(event.build_metadata);
638 }
639
640 if !self.executor_initialized {
641 self.executor.init(&self.executable);
642 self.executor_initialized = true;
643 }
644
645 Ok(build_metadata)
646 }
647
648 pub fn graph(&self) -> &ScheduleGraph {
650 &self.graph
651 }
652
653 pub fn graph_mut(&mut self) -> &mut ScheduleGraph {
655 &mut self.graph
656 }
657
658 pub(crate) fn executable(&self) -> &SystemSchedule {
660 &self.executable
661 }
662
663 pub fn check_change_ticks(&mut self, check: CheckChangeTicks) {
667 for system in &mut self.executable.systems {
668 if !is_apply_deferred(system) {
669 system.check_change_tick(check);
670 }
671 }
672
673 for conditions in &mut self.executable.system_conditions {
674 for condition in conditions {
675 condition.check_change_tick(check);
676 }
677 }
678
679 for conditions in &mut self.executable.set_conditions {
680 for condition in conditions {
681 condition.check_change_tick(check);
682 }
683 }
684 }
685
686 pub fn apply_deferred(&mut self, world: &mut World) {
695 for SystemWithAccess { system, .. } in &mut self.executable.systems {
696 system.apply_deferred(world);
697 }
698 }
699
700 pub fn systems(
705 &self,
706 ) -> Result<impl Iterator<Item = (SystemKey, &ScheduleSystem)> + Sized, ScheduleNotInitialized>
707 {
708 if !self.executor_initialized {
709 return Err(ScheduleNotInitialized);
710 }
711
712 let iter = self
713 .executable
714 .system_ids
715 .iter()
716 .zip(&self.executable.systems)
717 .map(|(&node_id, system)| (node_id, &system.system));
718
719 Ok(iter)
720 }
721
722 pub fn systems_with_access(
727 &self,
728 ) -> Result<impl Iterator<Item = (SystemKey, &SystemWithAccess)> + Sized, ScheduleNotInitialized>
729 {
730 if !self.executor_initialized {
731 return Err(ScheduleNotInitialized);
732 }
733
734 let iter = self
735 .executable
736 .system_ids
737 .iter()
738 .zip(&self.executable.systems)
739 .map(|(&node_id, system)| (node_id, system));
740
741 Ok(iter)
742 }
743
744 pub fn systems_len(&self) -> usize {
746 if !self.executor_initialized {
747 self.graph.systems.len()
748 } else {
749 self.executable.systems.len()
750 }
751 }
752}
753
754#[derive(Default)]
759pub struct ScheduleGraph {
760 pub systems: Systems,
762 pub system_sets: SystemSets,
764 hierarchy: Dag<NodeId>,
766 dependency: Dag<NodeId>,
768 set_systems: DagGroups<SystemSetKey, SystemKey>,
770 ambiguous_with: UnGraph<NodeId>,
771 pub ambiguous_with_all: HashSet<NodeId>,
773 conflicting_systems: ConflictingSystems,
774 weak_node_edges: HashSet<(NodeId, NodeId)>,
778 strict_node_edges: HashSet<(NodeId, NodeId)>,
782 anonymous_sets: usize,
783 changed: bool,
784 settings: ScheduleBuildSettings,
785 passes: IndexMap<TypeId, Box<dyn ScheduleBuildPassObj>, FixedHasher>,
786}
787
788impl ScheduleGraph {
789 pub fn new() -> Self {
791 Self {
792 systems: Systems::default(),
793 system_sets: SystemSets::default(),
794 hierarchy: Dag::new(),
795 dependency: Dag::new(),
796 set_systems: DagGroups::default(),
797 ambiguous_with: UnGraph::default(),
798 ambiguous_with_all: HashSet::default(),
799 conflicting_systems: ConflictingSystems::default(),
800 weak_node_edges: HashSet::default(),
801 strict_node_edges: HashSet::default(),
802 anonymous_sets: 0,
803 changed: false,
804 settings: default(),
805 passes: default(),
806 }
807 }
808
809 pub fn hierarchy(&self) -> &Dag<NodeId> {
814 &self.hierarchy
815 }
816
817 pub fn dependency(&self) -> &Dag<NodeId> {
822 &self.dependency
823 }
824
825 pub fn conflicting_systems(&self) -> &ConflictingSystems {
830 &self.conflicting_systems
831 }
832
833 fn process_config<T: ProcessScheduleConfig + Schedulable>(
834 &mut self,
835 config: ScheduleConfig<T>,
836 collect_nodes: bool,
837 ) -> ProcessConfigsResult {
838 ProcessConfigsResult {
839 densely_chained: true,
840 nodes: collect_nodes
841 .then_some(T::process_config(self, config))
842 .into_iter()
843 .collect(),
844 }
845 }
846
847 fn apply_collective_conditions<
848 T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
849 >(
850 &mut self,
851 configs: &mut [ScheduleConfigs<T>],
852 collective_conditions: Vec<BoxedCondition>,
853 ) {
854 if !collective_conditions.is_empty() {
855 if let [config] = configs {
856 for condition in collective_conditions {
857 config.run_if_dyn(condition);
858 }
859 } else {
860 let set = self.create_anonymous_set();
861 for config in configs.iter_mut() {
862 config.in_set_inner(set.intern());
863 }
864 let mut set_config = InternedSystemSet::into_config(set.intern());
865 set_config.conditions.extend(collective_conditions);
866 self.configure_set_inner(set_config);
867 }
868 }
869 }
870
871 #[track_caller]
880 fn process_configs<
881 T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
882 >(
883 &mut self,
884 configs: ScheduleConfigs<T>,
885 collect_nodes: bool,
886 ) -> ProcessConfigsResult {
887 match configs {
888 ScheduleConfigs::ScheduleConfig(config) => self.process_config(config, collect_nodes),
889 ScheduleConfigs::Configs {
890 metadata,
891 mut configs,
892 collective_conditions,
893 } => {
894 self.apply_collective_conditions(&mut configs, collective_conditions);
895
896 let is_chained = matches!(metadata, Chain::Chained(_));
897 let is_weak = matches!(
898 &metadata,
899 Chain::Chained(options) if options.contains_key(&TypeId::of::<Weak>())
900 );
901
902 let mut densely_chained = (is_chained && !is_weak) || configs.len() == 1;
906 let mut configs = configs.into_iter();
907 let mut nodes = Vec::new();
908
909 let Some(first) = configs.next() else {
910 return ProcessConfigsResult {
911 nodes: Vec::new(),
912 densely_chained,
913 };
914 };
915 let mut previous_result = self.process_configs(first, collect_nodes || is_chained);
916 densely_chained &= previous_result.densely_chained;
917
918 for current in configs {
919 let current_result = self.process_configs(current, collect_nodes || is_chained);
920 densely_chained &= current_result.densely_chained;
921
922 if let Chain::Chained(chain_options) = &metadata {
923 let current_nodes = if current_result.densely_chained {
925 ¤t_result.nodes[..1]
926 } else {
927 ¤t_result.nodes
928 };
929 let previous_nodes = if previous_result.densely_chained {
931 &previous_result.nodes[previous_result.nodes.len() - 1..]
932 } else {
933 &previous_result.nodes
934 };
935
936 self.dependency
937 .reserve_edges(previous_nodes.len() * current_nodes.len());
938 for previous_node in previous_nodes {
939 for current_node in current_nodes {
940 self.dependency.add_edge(*previous_node, *current_node);
941
942 if is_weak {
943 self.weak_node_edges.insert((*previous_node, *current_node));
944 } else {
945 self.strict_node_edges
946 .insert((*previous_node, *current_node));
947 }
948
949 for pass in self.passes.values_mut() {
950 pass.add_dependency(
951 *previous_node,
952 *current_node,
953 chain_options,
954 );
955 }
956 }
957 }
958 }
959 if collect_nodes {
960 nodes.append(&mut previous_result.nodes);
961 }
962
963 previous_result = current_result;
964 }
965 if collect_nodes {
966 nodes.append(&mut previous_result.nodes);
967 }
968
969 ProcessConfigsResult {
970 nodes,
971 densely_chained,
972 }
973 }
974 }
975 }
976
977 fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
979 let key = self.systems.insert(config.node, config.conditions);
980
981 self.update_graphs(NodeId::System(key), config.metadata);
983
984 key
985 }
986
987 #[track_caller]
988 fn configure_sets<M>(&mut self, sets: impl IntoScheduleConfigs<InternedSystemSet, M>) {
989 self.process_configs(sets.into_configs(), false);
990 }
991
992 fn configure_set_inner(&mut self, config: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
994 let key = self.system_sets.insert(config.node, config.conditions);
995
996 self.update_graphs(NodeId::Set(key), config.metadata);
998
999 key
1000 }
1001
1002 fn create_anonymous_set(&mut self) -> AnonymousSet {
1003 let id = self.anonymous_sets;
1004 self.anonymous_sets += 1;
1005 AnonymousSet::new(id)
1006 }
1007
1008 pub fn systems_in_set(
1019 &self,
1020 system_set: InternedSystemSet,
1021 ) -> Result<&IndexSet<SystemKey, FixedHasher>, ScheduleError> {
1022 if self.changed {
1023 return Err(ScheduleError::Uninitialized);
1024 }
1025 let system_set_id = self
1026 .system_sets
1027 .get_key(system_set)
1028 .ok_or(ScheduleError::SetNotFound)?;
1029 self.set_systems
1030 .get(&system_set_id)
1031 .ok_or(ScheduleError::SetNotFound)
1032 }
1033
1034 fn add_edges_for_transitive_dependencies(&mut self, node: NodeId) {
1035 let in_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Incoming).collect();
1036 let out_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Outgoing).collect();
1037
1038 self.hierarchy
1039 .reserve_edges(in_nodes.len() * out_nodes.len());
1040 for &in_node in &in_nodes {
1041 for &out_node in &out_nodes {
1042 self.hierarchy.add_edge(in_node, out_node);
1043 }
1044 }
1045
1046 let in_nodes: Vec<_> = self.dependency.neighbors_directed(node, Incoming).collect();
1047 let out_nodes: Vec<_> = self.dependency.neighbors_directed(node, Outgoing).collect();
1048
1049 self.dependency
1050 .reserve_edges(in_nodes.len() * out_nodes.len());
1051 for &in_node in &in_nodes {
1052 for &out_node in &out_nodes {
1053 self.dependency.add_edge(in_node, out_node);
1054 }
1055 }
1056 }
1057
1058 pub fn remove_systems_in_set<M>(
1060 &mut self,
1061 system_set: impl IntoSystemSet<M>,
1062 policy: ScheduleCleanupPolicy,
1063 ) -> Result<usize, ScheduleError> {
1064 let set = system_set.into_system_set();
1065 let interned = set.intern();
1066 let keys = self.systems_in_set(interned)?.clone();
1068
1069 self.changed = true;
1070
1071 match policy {
1072 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages => {
1073 let Some(set_key) = self.system_sets.get_key(interned) else {
1074 return Err(ScheduleError::SetNotFound);
1075 };
1076
1077 self.remove_systems_by_keys(&keys);
1078 self.remove_set_by_key(set_key);
1079
1080 Ok(keys.len())
1081 }
1082 ScheduleCleanupPolicy::RemoveSystemsOnlyAllowBreakages => {
1083 self.remove_systems_by_keys(&keys);
1084
1085 Ok(keys.len())
1086 }
1087 ScheduleCleanupPolicy::RemoveSetAndSystems => {
1088 let Some(set_key) = self.system_sets.get_key(interned) else {
1089 return Err(ScheduleError::SetNotFound);
1090 };
1091
1092 for &key in &keys {
1093 self.add_edges_for_transitive_dependencies(key.into());
1094 }
1095
1096 self.add_edges_for_transitive_dependencies(set_key.into());
1097
1098 self.remove_systems_by_keys(&keys);
1099 self.remove_set_by_key(set_key);
1100
1101 Ok(keys.len())
1102 }
1103 ScheduleCleanupPolicy::RemoveSystemsOnly => {
1104 for &key in &keys {
1105 self.add_edges_for_transitive_dependencies(key.into());
1106 }
1107
1108 self.remove_systems_by_keys(&keys);
1109
1110 Ok(keys.len())
1111 }
1112 }
1113 }
1114
1115 fn remove_systems_by_keys(&mut self, keys: &IndexSet<SystemKey, FixedHasher>) {
1116 for &key in keys {
1117 self.systems.remove(key);
1118
1119 let node = NodeId::from(key);
1120 self.hierarchy.remove_node(node);
1121 self.dependency.remove_node(node);
1122 self.ambiguous_with.remove_node(node);
1123 self.ambiguous_with_all.remove(&node);
1124 self.weak_node_edges
1125 .retain(|&(from, to)| from != node && to != node);
1126 self.strict_node_edges
1127 .retain(|&(from, to)| from != node && to != node);
1128 }
1129 }
1130
1131 fn remove_set_by_key(&mut self, key: SystemSetKey) {
1132 self.system_sets.remove(key);
1133 self.set_systems.remove(&key);
1134 let node = NodeId::from(key);
1135 self.hierarchy.remove_node(node);
1136 self.dependency.remove_node(node);
1137 self.ambiguous_with.remove_node(node);
1138 self.ambiguous_with_all.remove(&node);
1139 self.weak_node_edges
1140 .retain(|&(from, to)| from != node && to != node);
1141 self.strict_node_edges
1142 .retain(|&(from, to)| from != node && to != node);
1143 }
1144
1145 fn update_graphs(&mut self, id: NodeId, graph_info: GraphInfo) {
1147 self.changed = true;
1148
1149 let GraphInfo {
1150 hierarchy: sets,
1151 dependencies,
1152 ambiguous_with,
1153 ..
1154 } = graph_info;
1155
1156 self.hierarchy.add_node(id);
1157 self.dependency.add_node(id);
1158
1159 for key in sets
1160 .into_iter()
1161 .map(|set| self.system_sets.get_key_or_insert(set))
1162 {
1163 self.hierarchy.add_edge(NodeId::Set(key), id);
1164
1165 self.dependency.add_node(NodeId::Set(key));
1167 }
1168
1169 for (kind, key, options) in
1170 dependencies
1171 .into_iter()
1172 .map(|Dependency { kind, set, options }| {
1173 (kind, self.system_sets.get_key_or_insert(set), options)
1174 })
1175 {
1176 let (lhs, rhs) = match kind {
1177 DependencyKind::Before => (id, NodeId::Set(key)),
1178 DependencyKind::After => (NodeId::Set(key), id),
1179 };
1180 self.dependency.add_edge(lhs, rhs);
1181 if options.contains_key(&TypeId::of::<Weak>()) {
1182 self.weak_node_edges.insert((lhs, rhs));
1183 } else {
1184 self.strict_node_edges.insert((lhs, rhs));
1185 }
1186 for pass in self.passes.values_mut() {
1187 pass.add_dependency(lhs, rhs, &options);
1188 }
1189
1190 self.hierarchy.add_node(NodeId::Set(key));
1192 }
1193
1194 match ambiguous_with {
1195 Ambiguity::Check => (),
1196 Ambiguity::IgnoreWithSet(ambiguous_with) => {
1197 for key in ambiguous_with
1198 .into_iter()
1199 .map(|set| self.system_sets.get_key_or_insert(set))
1200 {
1201 self.ambiguous_with.add_edge(id, NodeId::Set(key));
1202 }
1203 }
1204 Ambiguity::IgnoreAll => {
1205 self.ambiguous_with_all.insert(id);
1206 }
1207 }
1208 }
1209
1210 pub fn initialize(&mut self, world: &mut World) {
1213 self.systems.initialize(world);
1214 self.system_sets.initialize(world);
1215 }
1216
1217 pub fn build_schedule(
1225 &mut self,
1226 world: &mut World,
1227 ignored_ambiguities: &BTreeSet<ComponentId>,
1228 ) -> Result<(SystemSchedule, ScheduleBuildMetadata), ScheduleBuildError> {
1229 let mut warnings = Vec::new();
1230
1231 let hierarchy_analysis = self
1233 .hierarchy
1234 .analyze()
1235 .map_err(ScheduleBuildError::HierarchySort)?;
1236
1237 if self.settings.hierarchy_detection != LogLevel::Ignore
1240 && let Err(e) = hierarchy_analysis.check_for_redundant_edges()
1241 {
1242 match self.settings.hierarchy_detection {
1243 LogLevel::Error => return Err(ScheduleBuildWarning::HierarchyRedundancy(e).into()),
1244 LogLevel::Warn => warnings.push(ScheduleBuildWarning::HierarchyRedundancy(e)),
1245 LogLevel::Ignore => unreachable!(),
1246 }
1247 }
1248 self.hierarchy.remove_redundant_edges(&hierarchy_analysis);
1250
1251 let dependency_analysis = self
1253 .dependency
1254 .analyze()
1255 .map_err(ScheduleBuildError::DependencySort)?;
1256
1257 dependency_analysis.check_for_cross_dependencies(&hierarchy_analysis)?;
1259
1260 self.set_systems = self
1262 .hierarchy
1263 .group_by_key(self.system_sets.len())
1264 .map_err(ScheduleBuildError::HierarchySort)?;
1265 dependency_analysis.check_for_overlapping_groups(&self.set_systems)?;
1267
1268 self.system_sets.check_type_set_ambiguity(
1270 &self.set_systems,
1271 &self.ambiguous_with,
1272 &self.dependency,
1273 )?;
1274
1275 let mut flat_dependency =
1279 self.set_systems
1280 .flatten(self.dependency.clone(), |set, systems, flattening, temp| {
1281 for pass in self.passes.values_mut() {
1282 pass.collapse_set(set, systems, flattening, temp);
1283 }
1284 });
1285
1286 let mut passes = core::mem::take(&mut self.passes);
1288 let mut added_edges = Default::default();
1289 for pass in passes.values_mut() {
1290 pass.build(
1291 world,
1292 self,
1293 FlattenedDependencies {
1294 dag: &mut flat_dependency,
1295 added_edges: &mut added_edges,
1296 },
1297 )?;
1298 }
1299 self.passes = passes;
1300
1301 self.initialize(world);
1304
1305 #[cfg(feature = "debug")]
1306 if let Some(shuffle_seed) = self.settings.shuffle_seed {
1307 let mut rng = rand::rngs::Xoshiro128PlusPlus::seed_from_u64(shuffle_seed);
1310
1311 let mut nodes = flat_dependency.graph().nodes().collect::<Vec<_>>();
1312 nodes.shuffle(&mut rng);
1313 let mut new_flat_dependency = Dag::new();
1314 for &node in &nodes {
1315 new_flat_dependency.add_node(node);
1316 }
1317 for node in nodes {
1318 for neighbor in flat_dependency.neighbors(node) {
1319 new_flat_dependency.add_edge(node, neighbor);
1320 }
1321 }
1322 flat_dependency = new_flat_dependency;
1323 }
1324
1325 let flat_dependency_analysis = flat_dependency
1330 .analyze()
1331 .map_err(ScheduleBuildError::FlatDependencySort)?;
1332
1333 let resolved_weak_edges =
1336 self.resolve_weak_edges(&mut flat_dependency, &flat_dependency_analysis);
1337
1338 let flat_dependency_analysis = if resolved_weak_edges {
1341 flat_dependency
1342 .analyze()
1343 .map_err(ScheduleBuildError::FlatDependencySort)?
1344 } else {
1345 flat_dependency_analysis
1346 };
1347 flat_dependency.remove_redundant_edges(&flat_dependency_analysis);
1348
1349 let flat_ambiguous_with = self.set_systems.flatten_undirected(&self.ambiguous_with);
1354
1355 self.conflicting_systems = self.systems.get_conflicting_systems(
1357 &flat_dependency_analysis,
1358 &flat_ambiguous_with,
1359 &self.ambiguous_with_all,
1360 ignored_ambiguities,
1361 );
1362 if self.settings.ambiguity_detection != LogLevel::Ignore
1364 && let Err(e) = self.conflicting_systems.check_if_not_empty()
1365 {
1366 match self.settings.ambiguity_detection {
1367 LogLevel::Error => return Err(ScheduleBuildWarning::Ambiguity(e).into()),
1368 LogLevel::Warn => warnings.push(ScheduleBuildWarning::Ambiguity(e)),
1369 LogLevel::Ignore => unreachable!(),
1370 }
1371 }
1372
1373 Ok((
1375 self.build_schedule_inner(flat_dependency, hierarchy_analysis),
1376 ScheduleBuildMetadata {
1377 warnings,
1378 edges_added_by_build_passes: added_edges,
1379 },
1380 ))
1381 }
1382
1383 fn resolve_weak_edges(
1397 &self,
1398 flat_dependency: &mut Dag<SystemKey>,
1399 analysis: &DagAnalysis<SystemKey>,
1400 ) -> bool {
1401 let weak_edges = self.flat_node_edges(&self.weak_node_edges, flat_dependency);
1402 if weak_edges.is_empty() {
1403 return false;
1404 }
1405 let strict_edges = self.flat_node_edges(&self.strict_node_edges, flat_dependency);
1406 let condition_accesses = self.condition_accesses();
1407
1408 for (from, to) in analysis.transitive_closure().all_edges() {
1412 if self.systems_conflict(from, to, &condition_accesses) {
1413 flat_dependency.add_edge(from, to);
1414 }
1415 }
1416
1417 for &(from, to) in &weak_edges {
1421 if !self.systems_conflict(from, to, &condition_accesses)
1422 && !strict_edges.contains(&(from, to))
1423 {
1424 flat_dependency.remove_edge(from, to);
1425 }
1426 }
1427
1428 true
1429 }
1430
1431 fn condition_accesses(&self) -> HashMap<SystemKey, Vec<&SystemAccess>> {
1437 let mut accesses: HashMap<SystemKey, Vec<&SystemAccess>> = HashMap::default();
1438 for (key, _, conditions) in self.systems.iter() {
1439 for condition in conditions {
1440 accesses.entry(key).or_default().push(&condition.access);
1441 }
1442 }
1443 for (key, _, conditions) in self.system_sets.iter() {
1444 if conditions.is_empty() {
1445 continue;
1446 }
1447 let Some(systems) = self.set_systems.get(&key) else {
1448 continue;
1449 };
1450 for &system in systems {
1451 accesses
1452 .entry(system)
1453 .or_default()
1454 .extend(conditions.iter().map(|condition| &condition.access));
1455 }
1456 }
1457 accesses
1458 }
1459
1460 fn flat_node_edges(
1467 &self,
1468 node_edges: &HashSet<(NodeId, NodeId)>,
1469 flat_dependency: &Dag<SystemKey>,
1470 ) -> HashSet<(SystemKey, SystemKey)> {
1471 let systems_of = |node: NodeId| -> Vec<SystemKey> {
1472 match node {
1473 NodeId::System(key) => vec![key],
1474 NodeId::Set(key) => self
1475 .set_systems
1476 .get(&key)
1477 .map(|systems| systems.iter().copied().collect())
1478 .unwrap_or_default(),
1479 }
1480 };
1481
1482 let mut edges = HashSet::default();
1483 for &(from, to) in node_edges {
1484 let (from_s, to_s) = (systems_of(from), systems_of(to));
1485 for &from in &from_s {
1486 for &to in &to_s {
1487 if flat_dependency.contains_edge(from, to) {
1488 edges.insert((from, to));
1489 }
1490 }
1491 }
1492 }
1493 edges
1494 }
1495
1496 fn systems_conflict(
1505 &self,
1506 from: SystemKey,
1507 to: SystemKey,
1508 condition_accesses: &HashMap<SystemKey, Vec<&SystemAccess>>,
1509 ) -> bool {
1510 let (from_system, to_system) = (&self.systems[from], &self.systems[to]);
1511 let conditions_conflict = |system_access: &SystemAccess, other: SystemKey| {
1514 condition_accesses.get(&other).is_some_and(|accesses| {
1515 accesses
1516 .iter()
1517 .any(|access| !system_access.is_compatible(access))
1518 })
1519 };
1520
1521 from_system.has_deferred()
1522 || from_system.access.is_exclusive()
1523 || to_system.access.is_exclusive()
1524 || !from_system.access.is_compatible(&to_system.access)
1525 || conditions_conflict(&from_system.access, to)
1526 || conditions_conflict(&to_system.access, from)
1527 }
1528
1529 fn build_schedule_inner(
1530 &self,
1531 flat_dependency: Dag<SystemKey>,
1532 hierarchy_analysis: DagAnalysis<NodeId>,
1533 ) -> SystemSchedule {
1534 let dg_system_ids = flat_dependency.get_toposort().unwrap().to_vec();
1535 let dg_system_idx_map = dg_system_ids
1536 .iter()
1537 .cloned()
1538 .enumerate()
1539 .map(|(i, id)| (id, i))
1540 .collect::<HashMap<_, _>>();
1541
1542 let hierarchy_toposort = self.hierarchy.get_toposort().unwrap();
1543 let hg_systems = hierarchy_toposort
1544 .iter()
1545 .cloned()
1546 .enumerate()
1547 .filter_map(|(i, id)| Some((i, id.as_system()?)))
1548 .collect::<Vec<_>>();
1549 let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = hierarchy_toposort
1550 .iter()
1551 .cloned()
1552 .enumerate()
1553 .filter_map(|(i, id)| {
1554 let key = id.as_set()?;
1557 self.system_sets.has_conditions(key).then_some((i, key))
1558 })
1559 .unzip();
1560
1561 let sys_count = self.systems.len();
1562 let set_with_conditions_count = hg_set_ids.len();
1563 let hg_node_count = self.hierarchy.node_count();
1564
1565 let mut system_dependencies = Vec::with_capacity(sys_count);
1568 let mut system_dependents = Vec::with_capacity(sys_count);
1569 for &sys_key in &dg_system_ids {
1570 let num_dependencies = flat_dependency
1571 .neighbors_directed(sys_key, Incoming)
1572 .count();
1573
1574 let dependents = flat_dependency
1575 .neighbors_directed(sys_key, Outgoing)
1576 .map(|dep_id| dg_system_idx_map[&dep_id])
1577 .collect::<Vec<_>>();
1578
1579 system_dependencies.push(num_dependencies);
1580 system_dependents.push(dependents);
1581 }
1582
1583 let mut systems_in_sets_with_conditions =
1586 vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
1587 for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
1588 let bitset = &mut systems_in_sets_with_conditions[i];
1589 for &(col, sys_key) in &hg_systems {
1590 let idx = dg_system_idx_map[&sys_key];
1591 let is_descendant = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1592 bitset.set(idx, is_descendant);
1593 }
1594 }
1595
1596 let mut sets_with_conditions_of_systems =
1597 vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
1598 for &(col, sys_key) in &hg_systems {
1599 let i = dg_system_idx_map[&sys_key];
1600 let bitset = &mut sets_with_conditions_of_systems[i];
1601 for (idx, &row) in hg_set_with_conditions_idxs
1602 .iter()
1603 .enumerate()
1604 .take_while(|&(_idx, &row)| row < col)
1605 {
1606 let is_ancestor = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1607 bitset.set(idx, is_ancestor);
1608 }
1609 }
1610
1611 SystemSchedule {
1612 systems: Vec::with_capacity(sys_count),
1613 system_conditions: Vec::with_capacity(sys_count),
1614 set_conditions: Vec::with_capacity(set_with_conditions_count),
1615 system_ids: dg_system_ids,
1616 set_ids: hg_set_ids,
1617 system_dependencies,
1618 system_dependents,
1619 sets_with_conditions_of_systems,
1620 systems_in_sets_with_conditions,
1621 }
1622 }
1623
1624 fn update_schedule(
1626 &mut self,
1627 world: &mut World,
1628 schedule: &mut SystemSchedule,
1629 ignored_ambiguities: &BTreeSet<ComponentId>,
1630 schedule_label: InternedScheduleLabel,
1631 ) -> Result<ScheduleBuildMetadata, ScheduleBuildError> {
1632 if !self.systems.is_initialized() || !self.system_sets.is_initialized() {
1633 return Err(ScheduleBuildError::Uninitialized);
1634 }
1635
1636 for ((key, system), conditions) in schedule
1638 .system_ids
1639 .drain(..)
1640 .zip(schedule.systems.drain(..))
1641 .zip(schedule.system_conditions.drain(..))
1642 {
1643 if let Some(node) = self.systems.node_mut(key) {
1644 node.inner = Some(system);
1645 }
1646
1647 if let Some(node_conditions) = self.systems.get_conditions_mut(key) {
1648 *node_conditions = conditions;
1649 }
1650 }
1651
1652 for (key, conditions) in schedule
1653 .set_ids
1654 .drain(..)
1655 .zip(schedule.set_conditions.drain(..))
1656 {
1657 if let Some(node_conditions) = self.system_sets.get_conditions_mut(key) {
1658 *node_conditions = conditions;
1659 }
1660 }
1661
1662 let (new_schedule, build_metadata) = self.build_schedule(world, ignored_ambiguities)?;
1663 *schedule = new_schedule;
1664
1665 for warning in &build_metadata.warnings {
1666 warn!(
1667 "{:?} schedule built successfully, however: {}",
1668 schedule_label,
1669 warning.to_string(self, world)
1670 );
1671 }
1672
1673 for &key in &schedule.system_ids {
1675 let system = self.systems.node_mut(key).unwrap().inner.take().unwrap();
1676 let conditions = core::mem::take(self.systems.get_conditions_mut(key).unwrap());
1677 schedule.systems.push(system);
1678 schedule.system_conditions.push(conditions);
1679 }
1680
1681 for &key in &schedule.set_ids {
1682 let conditions = core::mem::take(self.system_sets.get_conditions_mut(key).unwrap());
1683 schedule.set_conditions.push(conditions);
1684 }
1685
1686 Ok(build_metadata)
1687 }
1688}
1689
1690struct ProcessConfigsResult {
1692 nodes: Vec<NodeId>,
1695 densely_chained: bool,
1699}
1700
1701trait ProcessScheduleConfig: Schedulable + Sized {
1703 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId;
1705}
1706
1707impl ProcessScheduleConfig for ScheduleSystem {
1708 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1709 NodeId::System(schedule_graph.add_system_inner(config))
1710 }
1711}
1712
1713impl ProcessScheduleConfig for InternedSystemSet {
1714 fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1715 NodeId::Set(schedule_graph.configure_set_inner(config))
1716 }
1717}
1718
1719#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
1721pub enum ScheduleCleanupPolicy {
1722 #[default]
1727 RemoveSetAndSystems,
1728 RemoveSystemsOnly,
1732 RemoveSetAndSystemsAllowBreakages,
1737 RemoveSystemsOnlyAllowBreakages,
1741}
1742
1743impl ScheduleGraph {
1745 pub fn get_node_name(&self, id: &NodeId) -> String {
1752 self.get_node_name_inner(id, self.settings.report_sets)
1753 }
1754
1755 #[inline]
1756 fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
1757 match *id {
1758 NodeId::System(key) => {
1759 let name = self.systems[key].name();
1760 let name = if self.settings.use_shortnames {
1761 name.shortname().to_string()
1762 } else {
1763 name.to_string()
1764 };
1765 if report_sets {
1766 let sets = self.names_of_sets_containing_node(id);
1767 if sets.is_empty() {
1768 name
1769 } else if sets.len() == 1 {
1770 format!("{name} (in set {})", sets[0])
1771 } else {
1772 format!("{name} (in sets {})", sets.join(", "))
1773 }
1774 } else {
1775 name
1776 }
1777 }
1778 NodeId::Set(key) => {
1779 let set = &self.system_sets[key];
1780 if set.is_anonymous() {
1781 self.anonymous_set_name(id)
1782 } else {
1783 format!("{set:?}")
1784 }
1785 }
1786 }
1787 }
1788
1789 fn anonymous_set_name(&self, id: &NodeId) -> String {
1790 format!(
1791 "({})",
1792 self.hierarchy
1793 .edges_directed(*id, Outgoing)
1794 .map(|(_, member_id)| self.get_node_name_inner(&member_id, false))
1796 .reduce(|a, b| format!("{a}, {b}"))
1797 .unwrap_or_default()
1798 )
1799 }
1800
1801 fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(SystemSetKey) -> bool) {
1802 for (set_id, _) in self.hierarchy.edges_directed(id, Incoming) {
1803 let NodeId::Set(set_key) = set_id else {
1804 continue;
1805 };
1806 if f(set_key) {
1807 self.traverse_sets_containing_node(NodeId::Set(set_key), f);
1808 }
1809 }
1810 }
1811
1812 fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec<String> {
1813 let mut sets = <HashSet<_>>::default();
1814 self.traverse_sets_containing_node(*id, &mut |key| {
1815 self.system_sets[key].system_type().is_none() && sets.insert(key)
1816 });
1817 let mut sets: Vec<_> = sets
1818 .into_iter()
1819 .map(|key| self.get_node_name(&NodeId::Set(key)))
1820 .collect();
1821 sets.sort();
1822 sets
1823 }
1824}
1825
1826#[derive(Debug, Clone, Copy, PartialEq)]
1828pub enum LogLevel {
1829 Ignore,
1831 Warn,
1833 Error,
1835}
1836
1837#[derive(Clone, Debug)]
1839pub struct ScheduleBuildSettings {
1840 pub ambiguity_detection: LogLevel,
1846 pub hierarchy_detection: LogLevel,
1852 pub auto_insert_apply_deferred: bool,
1862 pub use_shortnames: bool,
1866 pub report_sets: bool,
1870 #[cfg(feature = "debug")]
1888 pub shuffle_seed: Option<u64>,
1889}
1890
1891impl Default for ScheduleBuildSettings {
1892 fn default() -> Self {
1893 Self::new()
1894 }
1895}
1896
1897impl ScheduleBuildSettings {
1898 pub const fn new() -> Self {
1901 Self {
1902 ambiguity_detection: LogLevel::Ignore,
1903 hierarchy_detection: LogLevel::Warn,
1904 auto_insert_apply_deferred: true,
1905 use_shortnames: true,
1906 report_sets: true,
1907 #[cfg(feature = "debug")]
1908 shuffle_seed: None,
1909 }
1910 }
1911}
1912
1913pub struct ScheduleBuildMetadata {
1915 pub warnings: Vec<ScheduleBuildWarning>,
1917 pub edges_added_by_build_passes: HashSet<(SystemKey, SystemKey)>,
1922}
1923
1924#[derive(Event)]
1930pub struct ScheduleBuilt {
1931 pub label: InternedScheduleLabel,
1933 pub build_metadata: ScheduleBuildMetadata,
1935}
1936
1937#[derive(Error, Debug)]
1940#[error("executable schedule has not been built")]
1941pub struct ScheduleNotInitialized;
1942
1943#[cfg(test)]
1944mod tests {
1945 use alloc::{vec, vec::Vec};
1946 use core::any::TypeId;
1947
1948 use bevy_ecs_macros::ScheduleLabel;
1949
1950 use crate::{
1951 error::{ignore, panic, FallbackErrorHandler, Result},
1952 prelude::{ApplyDeferred, IntoSystemSet, Res, Resource},
1953 schedule::{
1954 passes::AutoInsertApplyDeferredPass, tests::ResMut, FlattenedDependencies,
1955 IntoScheduleConfigs, MultiThreadedExecutor, Schedule, ScheduleBuildPass,
1956 ScheduleBuildSettings, ScheduleCleanupPolicy, SystemSet,
1957 },
1958 system::Commands,
1959 world::World,
1960 };
1961
1962 use super::Schedules;
1963
1964 #[derive(Resource)]
1965 struct Resource1;
1966
1967 #[derive(Resource)]
1968 struct Resource2;
1969
1970 #[test]
1971 fn unchanged_auto_insert_apply_deferred_has_no_effect() {
1972 use alloc::{vec, vec::Vec};
1973
1974 #[derive(PartialEq, Debug)]
1975 enum Entry {
1976 System(usize),
1977 SyncPoint(usize),
1978 }
1979
1980 #[derive(Resource, Default)]
1981 struct Log(Vec<Entry>);
1982
1983 fn system<const N: usize>(mut res: ResMut<Log>, mut commands: Commands) {
1984 res.0.push(Entry::System(N));
1985 commands
1986 .queue(|world: &mut World| world.resource_mut::<Log>().0.push(Entry::SyncPoint(N)));
1987 }
1988
1989 let mut world = World::default();
1990 world.init_resource::<Log>();
1991 let mut schedule = Schedule::default();
1992 schedule.add_systems((system::<1>, system::<2>).chain_ignore_deferred());
1993 schedule.set_build_settings(ScheduleBuildSettings {
1994 auto_insert_apply_deferred: true,
1995 ..Default::default()
1996 });
1997 schedule.run(&mut world);
1998 let actual = world.remove_resource::<Log>().unwrap().0;
1999
2000 let expected = vec![
2001 Entry::System(1),
2002 Entry::System(2),
2003 Entry::SyncPoint(1),
2004 Entry::SyncPoint(2),
2005 ];
2006
2007 assert_eq!(actual, expected);
2008 }
2009
2010 #[test]
2012 fn ambiguous_with_not_breaking_run_conditions() {
2013 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
2014 struct Set;
2015
2016 let mut world = World::new();
2017 let mut schedule = Schedule::default();
2018
2019 let system: fn() = || {
2020 panic!("This system must not run");
2021 };
2022
2023 schedule.configure_sets(Set.run_if(|| false));
2024 schedule.add_systems(system.ambiguous_with(|| ()).in_set(Set));
2025 schedule.run(&mut world);
2026 }
2027
2028 #[test]
2029 fn inserts_a_sync_point() {
2030 let mut schedule = Schedule::default();
2031 let mut world = World::default();
2032 schedule.add_systems(
2033 (
2034 |mut commands: Commands| commands.insert_resource(Resource1),
2035 |_: Res<Resource1>| {},
2036 )
2037 .chain(),
2038 );
2039 schedule.run(&mut world);
2040
2041 assert_eq!(schedule.executable.systems.len(), 3);
2043 }
2044
2045 #[test]
2046 fn explicit_sync_point_used_as_auto_sync_point() {
2047 let mut schedule = Schedule::default();
2048 let mut world = World::default();
2049 schedule.add_systems(
2050 (
2051 |mut commands: Commands| commands.insert_resource(Resource1),
2052 |_: Res<Resource1>| {},
2053 )
2054 .chain(),
2055 );
2056 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain());
2057 schedule.run(&mut world);
2058
2059 assert_eq!(schedule.executable.systems.len(), 5);
2061 }
2062
2063 #[test]
2064 fn conditional_explicit_sync_point_not_used_as_auto_sync_point() {
2065 let mut schedule = Schedule::default();
2066 let mut world = World::default();
2067 schedule.add_systems(
2068 (
2069 |mut commands: Commands| commands.insert_resource(Resource1),
2070 |_: Res<Resource1>| {},
2071 )
2072 .chain(),
2073 );
2074 schedule.add_systems((|| {}, ApplyDeferred.run_if(|| false), || {}).chain());
2075 schedule.run(&mut world);
2076
2077 assert_eq!(schedule.executable.systems.len(), 6);
2079 }
2080
2081 #[test]
2082 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_chain() {
2083 let mut schedule = Schedule::default();
2084 let mut world = World::default();
2085 schedule.add_systems(
2086 (
2087 |mut commands: Commands| commands.insert_resource(Resource1),
2088 |_: Res<Resource1>| {},
2089 )
2090 .chain(),
2091 );
2092 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().run_if(|| false));
2093 schedule.run(&mut world);
2094
2095 assert_eq!(schedule.executable.systems.len(), 6);
2097 }
2098
2099 #[test]
2100 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_system_set() {
2101 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
2102 struct Set;
2103
2104 let mut schedule = Schedule::default();
2105 let mut world = World::default();
2106 schedule.configure_sets(Set.run_if(|| false));
2107 schedule.add_systems(
2108 (
2109 |mut commands: Commands| commands.insert_resource(Resource1),
2110 |_: Res<Resource1>| {},
2111 )
2112 .chain(),
2113 );
2114 schedule.add_systems((|| {}, ApplyDeferred.in_set(Set), || {}).chain());
2115 schedule.run(&mut world);
2116
2117 assert_eq!(schedule.executable.systems.len(), 6);
2119 }
2120
2121 #[test]
2122 fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_nested_system_set()
2123 {
2124 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
2125 struct Set1;
2126 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
2127 struct Set2;
2128
2129 let mut schedule = Schedule::default();
2130 let mut world = World::default();
2131 schedule.configure_sets(Set2.run_if(|| false));
2132 schedule.configure_sets(Set1.in_set(Set2));
2133 schedule.add_systems(
2134 (
2135 |mut commands: Commands| commands.insert_resource(Resource1),
2136 |_: Res<Resource1>| {},
2137 )
2138 .chain(),
2139 );
2140 schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().in_set(Set1));
2141 schedule.run(&mut world);
2142
2143 assert_eq!(schedule.executable.systems.len(), 6);
2145 }
2146
2147 #[test]
2148 fn merges_sync_points_into_one() {
2149 let mut schedule = Schedule::default();
2150 let mut world = World::default();
2151 schedule.add_systems(
2153 (
2154 (
2155 |mut commands: Commands| commands.insert_resource(Resource1),
2156 |mut commands: Commands| commands.insert_resource(Resource2),
2157 ),
2158 |_: Res<Resource1>, _: Res<Resource2>| {},
2159 )
2160 .chain(),
2161 );
2162 schedule.run(&mut world);
2163
2164 assert_eq!(schedule.executable.systems.len(), 4);
2166
2167 schedule.add_systems(((
2169 (
2170 |mut commands: Commands| commands.insert_resource(Resource1),
2171 |mut commands: Commands| commands.insert_resource(Resource2),
2172 ),
2173 |_: Res<Resource1>, _: Res<Resource2>| {},
2174 )
2175 .chain(),));
2176 schedule.run(&mut world);
2177
2178 assert_eq!(schedule.executable.systems.len(), 7);
2179 }
2180
2181 #[test]
2182 fn adds_multiple_consecutive_syncs() {
2183 let mut schedule = Schedule::default();
2184 let mut world = World::default();
2185 schedule.add_systems(
2187 (
2188 |mut commands: Commands| commands.insert_resource(Resource1),
2189 |mut commands: Commands| commands.insert_resource(Resource2),
2190 |_: Res<Resource1>, _: Res<Resource2>| {},
2191 )
2192 .chain(),
2193 );
2194 schedule.run(&mut world);
2195
2196 assert_eq!(schedule.executable.systems.len(), 5);
2197 }
2198
2199 #[test]
2200 fn do_not_consider_ignore_deferred_before_exclusive_system() {
2201 let mut schedule = Schedule::default();
2202 let mut world = World::default();
2203 schedule.add_systems(
2205 (
2206 |_: Commands| {},
2207 |mut commands: Commands| commands.insert_resource(Resource1),
2209 |world: &mut World| assert!(world.contains_resource::<Resource1>()),
2211 |_: &mut World| {},
2213 |_: Commands| {},
2215 )
2216 .chain_ignore_deferred(),
2217 );
2218 schedule.run(&mut world);
2219
2220 assert_eq!(schedule.executable.systems.len(), 6); }
2222
2223 #[test]
2224 fn bubble_sync_point_through_ignore_deferred_node() {
2225 let mut schedule = Schedule::default();
2226 let mut world = World::default();
2227
2228 let insert_resource_config = (
2229 |mut commands: Commands| commands.insert_resource(Resource1),
2231 || {},
2233 )
2234 .chain_ignore_deferred();
2236
2237 schedule.add_systems(
2238 (
2239 insert_resource_config,
2240 |_: Res<Resource1>| {},
2242 )
2243 .chain(),
2245 );
2246
2247 schedule.run(&mut world);
2252
2253 assert_eq!(schedule.executable.systems.len(), 4); }
2255
2256 #[test]
2257 fn disable_auto_sync_points() {
2258 let mut schedule = Schedule::default();
2259 schedule.set_build_settings(ScheduleBuildSettings {
2260 auto_insert_apply_deferred: false,
2261 ..Default::default()
2262 });
2263 let mut world = World::default();
2264 schedule.add_systems(
2265 (
2266 |mut commands: Commands| commands.insert_resource(Resource1),
2267 |res: Option<Res<Resource1>>| assert!(res.is_none()),
2268 )
2269 .chain(),
2270 );
2271 schedule.run(&mut world);
2272
2273 assert_eq!(schedule.executable.systems.len(), 2);
2274 }
2275
2276 mod no_sync_edges {
2277 use super::*;
2278
2279 fn insert_resource(mut commands: Commands) {
2280 commands.insert_resource(Resource1);
2281 }
2282
2283 fn resource_does_not_exist(res: Option<Res<Resource1>>) {
2284 assert!(res.is_none());
2285 }
2286
2287 #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
2288 enum Sets {
2289 A,
2290 B,
2291 }
2292
2293 fn check_no_sync_edges(add_systems: impl FnOnce(&mut Schedule)) {
2294 let mut schedule = Schedule::default();
2295 let mut world = World::default();
2296 add_systems(&mut schedule);
2297
2298 schedule.run(&mut world);
2299
2300 assert_eq!(schedule.executable.systems.len(), 2);
2301 }
2302
2303 #[test]
2304 fn system_to_system_after() {
2305 check_no_sync_edges(|schedule| {
2306 schedule.add_systems((
2307 insert_resource,
2308 resource_does_not_exist.after_ignore_deferred(insert_resource),
2309 ));
2310 });
2311 }
2312
2313 #[test]
2314 fn system_to_system_before() {
2315 check_no_sync_edges(|schedule| {
2316 schedule.add_systems((
2317 insert_resource.before_ignore_deferred(resource_does_not_exist),
2318 resource_does_not_exist,
2319 ));
2320 });
2321 }
2322
2323 #[test]
2324 fn set_to_system_after() {
2325 check_no_sync_edges(|schedule| {
2326 schedule
2327 .add_systems((insert_resource, resource_does_not_exist.in_set(Sets::A)))
2328 .configure_sets(Sets::A.after_ignore_deferred(insert_resource));
2329 });
2330 }
2331
2332 #[test]
2333 fn set_to_system_before() {
2334 check_no_sync_edges(|schedule| {
2335 schedule
2336 .add_systems((insert_resource.in_set(Sets::A), resource_does_not_exist))
2337 .configure_sets(Sets::A.before_ignore_deferred(resource_does_not_exist));
2338 });
2339 }
2340
2341 #[test]
2342 fn set_to_set_after() {
2343 check_no_sync_edges(|schedule| {
2344 schedule
2345 .add_systems((
2346 insert_resource.in_set(Sets::A),
2347 resource_does_not_exist.in_set(Sets::B),
2348 ))
2349 .configure_sets(Sets::B.after_ignore_deferred(Sets::A));
2350 });
2351 }
2352
2353 #[test]
2354 fn set_to_set_before() {
2355 check_no_sync_edges(|schedule| {
2356 schedule
2357 .add_systems((
2358 insert_resource.in_set(Sets::A),
2359 resource_does_not_exist.in_set(Sets::B),
2360 ))
2361 .configure_sets(Sets::A.before_ignore_deferred(Sets::B));
2362 });
2363 }
2364 }
2365
2366 mod no_sync_chain {
2367 use super::*;
2368
2369 #[derive(Resource)]
2370 struct Ra;
2371
2372 #[derive(Resource)]
2373 struct Rb;
2374
2375 #[derive(Resource)]
2376 struct Rc;
2377
2378 fn run_schedule(expected_num_systems: usize, add_systems: impl FnOnce(&mut Schedule)) {
2379 let mut schedule = Schedule::default();
2380 let mut world = World::default();
2381 add_systems(&mut schedule);
2382
2383 schedule.run(&mut world);
2384
2385 assert_eq!(schedule.executable.systems.len(), expected_num_systems);
2386 }
2387
2388 #[test]
2389 fn only_chain_outside() {
2390 run_schedule(5, |schedule: &mut Schedule| {
2391 schedule.add_systems(
2392 (
2393 (
2394 |mut commands: Commands| commands.insert_resource(Ra),
2395 |mut commands: Commands| commands.insert_resource(Rb),
2396 ),
2397 (
2398 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2399 assert!(res_a.is_some());
2400 assert!(res_b.is_some());
2401 },
2402 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2403 assert!(res_a.is_some());
2404 assert!(res_b.is_some());
2405 },
2406 ),
2407 )
2408 .chain(),
2409 );
2410 });
2411
2412 run_schedule(4, |schedule: &mut Schedule| {
2413 schedule.add_systems(
2414 (
2415 (
2416 |mut commands: Commands| commands.insert_resource(Ra),
2417 |mut commands: Commands| commands.insert_resource(Rb),
2418 ),
2419 (
2420 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2421 assert!(res_a.is_none());
2422 assert!(res_b.is_none());
2423 },
2424 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2425 assert!(res_a.is_none());
2426 assert!(res_b.is_none());
2427 },
2428 ),
2429 )
2430 .chain_ignore_deferred(),
2431 );
2432 });
2433 }
2434
2435 #[test]
2436 fn chain_first() {
2437 run_schedule(6, |schedule: &mut Schedule| {
2438 schedule.add_systems(
2439 (
2440 (
2441 |mut commands: Commands| commands.insert_resource(Ra),
2442 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2443 commands.insert_resource(Rb);
2444 assert!(res_a.is_some());
2445 },
2446 )
2447 .chain(),
2448 (
2449 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2450 assert!(res_a.is_some());
2451 assert!(res_b.is_some());
2452 },
2453 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2454 assert!(res_a.is_some());
2455 assert!(res_b.is_some());
2456 },
2457 ),
2458 )
2459 .chain(),
2460 );
2461 });
2462
2463 run_schedule(5, |schedule: &mut Schedule| {
2464 schedule.add_systems(
2465 (
2466 (
2467 |mut commands: Commands| commands.insert_resource(Ra),
2468 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2469 commands.insert_resource(Rb);
2470 assert!(res_a.is_some());
2471 },
2472 )
2473 .chain(),
2474 (
2475 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2476 assert!(res_a.is_some());
2477 assert!(res_b.is_none());
2478 },
2479 |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2480 assert!(res_a.is_some());
2481 assert!(res_b.is_none());
2482 },
2483 ),
2484 )
2485 .chain_ignore_deferred(),
2486 );
2487 });
2488 }
2489
2490 #[test]
2491 fn chain_second() {
2492 run_schedule(6, |schedule: &mut Schedule| {
2493 schedule.add_systems(
2494 (
2495 (
2496 |mut commands: Commands| commands.insert_resource(Ra),
2497 |mut commands: Commands| commands.insert_resource(Rb),
2498 ),
2499 (
2500 |mut commands: Commands,
2501 res_a: Option<Res<Ra>>,
2502 res_b: Option<Res<Rb>>| {
2503 commands.insert_resource(Rc);
2504 assert!(res_a.is_some());
2505 assert!(res_b.is_some());
2506 },
2507 |res_a: Option<Res<Ra>>,
2508 res_b: Option<Res<Rb>>,
2509 res_c: Option<Res<Rc>>| {
2510 assert!(res_a.is_some());
2511 assert!(res_b.is_some());
2512 assert!(res_c.is_some());
2513 },
2514 )
2515 .chain(),
2516 )
2517 .chain(),
2518 );
2519 });
2520
2521 run_schedule(5, |schedule: &mut Schedule| {
2522 schedule.add_systems(
2523 (
2524 (
2525 |mut commands: Commands| commands.insert_resource(Ra),
2526 |mut commands: Commands| commands.insert_resource(Rb),
2527 ),
2528 (
2529 |mut commands: Commands,
2530 res_a: Option<Res<Ra>>,
2531 res_b: Option<Res<Rb>>| {
2532 commands.insert_resource(Rc);
2533 assert!(res_a.is_none());
2534 assert!(res_b.is_none());
2535 },
2536 |res_a: Option<Res<Ra>>,
2537 res_b: Option<Res<Rb>>,
2538 res_c: Option<Res<Rc>>| {
2539 assert!(res_a.is_some());
2540 assert!(res_b.is_some());
2541 assert!(res_c.is_some());
2542 },
2543 )
2544 .chain(),
2545 )
2546 .chain_ignore_deferred(),
2547 );
2548 });
2549 }
2550
2551 #[test]
2552 fn chain_all() {
2553 run_schedule(7, |schedule: &mut Schedule| {
2554 schedule.add_systems(
2555 (
2556 (
2557 |mut commands: Commands| commands.insert_resource(Ra),
2558 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2559 commands.insert_resource(Rb);
2560 assert!(res_a.is_some());
2561 },
2562 )
2563 .chain(),
2564 (
2565 |mut commands: Commands,
2566 res_a: Option<Res<Ra>>,
2567 res_b: Option<Res<Rb>>| {
2568 commands.insert_resource(Rc);
2569 assert!(res_a.is_some());
2570 assert!(res_b.is_some());
2571 },
2572 |res_a: Option<Res<Ra>>,
2573 res_b: Option<Res<Rb>>,
2574 res_c: Option<Res<Rc>>| {
2575 assert!(res_a.is_some());
2576 assert!(res_b.is_some());
2577 assert!(res_c.is_some());
2578 },
2579 )
2580 .chain(),
2581 )
2582 .chain(),
2583 );
2584 });
2585
2586 run_schedule(6, |schedule: &mut Schedule| {
2587 schedule.add_systems(
2588 (
2589 (
2590 |mut commands: Commands| commands.insert_resource(Ra),
2591 |mut commands: Commands, res_a: Option<Res<Ra>>| {
2592 commands.insert_resource(Rb);
2593 assert!(res_a.is_some());
2594 },
2595 )
2596 .chain(),
2597 (
2598 |mut commands: Commands,
2599 res_a: Option<Res<Ra>>,
2600 res_b: Option<Res<Rb>>| {
2601 commands.insert_resource(Rc);
2602 assert!(res_a.is_some());
2603 assert!(res_b.is_none());
2604 },
2605 |res_a: Option<Res<Ra>>,
2606 res_b: Option<Res<Rb>>,
2607 res_c: Option<Res<Rc>>| {
2608 assert!(res_a.is_some());
2609 assert!(res_b.is_some());
2610 assert!(res_c.is_some());
2611 },
2612 )
2613 .chain(),
2614 )
2615 .chain_ignore_deferred(),
2616 );
2617 });
2618 }
2619 }
2620
2621 #[derive(ScheduleLabel, Hash, Debug, Clone, PartialEq, Eq)]
2622 struct TestSchedule;
2623
2624 #[derive(Resource)]
2625 struct CheckSystemRan(usize);
2626
2627 #[test]
2628 fn add_systems_to_existing_schedule() {
2629 let mut schedules = Schedules::default();
2630 let schedule = Schedule::new(TestSchedule);
2631
2632 schedules.insert(schedule);
2633 schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2634
2635 let mut world = World::new();
2636
2637 world.insert_resource(CheckSystemRan(0));
2638 world.insert_resource(schedules);
2639 world.run_schedule(TestSchedule);
2640
2641 let value = world
2642 .get_resource::<CheckSystemRan>()
2643 .expect("CheckSystemRan Resource Should Exist");
2644 assert_eq!(value.0, 1);
2645 }
2646
2647 #[test]
2648 fn add_systems_to_non_existing_schedule() {
2649 let mut schedules = Schedules::default();
2650
2651 schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2652
2653 let mut world = World::new();
2654
2655 world.insert_resource(CheckSystemRan(0));
2656 world.insert_resource(schedules);
2657 world.run_schedule(TestSchedule);
2658
2659 let value = world
2660 .get_resource::<CheckSystemRan>()
2661 .expect("CheckSystemRan Resource Should Exist");
2662 assert_eq!(value.0, 1);
2663 }
2664
2665 #[derive(SystemSet, Debug, Hash, Clone, PartialEq, Eq)]
2666 enum TestSet {
2667 First,
2668 Second,
2669 }
2670
2671 #[test]
2672 fn configure_set_on_existing_schedule() {
2673 let mut schedules = Schedules::default();
2674 let schedule = Schedule::new(TestSchedule);
2675
2676 schedules.insert(schedule);
2677
2678 schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2679 schedules.add_systems(
2680 TestSchedule,
2681 (|mut ran: ResMut<CheckSystemRan>| {
2682 assert_eq!(ran.0, 0);
2683 ran.0 += 1;
2684 })
2685 .in_set(TestSet::First),
2686 );
2687
2688 schedules.add_systems(
2689 TestSchedule,
2690 (|mut ran: ResMut<CheckSystemRan>| {
2691 assert_eq!(ran.0, 1);
2692 ran.0 += 1;
2693 })
2694 .in_set(TestSet::Second),
2695 );
2696
2697 let mut world = World::new();
2698
2699 world.insert_resource(CheckSystemRan(0));
2700 world.insert_resource(schedules);
2701 world.run_schedule(TestSchedule);
2702
2703 let value = world
2704 .get_resource::<CheckSystemRan>()
2705 .expect("CheckSystemRan Resource Should Exist");
2706 assert_eq!(value.0, 2);
2707 }
2708
2709 #[test]
2710 fn configure_set_on_new_schedule() {
2711 let mut schedules = Schedules::default();
2712
2713 schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2714 schedules.add_systems(
2715 TestSchedule,
2716 (|mut ran: ResMut<CheckSystemRan>| {
2717 assert_eq!(ran.0, 0);
2718 ran.0 += 1;
2719 })
2720 .in_set(TestSet::First),
2721 );
2722
2723 schedules.add_systems(
2724 TestSchedule,
2725 (|mut ran: ResMut<CheckSystemRan>| {
2726 assert_eq!(ran.0, 1);
2727 ran.0 += 1;
2728 })
2729 .in_set(TestSet::Second),
2730 );
2731
2732 let mut world = World::new();
2733
2734 world.insert_resource(CheckSystemRan(0));
2735 world.insert_resource(schedules);
2736 world.run_schedule(TestSchedule);
2737
2738 let value = world
2739 .get_resource::<CheckSystemRan>()
2740 .expect("CheckSystemRan Resource Should Exist");
2741 assert_eq!(value.0, 2);
2742 }
2743
2744 #[test]
2745 fn test_default_error_handler() {
2746 #[derive(Resource, Default)]
2747 struct Ran(bool);
2748
2749 fn system(mut ran: ResMut<Ran>) -> Result {
2750 ran.0 = true;
2751 Err("I failed!".into())
2752 }
2753
2754 let mut world = World::default();
2756 world.init_resource::<Ran>();
2757 world.insert_resource(FallbackErrorHandler(ignore));
2758 let mut schedule = Schedule::default();
2759 schedule.add_systems(system).run(&mut world);
2760 assert!(world.resource::<Ran>().0);
2761
2762 schedule.add_systems(
2764 (|world: &mut World| {
2765 world.insert_resource(FallbackErrorHandler(panic));
2766 })
2767 .before(system),
2768 );
2769 schedule.run(&mut world);
2770 }
2771
2772 #[test]
2773 fn get_a_system_key() {
2774 fn test_system() {}
2775
2776 let mut schedule = Schedule::default();
2777 schedule.add_systems(test_system);
2778 let mut world = World::default();
2779 let _ = schedule.initialize(&mut world);
2780
2781 let keys = schedule
2782 .graph()
2783 .systems_in_set(test_system.into_system_set().intern())
2784 .unwrap();
2785 assert_eq!(keys.len(), 1);
2786 }
2787
2788 #[test]
2789 fn get_system_keys_in_set() {
2790 fn system_1() {}
2791 fn system_2() {}
2792
2793 let mut schedule = Schedule::default();
2794 schedule.add_systems((system_1, system_2).in_set(TestSet::First));
2795 let mut world = World::default();
2796 let _ = schedule.initialize(&mut world);
2797
2798 let keys = schedule
2799 .graph()
2800 .systems_in_set(TestSet::First.into_system_set().intern())
2801 .unwrap();
2802 assert_eq!(keys.len(), 2);
2803 }
2804
2805 #[test]
2806 fn get_system_keys_with_same_name() {
2807 fn test_system() {}
2808
2809 let mut schedule = Schedule::default();
2810 schedule.add_systems((test_system, test_system));
2811 let mut world = World::default();
2812 let _ = schedule.initialize(&mut world);
2813
2814 let keys = schedule
2815 .graph()
2816 .systems_in_set(test_system.into_system_set().intern())
2817 .unwrap();
2818 assert_eq!(keys.len(), 2);
2819 }
2820
2821 #[test]
2822 fn remove_a_system() {
2823 fn system() {}
2824
2825 let mut schedule = Schedule::default();
2826 schedule.add_systems(system);
2827 let mut world = World::default();
2828
2829 let remove_count = schedule.remove_systems_in_set(
2830 system,
2831 &mut world,
2832 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2833 );
2834 assert_eq!(remove_count.unwrap(), 1);
2835
2836 schedule.initialize(&mut world).unwrap();
2838 assert_eq!(schedule.graph().systems.len(), 0);
2839 }
2840
2841 #[test]
2842 fn remove_multiple_systems() {
2843 fn system() {}
2844
2845 let mut schedule = Schedule::default();
2846 schedule.add_systems((system, system));
2847 let mut world = World::default();
2848
2849 let remove_count = schedule.remove_systems_in_set(
2850 system,
2851 &mut world,
2852 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2853 );
2854 assert_eq!(remove_count.unwrap(), 2);
2855
2856 schedule.initialize(&mut world).unwrap();
2858 assert_eq!(schedule.graph().systems.len(), 0);
2859 }
2860
2861 #[test]
2862 fn remove_a_system_with_dependencies() {
2863 fn system_1() {}
2864 fn system_2() {}
2865
2866 let mut schedule = Schedule::default();
2867 schedule.add_systems((system_1, system_2).chain());
2868 let mut world = World::default();
2869
2870 let remove_count = schedule.remove_systems_in_set(
2871 system_1,
2872 &mut world,
2873 ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2874 );
2875 assert_eq!(remove_count.unwrap(), 1);
2876
2877 schedule.initialize(&mut world).unwrap();
2879 assert_eq!(schedule.graph().systems.len(), 1);
2880 }
2881
2882 #[test]
2883 fn remove_a_system_and_still_ordered() {
2884 #[derive(Resource)]
2885 struct A;
2886
2887 fn system_1(_: ResMut<A>) {}
2888 fn system_2() {}
2889 fn system_3(_: ResMut<A>) {}
2890
2891 let mut schedule = Schedule::default();
2892 schedule.add_systems((system_1, system_2, system_3).chain());
2893 let mut world = World::new();
2894
2895 let _ = schedule.remove_systems_in_set(
2896 system_2,
2897 &mut world,
2898 ScheduleCleanupPolicy::RemoveSetAndSystems,
2899 );
2900
2901 let result = schedule.initialize(&mut world);
2902 assert!(result.is_ok());
2903 let conflicts = schedule.graph().conflicting_systems();
2904 assert!(conflicts.is_empty());
2905 }
2906
2907 #[test]
2908 fn remove_a_set_and_still_ordered() {
2909 #[derive(Resource)]
2910 struct A;
2911
2912 #[derive(SystemSet, Hash, PartialEq, Eq, Clone, Debug)]
2913 struct B;
2914
2915 fn system_1(_: ResMut<A>) {}
2916 fn system_2() {}
2917 fn system_3(_: ResMut<A>) {}
2918
2919 let mut schedule = Schedule::default();
2920 schedule.add_systems((system_1.before(B), system_2, system_3.after(B)));
2921 let mut world = World::new();
2922
2923 let _ = schedule.remove_systems_in_set(
2924 B,
2925 &mut world,
2926 ScheduleCleanupPolicy::RemoveSetAndSystems,
2927 );
2928
2929 let result = schedule.initialize(&mut world);
2930 assert!(result.is_ok());
2931 let conflicts = schedule.graph().conflicting_systems();
2932 assert!(conflicts.is_empty());
2933 }
2934
2935 #[test]
2936 fn build_pass_iteration_order() {
2937 #[derive(Debug)]
2938 struct Pass<const N: usize>;
2939
2940 impl<const N: usize> ScheduleBuildPass for Pass<N> {
2941 type EdgeOptions = ();
2942 fn add_dependency(
2943 &mut self,
2944 _from: crate::schedule::NodeId,
2945 _to: crate::schedule::NodeId,
2946 _options: Option<&Self::EdgeOptions>,
2947 ) {
2948 }
2949 fn build(
2950 &mut self,
2951 _world: &mut World,
2952 _graph: &mut super::ScheduleGraph,
2953 _dependency_flattened: FlattenedDependencies<'_>,
2954 ) -> core::result::Result<(), crate::schedule::ScheduleBuildError> {
2955 Ok(())
2956 }
2957 fn collapse_set(
2958 &mut self,
2959 _set: crate::schedule::SystemSetKey,
2960 _systems: &indexmap::IndexSet<
2961 crate::schedule::SystemKey,
2962 bevy_platform::hash::FixedHasher,
2963 >,
2964 _dependency_flattening: &crate::schedule::graph::DiGraph<crate::schedule::NodeId>,
2965 ) -> impl Iterator<Item = (crate::schedule::NodeId, crate::schedule::NodeId)>
2966 {
2967 core::iter::empty()
2968 }
2969 }
2970
2971 let mut schedule = Schedule::default();
2972 schedule.add_build_pass(Pass::<0>);
2973 schedule.add_build_pass(Pass::<1>);
2974 schedule.add_build_pass(Pass::<2>);
2975
2976 let pass_order: Vec<TypeId> = schedule.graph().passes.keys().cloned().collect();
2977
2978 assert_eq!(
2979 pass_order,
2980 vec![
2981 TypeId::of::<AutoInsertApplyDeferredPass>(),
2982 TypeId::of::<Pass<0>>(),
2983 TypeId::of::<Pass<1>>(),
2984 TypeId::of::<Pass<2>>()
2985 ]
2986 );
2987 }
2988
2989 #[cfg(feature = "debug")]
2990 #[test]
2991 fn schedule_builds_randomly_with_shuffler() {
2992 fn run_schedule_with_shuffler(shuffle_seed: Option<u64>) -> Vec<u32> {
2993 #[derive(Resource, Default)]
2994 struct Counters(Vec<u32>);
2995
2996 let mut schedule = Schedule::default();
2997
2998 fn system<const N: u32>(mut counters: ResMut<Counters>) {
3001 counters.0.push(N);
3002 }
3003
3004 schedule.add_systems(system::<0>);
3011 schedule.add_systems((system::<10>, system::<11>).after(system::<0>));
3012 schedule.add_systems((system::<20>, system::<21>).after(system::<10>));
3013
3014 schedule.set_build_settings(ScheduleBuildSettings {
3015 shuffle_seed,
3016 ..Default::default()
3017 });
3018
3019 let mut world = World::new();
3020 world.init_resource::<Counters>();
3021 schedule.initialize(&mut world).unwrap();
3022 schedule.run(&mut world);
3023
3024 world.remove_resource::<Counters>().unwrap().0
3025 }
3026
3027 for _ in 0..10 {
3028 assert_eq!(
3029 run_schedule_with_shuffler(None),
3030 [0, 11, 10, 20, 21]
3032 );
3033 }
3034
3035 assert_eq!(
3038 run_schedule_with_shuffler(Some(100000001)),
3039 [0, 10, 20, 21, 11]
3040 );
3041 assert_eq!(
3042 run_schedule_with_shuffler(Some(100000030)),
3043 [0, 10, 21, 20, 11]
3044 );
3045 assert_eq!(
3046 run_schedule_with_shuffler(Some(100000020)),
3047 [0, 10, 20, 11, 21]
3048 );
3049 assert_eq!(
3050 run_schedule_with_shuffler(Some(100000063)),
3051 [0, 10, 21, 11, 20]
3052 );
3053 assert_eq!(
3054 run_schedule_with_shuffler(Some(100000003)),
3055 [0, 10, 11, 20, 21]
3056 );
3057 assert_eq!(
3058 run_schedule_with_shuffler(Some(100000080)),
3059 [0, 10, 11, 21, 20]
3060 );
3061 assert_eq!(
3062 run_schedule_with_shuffler(Some(100000000)),
3063 [0, 11, 10, 20, 21]
3064 );
3065 assert_eq!(
3066 run_schedule_with_shuffler(Some(100000004)),
3067 [0, 11, 10, 21, 20]
3068 );
3069
3070 }
3081
3082 fn total_dependencies(schedule: &Schedule) -> usize {
3084 schedule.executable.system_dependencies.iter().sum()
3085 }
3086
3087 #[test]
3088 fn chain_weak_adds_no_edges_for_non_conflicting_systems() {
3089 fn read<const N: usize>(_: Res<Resource1>) {}
3090
3091 let mut world = World::default();
3092
3093 let mut strict = Schedule::default();
3095 strict.add_systems((read::<1>, read::<2>, read::<3>).chain());
3096 strict.initialize(&mut world).unwrap();
3097 assert_eq!(total_dependencies(&strict), 2);
3098
3099 let mut weak = Schedule::default();
3103 weak.add_systems((read::<1>, read::<2>, read::<3>).chain_weak());
3104 weak.initialize(&mut world).unwrap();
3105 assert_eq!(total_dependencies(&weak), 0);
3106 }
3107
3108 #[test]
3109 fn chain_weak_orders_conflicting_systems_with_finish_edge() {
3110 fn write<const N: usize>(_: ResMut<Resource1>) {}
3111
3112 let mut world = World::default();
3113 let mut schedule = Schedule::default();
3114 schedule.add_systems((write::<1>, write::<2>).chain_weak());
3117 schedule.initialize(&mut world).unwrap();
3118
3119 assert_eq!(total_dependencies(&schedule), 1);
3120 }
3121
3122 #[test]
3123 fn chain_weak_keeps_finish_dependency_for_deferred() {
3124 let mut world = World::default();
3125 let mut schedule = Schedule::default();
3126 schedule.set_executor(MultiThreadedExecutor::new());
3127 schedule.add_systems(
3128 (
3129 |mut commands: Commands| commands.insert_resource(Resource1),
3130 |_: Res<Resource1>| {},
3131 )
3132 .chain_weak(),
3133 );
3134 schedule.run(&mut world);
3138
3139 assert_eq!(schedule.executable.systems.len(), 3);
3141 assert_eq!(total_dependencies(&schedule), 2);
3142 }
3143
3144 #[test]
3145 fn chain_weak_keeps_finish_dependency_for_exclusive() {
3146 fn read<const N: usize>(_: Res<Resource1>) {}
3147 fn exclusive(_: &mut World) {}
3148
3149 let mut world = World::default();
3150 let mut schedule = Schedule::default();
3151 schedule.add_systems((read::<1>, exclusive, read::<2>).chain_weak());
3152 schedule.initialize(&mut world).unwrap();
3153
3154 assert_eq!(total_dependencies(&schedule), 2);
3158 }
3159
3160 #[test]
3161 fn chain_weak_between_non_conflicting_sets_adds_no_edges() {
3162 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3163 enum Sets {
3164 A,
3165 B,
3166 }
3167
3168 fn read<const N: usize>(_: Res<Resource1>) {}
3169
3170 let mut world = World::default();
3171 let mut schedule = Schedule::default();
3172 schedule.configure_sets((Sets::A, Sets::B).chain_weak());
3173 schedule.add_systems((read::<1>.in_set(Sets::A), read::<2>.in_set(Sets::B)));
3174 schedule.initialize(&mut world).unwrap();
3175
3176 assert_eq!(total_dependencies(&schedule), 0);
3178 }
3179
3180 #[test]
3181 fn chain_weak_between_sets_fans_out_conflict_edges() {
3182 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3183 enum Sets {
3184 A,
3185 B,
3186 }
3187
3188 fn write<const N: usize>(_: ResMut<Resource1>) {}
3189
3190 let mut world = World::default();
3191 let mut schedule = Schedule::default();
3192 schedule.configure_sets((Sets::A, Sets::B).chain_weak());
3193 schedule.add_systems((
3194 (write::<1>, write::<2>).in_set(Sets::A),
3195 (write::<3>, write::<4>).in_set(Sets::B),
3196 ));
3197 schedule.initialize(&mut world).unwrap();
3198
3199 assert_eq!(total_dependencies(&schedule), 4);
3203 }
3204
3205 #[test]
3206 fn before_weak_between_non_conflicting_sets_adds_no_edges() {
3207 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3208 enum Sets {
3209 A,
3210 B,
3211 }
3212
3213 fn read<const N: usize>(_: Res<Resource1>) {}
3214
3215 let mut world = World::default();
3216 let mut schedule = Schedule::default();
3217 schedule.configure_sets(Sets::A.before_weak(Sets::B));
3218 schedule.add_systems((read::<1>.in_set(Sets::A), read::<2>.in_set(Sets::B)));
3219 schedule.initialize(&mut world).unwrap();
3220
3221 assert_eq!(total_dependencies(&schedule), 0);
3223 }
3224
3225 #[test]
3226 fn after_weak_orders_conflicting_sets_with_finish_edge() {
3227 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3228 enum Sets {
3229 A,
3230 B,
3231 }
3232
3233 fn write<const N: usize>(_: ResMut<Resource1>) {}
3234
3235 let mut world = World::default();
3236 let mut schedule = Schedule::default();
3237 schedule.configure_sets(Sets::B.after_weak(Sets::A));
3238 schedule.add_systems((write::<1>.in_set(Sets::A), write::<2>.in_set(Sets::B)));
3239 schedule.initialize(&mut world).unwrap();
3240
3241 assert_eq!(total_dependencies(&schedule), 1);
3244 }
3245
3246 #[test]
3247 fn before_weak_keeps_finish_dependency_for_deferred() {
3248 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3249 enum Sets {
3250 A,
3251 B,
3252 }
3253
3254 fn with_commands(_: Commands) {}
3255 fn read<const N: usize>(_: Res<Resource1>) {}
3256
3257 let mut world = World::default();
3258 let mut schedule = Schedule::default();
3259 schedule.configure_sets(Sets::A.before_weak(Sets::B));
3260 schedule.add_systems((with_commands.in_set(Sets::A), read::<2>.in_set(Sets::B)));
3263 schedule.initialize(&mut world).unwrap();
3264
3265 assert_eq!(total_dependencies(&schedule), 2);
3267 }
3268
3269 #[test]
3270 fn before_weak_orders_conflicting_systems() {
3271 #[derive(Resource, Default)]
3272 struct Order(Vec<u32>);
3273 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3274 enum Sets {
3275 A,
3276 B,
3277 }
3278
3279 fn record<const N: u32>(mut order: ResMut<Order>) {
3280 order.0.push(N);
3281 }
3282
3283 let mut world = World::default();
3284 world.init_resource::<Order>();
3285 let mut schedule = Schedule::default();
3286 schedule.set_executor(MultiThreadedExecutor::new());
3289 schedule.configure_sets(Sets::A.before_weak(Sets::B));
3290 schedule.add_systems((record::<1>.in_set(Sets::A), record::<2>.in_set(Sets::B)));
3292 schedule.run(&mut world);
3293
3294 assert_eq!(world.resource::<Order>().0, vec![1, 2]);
3295 }
3296
3297 #[test]
3298 fn chain_weak_orders_conflicting_systems() {
3299 #[derive(Resource, Default)]
3300 struct Order(Vec<u32>);
3301
3302 fn record<const N: u32>(mut order: ResMut<Order>) {
3303 order.0.push(N);
3304 }
3305
3306 let mut world = World::default();
3307 world.init_resource::<Order>();
3308 let mut schedule = Schedule::default();
3309 schedule.set_executor(MultiThreadedExecutor::new());
3312 schedule.add_systems((record::<1>, record::<2>).chain_weak());
3314 schedule.run(&mut world);
3315
3316 assert_eq!(world.resource::<Order>().0, vec![1, 2]);
3317 }
3318
3319 #[test]
3320 fn chain_weak_materializes_transitive_conflict_edge() {
3321 fn write<const N: usize>(_: ResMut<Resource1>) {}
3322 fn read(_: Res<Resource2>) {}
3323
3324 let mut world = World::default();
3325 let mut schedule = Schedule::default();
3326 schedule.add_systems((write::<1>, read, write::<3>).chain_weak());
3331 schedule.initialize(&mut world).unwrap();
3332
3333 assert_eq!(total_dependencies(&schedule), 1);
3335 assert!(schedule.graph().conflicting_systems().is_empty());
3337 }
3338
3339 #[test]
3340 fn weak_chain_leaves_explicit_strict_edge_intact() {
3341 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3342 enum Sets {
3343 A,
3344 B,
3345 C,
3346 }
3347
3348 fn read<const N: usize>(_: Res<Resource1>) {}
3349
3350 let mut world = World::default();
3351 let mut schedule = Schedule::default();
3352 schedule.configure_sets((Sets::A, Sets::B, Sets::C).chain_weak());
3354 schedule.configure_sets(Sets::C.after(Sets::A));
3355 schedule.add_systems((
3356 read::<1>.in_set(Sets::A),
3357 read::<2>.in_set(Sets::B),
3358 read::<3>.in_set(Sets::C),
3359 ));
3360 schedule.initialize(&mut world).unwrap();
3361
3362 assert_eq!(total_dependencies(&schedule), 1);
3365 }
3366
3367 #[test]
3368 fn weak_ordering_keeps_edge_shared_with_strict_ordering() {
3369 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3370 enum Sets {
3371 A,
3372 B,
3373 }
3374
3375 fn read<const N: usize>(_: Res<Resource1>) {}
3376
3377 let mut world = World::default();
3378 let mut schedule = Schedule::default();
3379 schedule.configure_sets((Sets::A, Sets::B).chain_weak());
3383 schedule.configure_sets(Sets::B.after(Sets::A));
3384 schedule.add_systems((read::<1>.in_set(Sets::A), read::<2>.in_set(Sets::B)));
3385 schedule.initialize(&mut world).unwrap();
3386
3387 assert_eq!(total_dependencies(&schedule), 1);
3388 }
3389
3390 #[test]
3391 fn strict_chain_after_weak_group_waits_for_whole_group() {
3392 fn read<const N: usize>(_: Res<Resource1>) {}
3393
3394 let mut world = World::default();
3395 let mut schedule = Schedule::default();
3396 schedule.add_systems(((read::<1>, read::<2>, read::<3>).chain_weak(), read::<4>).chain());
3399 schedule.initialize(&mut world).unwrap();
3400
3401 assert_eq!(total_dependencies(&schedule), 3);
3404 }
3405
3406 #[test]
3407 fn chain_weak_orders_writer_before_system_with_conflicting_condition() {
3408 fn write(_: ResMut<Resource1>) {}
3409 fn noop() {}
3410
3411 let mut world = World::default();
3412 let mut schedule = Schedule::default();
3413 schedule.add_systems((write, noop.run_if(|_: Res<Resource1>| true)).chain_weak());
3417 schedule.initialize(&mut world).unwrap();
3418
3419 assert_eq!(total_dependencies(&schedule), 1);
3420 }
3421
3422 #[test]
3423 fn chain_weak_orders_system_with_conflicting_condition_before_writer() {
3424 fn write(_: ResMut<Resource1>) {}
3425 fn noop() {}
3426
3427 let mut world = World::default();
3428 let mut schedule = Schedule::default();
3429 schedule.add_systems((noop.run_if(|_: Res<Resource1>| true), write).chain_weak());
3433 schedule.initialize(&mut world).unwrap();
3434
3435 assert_eq!(total_dependencies(&schedule), 1);
3436 }
3437
3438 #[test]
3439 fn chain_weak_orders_writer_before_set_with_conflicting_condition() {
3440 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3441 enum Sets {
3442 A,
3443 B,
3444 }
3445
3446 fn write(_: ResMut<Resource1>) {}
3447 fn noop() {}
3448
3449 let mut world = World::default();
3450 let mut schedule = Schedule::default();
3451 schedule.configure_sets((Sets::A, Sets::B).chain_weak());
3452 schedule.configure_sets(Sets::B.run_if(|_: Res<Resource1>| true));
3456 schedule.add_systems((write.in_set(Sets::A), noop.in_set(Sets::B)));
3457 schedule.initialize(&mut world).unwrap();
3458
3459 assert_eq!(total_dependencies(&schedule), 1);
3460 }
3461
3462 #[test]
3463 fn weak_set_ordering_with_ignore_deferred_adds_no_sync_point() {
3464 #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
3465 enum Sets {
3466 A,
3467 B,
3468 }
3469
3470 fn insert_resource(mut commands: Commands) {
3471 commands.insert_resource(Resource1);
3472 }
3473 fn resource_does_not_exist(res: Option<Res<Resource1>>) {
3474 assert!(res.is_none());
3475 }
3476
3477 let mut world = World::default();
3478 let mut schedule = Schedule::default();
3479 schedule.configure_sets((Sets::A, Sets::B).chain_weak());
3482 schedule.configure_sets(Sets::A.before_ignore_deferred(Sets::B));
3483 schedule.add_systems((
3484 insert_resource.in_set(Sets::A),
3485 resource_does_not_exist.in_set(Sets::B),
3486 ));
3487 schedule.run(&mut world);
3488
3489 assert_eq!(schedule.executable.systems.len(), 2);
3490 assert_eq!(total_dependencies(&schedule), 1);
3491 }
3492
3493 #[test]
3494 fn weak_edge_after_ignore_deferred_edge_gets_bubbled_sync_point() {
3495 fn insert_resource(mut commands: Commands) {
3496 commands.insert_resource(Resource1);
3497 }
3498 fn read_other(_: Res<Resource2>) {}
3499 fn resource_exists(res: Option<Res<Resource1>>) {
3500 assert!(res.is_some());
3501 }
3502
3503 let mut world = World::default();
3504 world.insert_resource(Resource2);
3505 let mut schedule = Schedule::default();
3506 schedule.set_executor(MultiThreadedExecutor::new());
3507 schedule.add_systems(
3508 (
3509 (insert_resource, read_other).chain_ignore_deferred(),
3510 resource_exists,
3511 )
3512 .chain_weak(),
3513 );
3514 schedule.run(&mut world);
3518
3519 assert_eq!(schedule.executable.systems.len(), 4); assert_eq!(total_dependencies(&schedule), 3);
3521 }
3522
3523 #[test]
3524 fn chain_ignore_deferred_around_weak_group_fans_out_without_sync_point() {
3525 fn read<const N: usize>(_: Res<Resource1>) {}
3526 fn with_commands(_: Commands) {}
3527
3528 let mut world = World::default();
3529 let mut schedule = Schedule::default();
3530 schedule.add_systems(
3532 (
3533 (read::<1>, read::<2>).chain_weak(),
3534 with_commands,
3535 read::<3>,
3536 )
3537 .chain_ignore_deferred(),
3538 );
3539 schedule.initialize(&mut world).unwrap();
3540
3541 assert_eq!(schedule.executable.systems.len(), 4);
3545 assert_eq!(total_dependencies(&schedule), 3);
3546 }
3547
3548 #[test]
3549 fn chain_weak_between_ignore_deferred_groups_drops_edge_between_them() {
3550 fn read<const N: usize>(_: Res<Resource1>) {}
3551 fn with_commands(_: Commands) {}
3552
3553 let mut world = World::default();
3554 let mut schedule = Schedule::default();
3555 schedule.add_systems(
3558 (
3559 (read::<1>, read::<2>).chain_ignore_deferred(),
3560 (with_commands, read::<3>).chain_ignore_deferred(),
3561 )
3562 .chain_weak(),
3563 );
3564 schedule.initialize(&mut world).unwrap();
3565
3566 assert_eq!(schedule.executable.systems.len(), 4);
3568 assert_eq!(total_dependencies(&schedule), 2);
3569 }
3570
3571 #[test]
3572 fn ignore_deferred_still_syncs_before_exclusive_system_in_weak_chain() {
3573 fn insert_resource(mut commands: Commands) {
3574 commands.insert_resource(Resource1);
3575 }
3576 fn exclusive(world: &mut World) {
3577 assert!(world.contains_resource::<Resource1>());
3578 }
3579 fn read(_: Res<Resource1>) {}
3580
3581 let mut world = World::default();
3582 let mut schedule = Schedule::default();
3583 schedule
3584 .add_systems(((insert_resource, exclusive).chain_ignore_deferred(), read).chain_weak());
3585 schedule.run(&mut world);
3588
3589 assert_eq!(schedule.executable.systems.len(), 4); assert_eq!(total_dependencies(&schedule), 3);
3593 }
3594}