From b1afe2dcca52fc28c8f412e2e2ee01e2192c9191 Mon Sep 17 00:00:00 2001 From: Torne Wuff Date: Thu, 7 Apr 2022 20:50:43 +0000 Subject: [PATCH] Make `System` responsible for updating its own archetypes (#4115) # Objective - Make it possible to use `System`s outside of the scheduler/executor without having to define logic to track new archetypes and call `System::add_archetype()` for each. ## Solution - Replace `System::add_archetype(&Archetype)` with `System::update_archetypes(&World)`, making systems responsible for tracking their own most recent archetype generation the way that `SystemState` already does. This has minimal (or simplifying) effect on most of the code with the exception of `FunctionSystem`, which must now track the latest `ArchetypeGeneration` it saw instead of relying on the executor to do it. Co-authored-by: Carter Anderson --- benches/Cargo.toml | 7 +- benches/benches/bevy_ecs/archetype_updates.rs | 119 ++++++++++++++++++ .../ecs_bench_suite/get_component_system.rs | 4 +- .../bevy_ecs/ecs_bench_suite/heavy_compute.rs | 4 +- .../ecs_bench_suite/simple_iter_system.rs | 4 +- crates/bevy_core/src/time/fixed_timestep.rs | 11 +- crates/bevy_ecs/src/schedule/executor.rs | 35 +----- .../src/schedule/executor_parallel.rs | 38 ++---- crates/bevy_ecs/src/schedule/run_criteria.rs | 48 +------ crates/bevy_ecs/src/schedule/stage.rs | 2 - .../bevy_ecs/src/system/exclusive_system.rs | 12 -- crates/bevy_ecs/src/system/function_system.rs | 28 +++-- crates/bevy_ecs/src/system/mod.rs | 79 +++++++++++- crates/bevy_ecs/src/system/system.rs | 10 +- crates/bevy_ecs/src/system/system_chaining.rs | 22 ++-- 15 files changed, 264 insertions(+), 159 deletions(-) create mode 100644 benches/benches/bevy_ecs/archetype_updates.rs diff --git a/benches/Cargo.toml b/benches/Cargo.toml index a124d8d197..40c2c4f4b4 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -8,10 +8,15 @@ license = "MIT OR Apache-2.0" [dev-dependencies] glam = "0.20" -criterion = "0.3" +criterion = { version = "0.3", features = ["html_reports"] } bevy_ecs = { path = "../crates/bevy_ecs" } bevy_tasks = { path = "../crates/bevy_tasks" } +[[bench]] +name = "archetype_updates" +path = "benches/bevy_ecs/archetype_updates.rs" +harness = false + [[bench]] name = "ecs_bench_suite" path = "benches/bevy_ecs/ecs_bench_suite/mod.rs" diff --git a/benches/benches/bevy_ecs/archetype_updates.rs b/benches/benches/bevy_ecs/archetype_updates.rs new file mode 100644 index 0000000000..dabe7fe0e7 --- /dev/null +++ b/benches/benches/bevy_ecs/archetype_updates.rs @@ -0,0 +1,119 @@ +use bevy_ecs::{ + component::Component, + schedule::{Stage, SystemStage}, + world::World, +}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; + +criterion_group!(benches, no_archetypes, added_archetypes); +criterion_main!(benches); + +#[derive(Component)] +struct A(f32); + +fn setup(system_count: usize) -> (World, SystemStage) { + let mut world = World::new(); + fn empty() {} + let mut stage = SystemStage::parallel(); + for _ in 0..system_count { + stage.add_system(empty); + } + stage.run(&mut world); + (world, stage) +} + +/// create `count` entities with distinct archetypes +fn add_archetypes(world: &mut World, count: u16) { + for i in 0..count { + let mut e = world.spawn(); + if i & 1 << 0 != 0 { + e.insert(A::<0>(1.0)); + } + if i & 1 << 1 != 0 { + e.insert(A::<1>(1.0)); + } + if i & 1 << 2 != 0 { + e.insert(A::<2>(1.0)); + } + if i & 1 << 3 != 0 { + e.insert(A::<3>(1.0)); + } + if i & 1 << 4 != 0 { + e.insert(A::<4>(1.0)); + } + if i & 1 << 5 != 0 { + e.insert(A::<5>(1.0)); + } + if i & 1 << 6 != 0 { + e.insert(A::<6>(1.0)); + } + if i & 1 << 7 != 0 { + e.insert(A::<7>(1.0)); + } + if i & 1 << 8 != 0 { + e.insert(A::<8>(1.0)); + } + if i & 1 << 9 != 0 { + e.insert(A::<9>(1.0)); + } + if i & 1 << 10 != 0 { + e.insert(A::<10>(1.0)); + } + if i & 1 << 11 != 0 { + e.insert(A::<11>(1.0)); + } + if i & 1 << 12 != 0 { + e.insert(A::<12>(1.0)); + } + if i & 1 << 13 != 0 { + e.insert(A::<13>(1.0)); + } + if i & 1 << 14 != 0 { + e.insert(A::<14>(1.0)); + } + if i & 1 << 15 != 0 { + e.insert(A::<15>(1.0)); + } + } +} + +fn no_archetypes(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("no_archetypes"); + for i in 0..=5 { + let system_count = i * 20; + let (mut world, mut stage) = setup(system_count); + group.bench_with_input( + BenchmarkId::new("system_count", system_count), + &system_count, + |bencher, &_system_count| { + bencher.iter(|| { + stage.run(&mut world); + }); + }, + ); + } +} + +fn added_archetypes(criterion: &mut Criterion) { + const SYSTEM_COUNT: usize = 100; + let mut group = criterion.benchmark_group("added_archetypes"); + for archetype_count in [100, 200, 500, 1000, 2000, 5000, 10000] { + group.bench_with_input( + BenchmarkId::new("archetype_count", archetype_count), + &archetype_count, + |bencher, &archetype_count| { + bencher.iter_batched( + || { + let (mut world, stage) = setup(SYSTEM_COUNT); + add_archetypes(&mut world, archetype_count); + (world, stage) + }, + |(mut world, mut stage)| { + stage.run(&mut world); + }, + criterion::BatchSize::LargeInput, + ) + }, + ); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs b/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs index 97fd004b9b..c746a22330 100644 --- a/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs +++ b/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs @@ -19,9 +19,7 @@ impl Benchmark { let mut system = IntoSystem::into_system(query_system); system.initialize(&mut world); - for archetype in world.archetypes().iter() { - system.new_archetype(archetype); - } + system.update_archetype_component_access(&world); Self(world, entity, Box::new(system)) } diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs b/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs index 1fde2ce698..4ddae1781e 100644 --- a/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs +++ b/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs @@ -42,9 +42,7 @@ impl Benchmark { world.insert_resource(TaskPool::default()); let mut system = IntoSystem::into_system(sys); system.initialize(&mut world); - for archetype in world.archetypes().iter() { - system.new_archetype(archetype); - } + system.update_archetype_component_access(&world); Self(world, Box::new(system)) } diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs index 88b76e70c7..039de135c2 100644 --- a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs @@ -37,9 +37,7 @@ impl Benchmark { let mut system = IntoSystem::into_system(query_system); system.initialize(&mut world); - for archetype in world.archetypes().iter() { - system.new_archetype(archetype); - } + system.update_archetype_component_access(&world); Self(world, Box::new(system)) } diff --git a/crates/bevy_core/src/time/fixed_timestep.rs b/crates/bevy_core/src/time/fixed_timestep.rs index d4469ec9b8..d79f4e6ccf 100644 --- a/crates/bevy_core/src/time/fixed_timestep.rs +++ b/crates/bevy_core/src/time/fixed_timestep.rs @@ -1,6 +1,6 @@ use crate::Time; use bevy_ecs::{ - archetype::{Archetype, ArchetypeComponentId}, + archetype::ArchetypeComponentId, component::ComponentId, query::Access, schedule::ShouldRun, @@ -177,10 +177,6 @@ impl System for FixedTimestep { Cow::Borrowed(std::any::type_name::()) } - fn new_archetype(&mut self, archetype: &Archetype) { - self.internal_system.new_archetype(archetype); - } - fn archetype_component_access(&self) -> &Access { self.internal_system.archetype_component_access() } @@ -220,6 +216,11 @@ impl System for FixedTimestep { } } + fn update_archetype_component_access(&mut self, world: &World) { + self.internal_system + .update_archetype_component_access(world); + } + fn check_change_tick(&mut self, change_tick: u32) { self.internal_system.check_change_tick(change_tick); } diff --git a/crates/bevy_ecs/src/schedule/executor.rs b/crates/bevy_ecs/src/schedule/executor.rs index e5cc61960a..06db889ec2 100644 --- a/crates/bevy_ecs/src/schedule/executor.rs +++ b/crates/bevy_ecs/src/schedule/executor.rs @@ -1,4 +1,4 @@ -use crate::{archetype::ArchetypeGeneration, schedule::ParallelSystemContainer, world::World}; +use crate::{schedule::ParallelSystemContainer, world::World}; use downcast_rs::{impl_downcast, Downcast}; pub trait ParallelSystemExecutor: Downcast + Send + Sync { @@ -10,24 +10,13 @@ pub trait ParallelSystemExecutor: Downcast + Send + Sync { impl_downcast!(ParallelSystemExecutor); -pub struct SingleThreadedExecutor { - /// Last archetypes generation observed by parallel systems. - archetype_generation: ArchetypeGeneration, -} +#[derive(Default)] +pub struct SingleThreadedExecutor; -impl Default for SingleThreadedExecutor { - fn default() -> Self { - Self { - archetype_generation: ArchetypeGeneration::initial(), - } - } -} 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() { #[cfg(feature = "trace")] @@ -38,21 +27,3 @@ 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 new_generation = archetypes.generation(); - let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); - let archetype_index_range = old_generation.value()..new_generation.value(); - - for archetype in archetypes.archetypes[archetype_index_range].iter() { - for container in systems.iter_mut() { - let system = container.system_mut(); - system.new_archetype(archetype); - } - } - } -} diff --git a/crates/bevy_ecs/src/schedule/executor_parallel.rs b/crates/bevy_ecs/src/schedule/executor_parallel.rs index b2ab15c7a5..149d8d02bc 100644 --- a/crates/bevy_ecs/src/schedule/executor_parallel.rs +++ b/crates/bevy_ecs/src/schedule/executor_parallel.rs @@ -1,5 +1,5 @@ use crate::{ - archetype::{ArchetypeComponentId, ArchetypeGeneration}, + archetype::ArchetypeComponentId, query::Access, schedule::{ParallelSystemContainer, ParallelSystemExecutor}, world::World, @@ -32,8 +32,6 @@ struct SystemSchedulingMetadata { } pub struct ParallelExecutor { - /// Last archetypes generation observed by parallel systems. - archetype_generation: ArchetypeGeneration, /// Cached metadata of every system. system_metadata: Vec, /// Used by systems to notify the executor that they have finished. @@ -60,7 +58,6 @@ impl Default for ParallelExecutor { fn default() -> Self { let (finish_sender, finish_receiver) = async_channel::unbounded(); Self { - archetype_generation: ArchetypeGeneration::initial(), system_metadata: Default::default(), finish_sender, finish_receiver, @@ -114,7 +111,17 @@ impl ParallelSystemExecutor for ParallelExecutor { self.events_sender = Some(sender); } - self.update_archetypes(systems, world); + { + #[cfg(feature = "trace")] + let _span = bevy_utils::tracing::info_span!("update_archetypes").entered(); + for (index, container) in systems.iter_mut().enumerate() { + let meta = &mut self.system_metadata[index]; + let system = container.system_mut(); + system.update_archetype_component_access(world); + meta.archetype_component_access + .extend(system.archetype_component_access()); + } + } let compute_pool = world .get_resource_or_insert_with(|| ComputeTaskPool(TaskPool::default())) @@ -154,27 +161,6 @@ impl ParallelSystemExecutor for ParallelExecutor { } impl ParallelExecutor { - /// 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) { - #[cfg(feature = "trace")] - let _span = bevy_utils::tracing::info_span!("update_archetypes").entered(); - let archetypes = world.archetypes(); - let new_generation = archetypes.generation(); - let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); - let archetype_index_range = old_generation.value()..new_generation.value(); - - for archetype in archetypes.archetypes[archetype_index_range].iter() { - for (index, container) in systems.iter_mut().enumerate() { - let meta = &mut self.system_metadata[index]; - let system = container.system_mut(); - system.new_archetype(archetype); - meta.archetype_component_access - .extend(system.archetype_component_access()); - } - } - } - /// Populates `should_run` bitset, spawns tasks for systems that should run this iteration, /// queues systems with no dependencies to run (or skip) at next opportunity. fn prepare_systems<'scope>( diff --git a/crates/bevy_ecs/src/schedule/run_criteria.rs b/crates/bevy_ecs/src/schedule/run_criteria.rs index 7c7b9ecee4..f3b42500b6 100644 --- a/crates/bevy_ecs/src/schedule/run_criteria.rs +++ b/crates/bevy_ecs/src/schedule/run_criteria.rs @@ -1,5 +1,5 @@ use crate::{ - archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration}, + archetype::ArchetypeComponentId, component::ComponentId, query::Access, schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel}, @@ -44,20 +44,10 @@ pub enum ShouldRun { NoAndCheckAgain, } +#[derive(Default)] pub(crate) struct BoxedRunCriteria { criteria_system: Option>, initialized: bool, - archetype_generation: ArchetypeGeneration, -} - -impl Default for BoxedRunCriteria { - fn default() -> Self { - Self { - criteria_system: None, - initialized: false, - archetype_generation: ArchetypeGeneration::initial(), - } - } } impl BoxedRunCriteria { @@ -72,15 +62,6 @@ impl BoxedRunCriteria { run_criteria.initialize(world); self.initialized = true; } - let archetypes = world.archetypes(); - let new_generation = archetypes.generation(); - let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); - let archetype_index_range = old_generation.value()..new_generation.value(); - - for archetype in archetypes.archetypes[archetype_index_range].iter() { - run_criteria.new_archetype(archetype); - } - let should_run = run_criteria.run((), world); run_criteria.apply_buffers(world); should_run @@ -104,7 +85,6 @@ pub(crate) struct RunCriteriaContainer { pub(crate) label: Option, pub(crate) before: Vec, pub(crate) after: Vec, - archetype_generation: ArchetypeGeneration, } impl RunCriteriaContainer { @@ -118,7 +98,6 @@ impl RunCriteriaContainer { label: descriptor.label, before: descriptor.before, after: descriptor.after, - archetype_generation: ArchetypeGeneration::initial(), } } @@ -135,25 +114,6 @@ impl RunCriteriaContainer { RunCriteriaInner::Piped { system, .. } => system.initialize(world), } } - - pub(crate) fn update_archetypes(&mut self, world: &World) { - let archetypes = world.archetypes(); - let new_generation = archetypes.generation(); - let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); - let archetype_index_range = old_generation.value()..new_generation.value(); - for archetype in archetypes.archetypes[archetype_index_range].iter() { - match &mut self.inner { - RunCriteriaInner::Single(system) => { - system.new_archetype(archetype); - } - - RunCriteriaInner::Piped { system, .. } => { - system.new_archetype(archetype); - } - } - } - self.archetype_generation = new_generation; - } } impl GraphNode for RunCriteriaContainer { @@ -380,8 +340,6 @@ impl System for RunOnce { Cow::Borrowed(std::any::type_name::()) } - fn new_archetype(&mut self, _archetype: &Archetype) {} - fn component_access(&self) -> &Access { &self.component_access } @@ -407,5 +365,7 @@ impl System for RunOnce { fn initialize(&mut self, _world: &mut World) {} + fn update_archetype_component_access(&mut self, _world: &World) {} + fn check_change_tick(&mut self, _change_tick: u32) {} } diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index f53ad9dc49..c2a040d648 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -801,7 +801,6 @@ impl Stage for SystemStage { for index in 0..self.run_criteria.len() { let (run_criteria, tail) = self.run_criteria.split_at_mut(index); let mut criteria = &mut tail[0]; - criteria.update_archetypes(world); match &mut criteria.inner { RunCriteriaInner::Single(system) => criteria.should_run = system.run((), world), RunCriteriaInner::Piped { @@ -901,7 +900,6 @@ impl Stage for SystemStage { for index in 0..run_criteria.len() { let (run_criteria, tail) = run_criteria.split_at_mut(index); let criteria = &mut tail[0]; - criteria.update_archetypes(world); match criteria.should_run { ShouldRun::No => (), ShouldRun::Yes => criteria.should_run = ShouldRun::No, diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index ee643008f7..7baacb24fe 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -1,5 +1,4 @@ use crate::{ - archetype::ArchetypeGeneration, system::{check_system_change_tick, BoxedSystem, IntoSystem}, world::World, }; @@ -70,7 +69,6 @@ where pub struct ExclusiveSystemCoerced { system: BoxedSystem<(), ()>, - archetype_generation: ArchetypeGeneration, } impl ExclusiveSystem for ExclusiveSystemCoerced { @@ -79,15 +77,6 @@ impl ExclusiveSystem for ExclusiveSystemCoerced { } fn run(&mut self, world: &mut World) { - let archetypes = world.archetypes(); - let new_generation = archetypes.generation(); - let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); - let archetype_index_range = old_generation.value()..new_generation.value(); - - for archetype in archetypes.archetypes[archetype_index_range].iter() { - self.system.new_archetype(archetype); - } - self.system.run((), world); self.system.apply_buffers(world); } @@ -108,7 +97,6 @@ where fn exclusive_system(self) -> ExclusiveSystemCoerced { ExclusiveSystemCoerced { system: Box::new(IntoSystem::into_system(self)), - archetype_generation: ArchetypeGeneration::initial(), } } } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index f3cade700f..30f421caba 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -1,5 +1,5 @@ use crate::{ - archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId}, + archetype::{ArchetypeComponentId, ArchetypeGeneration, ArchetypeId}, component::ComponentId, prelude::FromWorld, query::{Access, FilteredAccessSet}, @@ -332,6 +332,8 @@ where func: F, param_state: Option, system_meta: SystemMeta, + world_id: Option, + archetype_generation: ArchetypeGeneration, // NOTE: PhantomData T> gives this safe Send/Sync impls #[allow(clippy::type_complexity)] marker: PhantomData (In, Out, Marker)>, @@ -353,6 +355,8 @@ where func, param_state: None, system_meta: SystemMeta::new::(), + world_id: None, + archetype_generation: ArchetypeGeneration::initial(), marker: PhantomData, } } @@ -374,12 +378,6 @@ where self.system_meta.name.clone() } - #[inline] - fn new_archetype(&mut self, archetype: &Archetype) { - let param_state = self.param_state.as_mut().unwrap(); - param_state.new_archetype(archetype, &mut self.system_meta); - } - #[inline] fn component_access(&self) -> &Access { self.system_meta.component_access_set.combined_access() @@ -417,12 +415,28 @@ where #[inline] fn initialize(&mut self, world: &mut World) { + self.world_id = Some(world.id()); self.param_state = Some(::init( world, &mut self.system_meta, )); } + fn update_archetype_component_access(&mut self, world: &World) { + assert!(self.world_id == Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with."); + let archetypes = world.archetypes(); + let new_generation = archetypes.generation(); + let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); + let archetype_index_range = old_generation.value()..new_generation.value(); + + for archetype_index in archetype_index_range { + self.param_state.as_mut().unwrap().new_archetype( + &archetypes[ArchetypeId::new(archetype_index)], + &mut self.system_meta, + ); + } + } + #[inline] fn check_change_tick(&mut self, change_tick: u32) { check_system_change_tick( diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 27dee44f25..52d8b0a12d 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -96,7 +96,7 @@ mod tests { use crate::{ self as bevy_ecs, - archetype::Archetypes, + archetype::{ArchetypeComponentId, Archetypes}, bundle::Bundles, component::{Component, Components}, entity::{Entities, Entity}, @@ -138,9 +138,6 @@ mod tests { world.spawn().insert(A); system.initialize(&mut world); - for archetype in world.archetypes.iter() { - system.new_archetype(archetype); - } system.run((), &mut world); } @@ -835,4 +832,78 @@ mod tests { ); } } + + #[test] + fn update_archetype_component_access_works() { + use std::collections::HashSet; + + fn a_not_b_system(_query: Query<&A, Without>) {} + + let mut world = World::default(); + let mut system = IntoSystem::into_system(a_not_b_system); + let mut expected_ids = HashSet::::new(); + let a_id = world.init_component::(); + + // set up system and verify its access is empty + system.initialize(&mut world); + system.update_archetype_component_access(&world); + assert_eq!( + system + .archetype_component_access() + .reads() + .collect::>(), + expected_ids + ); + + // add some entities with archetypes that should match and save their ids + expected_ids.insert( + world + .spawn() + .insert_bundle((A,)) + .archetype() + .get_archetype_component_id(a_id) + .unwrap(), + ); + expected_ids.insert( + world + .spawn() + .insert_bundle((A, C)) + .archetype() + .get_archetype_component_id(a_id) + .unwrap(), + ); + + // add some entities with archetypes that should not match + world.spawn().insert_bundle((A, B)); + world.spawn().insert_bundle((B, C)); + + // update system and verify its accesses are correct + system.update_archetype_component_access(&world); + assert_eq!( + system + .archetype_component_access() + .reads() + .collect::>(), + expected_ids + ); + + // one more round + expected_ids.insert( + world + .spawn() + .insert_bundle((A, D)) + .archetype() + .get_archetype_component_id(a_id) + .unwrap(), + ); + world.spawn().insert_bundle((A, B, D)); + system.update_archetype_component_access(&world); + assert_eq!( + system + .archetype_component_access() + .reads() + .collect::>(), + expected_ids + ); + } } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 3cf0f9e773..92f30532ca 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -1,10 +1,7 @@ use bevy_utils::tracing::warn; use crate::{ - archetype::{Archetype, ArchetypeComponentId}, - component::ComponentId, - query::Access, - schedule::SystemLabel, + archetype::ArchetypeComponentId, component::ComponentId, query::Access, schedule::SystemLabel, world::World, }; use std::borrow::Cow; @@ -28,8 +25,6 @@ pub trait System: Send + Sync + 'static { type Out; /// Returns the system's name. fn name(&self) -> Cow<'static, str>; - /// Register a new archetype for this system. - fn new_archetype(&mut self, archetype: &Archetype); /// Returns the system's component [`Access`]. fn component_access(&self) -> &Access; /// Returns the system's archetype component [`Access`]. @@ -50,12 +45,15 @@ pub trait System: Send + Sync + 'static { unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out; /// Runs the system with the given input in the world. fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { + self.update_archetype_component_access(world); // SAFE: world and resources are exclusively borrowed unsafe { self.run_unsafe(input, world) } } fn apply_buffers(&mut self, world: &mut World); /// Initialize the system. fn initialize(&mut self, _world: &mut World); + /// Update the system's archetype component [`Access`]. + fn update_archetype_component_access(&mut self, world: &World); fn check_change_tick(&mut self, change_tick: u32); /// The default labels for the system fn default_labels(&self) -> Vec> { diff --git a/crates/bevy_ecs/src/system/system_chaining.rs b/crates/bevy_ecs/src/system/system_chaining.rs index 77bc8d55f4..1e1a8e7643 100644 --- a/crates/bevy_ecs/src/system/system_chaining.rs +++ b/crates/bevy_ecs/src/system/system_chaining.rs @@ -1,5 +1,5 @@ use crate::{ - archetype::{Archetype, ArchetypeComponentId}, + archetype::ArchetypeComponentId, component::ComponentId, query::Access, system::{IntoSystem, System}, @@ -60,16 +60,6 @@ impl> System for ChainSystem self.name.clone() } - fn new_archetype(&mut self, archetype: &Archetype) { - self.system_a.new_archetype(archetype); - self.system_b.new_archetype(archetype); - - self.archetype_component_access - .extend(self.system_a.archetype_component_access()); - self.archetype_component_access - .extend(self.system_b.archetype_component_access()); - } - fn archetype_component_access(&self) -> &Access { &self.archetype_component_access } @@ -101,6 +91,16 @@ impl> System for ChainSystem .extend(self.system_b.component_access()); } + fn update_archetype_component_access(&mut self, world: &World) { + self.system_a.update_archetype_component_access(world); + self.system_b.update_archetype_component_access(world); + + self.archetype_component_access + .extend(self.system_a.archetype_component_access()); + self.archetype_component_access + .extend(self.system_b.archetype_component_access()); + } + fn check_change_tick(&mut self, change_tick: u32) { self.system_a.check_change_tick(change_tick); self.system_b.check_change_tick(change_tick);