Skip to main content

bevy_ecs/schedule/
schedule.rs

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/// Resource that stores [`Schedule`]s mapped to [`ScheduleLabel`]s excluding the current running [`Schedule`].
45#[derive(Default, Resource)]
46pub struct Schedules {
47    inner: HashMap<InternedScheduleLabel, Schedule>,
48    /// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts
49    pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
50    /// Set of schedule labels that have been removed to execute in [`World::try_schedule_scope`].
51    temporarily_removed: HashSet<InternedScheduleLabel>,
52    /// Set of schedule labels that have attempted to be read in [`World::try_schedule_scope`],
53    /// but have no associated [`Schedule`] in `inner`
54    empty_labels: HashSet<InternedScheduleLabel>,
55}
56
57impl Schedules {
58    /// Constructs an empty `Schedules` with zero initial capacity.
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    /// Inserts a labeled schedule into the map.
64    ///
65    /// If the map already had an entry for `label`, `schedule` is inserted,
66    /// and the old schedule is returned. Otherwise, `None` is returned.
67    pub fn insert(&mut self, schedule: Schedule) -> Option<Schedule> {
68        self.temporarily_removed.remove(&schedule.label);
69        // error if above is true
70        self.inner.insert(schedule.label, schedule)
71    }
72
73    /// Inserts a labeled schedule into the map.
74    ///
75    /// If the map already had an entry for `label`, `schedule` is inserted,
76    /// and the old schedule is returned. Otherwise, `None` is returned.
77    pub fn reinsert(&mut self, schedule: Schedule) -> Option<Schedule> {
78        self.temporarily_removed.remove(&schedule.label);
79        // error if above false
80        self.inner.insert(schedule.label, schedule)
81    }
82
83    /// Removes the schedule corresponding to the `label` from the map, returning it if it existed.
84    pub fn remove(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
85        self.inner.remove(&label.intern())
86    }
87
88    /// Removes the schedule corresponding to the `label` from the map, returning it if it existed, tracks.
89    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            // error if above false
95            self.empty_labels.remove(&label);
96        } else {
97            self.empty_labels.insert(label);
98        }
99        k
100    }
101
102    /// Removes the (schedule, label) pair corresponding to the `label` from the map, returning it if it existed.
103    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    /// Gets a set of temporarily removed schedules
111    pub fn get_temporarily_removed(&self) -> HashSet<InternedScheduleLabel> {
112        self.temporarily_removed.clone()
113    }
114
115    /// Gets a set of empty schedule labels
116    pub fn get_empty_labels(&self) -> HashSet<InternedScheduleLabel> {
117        self.empty_labels.clone()
118    }
119
120    /// Does a schedule with the provided label already exist?
121    pub fn contains(&self, label: impl ScheduleLabel) -> bool {
122        self.inner.contains_key(&label.intern())
123    }
124
125    /// Returns a reference to the schedule associated with `label`, if it exists.
126    pub fn get(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
127        self.inner.get(&label.intern())
128    }
129
130    /// Returns a mutable reference to the schedule associated with `label`, if it exists.
131    pub fn get_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
132        self.inner.get_mut(&label.intern())
133    }
134
135    /// Returns a mutable reference to the schedules associated with `label`, creating one if it doesn't already exist.
136    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    /// Returns an iterator over all schedules. Iteration order is undefined.
143    pub fn iter(&self) -> impl Iterator<Item = (&dyn ScheduleLabel, &Schedule)> {
144        self.inner
145            .iter()
146            .map(|(label, schedule)| (&**label, schedule))
147    }
148    /// Returns an iterator over mutable references to all schedules. Iteration order is undefined.
149    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    /// Iterates the change ticks of all systems in all stored schedules and clamps any older than
156    /// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
157    /// This prevents overflow and thus prevents false positives.
158    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    /// Applies the provided [`ScheduleBuildSettings`] to all schedules.
178    ///
179    /// This mutates all currently present schedules, but does not apply to schedules added
180    /// in the future.
181    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    /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`.
188    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    /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`.
194    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    /// Iterate through the [`ComponentId`]'s that will be ignored.
200    pub fn iter_ignored_ambiguities(&self) -> impl Iterator<Item = &ComponentId> + '_ {
201        self.ignored_scheduling_ambiguities.iter()
202    }
203
204    /// Prints the names of the components and resources with [`info`]
205    ///
206    /// May panic or retrieve incorrect names if [`Components`] is not from the same
207    /// world
208    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    /// Adds one or more systems to the [`Schedule`] matching the provided [`ScheduleLabel`].
220    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    /// Removes all systems in a [`SystemSet`]. This will cause the schedule to be rebuilt when
231    /// the schedule is run again. A [`ScheduleError`] is returned if the schedule needs to be
232    /// [`Schedule::initialize`]'d or the `set` is not found.
233    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    /// Configures a collection of system sets in the provided schedule, adding any sets that do not exist.
246    #[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    /// Suppress warnings and errors that would result from systems in these sets having ambiguities
258    /// (conflicting access but indeterminate order) with systems in `set`.
259    ///
260    /// When possible, do this directly in the `.add_systems(Update, a.ambiguous_with(b))` call.
261    /// However, sometimes two independent plugins `A` and `B` are reported as ambiguous, which you
262    /// can only suppress as the consumer of both.
263    #[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/// Chain systems into dependencies
281#[derive(Default)]
282pub enum Chain {
283    /// Systems are independent. Nodes are allowed to run in any order.
284    #[default]
285    Unchained,
286    /// Systems are chained. `before -> after` ordering constraints
287    /// will be added between the successive elements.
288    Chained(TypeIdMap<Box<dyn Any>>),
289}
290
291impl Chain {
292    /// Specify that the systems must be chained.
293    pub fn set_chained(&mut self) {
294        if matches!(self, Chain::Unchained) {
295            *self = Self::Chained(Default::default());
296        };
297    }
298    /// Specify that the systems must be chained, and add the specified configuration for
299    /// all dependencies created between these systems.
300    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
310/// A collection of systems, and the metadata and executor needed to run them
311/// in a certain order under certain conditions.
312///
313/// # Schedule labels
314///
315/// Each schedule has a [`ScheduleLabel`] value. This value is used to uniquely identify the
316/// schedule when added to a [`World`]’s [`Schedules`], and may be used to specify which schedule
317/// a system should be added to.
318///
319/// # Example
320///
321/// Here is an example of a `Schedule` running a "Hello world" system:
322///
323/// ```
324/// # use bevy_ecs::prelude::*;
325/// fn hello_world() { println!("Hello world!") }
326///
327/// fn main() {
328///     let mut world = World::new();
329///     let mut schedule = Schedule::default();
330///     schedule.add_systems(hello_world);
331///
332///     schedule.run(&mut world);
333/// }
334/// ```
335///
336/// A schedule can also run several systems in an ordered way:
337///
338/// ```
339/// # use bevy_ecs::prelude::*;
340/// fn system_one() { println!("System 1 works!") }
341/// fn system_two() { println!("System 2 works!") }
342/// fn system_three() { println!("System 3 works!") }
343///
344/// fn main() {
345///     let mut world = World::new();
346///     let mut schedule = Schedule::default();
347///     schedule.add_systems((
348///         system_two,
349///         system_one.before(system_two),
350///         system_three.after(system_two),
351///     ));
352///
353///     schedule.run(&mut world);
354/// }
355/// ```
356///
357/// Schedules are often inserted into a [`World`] and identified by their [`ScheduleLabel`] only:
358///
359/// ```
360/// # use bevy_ecs::prelude::*;
361/// use bevy_ecs::schedule::ScheduleLabel;
362///
363/// // Declare a new schedule label.
364/// #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
365/// struct Update;
366///
367/// // This system shall be part of the schedule.
368/// fn an_update_system() {
369///     println!("Hello world!");
370/// }
371///
372/// fn main() {
373///     let mut world = World::new();
374///
375///     // Add a system to the schedule with that label (creating it automatically).
376///     world.get_resource_or_init::<Schedules>().add_systems(Update, an_update_system);
377///
378///     // Run the schedule, and therefore run the system.
379///     world.run_schedule(Update);
380/// }
381/// ```
382pub 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    /// Creates a schedule with a default label. Only use in situations where
395    /// you don't care about the [`ScheduleLabel`]. Inserting a default schedule
396    /// into the world risks overwriting another schedule. For most situations
397    /// you should use [`Schedule::new`].
398    fn default() -> Self {
399        Self::new(DefaultSchedule)
400    }
401}
402
403impl Schedule {
404    /// Constructs an empty `Schedule`.
405    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        // Call `set_build_settings` to add any default build passes
414        this.set_build_settings(Default::default());
415        this
416    }
417
418    /// Returns whether this schedule has been changed since the last time it was built.
419    pub fn is_changed(&self) -> bool {
420        self.graph.changed
421    }
422
423    /// Returns the [`InternedScheduleLabel`] for this `Schedule`,
424    /// corresponding to the [`ScheduleLabel`] this schedule was created with.
425    pub fn label(&self) -> InternedScheduleLabel {
426        self.label
427    }
428
429    /// Add a collection of systems to the schedule.
430    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    /// Removes all systems in a [`SystemSet`]. This will cause the schedule to be rebuilt when
439    /// the schedule is run again. A [`ScheduleError`] is returned if the schedule needs to be
440    /// [`Schedule::initialize`]'d or the `set` is not found.
441    ///
442    /// Note that this can remove all systems of a type if you pass
443    /// the system to this function as systems implicitly create a set based
444    /// on the system type.
445    ///
446    /// ## Example
447    /// ```
448    /// # use bevy_ecs::prelude::*;
449    /// # use bevy_ecs::schedule::ScheduleCleanupPolicy;
450    /// #
451    /// # fn my_system() {}
452    /// #
453    /// let mut schedule = Schedule::default();
454    /// // add the system to the schedule
455    /// schedule.add_systems(my_system);
456    /// let mut world = World::default();
457    ///
458    /// // remove the system
459    /// schedule.remove_systems_in_set(my_system, &mut world, ScheduleCleanupPolicy::RemoveSystemsOnly);
460    /// ```
461    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    /// Suppress warnings and errors that would result from systems in these sets having ambiguities
474    /// (conflicting access but indeterminate order) with systems in `set`.
475    #[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    /// Configures a collection of system sets in this schedule, adding them if they does not exist.
495    #[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    /// Add a custom build pass to the schedule.
505    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    /// Remove a custom build pass.
511    pub fn remove_build_pass<T: ScheduleBuildPass>(&mut self) {
512        self.graph.passes.shift_remove(&TypeId::of::<T>());
513    }
514
515    /// Changes miscellaneous build settings.
516    ///
517    /// If [`settings.auto_insert_apply_deferred`][ScheduleBuildSettings::auto_insert_apply_deferred]
518    /// is `false`, this clears `*_ignore_deferred` edge settings configured so far.
519    ///
520    /// Generally this method should be used before adding systems or set configurations to the schedule,
521    /// not after.
522    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    /// Returns the schedule's current `ScheduleBuildSettings`.
539    pub fn get_build_settings(&self) -> ScheduleBuildSettings {
540        self.graph.settings.clone()
541    }
542
543    /// Replaces the schedule's executor.
544    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    /// Set whether the schedule applies deferred system buffers on final time or not. This is a catch-all
551    /// in case a system uses commands but was not explicitly ordered before an instance of
552    /// [`ApplyDeferred`]. By default this
553    /// setting is true, but may be disabled if needed.
554    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    /// Runs all systems in this schedule on the `world`, using its current execution strategy.
560    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    /// Initializes any newly-added systems and conditions, rebuilds the executable schedule,
596    /// and re-initializes the executor.
597    ///
598    /// Moves all systems and run conditions out of the [`ScheduleGraph`]. If the schedule is built
599    /// successfully, returns [`Some`] with the metadata. If the schedule has previously been built
600    /// successfully, returns [`None`].
601    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    /// Returns the [`ScheduleGraph`].
638    pub fn graph(&self) -> &ScheduleGraph {
639        &self.graph
640    }
641
642    /// Returns a mutable reference to the [`ScheduleGraph`].
643    pub fn graph_mut(&mut self) -> &mut ScheduleGraph {
644        &mut self.graph
645    }
646
647    /// Returns the [`SystemSchedule`].
648    pub(crate) fn executable(&self) -> &SystemSchedule {
649        &self.executable
650    }
651
652    /// Iterates the change ticks of all systems in the schedule and clamps any older than
653    /// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
654    /// This prevents overflow and thus prevents false positives.
655    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    /// Directly applies any accumulated [`Deferred`](crate::system::Deferred) system parameters (like [`Commands`](crate::prelude::Commands)) to the `world`.
676    ///
677    /// Like always, deferred system parameters are applied in the "topological sort order" of the schedule graph.
678    /// As a result, buffers from one system are only guaranteed to be applied before those of other systems
679    /// if there is an explicit system ordering between the two systems.
680    ///
681    /// This is used in rendering to extract data from the main world, storing the data in system buffers,
682    /// before applying their buffers in a different world.
683    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    /// Returns an iterator over all systems in this schedule.
690    ///
691    /// Note: this method will return [`ScheduleNotInitialized`] if the
692    /// schedule has never been initialized or run.
693    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    /// Returns the number of systems in this schedule.
712    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/// Metadata for a [`Schedule`].
722///
723/// The order isn't optimized; calling `ScheduleGraph::build_schedule` will return a
724/// `SystemSchedule` where the order is optimized for execution.
725#[derive(Default)]
726pub struct ScheduleGraph {
727    /// Container of systems in the schedule.
728    pub systems: Systems,
729    /// Container of system sets in the schedule.
730    pub system_sets: SystemSets,
731    /// Directed acyclic graph of the hierarchy (which systems/sets are children of which sets)
732    hierarchy: Dag<NodeId>,
733    /// Directed acyclic graph of the dependency (which systems/sets have to run before which other systems/sets)
734    dependency: Dag<NodeId>,
735    /// Map of systems in each set
736    set_systems: DagGroups<SystemSetKey, SystemKey>,
737    ambiguous_with: UnGraph<NodeId>,
738    /// Nodes that are allowed to have ambiguous ordering relationship with any other systems.
739    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    /// Creates an empty [`ScheduleGraph`] with default settings.
749    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    /// Returns the [`Dag`] of the hierarchy.
767    ///
768    /// The hierarchy is a directed acyclic graph of the systems and sets,
769    /// where an edge denotes that a system or set is the child of another set.
770    pub fn hierarchy(&self) -> &Dag<NodeId> {
771        &self.hierarchy
772    }
773
774    /// Returns the [`Dag`] of the dependencies in the schedule.
775    ///
776    /// Nodes in this graph are systems and sets, and edges denote that
777    /// a system or set has to run before another system or set.
778    pub fn dependency(&self) -> &Dag<NodeId> {
779        &self.dependency
780    }
781
782    /// Returns the list of systems that conflict with each other, i.e. have ambiguities in their access.
783    ///
784    /// If the `Vec<ComponentId>` is empty, the systems conflict on [`World`] access.
785    /// Must be called after [`ScheduleGraph::build_schedule`] to be non-empty.
786    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    /// Adds the config nodes to the graph.
829    ///
830    /// `collect_nodes` controls whether the `NodeId`s of the processed config nodes are stored in the returned [`ProcessConfigsResult`].
831    /// `process_config` is the function which processes each individual config node and returns a corresponding `NodeId`.
832    ///
833    /// The fields on the returned [`ProcessConfigsResult`] are:
834    /// - `nodes`: a vector of all node ids contained in the nested `ScheduleConfigs`
835    /// - `densely_chained`: a boolean that is true if all nested nodes are linearly chained (with successive `after` orderings) in the order they are defined
836    #[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                // Densely chained if
856                // * chained and all configs in the chain are densely chained, or
857                // * unchained with a single densely chained config
858                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                        // if the current result is densely chained, we only need to chain the first node
877                        let current_nodes = if current_result.densely_chained {
878                            &current_result.nodes[..1]
879                        } else {
880                            &current_result.nodes
881                        };
882                        // if the previous result was densely chained, we only need to chain the last node
883                        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    /// Add a [`ScheduleConfig`] to the graph, including its dependencies and conditions.
924    fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
925        let key = self.systems.insert(config.node, config.conditions);
926
927        // graph updates are immediate
928        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    /// Add a single `ScheduleConfig` to the graph, including its dependencies and conditions.
939    fn configure_set_inner(&mut self, config: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
940        let key = self.system_sets.insert(config.node, config.conditions);
941
942        // graph updates are immediate
943        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    /// Returns a `Vec` containing all [`SystemKey`]s in a [`SystemSet`].
955    ///
956    /// # Errors
957    ///
958    /// This method may return an error. It'll be:
959    ///
960    /// - `ScheduleError::Uninitialized` if the schedule has been changed,
961    ///   and `Self::initialize` has not been called.
962    /// - `ScheduleError::NotFound` if `system_set` isn't present in the
963    ///   schedule.
964    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    /// Remove all systems in a set and any dependencies on those systems and set.
1005    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        // clone the keys out of the schedule as the systems are getting removed from self
1013        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    /// Update the internal graphs (hierarchy, dependency, ambiguity) by adding a single [`GraphInfo`]
1082    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            // ensure set also appears in dependency graph
1102            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            // ensure set also appears in hierarchy graph
1122            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    /// Initializes any newly-added systems and conditions by calling
1142    /// [`System::initialize`](crate::system::System).
1143    pub fn initialize(&mut self, world: &mut World) {
1144        self.systems.initialize(world);
1145        self.system_sets.initialize(world);
1146    }
1147
1148    /// Builds an execution-optimized [`SystemSchedule`] from the current state
1149    /// of the graph. Also returns any warnings that were generated during the
1150    /// build process.
1151    ///
1152    /// This method also
1153    /// - checks for dependency or hierarchy cycles
1154    /// - checks for system access conflicts and reports ambiguities
1155    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        // Check system set memberships for cycles.
1163        let hierarchy_analysis = self
1164            .hierarchy
1165            .analyze()
1166            .map_err(ScheduleBuildError::HierarchySort)?;
1167
1168        // Check for redundant system set memberships, logging warnings or
1169        // returning errors as configured.
1170        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        // Remove redundant system set memberships.
1180        self.hierarchy.remove_redundant_edges(&hierarchy_analysis);
1181
1182        // Check system and system set ordering dependencies for cycles.
1183        let dependency_analysis = self
1184            .dependency
1185            .analyze()
1186            .map_err(ScheduleBuildError::DependencySort)?;
1187
1188        // System sets that share systems and have an ordering dependency cannot be ordered.
1189        dependency_analysis.check_for_cross_dependencies(&hierarchy_analysis)?;
1190
1191        // Group all systems by the system sets they belong to.
1192        self.set_systems = self
1193            .hierarchy
1194            .group_by_key(self.system_sets.len())
1195            .map_err(ScheduleBuildError::HierarchySort)?;
1196        // Check for system sets that share systems but have an ordering dependency.
1197        dependency_analysis.check_for_overlapping_groups(&self.set_systems)?;
1198
1199        // There can be no edges to system-type sets that have multiple instances.
1200        self.system_sets.check_type_set_ambiguity(
1201            &self.set_systems,
1202            &self.ambiguous_with,
1203            &self.dependency,
1204        )?;
1205
1206        // Flatten system ordering dependencies by collapsing system sets. This
1207        // means that if a system set has ordering dependencies, those
1208        // dependencies are applied to all systems in the set.
1209        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        // Allow modification of the schedule graph by build passes.
1218        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        // Check system ordering dependencies for cycles after collapsing sets
1233        // and applying build passes.
1234        let flat_dependency_analysis = flat_dependency
1235            .analyze()
1236            .map_err(ScheduleBuildError::FlatDependencySort)?;
1237        flat_dependency.remove_redundant_edges(&flat_dependency_analysis);
1238
1239        // Flatten accepted system ordering ambiguities by collapsing system sets.
1240        // This means that if a system set is allowed to have ambiguous ordering
1241        // with another set, all systems in the first set are allowed to have
1242        // ambiguous ordering with all systems in the second set.
1243        let flat_ambiguous_with = self.set_systems.flatten_undirected(&self.ambiguous_with);
1244
1245        // Find all system ordering ambiguities, ignoring those that are accepted.
1246        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 there are any ambiguities, log warnings or return errors as configured.
1253        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        // build the schedule
1264        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                // ignore system sets that have no conditions
1299                // ignore system type sets (already covered, they don't have conditions)
1300                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        // get the number of dependencies and the immediate dependents of each system
1310        // (needed by multi_threaded executor to run systems in the correct order)
1311        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        // get the rows and columns of the hierarchy graph's reachability matrix
1328        // (needed to we can evaluate conditions in the correct order)
1329        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    /// Updates the `SystemSchedule` from the `ScheduleGraph`.
1369    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        // move systems out of old schedule
1381        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        // move systems into new schedule
1418        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
1434/// Values returned by [`ScheduleGraph::process_configs`]
1435struct ProcessConfigsResult {
1436    /// All nodes contained inside this `process_configs` call's [`ScheduleConfigs`] hierarchy,
1437    /// if `ancestor_chained` is true
1438    nodes: Vec<NodeId>,
1439    /// True if and only if all nodes are "densely chained", meaning that all nested nodes
1440    /// are linearly chained (as if `after` system ordering had been applied between each node)
1441    /// in the order they are defined
1442    densely_chained: bool,
1443}
1444
1445/// Trait used by [`ScheduleGraph::process_configs`] to process a single [`ScheduleConfig`].
1446trait ProcessScheduleConfig: Schedulable + Sized {
1447    /// Process a single [`ScheduleConfig`].
1448    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/// Policy to use when removing systems.
1464#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
1465pub enum ScheduleCleanupPolicy {
1466    /// Remove the referenced set and any systems in the set.
1467    /// Attempts to maintain the order between the transitive dependencies by adding new edges
1468    /// between the existing before and after dependencies on the set and the systems.
1469    /// This does not remove sets that might sub sets of the set.
1470    #[default]
1471    RemoveSetAndSystems,
1472    /// Remove only the systems in the set. The set
1473    /// Attempts to maintain the order between the transitive dependencies by adding new edges
1474    /// between the existing before and after dependencies on the systems.
1475    RemoveSystemsOnly,
1476    /// Remove the set and any systems in the set.
1477    /// Note that this will not add new edges and
1478    /// so will break any transitive dependencies on that set or systems.
1479    /// This does not remove sets that might sub sets of the set.
1480    RemoveSetAndSystemsAllowBreakages,
1481    /// Remove only the systems in the set.
1482    /// Note that this will not add new edges and
1483    /// so will break any transitive dependencies on that set or systems.
1484    RemoveSystemsOnlyAllowBreakages,
1485}
1486
1487// methods for reporting errors
1488impl ScheduleGraph {
1489    /// Returns the name of the node with the given [`NodeId`]. Resolves
1490    /// anonymous sets to a string that describes their contents.
1491    ///
1492    /// Also displays the set(s) the node is contained in if
1493    /// [`ScheduleBuildSettings::report_sets`] is true, and shortens system names
1494    /// if [`ScheduleBuildSettings::use_shortnames`] is true.
1495    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                // never get the sets of the members or this will infinite recurse when the report_sets setting is on.
1539                .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/// Specifies how schedule construction should respond to detecting a certain kind of issue.
1571#[derive(Debug, Clone, Copy, PartialEq)]
1572pub enum LogLevel {
1573    /// Occurrences are completely ignored.
1574    Ignore,
1575    /// Occurrences are logged only.
1576    Warn,
1577    /// Occurrences are logged and result in errors.
1578    Error,
1579}
1580
1581/// Specifies miscellaneous settings for schedule construction.
1582#[derive(Clone, Debug)]
1583pub struct ScheduleBuildSettings {
1584    /// Determines whether the presence of ambiguities (systems with conflicting access but indeterminate order)
1585    /// is only logged or also results in an [`Ambiguity`](ScheduleBuildWarning::Ambiguity)
1586    /// warning or error.
1587    ///
1588    /// Defaults to [`LogLevel::Ignore`].
1589    pub ambiguity_detection: LogLevel,
1590    /// Determines whether the presence of redundant edges in the hierarchy of system sets is only
1591    /// logged or also results in a [`HierarchyRedundancy`](ScheduleBuildWarning::HierarchyRedundancy)
1592    /// warning or error.
1593    ///
1594    /// Defaults to [`LogLevel::Warn`].
1595    pub hierarchy_detection: LogLevel,
1596    /// Auto insert [`ApplyDeferred`] systems into the schedule,
1597    /// when there are [`Deferred`](crate::prelude::Deferred)
1598    /// in one system and there are ordering dependencies on that system. [`Commands`](crate::system::Commands) is one
1599    /// such deferred buffer.
1600    ///
1601    /// You may want to disable this if you only want to sync deferred params at the end of the schedule,
1602    /// or want to manually insert all your sync points.
1603    ///
1604    /// Defaults to `true`
1605    pub auto_insert_apply_deferred: bool,
1606    /// If set to true, node names will be shortened instead of the fully qualified type path.
1607    ///
1608    /// Defaults to `true`.
1609    pub use_shortnames: bool,
1610    /// If set to true, report all system sets the conflicting systems are part of.
1611    ///
1612    /// Defaults to `true`.
1613    pub report_sets: bool,
1614}
1615
1616impl Default for ScheduleBuildSettings {
1617    fn default() -> Self {
1618        Self::new()
1619    }
1620}
1621
1622impl ScheduleBuildSettings {
1623    /// Default build settings.
1624    /// See the field-level documentation for the default value of each field.
1625    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
1636/// Metadata about the schedule build process.
1637pub struct ScheduleBuildMetadata {
1638    /// Warnings about the schedule graph detected by the build process.
1639    pub warnings: Vec<ScheduleBuildWarning>,
1640    /// Edges added by [`ScheduleBuildPass`]es.
1641    ///
1642    /// These edges are not stored in the [`ScheduleGraph`], and so are only available during the
1643    /// build process.
1644    pub edges_added_by_build_passes: HashSet<(SystemKey, SystemKey)>,
1645}
1646
1647/// An event triggered when a schedule is successfully built.
1648///
1649/// Note: When this event is triggered, the corresponding [`Schedule`] is not present in the world.
1650/// So, observers will need to cache whatever data they need from this and access it later once the
1651/// schedule is not running.
1652#[derive(Event)]
1653pub struct ScheduleBuilt {
1654    /// The schedule that was built.
1655    pub label: InternedScheduleLabel,
1656    /// The metadata for the build process of this schedule.
1657    pub build_metadata: ScheduleBuildMetadata,
1658}
1659
1660/// Error to denote that [`Schedule::initialize`] or [`Schedule::run`] has not yet been called for
1661/// this schedule.
1662#[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    // regression test for https://github.com/bevyengine/bevy/issues/9114
1734    #[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        // inserted a sync point
1765        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        // No sync point was inserted, since we can reuse the explicit sync point.
1783        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        // A sync point was inserted, since the explicit sync point is not always run.
1801        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        // A sync point was inserted, since the explicit sync point is not always run.
1819        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        // A sync point was inserted, since the explicit sync point is not always run.
1841        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        // A sync point was inserted, since the explicit sync point is not always run.
1867        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        // insert two parallel command systems, it should only create one sync point
1875        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        // inserted sync points
1888        assert_eq!(schedule.executable.systems.len(), 4);
1889
1890        // merges sync points on rebuild
1891        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        // insert two consecutive command systems, it should create two sync points
1909        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        // chain_ignore_deferred adds no sync points usually but an exception is made for exclusive systems
1927        schedule.add_systems(
1928            (
1929                |_: Commands| {},
1930                // <- no sync point is added here because the following system is not exclusive
1931                |mut commands: Commands| commands.insert_resource(Resource1),
1932                // <- sync point is added here because the following system is exclusive which expects to see all commands to that point
1933                |world: &mut World| assert!(world.contains_resource::<Resource1>()),
1934                // <- no sync point is added here because the previous system has no deferred parameters
1935                |_: &mut World| {},
1936                // <- no sync point is added here because the following system is not exclusive
1937                |_: Commands| {},
1938            )
1939                .chain_ignore_deferred(),
1940        );
1941        schedule.run(&mut world);
1942
1943        assert_eq!(schedule.executable.systems.len(), 6); // 5 systems + 1 sync point
1944    }
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            // the first system has deferred commands
1953            |mut commands: Commands| commands.insert_resource(Resource1),
1954            // the second system has no deferred commands
1955            || {},
1956        )
1957            // the first two systems are chained without a sync point in between
1958            .chain_ignore_deferred();
1959
1960        schedule.add_systems(
1961            (
1962                insert_resource_config,
1963                // the third system would panic if the command of the first system was not applied
1964                |_: Res<Resource1>| {},
1965            )
1966                // the third system is chained after the first two, possibly with a sync point in between
1967                .chain(),
1968        );
1969
1970        // To add a sync point between the second and third system despite the second having no commands,
1971        // the first system has to signal the second system that there are unapplied commands.
1972        // With that the second system will add a sync point after it so the third system will find the resource.
1973
1974        schedule.run(&mut world);
1975
1976        assert_eq!(schedule.executable.systems.len(), 4); // 3 systems + 1 sync point
1977    }
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        // Test that the fallback error handler is used
2478        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        // Test that the handler doesn't change within the schedule
2486        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 has changed, so we check initializing again
2560        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 has changed, so we check initializing again
2580        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 has changed, so we check initializing again
2601        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}