diff --git a/crates/bevy_ecs/src/schedule/executor.rs b/crates/bevy_ecs/src/schedule/executor.rs index 18184cc25c..ba08d0d10f 100644 --- a/crates/bevy_ecs/src/schedule/executor.rs +++ b/crates/bevy_ecs/src/schedule/executor.rs @@ -1,4 +1,4 @@ -use crate::{schedule::ParallelSystemContainer, world::World}; +use crate::{archetype::ArchetypeGeneration, schedule::ParallelSystemContainer, world::World}; use downcast_rs::{impl_downcast, Downcast}; pub trait ParallelSystemExecutor: Downcast + Send + Sync { @@ -10,13 +10,25 @@ pub trait ParallelSystemExecutor: Downcast + Send + Sync { impl_downcast!(ParallelSystemExecutor); -#[derive(Default)] -pub struct SingleThreadedExecutor; +pub struct SingleThreadedExecutor { + /// Last archetypes generation observed by parallel systems. + archetype_generation: ArchetypeGeneration, +} +impl Default for SingleThreadedExecutor { + fn default() -> Self { + Self { + // MAX ensures access information will be initialized on first run. + archetype_generation: ArchetypeGeneration::new(usize::MAX), + } + } +} impl ParallelSystemExecutor for SingleThreadedExecutor { fn rebuild_cached_data(&mut self, _: &[ParallelSystemContainer]) {} fn run_systems(&mut self, systems: &mut [ParallelSystemContainer], world: &mut World) { + self.update_archetypes(systems, world); + for system in systems { if system.should_run() { system.system_mut().run((), world); @@ -24,3 +36,30 @@ impl ParallelSystemExecutor for SingleThreadedExecutor { } } } + +impl SingleThreadedExecutor { + /// Calls system.new_archetype() for each archetype added since the last call to [update_archetypes] and + /// updates cached archetype_component_access. + fn update_archetypes(&mut self, systems: &mut [ParallelSystemContainer], world: &World) { + let archetypes = world.archetypes(); + let old_generation = self.archetype_generation; + let new_generation = archetypes.generation(); + if old_generation == new_generation { + return; + } + + let archetype_index_range = if old_generation.value() == usize::MAX { + 0..archetypes.len() + } else { + old_generation.value()..archetypes.len() + }; + for archetype in archetypes.archetypes[archetype_index_range].iter() { + for container in systems.iter_mut() { + let system = container.system_mut(); + system.new_archetype(archetype); + } + } + + self.archetype_generation = new_generation; + } +} diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index 7a601438bd..94306c5f6a 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -1528,4 +1528,49 @@ mod tests { stage.run(&mut world_a); stage.run(&mut world_b); } + + #[test] + fn archetype_update_single_executor() { + fn query_count_system( + mut entity_count: ResMut, + query: Query, + ) { + *entity_count = query.iter().count(); + } + + let mut world = World::new(); + world.insert_resource(0_usize); + let mut stage = SystemStage::single(query_count_system.system()); + + let entity = world.spawn().insert_bundle(()).id(); + stage.run(&mut world); + assert_eq!(*world.get_resource::().unwrap(), 1); + + world.get_entity_mut(entity).unwrap().insert(1); + stage.run(&mut world); + assert_eq!(*world.get_resource::().unwrap(), 1); + } + + #[test] + fn archetype_update_parallel_executor() { + fn query_count_system( + mut entity_count: ResMut, + query: Query, + ) { + *entity_count = query.iter().count(); + } + + let mut world = World::new(); + world.insert_resource(0_usize); + let mut stage = SystemStage::parallel(); + stage.add_system(query_count_system.system()); + + let entity = world.spawn().insert_bundle(()).id(); + stage.run(&mut world); + assert_eq!(*world.get_resource::().unwrap(), 1); + + world.get_entity_mut(entity).unwrap().insert(1); + stage.run(&mut world); + assert_eq!(*world.get_resource::().unwrap(), 1); + } }