diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index ddcd844cb19cf..18a2ffa8d8771 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -4,19 +4,23 @@ )] use alloc::{ boxed::Box, - collections::{BTreeMap, BTreeSet}, + collections::BTreeSet, format, string::{String, ToString}, vec, vec::Vec, }; -use bevy_platform::collections::{HashMap, HashSet}; +use bevy_platform::{ + collections::{HashMap, HashSet}, + hash::FixedHasher, +}; use bevy_utils::{default, TypeIdMap}; use core::{ any::{Any, TypeId}, fmt::{Debug, Write}, }; use fixedbitset::FixedBitSet; +use indexmap::IndexMap; use log::{info, warn}; use pass::ScheduleBuildPassObj; use thiserror::Error; @@ -465,7 +469,7 @@ impl Schedule { /// Remove a custom build pass. pub fn remove_build_pass(&mut self) { - self.graph.passes.remove(&TypeId::of::()); + self.graph.passes.shift_remove(&TypeId::of::()); } /// Changes miscellaneous build settings. @@ -697,7 +701,7 @@ pub struct ScheduleGraph { anonymous_sets: usize, changed: bool, settings: ScheduleBuildSettings, - passes: BTreeMap>, + passes: IndexMap, FixedHasher>, } impl ScheduleGraph { @@ -1580,14 +1584,17 @@ pub struct ScheduleNotInitialized; #[cfg(test)] mod tests { + use alloc::{vec, vec::Vec}; + use core::any::TypeId; + use bevy_ecs_macros::ScheduleLabel; use crate::{ error::{ignore, panic, DefaultErrorHandler, Result}, prelude::{ApplyDeferred, IntoSystemSet, Res, Resource}, schedule::{ - tests::ResMut, IntoScheduleConfigs, Schedule, ScheduleBuildSettings, - ScheduleCleanupPolicy, SystemSet, + passes::AutoInsertApplyDeferredPass, tests::ResMut, IntoScheduleConfigs, Schedule, + ScheduleBuildPass, ScheduleBuildSettings, ScheduleCleanupPolicy, SystemSet, }, system::Commands, world::World, @@ -2565,4 +2572,55 @@ mod tests { let conflicts = schedule.graph().conflicting_systems(); assert!(conflicts.is_empty()); } + + #[test] + fn build_pass_iteration_order() { + #[derive(Debug)] + struct Pass; + + impl ScheduleBuildPass for Pass { + type EdgeOptions = (); + fn add_dependency( + &mut self, + _from: crate::schedule::NodeId, + _to: crate::schedule::NodeId, + _options: Option<&Self::EdgeOptions>, + ) { + } + fn build( + &mut self, + _world: &mut World, + _graph: &mut super::ScheduleGraph, + _dependency_flattened: &mut crate::schedule::graph::Dag, + ) -> core::result::Result<(), crate::schedule::ScheduleBuildError> { + Ok(()) + } + fn collapse_set( + &mut self, + _set: crate::schedule::SystemSetKey, + _systems: &bevy_platform::collections::HashSet, + _dependency_flattening: &crate::schedule::graph::DiGraph, + ) -> impl Iterator + { + core::iter::empty() + } + } + + let mut schedule = Schedule::default(); + schedule.add_build_pass(Pass::<0>); + schedule.add_build_pass(Pass::<1>); + schedule.add_build_pass(Pass::<2>); + + let pass_order: Vec = schedule.graph().passes.keys().cloned().collect(); + + assert_eq!( + pass_order, + vec![ + TypeId::of::(), + TypeId::of::>(), + TypeId::of::>(), + TypeId::of::>() + ] + ); + } }