From 9a2c91738062ffe33129d0a4338050eaf66a69b1 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Sun, 7 Mar 2021 23:02:35 +0900 Subject: [PATCH 01/41] Counter-based reliable change detection --- crates/bevy_ecs/macros/src/lib.rs | 8 +- crates/bevy_ecs/src/archetype.rs | 13 +- crates/bevy_ecs/src/bundle.rs | 35 ++- crates/bevy_ecs/src/component/mod.rs | 52 +++- crates/bevy_ecs/src/lib.rs | 260 ++++++++-------- crates/bevy_ecs/src/query/direct.rs | 288 ++++++++++++++++++ crates/bevy_ecs/src/query/fetch.rs | 170 +++++++---- crates/bevy_ecs/src/query/filter.rs | 70 +++-- crates/bevy_ecs/src/query/iter.rs | 21 +- crates/bevy_ecs/src/query/mod.rs | 14 +- crates/bevy_ecs/src/query/state.rs | 159 ++++++++-- crates/bevy_ecs/src/reflect.rs | 33 +- crates/bevy_ecs/src/storage/sparse_set.rs | 47 +-- crates/bevy_ecs/src/storage/table.rs | 66 ++-- crates/bevy_ecs/src/system/commands.rs | 14 +- .../bevy_ecs/src/system/exclusive_system.rs | 12 + crates/bevy_ecs/src/system/into_system.rs | 19 +- crates/bevy_ecs/src/system/mod.rs | 4 +- crates/bevy_ecs/src/system/query.rs | 107 ++++++- crates/bevy_ecs/src/system/system_param.rs | 192 +++++++++--- crates/bevy_ecs/src/world/entity_ref.rs | 74 +++-- crates/bevy_ecs/src/world/mod.rs | 133 ++++++-- crates/bevy_ecs/src/world/pointer.rs | 25 +- crates/bevy_ecs/src/world/spawn_batch.rs | 5 +- .../src/render_graph/nodes/pass_node.rs | 4 +- .../bevy_transform/src/hierarchy/hierarchy.rs | 2 +- crates/bevy_ui/src/flex/mod.rs | 12 +- crates/bevy_ui/src/update.rs | 2 +- examples/ecs/change_detection.rs | 12 +- 29 files changed, 1378 insertions(+), 475 deletions(-) create mode 100644 crates/bevy_ecs/src/query/direct.rs diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 9b4ccbfc2be6e..b3e761c94cc10 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -270,11 +270,12 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { #[inline] unsafe fn get_param( state: &'a mut Self, - _system_state: &'a SystemState, + system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { let (#(#query,)*) = &state.0; - QuerySet((#(Query::new(world, #query),)*)) + QuerySet((#(Query::new(world, #query, system_state.system_counter, global_system_counter),)*)) } } @@ -402,9 +403,10 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { state: &'a mut Self, system_state: &'a #path::system::SystemState, world: &'a #path::world::World, + global_system_counter: u32, ) -> Self::Item { #struct_name { - #(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_state, world),)* + #(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_state, world, global_system_counter),)* #(#ignored_fields: <#ignored_field_types>::default(),)* } } diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index ba9220d89037f..625def2b74279 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -1,6 +1,6 @@ use crate::{ bundle::BundleId, - component::{ComponentFlags, ComponentId, StorageType}, + component::{ComponentId, StorageType}, entity::{Entity, EntityLocation}, storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId}, }; @@ -31,9 +31,14 @@ impl ArchetypeId { } } +pub enum ComponentStatus { + Added, + Mutated, +} + pub struct FromBundle { pub archetype_id: ArchetypeId, - pub bundle_flags: Vec, + pub bundle_status: Vec, } #[derive(Default)] @@ -65,13 +70,13 @@ impl Edges { &mut self, bundle_id: BundleId, archetype_id: ArchetypeId, - bundle_flags: Vec, + bundle_status: Vec, ) { self.from_bundle.insert( bundle_id, FromBundle { archetype_id, - bundle_flags, + bundle_status, }, ); } diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index e6b1012b4cfb6..b3abc4981692a 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -1,7 +1,8 @@ pub use bevy_ecs_macros::Bundle; use crate::{ - component::{Component, ComponentFlags, ComponentId, Components, StorageType, TypeInfo}, + archetype::ComponentStatus, + component::{Component, ComponentCounters, ComponentId, Components, StorageType, TypeInfo}, entity::Entity, storage::{SparseSetIndex, SparseSets, Table}, }; @@ -116,6 +117,7 @@ pub struct BundleInfo { impl BundleInfo { /// # Safety /// table row must exist, entity must be valid + #[allow(clippy::clippy::too_many_arguments)] #[inline] pub(crate) unsafe fn write_components( &self, @@ -123,24 +125,47 @@ impl BundleInfo { entity: Entity, table: &Table, table_row: usize, - bundle_flags: &[ComponentFlags], + bundle_status: &[ComponentStatus], bundle: T, + global_system_counter: u32, ) { // NOTE: get_components calls this closure on each component in "bundle order". bundle_info.component_ids are also in "bundle order" let mut bundle_component = 0; bundle.get_components(|component_ptr| { // SAFE: component_id was initialized by get_dynamic_bundle_info let component_id = *self.component_ids.get_unchecked(bundle_component); - let flags = *bundle_flags.get_unchecked(bundle_component); + let component_status = bundle_status.get_unchecked(bundle_component); match self.storage_types[bundle_component] { StorageType::Table => { let column = table.get_column(component_id).unwrap(); column.set_unchecked(table_row, component_ptr); - column.get_flags_unchecked_mut(table_row).insert(flags); + let column_status = column.get_counters_unchecked_mut(table_row); + match component_status { + ComponentStatus::Added => { + *column_status = ComponentCounters::new(global_system_counter); + } + ComponentStatus::Mutated => { + column_status.set_changed(global_system_counter); + } + } } StorageType::SparseSet => { let sparse_set = sparse_sets.get_mut(component_id).unwrap(); - sparse_set.insert(entity, component_ptr, flags); + match component_status { + ComponentStatus::Added => { + sparse_set.insert( + entity, + component_ptr, + ComponentCounters::new(global_system_counter), + ); + } + ComponentStatus::Mutated => { + sparse_set + .get_counters(entity) + .unwrap() + .set_changed(global_system_counter); + } + } } } bundle_component += 1; diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index 914030eff4771..672d02512554e 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -3,7 +3,6 @@ mod type_info; pub use type_info::*; use crate::storage::SparseSetIndex; -use bitflags::bitflags; use std::{ alloc::Layout, any::{Any, TypeId}, @@ -288,9 +287,52 @@ impl Components { } } -bitflags! { - pub struct ComponentFlags: u8 { - const ADDED = 1; - const MUTATED = 2; +#[derive(Copy, Clone, Debug)] +pub struct ComponentCounters { + added: u32, + changed: u32, +} + +impl ComponentCounters { + pub fn is_added(&self, system_counter: Option, global_system_counter: u32) -> bool { + if let Some(system_counter) = system_counter { + let component_age = global_system_counter.wrapping_sub(self.added); + let system_age = global_system_counter.wrapping_sub(system_counter); + + component_age < system_age + } else { + true + } + } + + pub fn is_changed(&self, system_counter: Option, global_system_counter: u32) -> bool { + if let Some(system_counter) = system_counter { + let component_age = global_system_counter.wrapping_sub(self.changed); + let system_age = global_system_counter.wrapping_sub(system_counter); + + component_age < system_age + } else { + true + } + } + + pub fn is_mutated(&self, system_counter: Option, global_system_counter: u32) -> bool { + self.is_changed(system_counter, global_system_counter) + && !self.is_added(system_counter, global_system_counter) + } + + pub(crate) fn new(global_system_counter: u32) -> Self { + Self { + added: global_system_counter, + changed: global_system_counter, + } + } + + pub(crate) fn clear(&mut self, _global_system_counter: u32) { + // TODO: advance old component counters before they are passed by the global counter + } + + pub(crate) fn set_changed(&mut self, global_system_counter: u32) { + self.changed = global_system_counter; } } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 81d55c85ff65c..833859ae2429f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -16,7 +16,7 @@ pub mod prelude { pub use crate::{ bundle::Bundle, entity::Entity, - query::{Added, Changed, Flags, Mutated, Or, QueryState, With, WithBundle, Without}, + query::{Added, Changed, Counters, Mutated, Or, QueryState, With, WithBundle, Without}, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, Schedule, Stage, StageLabel, State, StateStage, SystemLabel, SystemStage, @@ -35,7 +35,7 @@ mod tests { bundle::Bundle, component::{Component, ComponentDescriptor, StorageType, TypeInfo}, entity::Entity, - query::{Added, Changed, FilterFetch, Flags, Mutated, Or, With, Without, WorldQuery}, + query::{Added, Changed, Counters, FilterFetch, Mutated, Or, With, Without, WorldQuery}, world::{Mut, World}, }; use bevy_tasks::TaskPool; @@ -187,7 +187,7 @@ mod tests { let ents = world .query::<(Entity, &i32, &&str)>() - .iter(&world) + .iter() .map(|(e, &i, &s)| (e, i, s)) .collect::>(); assert_eq!(ents, &[(e, 123, "abc"), (f, 456, "def")]); @@ -202,7 +202,7 @@ mod tests { let mut results = Vec::new(); world .query::<(Entity, &i32, &&str)>() - .for_each(&world, |(e, &i, &s)| results.push((e, i, s))); + .for_each(|(e, &i, &s)| results.push((e, i, s))); assert_eq!(results, &[(e, 123, "abc"), (f, 456, "def")]); } @@ -213,7 +213,7 @@ mod tests { let f = world.spawn().insert_bundle(("def", 456, true)).id(); let ents = world .query::<(Entity, &i32)>() - .iter(&world) + .iter() .map(|(e, &i)| (e, i)) .collect::>(); assert_eq!(ents, &[(e, 123), (f, 456)]); @@ -223,13 +223,20 @@ mod tests { fn stateful_query_handles_new_archetype() { let mut world = World::new(); let e = world.spawn().insert_bundle(("abc", 123)).id(); - let mut query = world.query::<(Entity, &i32)>(); - let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); + let ents = world + .query::<(Entity, &i32)>() + .iter() + .map(|(e, &i)| (e, i)) + .collect::>(); assert_eq!(ents, &[(e, 123)]); let f = world.spawn().insert_bundle(("def", 456, true)).id(); - let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); + let ents = world + .query::<(Entity, &i32)>() + .iter() + .map(|(e, &i)| (e, i)) + .collect::>(); assert_eq!(ents, &[(e, 123), (f, 456)]); } @@ -241,7 +248,7 @@ mod tests { let mut results = Vec::new(); world .query::<(Entity, &i32)>() - .for_each(&world, |(e, &i)| results.push((e, i))); + .for_each(|(e, &i)| results.push((e, i))); assert_eq!(results, &[(e, 123), (f, 456)]); } @@ -257,7 +264,7 @@ mod tests { let results = Arc::new(Mutex::new(Vec::new())); world .query::<(Entity, &i32)>() - .par_for_each(&world, &task_pool, 2, |(e, &i)| results.lock().push((e, i))); + .par_for_each(&task_pool, 2, |(e, &i)| results.lock().push((e, i))); results.lock().sort(); assert_eq!( &*results.lock(), @@ -270,7 +277,7 @@ mod tests { let mut world = World::new(); world.spawn().insert_bundle(("abc", 123)); world.spawn().insert_bundle(("def", 456)); - assert!(world.query::<(&bool, &i32)>().iter(&world).next().is_none()); + assert!(world.query::<(&bool, &i32)>().iter().next().is_none()); } #[test] @@ -280,7 +287,7 @@ mod tests { let f = world.spawn().insert_bundle(("def", 456, true)).id(); let ents = world .query::<(Entity, &bool)>() - .iter(&world) + .iter() .map(|(e, &b)| (e, b)) .collect::>(); assert_eq!(ents, &[(f, true)]); @@ -293,7 +300,7 @@ mod tests { world.spawn().insert(456u32); let result = world .query_filtered::<&u32, With>() - .iter(&world) + .iter() .cloned() .collect::>(); assert_eq!(result, vec![123]); @@ -308,7 +315,7 @@ mod tests { let mut results = Vec::new(); world .query_filtered::<&u32, With>() - .for_each(&world, |i| results.push(*i)); + .for_each(|i| results.push(*i)); assert_eq!(results, vec![123]); } @@ -322,7 +329,7 @@ mod tests { world.spawn().insert(456u32); let result = world .query_filtered::<&u32, With>() - .iter(&world) + .iter() .cloned() .collect::>(); assert_eq!(result, vec![123]); @@ -339,7 +346,7 @@ mod tests { let mut results = Vec::new(); world .query_filtered::<&u32, With>() - .for_each(&world, |i| results.push(*i)); + .for_each(|i| results.push(*i)); assert_eq!(results, vec![123]); } @@ -350,7 +357,7 @@ mod tests { world.spawn().insert(456u32); let result = world .query_filtered::<&u32, Without>() - .iter(&world) + .iter() .cloned() .collect::>(); assert_eq!(result, vec![456]); @@ -365,7 +372,7 @@ mod tests { world.spawn().insert("abc"); let ents = world .query::<(Entity, Option<&bool>, &i32)>() - .iter(&world) + .iter() .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); assert_eq!(ents, &[(e, None, 123), (f, Some(true), 456)]); @@ -383,7 +390,7 @@ mod tests { // world.spawn().insert("abc"); let ents = world .query::<(Entity, Option<&bool>, &i32)>() - .iter(&world) + .iter() .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); assert_eq!(ents, &[(e, None, 123), (f, Some(true), 456)]); @@ -401,7 +408,7 @@ mod tests { world.spawn().insert("abc"); let ents = world .query::<(Entity, Option<&bool>, &i32)>() - .iter(&world) + .iter() .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); assert_eq!(ents, &[(e, None, 123), (f, None, 456)]); @@ -416,7 +423,7 @@ mod tests { assert_eq!( world .query::<(Entity, &i32, &bool)>() - .iter(&world) + .iter() .map(|(e, &i, &b)| (e, i, b)) .collect::>(), &[(e1, 42, true), (e2, 0, false)] @@ -426,7 +433,7 @@ mod tests { assert_eq!( world .query::<(Entity, &i32, &bool)>() - .iter(&world) + .iter() .map(|(e, &i, &b)| (e, i, b)) .collect::>(), &[(e2, 0, false)] @@ -434,7 +441,7 @@ mod tests { assert_eq!( world .query::<(Entity, &bool, &&str)>() - .iter(&world) + .iter() .map(|(e, &b, &s)| (e, b, s)) .collect::>(), &[(e2, false, "xyz"), (e1, true, "abc")] @@ -443,7 +450,7 @@ mod tests { assert_eq!( world .query::<(Entity, &i32, &bool)>() - .iter(&world) + .iter() .map(|(e, &i, &b)| (e, i, b)) .collect::>(), &[(e2, 0, false), (e1, 43, true)] @@ -452,7 +459,7 @@ mod tests { assert_eq!( world .query::<(Entity, &f32)>() - .iter(&world) + .iter() .map(|(e, &f)| (e, f)) .collect::>(), &[(e1, 1.0)] @@ -507,11 +514,7 @@ mod tests { fn spawn_batch() { let mut world = World::new(); world.spawn_batch((0..100).map(|x| (x, "abc"))); - let values = world - .query::<&i32>() - .iter(&world) - .copied() - .collect::>(); + let values = world.query::<&i32>().iter().copied().collect::>(); let expected = (0..100).collect::>(); assert_eq!(values, expected); } @@ -523,15 +526,16 @@ mod tests { let b = world.spawn().insert_bundle(("def", 456)).id(); let c = world.spawn().insert_bundle(("ghi", 789, true)).id(); - let mut i32_query = world.query::<&i32>(); - assert_eq!(i32_query.get(&world, a).unwrap(), &123); - assert_eq!(i32_query.get(&world, b).unwrap(), &456); + assert_eq!(world.query::<&i32>().get(a).unwrap(), &123); + assert_eq!(world.query::<&i32>().get(b).unwrap(), &456); - let mut i32_bool_query = world.query::<(&i32, &bool)>(); - assert!(i32_bool_query.get(&world, a).is_err()); - assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&789, &true)); + assert!(world.query::<(&i32, &bool)>().get(a).is_err()); + assert_eq!( + world.query::<(&i32, &bool)>().get(c).unwrap(), + (&789, &true) + ); assert!(world.despawn(a)); - assert!(i32_query.get(&world, a).is_err()); + assert!(world.query::<&i32>().get(a).is_err()); } #[test] @@ -614,61 +618,26 @@ mod tests { let mut world = World::new(); let a = world.spawn().insert(123i32).id(); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); - assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), - 1 - ); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); - assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), - 1 - ); - assert!(world.query::<&i32>().get(&world, a).is_ok()); - assert!(world - .query_filtered::<(), Added>() - .get(&world, a) - .is_ok()); - assert!(world.query::<&i32>().get(&world, a).is_ok()); - assert!(world - .query_filtered::<(), Added>() - .get(&world, a) - .is_ok()); + assert_eq!(world.query::<&i32>().iter().count(), 1); + assert_eq!(world.query_filtered::<(), Added>().iter().count(), 1); + assert_eq!(world.query::<&i32>().iter().count(), 1); + assert_eq!(world.query_filtered::<(), Added>().iter().count(), 1); + assert!(world.query::<&i32>().get(a).is_ok()); + assert!(world.query_filtered::<(), Added>().get(a).is_ok()); + assert!(world.query::<&i32>().get(a).is_ok()); + assert!(world.query_filtered::<(), Added>().get(a).is_ok()); world.clear_trackers(); - - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); - assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), - 0 - ); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); - assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), - 0 - ); - assert!(world.query::<&i32>().get(&world, a).is_ok()); - assert!(world - .query_filtered::<(), Added>() - .get(&world, a) - .is_err()); - assert!(world.query::<&i32>().get(&world, a).is_ok()); - assert!(world - .query_filtered::<(), Added>() - .get(&world, a) - .is_err()); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); + + assert_eq!(world.query::<&i32>().iter().count(), 1); + assert_eq!(world.query_filtered::<(), Added>().iter().count(), 0); + assert_eq!(world.query::<&i32>().iter().count(), 1); + assert_eq!(world.query_filtered::<(), Added>().iter().count(), 0); + assert!(world.query::<&i32>().get(a).is_ok()); + assert!(world.query_filtered::<(), Added>().get(a).is_err()); + assert!(world.query::<&i32>().get(a).is_ok()); + assert!(world.query_filtered::<(), Added>().get(a).is_err()); } #[test] @@ -679,7 +648,7 @@ mod tests { fn get_added(world: &mut World) -> Vec { world .query_filtered::>() - .iter(&world) + .iter() .collect::>() } @@ -689,6 +658,7 @@ mod tests { assert_eq!(get_added::(&mut world), vec![e1]); world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); assert!(get_added::(&mut world).is_empty()); let e2 = world.spawn().insert_bundle((A(1), B(1))).id(); assert_eq!(get_added::(&mut world), vec![e2]); @@ -696,7 +666,7 @@ mod tests { let added = world .query_filtered::, Added)>() - .iter(&world) + .iter() .collect::>(); assert_eq!(added, vec![e2]); } @@ -708,8 +678,10 @@ mod tests { let e2 = world.spawn().insert_bundle((A(0), B(0))).id(); let e3 = world.spawn().insert_bundle((A(0), B(0))).id(); world.spawn().insert_bundle((A(0), B)); + world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); - for (i, mut a) in world.query::<&mut A>().iter_mut(&mut world).enumerate() { + for (i, mut a) in world.query::<&mut A>().iter_mut().enumerate() { if i % 2 == 0 { a.0 += 1; } @@ -721,7 +693,7 @@ mod tests { { world .query_filtered::() - .iter(&world) + .iter() .collect::>() } @@ -757,6 +729,7 @@ mod tests { ); world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); assert!(get_filtered::>(&mut world).is_empty()); @@ -767,9 +740,10 @@ mod tests { assert_eq!(get_filtered::>(&mut world), vec![e4]); world.entity_mut(e4).insert(A(1)); - assert_eq!(get_filtered::>(&mut world), vec![e4]); + // assert_eq!(get_filtered::>(&mut world), vec![e4]); // This case is no longer possible to detect world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); // ensure inserting multiple components set mutated state for // already existing components and set added state for @@ -807,18 +781,20 @@ mod tests { world.spawn().insert_bundle((A(0), B(0))).id(); let e2 = world.spawn().insert_bundle((A(0), B(0))).id(); world.spawn().insert_bundle((A(0), B(0))); + world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); - for mut a in world.query::<&mut A>().iter_mut(&mut world) { + for mut a in world.query::<&mut A>().iter_mut() { a.0 += 1; } - for mut b in world.query::<&mut B>().iter_mut(&mut world).skip(1).take(1) { + for mut b in world.query::<&mut B>().iter_mut().skip(1).take(1) { b.0 += 1; } let a_b_mutated = world .query_filtered::, Mutated)>() - .iter(&world) + .iter() .collect::>(); assert_eq!(a_b_mutated, vec![e2]); } @@ -830,6 +806,8 @@ mod tests { let e2 = world.spawn().insert_bundle((A(0), B(0))).id(); let _e3 = world.spawn().insert_bundle((A(0), B(0))).id(); let e4 = world.spawn().insert(A(0)).id(); // ensure filters work for archetypes with only one of the Or filter items + world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); *world.entity_mut(e1).get_mut::().unwrap() = A(1); *world.entity_mut(e2).get_mut::().unwrap() = B(1); @@ -837,7 +815,7 @@ mod tests { let a_b_mutated = world .query_filtered::, Mutated)>>() - .iter(&world) + .iter() .collect::>(); // e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component assert_eq!(a_b_mutated, vec![e1, e2, e4]); @@ -851,11 +829,12 @@ mod tests { fn get_changed(world: &mut World) -> Vec { world .query_filtered::>() - .iter(&world) + .iter() .collect::>() } assert_eq!(get_changed(&mut world), vec![e1]); world.clear_trackers(); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); assert_eq!(get_changed(&mut world), vec![]); *world.get_mut(e1).unwrap() = A(1); assert_eq!(get_changed(&mut world), vec![e1]); @@ -997,9 +976,9 @@ mod tests { let e2 = world.spawn().insert_bundle((2, 2.0, 2usize)).id(); world.spawn().insert_bundle((3, 3.0, 3usize)).id(); - let mut query = world.query::<(&f64, &usize)>(); - let results = query - .iter(&world) + let results = world + .query::<(&f64, &usize)>() + .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results, vec![(1.0, 1usize), (2.0, 2usize), (3.0, 3usize),]); @@ -1010,14 +989,14 @@ mod tests { .unwrap(); assert_eq!(removed_bundle, (2.0, 2usize)); - let results = query - .iter(&world) + let results = world + .query::<(&f64, &usize)>() + .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results, vec![(1.0, 1usize), (3.0, 3usize),]); - let mut i32_query = world.query::<&i32>(); - let results = i32_query.iter(&world).cloned().collect::>(); + let results = world.query::<&i32>().iter().cloned().collect::>(); assert_eq!(results, vec![1, 3, 2]); let entity_ref = world.entity(e2); @@ -1060,30 +1039,39 @@ mod tests { } #[test] - fn flags_query() { + fn counters_query() { let mut world = World::default(); let e1 = world.spawn().insert_bundle((A(0), B(0))).id(); world.spawn().insert(B(0)); - let mut flags_query = world.query::>>(); - let flags = flags_query.iter(&world).collect::>(); - let a_flags = flags[0].as_ref().unwrap(); - assert!(flags[1].is_none()); - assert!(a_flags.added()); - assert!(!a_flags.mutated()); - assert!(a_flags.changed()); + let counters = world + .query::>>() + .iter() + .collect::>(); + let a_counters = counters[0].as_ref().unwrap(); + assert!(counters[1].is_none()); + assert!(a_counters.is_added()); + assert!(!a_counters.is_mutated()); + assert!(a_counters.is_changed()); world.clear_trackers(); - let flags = flags_query.iter(&world).collect::>(); - let a_flags = flags[0].as_ref().unwrap(); - assert!(!a_flags.added()); - assert!(!a_flags.mutated()); - assert!(!a_flags.changed()); + world.exclusive_system_counter = Some(world.increment_global_system_counter()); + let counters = world + .query::>>() + .iter() + .collect::>(); + let a_counters = counters[0].as_ref().unwrap(); + assert!(!a_counters.is_added()); + assert!(!a_counters.is_mutated()); + assert!(!a_counters.is_changed()); *world.get_mut(e1).unwrap() = A(1); - let flags = flags_query.iter(&world).collect::>(); - let a_flags = flags[0].as_ref().unwrap(); - assert!(!a_flags.added()); - assert!(a_flags.mutated()); - assert!(a_flags.changed()); + let counters = world + .query::>>() + .iter() + .collect::>(); + let a_counters = counters[0].as_ref().unwrap(); + assert!(!a_counters.is_added()); + assert!(a_counters.is_changed()); + assert!(a_counters.is_mutated()); } #[test] @@ -1094,8 +1082,8 @@ mod tests { world.spawn().insert_bundle((A(0), B(0), C)); world.spawn().insert(C); - let mut query = world.query::<(&A, &B)>(); - assert_eq!(query.iter(&world).len(), 3); + let query = world.query::<(&A, &B)>(); + assert_eq!(query.iter().len(), 3); } #[test] @@ -1124,9 +1112,9 @@ mod tests { fn multiple_worlds_same_query_iter() { let mut world_a = World::new(); let world_b = World::new(); - let mut query = world_a.query::<&i32>(); - query.iter(&world_a); - query.iter(&world_b); + let mut query = world_a.query_state::<&i32>(); + query.iter(&world_a, None, 0); + query.iter(&world_b, None, 0); } #[test] @@ -1134,9 +1122,9 @@ mod tests { fn multiple_worlds_same_query_get() { let mut world_a = World::new(); let world_b = World::new(); - let mut query = world_a.query::<&i32>(); - let _ = query.get(&world_a, Entity::new(0)); - let _ = query.get(&world_b, Entity::new(0)); + let mut query = world_a.query_state::<&i32>(); + let _ = query.get(&world_a, Entity::new(0), None, 0); + let _ = query.get(&world_b, Entity::new(0), None, 0); } #[test] @@ -1144,9 +1132,9 @@ mod tests { fn multiple_worlds_same_query_for_each() { let mut world_a = World::new(); let world_b = World::new(); - let mut query = world_a.query::<&i32>(); - query.for_each(&world_a, |_| {}); - query.for_each(&world_b, |_| {}); + let mut query = world_a.query_state::<&i32>(); + query.for_each(&world_a, |_| {}, None, 0); + query.for_each(&world_b, |_| {}, None, 0); } #[test] diff --git a/crates/bevy_ecs/src/query/direct.rs b/crates/bevy_ecs/src/query/direct.rs new file mode 100644 index 0000000000000..1fb10cfa57a4e --- /dev/null +++ b/crates/bevy_ecs/src/query/direct.rs @@ -0,0 +1,288 @@ +use crate::{ + component::Component, + entity::Entity, + query::{ + Fetch, FilterFetch, QueryEntityError, QueryIter, QueryState, ReadOnlyFetch, WorldQuery, + }, + system::QueryComponentError, + world::{Mut, World}, +}; +use bevy_tasks::TaskPool; +use std::any::TypeId; + +// FIXME: this is a copy of [Query] that owns its [QueryState]. They should be merged. +pub struct DirectQuery<'w, Q: WorldQuery, F: WorldQuery = ()> +where + F::Fetch: FilterFetch, +{ + pub(crate) world: &'w World, + pub(crate) state: QueryState, + pub(crate) system_counter: Option, + pub(crate) global_system_counter: u32, +} + +impl<'w, Q: WorldQuery, F: WorldQuery> DirectQuery<'w, Q, F> +where + F::Fetch: FilterFetch, +{ + /// # Safety + /// This will create a Query that could violate memory safety rules. Make sure that this is only called in + /// ways that ensure the Queries have unique mutable access. + #[inline] + pub(crate) unsafe fn new( + world: &'w World, + state: QueryState, + system_counter: Option, + global_system_counter: u32, + ) -> Self { + Self { + world, + state, + system_counter, + global_system_counter, + } + } + + /// Iterates over the query results. This can only be called for read-only queries + #[inline] + pub fn iter(&self) -> QueryIter<'w, '_, Q, F> + where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.iter_unchecked_manual( + self.world, + self.system_counter, + self.global_system_counter, + ) + } + } + + /// Iterates over the query results + #[inline] + pub fn iter_mut(&mut self) -> QueryIter<'w, '_, Q, F> { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.iter_unchecked_manual( + self.world, + self.system_counter, + self.global_system_counter, + ) + } + } + + /// Iterates over the query results + /// # Safety + /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] + pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> { + // SEMI-SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + self.state.iter_unchecked_manual( + self.world, + self.system_counter, + self.global_system_counter, + ) + } + + /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot be chained like a normal iterator. + /// This can only be called for read-only queries + #[inline] + pub fn for_each(&self, f: impl FnMut(>::Item)) + where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.for_each_unchecked_manual( + self.world, + f, + self.system_counter, + self.global_system_counter, + ) + }; + } + + /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot be chained like a normal iterator. + #[inline] + pub fn for_each_mut(&self, f: impl FnMut(>::Item)) { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.for_each_unchecked_manual( + self.world, + f, + self.system_counter, + self.global_system_counter, + ) + }; + } + + /// Runs `f` on each query result in parallel using the given task pool. + #[inline] + pub fn par_for_each( + &self, + task_pool: &TaskPool, + batch_size: usize, + f: impl Fn(>::Item) + Send + Sync + Clone, + ) where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.par_for_each_unchecked_manual( + self.world, + task_pool, + batch_size, + f, + self.system_counter, + self.global_system_counter, + ) + }; + } + + /// Runs `f` on each query result in parallel using the given task pool. + #[inline] + pub fn par_for_each_mut( + &mut self, + task_pool: &TaskPool, + batch_size: usize, + f: impl Fn(>::Item) + Send + Sync + Clone, + ) { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.par_for_each_unchecked_manual( + self.world, + task_pool, + batch_size, + f, + self.system_counter, + self.global_system_counter, + ) + }; + } + + /// Gets the query result for the given `entity` + #[inline] + pub fn get(&self, entity: Entity) -> Result<::Item, QueryEntityError> + where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.get_unchecked_manual( + self.world, + entity, + self.system_counter, + self.global_system_counter, + ) + } + } + + /// Gets the query result for the given `entity` + #[inline] + pub fn get_mut( + &mut self, + entity: Entity, + ) -> Result<::Item, QueryEntityError> { + // // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + unsafe { + self.state.get_unchecked_manual( + self.world, + entity, + self.system_counter, + self.global_system_counter, + ) + } + } + + /// Gets the query result for the given `entity` + /// # Safety + /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] + pub unsafe fn get_unchecked( + &self, + entity: Entity, + ) -> Result<::Item, QueryEntityError> { + // SEMI-SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict + self.state.get_unchecked_manual( + self.world, + entity, + self.system_counter, + self.global_system_counter, + ) + } + + /// Gets a reference to the entity's component of the given type. This will fail if the entity does not have + /// the given component type or if the given component type does not match this query. + #[inline] + pub fn get_component(&self, entity: Entity) -> Result<&T, QueryComponentError> { + let world = self.world; + let entity_ref = world + .get_entity(entity) + .ok_or(QueryComponentError::NoSuchEntity)?; + let component_id = world + .components() + .get_id(TypeId::of::()) + .ok_or(QueryComponentError::MissingComponent)?; + let archetype_component = entity_ref + .archetype() + .get_archetype_component_id(component_id) + .ok_or(QueryComponentError::MissingComponent)?; + if self + .state + .archetype_component_access + .has_read(archetype_component) + { + entity_ref + .get::() + .ok_or(QueryComponentError::MissingComponent) + } else { + Err(QueryComponentError::MissingReadAccess) + } + } + + /// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have + /// the given component type or if the given component type does not match this query. + #[inline] + pub fn get_component_mut( + &mut self, + entity: Entity, + ) -> Result, QueryComponentError> { + // SAFE: unique access to query (preventing aliased access) + unsafe { self.get_component_unchecked_mut(entity) } + } + + /// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have + /// the given component type or the component does not match the query. + /// # Safety + /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] + pub unsafe fn get_component_unchecked_mut( + &self, + entity: Entity, + ) -> Result, QueryComponentError> { + let world = self.world; + let entity_ref = world + .get_entity(entity) + .ok_or(QueryComponentError::NoSuchEntity)?; + let component_id = world + .components() + .get_id(TypeId::of::()) + .ok_or(QueryComponentError::MissingComponent)?; + let archetype_component = entity_ref + .archetype() + .get_archetype_component_id(component_id) + .ok_or(QueryComponentError::MissingComponent)?; + if self + .state + .archetype_component_access + .has_write(archetype_component) + { + entity_ref + .get_unchecked_mut::() + .ok_or(QueryComponentError::MissingComponent) + } else { + Err(QueryComponentError::MissingWriteAccess) + } + } +} diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 10f7f6b4ab094..c9c0cc096128a 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1,6 +1,6 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, - component::{Component, ComponentFlags, ComponentId, StorageType}, + component::{Component, ComponentCounters, ComponentId, StorageType}, entity::Entity, query::{Access, FilteredAccess}, storage::{ComponentSparseSet, Table, Tables}, @@ -24,7 +24,12 @@ pub trait Fetch<'w>: Sized { /// Creates a new instance of this fetch. /// # Safety /// `state` must have been initialized (via [FetchState::init]) using the same `world` passed in to this function. - unsafe fn init(world: &World, state: &Self::State) -> Self; + unsafe fn init( + world: &World, + state: &Self::State, + system_counter: Option, + global_system_counter: u32, + ) -> Self; /// Returns true if (and only if) every table of every archetype matched by this Fetch contains all of the matched components. /// This is used to select a more efficient "table iterator" for "dense" queries. @@ -124,7 +129,12 @@ impl<'w> Fetch<'w> for EntityFetch { true } - unsafe fn init(_world: &World, _state: &Self::State) -> Self { + unsafe fn init( + _world: &World, + _state: &Self::State, + _system_counter: Option, + _global_system_counter: u32, + ) -> Self { Self { entities: std::ptr::null::(), } @@ -226,7 +236,12 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { } } - unsafe fn init(world: &World, state: &Self::State) -> Self { + unsafe fn init( + world: &World, + state: &Self::State, + _system_counter: Option, + _global_system_counter: u32, + ) -> Self { let mut value = Self { storage_type: state.storage_type, table_components: NonNull::dangling(), @@ -300,10 +315,12 @@ impl WorldQuery for &mut T { pub struct WriteFetch { storage_type: StorageType, table_components: NonNull, - table_flags: *mut ComponentFlags, + table_counters: *mut ComponentCounters, entities: *const Entity, entity_table_rows: *const usize, sparse_set: *const ComponentSparseSet, + system_counter: Option, + global_system_counter: u32, } pub struct WriteState { @@ -364,14 +381,21 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { } } - unsafe fn init(world: &World, state: &Self::State) -> Self { + unsafe fn init( + world: &World, + state: &Self::State, + system_counter: Option, + global_system_counter: u32, + ) -> Self { let mut value = Self { storage_type: state.storage_type, table_components: NonNull::dangling(), entities: ptr::null::(), entity_table_rows: ptr::null::(), sparse_set: ptr::null::(), - table_flags: ptr::null_mut::(), + table_counters: ptr::null_mut::(), + system_counter, + global_system_counter, }; if state.storage_type == StorageType::SparseSet { value.sparse_set = world @@ -397,7 +421,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { let table = tables.get_unchecked(archetype.table_id()); let column = table.get_column(state.component_id).unwrap(); self.table_components = column.get_ptr().cast::(); - self.table_flags = column.get_flags_mut_ptr(); + self.table_counters = column.get_counters_mut_ptr(); } StorageType::SparseSet => self.entities = archetype.entities().as_ptr(), } @@ -407,7 +431,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { let column = table.get_column(state.component_id).unwrap(); self.table_components = column.get_ptr().cast::(); - self.table_flags = column.get_flags_mut_ptr(); + self.table_counters = column.get_counters_mut_ptr(); } #[inline] @@ -417,15 +441,20 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { let table_row = *self.entity_table_rows.add(archetype_index); Mut { value: &mut *self.table_components.as_ptr().add(table_row), - flags: &mut *self.table_flags.add(table_row), + component_counters: &mut *self.table_counters.add(table_row), + global_system_counter: self.global_system_counter, + system_counter: self.system_counter, } } StorageType::SparseSet => { let entity = *self.entities.add(archetype_index); - let (component, flags) = (*self.sparse_set).get_with_flags(entity).unwrap(); + let (component, component_counters) = + (*self.sparse_set).get_with_counters(entity).unwrap(); Mut { value: &mut *component.cast::(), - flags: &mut *flags, + component_counters: &mut *component_counters, + global_system_counter: self.global_system_counter, + system_counter: self.system_counter, } } } @@ -435,7 +464,9 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { Mut { value: &mut *self.table_components.as_ptr().add(table_row), - flags: &mut *self.table_flags.add(table_row), + component_counters: &mut *self.table_counters.add(table_row), + global_system_counter: self.global_system_counter, + system_counter: self.system_counter, } } } @@ -498,9 +529,14 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { self.fetch.is_dense() } - unsafe fn init(world: &World, state: &Self::State) -> Self { + unsafe fn init( + world: &World, + state: &Self::State, + system_counter: Option, + global_system_counter: u32, + ) -> Self { Self { - fetch: T::init(world, &state.state), + fetch: T::init(world, &state.state, system_counter, global_system_counter), matches: false, } } @@ -547,50 +583,55 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { /// Flags on component `T` that happened since the start of the frame. #[derive(Clone)] -pub struct Flags { - flags: ComponentFlags, +pub struct Counters { + component_counters: ComponentCounters, + system_counter: Option, + global_system_counter: u32, marker: PhantomData, } -impl std::fmt::Debug for Flags { +impl std::fmt::Debug for Counters { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Flags") - .field("added", &self.added()) - .field("mutated", &self.mutated()) + f.debug_struct("Counters") + .field("component_counters", &self.component_counters) + .field("system_counter", &self.system_counter) + .field("global_system_counter", &self.global_system_counter) .finish() } } -impl Flags { - /// Has this component been added since the start of the frame. - pub fn added(&self) -> bool { - self.flags.contains(ComponentFlags::ADDED) +impl Counters { + /// Has this component been added since the last execution of this system. + pub fn is_added(&self) -> bool { + self.component_counters + .is_added(self.system_counter, self.global_system_counter) } - /// Has this component been mutated since the start of the frame. - pub fn mutated(&self) -> bool { - self.flags.contains(ComponentFlags::MUTATED) + /// Has this component been mutated since the last execution of this system. + pub fn is_mutated(&self) -> bool { + self.component_counters + .is_mutated(self.system_counter, self.global_system_counter) } - /// Has this component been either mutated or added since the start of the frame. - pub fn changed(&self) -> bool { - self.flags - .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) + /// Has this component been mutated since the last execution of this system. + pub fn is_changed(&self) -> bool { + self.component_counters + .is_changed(self.system_counter, self.global_system_counter) } } -impl WorldQuery for Flags { - type Fetch = FlagsFetch; - type State = FlagsState; +impl WorldQuery for Counters { + type Fetch = CountersFetch; + type State = CountersState; } -pub struct FlagsState { +pub struct CountersState { component_id: ComponentId, storage_type: StorageType, marker: PhantomData, } // SAFE: component access and archetype component access are properly updated to reflect that T is read -unsafe impl FetchState for FlagsState { +unsafe impl FetchState for CountersState { fn init(world: &mut World) -> Self { let component_info = world.components.get_or_insert_info::(); Self { @@ -625,21 +666,23 @@ unsafe impl FetchState for FlagsState { } } -pub struct FlagsFetch { +pub struct CountersFetch { storage_type: StorageType, - table_flags: *const ComponentFlags, + table_counters: *const ComponentCounters, entity_table_rows: *const usize, entities: *const Entity, sparse_set: *const ComponentSparseSet, marker: PhantomData, + system_counter: Option, + global_system_counter: u32, } /// SAFE: access is read only -unsafe impl ReadOnlyFetch for FlagsFetch {} +unsafe impl ReadOnlyFetch for CountersFetch {} -impl<'w, T: Component> Fetch<'w> for FlagsFetch { - type Item = Flags; - type State = FlagsState; +impl<'w, T: Component> Fetch<'w> for CountersFetch { + type Item = Counters; + type State = CountersState; #[inline] fn is_dense(&self) -> bool { @@ -649,14 +692,21 @@ impl<'w, T: Component> Fetch<'w> for FlagsFetch { } } - unsafe fn init(world: &World, state: &Self::State) -> Self { + unsafe fn init( + world: &World, + state: &Self::State, + system_counter: Option, + global_system_counter: u32, + ) -> Self { let mut value = Self { storage_type: state.storage_type, - table_flags: ptr::null::(), + table_counters: ptr::null::(), entities: ptr::null::(), entity_table_rows: ptr::null::(), sparse_set: ptr::null::(), marker: PhantomData, + system_counter, + global_system_counter, }; if state.storage_type == StorageType::SparseSet { value.sparse_set = world @@ -681,7 +731,7 @@ impl<'w, T: Component> Fetch<'w> for FlagsFetch { // SAFE: archetype tables always exist let table = tables.get_unchecked(archetype.table_id()); let column = table.get_column(state.component_id).unwrap(); - self.table_flags = column.get_flags_mut_ptr().cast::(); + self.table_counters = column.get_counters_mut_ptr().cast::(); } StorageType::SparseSet => self.entities = archetype.entities().as_ptr(), } @@ -689,11 +739,11 @@ impl<'w, T: Component> Fetch<'w> for FlagsFetch { #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { - self.table_flags = table + self.table_counters = table .get_column(state.component_id) .unwrap() - .get_flags_mut_ptr() - .cast::(); + .get_counters_mut_ptr() + .cast::(); } #[inline] @@ -701,16 +751,20 @@ impl<'w, T: Component> Fetch<'w> for FlagsFetch { match self.storage_type { StorageType::Table => { let table_row = *self.entity_table_rows.add(archetype_index); - Flags { - flags: *self.table_flags.add(table_row), + Counters { + component_counters: *self.table_counters.add(table_row), marker: PhantomData, + system_counter: self.system_counter, + global_system_counter: self.global_system_counter, } } StorageType::SparseSet => { let entity = *self.entities.add(archetype_index); - Flags { - flags: *(*self.sparse_set).get_flags(entity).unwrap(), + Counters { + component_counters: *(*self.sparse_set).get_counters(entity).unwrap(), marker: PhantomData, + system_counter: self.system_counter, + global_system_counter: self.global_system_counter, } } } @@ -718,9 +772,11 @@ impl<'w, T: Component> Fetch<'w> for FlagsFetch { #[inline] unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - Flags { - flags: *self.table_flags.add(table_row), + Counters { + component_counters: *self.table_counters.add(table_row), marker: PhantomData, + system_counter: self.system_counter, + global_system_counter: self.global_system_counter, } } } @@ -732,9 +788,9 @@ macro_rules! impl_tuple_fetch { type Item = ($($name::Item,)*); type State = ($($name::State,)*); - unsafe fn init(_world: &World, state: &Self::State) -> Self { + unsafe fn init(_world: &World, state: &Self::State, _system_counter: Option, _global_system_counter: u32) -> Self { let ($($name,)*) = state; - ($($name::init(_world, $name),)*) + ($($name::init(_world, $name, _system_counter, _global_system_counter),)*) } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index ff977445d3d98..afeea657e39dd 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, bundle::Bundle, - component::{Component, ComponentFlags, ComponentId, StorageType}, + component::{Component, ComponentCounters, ComponentId, StorageType}, entity::Entity, query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, Tables}, @@ -102,7 +102,12 @@ impl<'a, T: Component> Fetch<'a> for WithFetch { type Item = bool; type State = WithState; - unsafe fn init(_world: &World, state: &Self::State) -> Self { + unsafe fn init( + _world: &World, + state: &Self::State, + _system_counter: Option, + _global_system_counter: u32, + ) -> Self { Self { storage_type: state.storage_type, marker: PhantomData, @@ -193,7 +198,12 @@ impl<'a, T: Component> Fetch<'a> for WithoutFetch { type Item = bool; type State = WithoutState; - unsafe fn init(_world: &World, state: &Self::State) -> Self { + unsafe fn init( + _world: &World, + state: &Self::State, + _system_counter: Option, + _global_system_counter: u32, + ) -> Self { Self { storage_type: state.storage_type, marker: PhantomData, @@ -283,7 +293,12 @@ impl<'a, T: Bundle> Fetch<'a> for WithBundleFetch { type Item = bool; type State = WithBundleState; - unsafe fn init(_world: &World, state: &Self::State) -> Self { + unsafe fn init( + _world: &World, + state: &Self::State, + _system_counter: Option, + _global_system_counter: u32, + ) -> Self { Self { is_dense: state.is_dense, marker: PhantomData, @@ -356,10 +371,10 @@ macro_rules! impl_query_filter_tuple { type State = Or<($(<$filter as Fetch<'a>>::State,)*)>; type Item = bool; - unsafe fn init(world: &World, state: &Self::State) -> Self { + unsafe fn init(world: &World, state: &Self::State, system_counter: Option, global_system_counter: u32) -> Self { let ($($filter,)*) = &state.0; Or(($(OrFetch { - fetch: $filter::init(world, $filter), + fetch: $filter::init(world, $filter, system_counter, global_system_counter), matches: false, },)*)) } @@ -440,20 +455,22 @@ macro_rules! impl_query_filter_tuple { all_tuples!(impl_query_filter_tuple, 0, 15, F, S); -macro_rules! impl_flag_filter { +macro_rules! impl_counter_filter { ( $(#[$meta:meta])* - $name: ident, $state_name: ident, $fetch_name: ident, $($flags: expr),+) => { + $name: ident, $state_name: ident, $fetch_name: ident, $($counters: expr),+) => { $(#[$meta])* pub struct $name(PhantomData); pub struct $fetch_name { storage_type: StorageType, - table_flags: *mut ComponentFlags, + table_counters: *mut ComponentCounters, entity_table_rows: *const usize, marker: PhantomData, entities: *const Entity, sparse_set: *const ComponentSparseSet, + system_counter: Option, + global_system_counter: u32, } pub struct $state_name { @@ -508,14 +525,16 @@ macro_rules! impl_flag_filter { type State = $state_name; type Item = bool; - unsafe fn init(world: &World, state: &Self::State) -> Self { + unsafe fn init(world: &World, state: &Self::State, system_counter: Option, global_system_counter: u32) -> Self { let mut value = Self { storage_type: state.storage_type, - table_flags: ptr::null_mut::(), + table_counters: ptr::null_mut::(), entities: ptr::null::(), entity_table_rows: ptr::null::(), sparse_set: ptr::null::(), marker: PhantomData, + system_counter, + global_system_counter, }; if state.storage_type == StorageType::SparseSet { value.sparse_set = world @@ -532,9 +551,9 @@ macro_rules! impl_flag_filter { } unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { - self.table_flags = table + self.table_counters = table .get_column(state.component_id).unwrap() - .get_flags_mut_ptr(); + .get_counters_mut_ptr(); } unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) { @@ -543,28 +562,28 @@ macro_rules! impl_flag_filter { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); // SAFE: archetype tables always exist let table = tables.get_unchecked(archetype.table_id()); - self.table_flags = table + self.table_counters = table .get_column(state.component_id).unwrap() - .get_flags_mut_ptr(); + .get_counters_mut_ptr(); } StorageType::SparseSet => self.entities = archetype.entities().as_ptr(), } } unsafe fn table_fetch(&mut self, table_row: usize) -> bool { - false $(|| (*self.table_flags.add(table_row)).contains($flags))+ + false $(|| $counters(&*self.table_counters.add(table_row), self.system_counter, self.global_system_counter))+ } unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool { match self.storage_type { StorageType::Table => { let table_row = *self.entity_table_rows.add(archetype_index); - false $(|| (*self.table_flags.add(table_row)).contains($flags))+ + false $(|| $counters(&*self.table_counters.add(table_row), self.system_counter, self.global_system_counter))+ } StorageType::SparseSet => { let entity = *self.entities.add(archetype_index); - let flags = (*(*self.sparse_set).get_flags(entity).unwrap()); - false $(|| flags.contains($flags))+ + let counters = (*(*self.sparse_set).get_counters(entity).unwrap()); + false $(|| $counters(&counters, self.system_counter, self.global_system_counter))+ } } } @@ -572,7 +591,7 @@ macro_rules! impl_flag_filter { }; } -impl_flag_filter!( +impl_counter_filter!( /// Filter that retrieves components of type `T` that have been added since the start of the frame /// /// This filter is useful as a performance optimization as it means that the query contains fewer items @@ -600,10 +619,10 @@ impl_flag_filter!( Added, AddedState, AddedFetch, - ComponentFlags::ADDED + ComponentCounters::is_added ); -impl_flag_filter!( +impl_counter_filter!( /// Filter that retrieves components of type `T` that have been mutated since the start of the frame. /// Added components do not count as mutated. /// @@ -631,10 +650,10 @@ impl_flag_filter!( Mutated, MutatedState, MutatedFetch, - ComponentFlags::MUTATED + ComponentCounters::is_mutated ); -impl_flag_filter!( +impl_counter_filter!( /// Filter that retrieves components of type `T` that have been added or mutated since the start of the frame /// /// This filter is useful as a performance optimization as it means that the query contains fewer items @@ -647,6 +666,5 @@ impl_flag_filter!( Changed, ChangedState, ChangedFetch, - ComponentFlags::ADDED, - ComponentFlags::MUTATED + ComponentCounters::is_changed ); diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 9a38d9135d883..ce92f7c34114e 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -26,9 +26,24 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 's, Q, F> where F::Fetch: FilterFetch, { - pub(crate) unsafe fn new(world: &'w World, query_state: &'s QueryState) -> Self { - let fetch = ::init(world, &query_state.fetch_state); - let filter = ::init(world, &query_state.filter_state); + pub(crate) unsafe fn new( + world: &'w World, + query_state: &'s QueryState, + system_counter: Option, + global_system_counter: u32, + ) -> Self { + let fetch = ::init( + world, + &query_state.fetch_state, + system_counter, + global_system_counter, + ); + let filter = ::init( + world, + &query_state.filter_state, + system_counter, + global_system_counter, + ); QueryIter { is_dense: fetch.is_dense() && filter.is_dense(), world, diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 95b4d7b6e8912..2ee4493f82b4f 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -1,10 +1,12 @@ mod access; +mod direct; mod fetch; mod filter; mod iter; mod state; pub use access::*; +pub use direct::*; pub use fetch::*; pub use filter::*; pub use iter::*; @@ -27,13 +29,13 @@ mod tests { let mut world = World::new(); world.spawn().insert_bundle((A(1), B(1))); world.spawn().insert_bundle((A(2),)); - let values = world.query::<&A>().iter(&world).collect::>(); + let values = world.query::<&A>().iter().collect::>(); assert_eq!(values, vec![&A(1), &A(2)]); - for (_a, mut b) in world.query::<(&A, &mut B)>().iter_mut(&mut world) { + for (_a, mut b) in world.query::<(&A, &mut B)>().iter_mut() { b.0 = 3; } - let values = world.query::<&B>().iter(&world).collect::>(); + let values = world.query::<&B>().iter().collect::>(); assert_eq!(values, vec![&B(3)]); } @@ -47,14 +49,14 @@ mod tests { world.spawn().insert_bundle((A(1), B(2))); world.spawn().insert_bundle((A(2),)); - let values = world.query::<&A>().iter(&world).collect::>(); + let values = world.query::<&A>().iter().collect::>(); assert_eq!(values, vec![&A(1), &A(2)]); - for (_a, mut b) in world.query::<(&A, &mut B)>().iter_mut(&mut world) { + for (_a, mut b) in world.query::<(&A, &mut B)>().iter_mut() { b.0 = 3; } - let values = world.query::<&B>().iter(&world).collect::>(); + let values = world.query::<&B>().iter().collect::>(); assert_eq!(values, vec![&B(3)]); } } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index b87fc7c637dc6..d64b85fcd9125 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -106,12 +106,14 @@ where &mut self, world: &'w World, entity: Entity, + system_counter: Option, + global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> where Q::Fetch: ReadOnlyFetch, { // SAFE: query is read only - unsafe { self.get_unchecked(world, entity) } + unsafe { self.get_unchecked(world, entity, system_counter, global_system_counter) } } #[inline] @@ -119,9 +121,11 @@ where &mut self, world: &'w mut World, entity: Entity, + system_counter: Option, + global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> { // SAFE: query has unique world access - unsafe { self.get_unchecked(world, entity) } + unsafe { self.get_unchecked(world, entity, system_counter, global_system_counter) } } /// # Safety @@ -132,9 +136,11 @@ where &mut self, world: &'w World, entity: Entity, + system_counter: Option, + global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> { self.validate_world_and_update_archetypes(world); - self.get_unchecked_manual(world, entity) + self.get_unchecked_manual(world, entity, system_counter, global_system_counter) } /// # Safety @@ -144,6 +150,8 @@ where &self, world: &'w World, entity: Entity, + system_counter: Option, + global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> { let location = world .entities @@ -157,8 +165,18 @@ where } // SAFE: live entities always exist in an archetype let archetype = world.archetypes.get_unchecked(location.archetype_id); - let mut fetch = ::init(world, &self.fetch_state); - let mut filter = ::init(world, &self.filter_state); + let mut fetch = ::init( + world, + &self.fetch_state, + system_counter, + global_system_counter, + ); + let mut filter = ::init( + world, + &self.filter_state, + system_counter, + global_system_counter, + ); fetch.set_archetype(&self.fetch_state, archetype, &world.storages().tables); filter.set_archetype(&self.filter_state, archetype, &world.storages().tables); @@ -170,18 +188,28 @@ where } #[inline] - pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q, F> + pub fn iter<'w, 's>( + &'s mut self, + world: &'w World, + system_counter: Option, + global_system_counter: u32, + ) -> QueryIter<'w, 's, Q, F> where Q::Fetch: ReadOnlyFetch, { // SAFE: query is read only - unsafe { self.iter_unchecked(world) } + unsafe { self.iter_unchecked(world, system_counter, global_system_counter) } } #[inline] - pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> { + pub fn iter_mut<'w, 's>( + &'s mut self, + world: &'w mut World, + system_counter: Option, + global_system_counter: u32, + ) -> QueryIter<'w, 's, Q, F> { // SAFE: query has unique world access - unsafe { self.iter_unchecked(world) } + unsafe { self.iter_unchecked(world, system_counter, global_system_counter) } } /// # Safety @@ -191,9 +219,11 @@ where pub unsafe fn iter_unchecked<'w, 's>( &'s mut self, world: &'w World, + system_counter: Option, + global_system_counter: u32, ) -> QueryIter<'w, 's, Q, F> { self.validate_world_and_update_archetypes(world); - self.iter_unchecked_manual(world) + self.iter_unchecked_manual(world, system_counter, global_system_counter) } /// # Safety @@ -205,8 +235,10 @@ where pub(crate) unsafe fn iter_unchecked_manual<'w, 's>( &'s self, world: &'w World, + system_counter: Option, + global_system_counter: u32, ) -> QueryIter<'w, 's, Q, F> { - QueryIter::new(world, self) + QueryIter::new(world, self, system_counter, global_system_counter) } #[inline] @@ -214,12 +246,14 @@ where &mut self, world: &'w World, func: impl FnMut(>::Item), + system_counter: Option, + global_system_counter: u32, ) where Q::Fetch: ReadOnlyFetch, { // SAFE: query is read only unsafe { - self.for_each_unchecked(world, func); + self.for_each_unchecked(world, func, system_counter, global_system_counter); } } @@ -228,10 +262,12 @@ where &mut self, world: &'w mut World, func: impl FnMut(>::Item), + system_counter: Option, + global_system_counter: u32, ) { // SAFE: query has unique world access unsafe { - self.for_each_unchecked(world, func); + self.for_each_unchecked(world, func, system_counter, global_system_counter); } } @@ -243,9 +279,11 @@ where &mut self, world: &'w World, func: impl FnMut(>::Item), + system_counter: Option, + global_system_counter: u32, ) { self.validate_world_and_update_archetypes(world); - self.for_each_unchecked_manual(world, func); + self.for_each_unchecked_manual(world, func, system_counter, global_system_counter); } #[inline] @@ -255,12 +293,21 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, + system_counter: Option, + global_system_counter: u32, ) where Q::Fetch: ReadOnlyFetch, { // SAFE: query is read only unsafe { - self.par_for_each_unchecked(world, task_pool, batch_size, func); + self.par_for_each_unchecked( + world, + task_pool, + batch_size, + func, + system_counter, + global_system_counter, + ); } } @@ -271,10 +318,19 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, + system_counter: Option, + global_system_counter: u32, ) { // SAFE: query has unique world access unsafe { - self.par_for_each_unchecked(world, task_pool, batch_size, func); + self.par_for_each_unchecked( + world, + task_pool, + batch_size, + func, + system_counter, + global_system_counter, + ); } } @@ -288,9 +344,18 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, + system_counter: Option, + global_system_counter: u32, ) { self.validate_world_and_update_archetypes(world); - self.par_for_each_unchecked_manual(world, task_pool, batch_size, func); + self.par_for_each_unchecked_manual( + world, + task_pool, + batch_size, + func, + system_counter, + global_system_counter, + ); } /// # Safety @@ -302,9 +367,21 @@ where &'s self, world: &'w World, mut func: impl FnMut(>::Item), + system_counter: Option, + global_system_counter: u32, ) { - let mut fetch = ::init(world, &self.fetch_state); - let mut filter = ::init(world, &self.filter_state); + let mut fetch = ::init( + world, + &self.fetch_state, + system_counter, + global_system_counter, + ); + let mut filter = ::init( + world, + &self.filter_state, + system_counter, + global_system_counter, + ); if fetch.is_dense() && filter.is_dense() { let tables = &world.storages().tables; for table_id in self.matched_table_ids.iter() { @@ -349,10 +426,22 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, + system_counter: Option, + global_system_counter: u32, ) { task_pool.scope(|scope| { - let fetch = ::init(world, &self.fetch_state); - let filter = ::init(world, &self.filter_state); + let fetch = ::init( + world, + &self.fetch_state, + system_counter, + global_system_counter, + ); + let filter = ::init( + world, + &self.filter_state, + system_counter, + global_system_counter, + ); if fetch.is_dense() && filter.is_dense() { let tables = &world.storages().tables; @@ -362,8 +451,18 @@ where while offset < table.len() { let func = func.clone(); scope.spawn(async move { - let mut fetch = ::init(world, &self.fetch_state); - let mut filter = ::init(world, &self.filter_state); + let mut fetch = ::init( + world, + &self.fetch_state, + system_counter, + global_system_counter, + ); + let mut filter = ::init( + world, + &self.filter_state, + system_counter, + global_system_counter, + ); let tables = &world.storages().tables; let table = tables.get_unchecked(*table_id); fetch.set_table(&self.fetch_state, table); @@ -388,8 +487,18 @@ where while offset < archetype.len() { let func = func.clone(); scope.spawn(async move { - let mut fetch = ::init(world, &self.fetch_state); - let mut filter = ::init(world, &self.filter_state); + let mut fetch = ::init( + world, + &self.fetch_state, + system_counter, + global_system_counter, + ); + let mut filter = ::init( + world, + &self.filter_state, + system_counter, + global_system_counter, + ); let tables = &world.storages().tables; let archetype = world.archetypes.get_unchecked(*archetype_id); fetch.set_archetype(&self.fetch_state, archetype, tables); diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index c2a462027feee..4d6bf4ea40139 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use crate::{ - component::{Component, ComponentFlags}, + component::{Component, ComponentCounters}, entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, world::{FromWorld, World}, }; @@ -102,7 +102,9 @@ impl FromType for ReflectComponent { .get_unchecked_mut::() .map(|c| ReflectMut { value: c.value as &mut dyn Reflect, - flags: c.flags, + component_counters: c.component_counters, + system_counter: world.get_exclusive_system_counter(), + global_system_counter: world.get_global_system_counter(), }) }, } @@ -112,7 +114,9 @@ impl FromType for ReflectComponent { /// Unique borrow of a Reflected component pub struct ReflectMut<'a> { pub(crate) value: &'a mut dyn Reflect, - pub(crate) flags: &'a mut ComponentFlags, + pub(crate) component_counters: &'a mut ComponentCounters, + pub(crate) system_counter: Option, + pub(crate) global_system_counter: u32, } impl<'a> Deref for ReflectMut<'a> { @@ -127,11 +131,32 @@ impl<'a> Deref for ReflectMut<'a> { impl<'a> DerefMut for ReflectMut<'a> { #[inline] fn deref_mut(&mut self) -> &mut dyn Reflect { - self.flags.insert(ComponentFlags::MUTATED); + self.component_counters + .set_changed(self.global_system_counter); self.value } } +impl<'a> ReflectMut<'a> { + /// Returns true if (and only if) this component been added since the start of the frame. + pub fn is_added(&self) -> bool { + self.component_counters + .is_added(self.system_counter, self.global_system_counter) + } + + /// Returns true if (and only if) this component been mutated since the start of the frame. + pub fn is_mutated(&self) -> bool { + self.component_counters + .is_mutated(self.system_counter, self.global_system_counter) + } + + /// Returns true if (and only if) this component been either mutated or added since the start of the frame. + pub fn is_changed(&self) -> bool { + self.component_counters + .is_changed(self.system_counter, self.global_system_counter) + } +} + impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); #[derive(Clone)] diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index cf4ea2b3d40ec..0c7d8c7920b7a 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -1,5 +1,5 @@ use crate::{ - component::{ComponentFlags, ComponentId, ComponentInfo}, + component::{ComponentCounters, ComponentId, ComponentInfo}, entity::Entity, storage::BlobVec, }; @@ -87,7 +87,7 @@ impl SparseArray { #[derive(Debug)] pub struct ComponentSparseSet { dense: BlobVec, - flags: UnsafeCell>, + counters: UnsafeCell>, entities: Vec, sparse: SparseArray, } @@ -96,7 +96,7 @@ impl ComponentSparseSet { pub fn new(component_info: &ComponentInfo, capacity: usize) -> Self { Self { dense: BlobVec::new(component_info.layout(), component_info.drop(), capacity), - flags: UnsafeCell::new(Vec::with_capacity(capacity)), + counters: UnsafeCell::new(Vec::with_capacity(capacity)), entities: Vec::with_capacity(capacity), sparse: Default::default(), } @@ -117,20 +117,18 @@ impl ComponentSparseSet { /// # Safety /// The `value` pointer must point to a valid address that matches the `Layout` inside the `ComponentInfo` given /// when constructing this sparse set. - pub unsafe fn insert(&mut self, entity: Entity, value: *mut u8, flags: ComponentFlags) { + pub unsafe fn insert(&mut self, entity: Entity, value: *mut u8, counters: ComponentCounters) { let dense = &mut self.dense; let entities = &mut self.entities; - let flag_list = self.flags.get_mut(); + let counters_list = self.counters.get_mut(); let dense_index = *self.sparse.get_or_insert_with(entity, move || { - flag_list.push(ComponentFlags::empty()); + counters_list.push(counters); entities.push(entity); dense.push_uninit() }); // SAFE: dense_index exists thanks to the call above self.dense.set_unchecked(dense_index, value); - (*self.flags.get()) - .get_unchecked_mut(dense_index) - .insert(flags); + *(*self.counters.get()).get_unchecked_mut(dense_index) = counters; } #[inline] @@ -151,14 +149,17 @@ impl ComponentSparseSet { /// # Safety /// ensure the same entity is not accessed twice at the same time #[inline] - pub unsafe fn get_with_flags(&self, entity: Entity) -> Option<(*mut u8, *mut ComponentFlags)> { - let flags = &mut *self.flags.get(); + pub unsafe fn get_with_counters( + &self, + entity: Entity, + ) -> Option<(*mut u8, *mut ComponentCounters)> { + let counters = &mut *self.counters.get(); self.sparse.get(entity).map(move |dense_index| { let dense_index = *dense_index; // SAFE: if the sparse index points to something in the dense vec, it exists ( self.dense.get_unchecked(dense_index), - flags.get_unchecked_mut(dense_index) as *mut ComponentFlags, + counters.get_unchecked_mut(dense_index) as *mut ComponentCounters, ) }) } @@ -166,12 +167,12 @@ impl ComponentSparseSet { /// # Safety /// ensure the same entity is not accessed twice at the same time #[inline] - pub unsafe fn get_flags(&self, entity: Entity) -> Option<&mut ComponentFlags> { - let flags = &mut *self.flags.get(); + pub unsafe fn get_counters(&self, entity: Entity) -> Option<&mut ComponentCounters> { + let counters = &mut *self.counters.get(); self.sparse.get(entity).map(move |dense_index| { let dense_index = *dense_index; // SAFE: if the sparse index points to something in the dense vec, it exists - flags.get_unchecked_mut(dense_index) + counters.get_unchecked_mut(dense_index) }) } @@ -181,7 +182,7 @@ impl ComponentSparseSet { self.sparse.remove(entity).map(|dense_index| { // SAFE: unique access to flags unsafe { - (*self.flags.get()).swap_remove(dense_index); + (*self.counters.get()).swap_remove(dense_index); } self.entities.swap_remove(dense_index); let is_last = dense_index == self.dense.len() - 1; @@ -197,7 +198,7 @@ impl ComponentSparseSet { pub fn remove(&mut self, entity: Entity) -> bool { if let Some(dense_index) = self.sparse.remove(entity) { - self.flags.get_mut().swap_remove(dense_index); + self.counters.get_mut().swap_remove(dense_index); self.entities.swap_remove(dense_index); let is_last = dense_index == self.dense.len() - 1; // SAFE: if the sparse index points to something in the dense vec, it exists @@ -212,10 +213,10 @@ impl ComponentSparseSet { } } - pub(crate) fn clear_flags(&mut self) { - let flags = self.flags.get_mut().iter_mut(); - for component_flags in flags { - *component_flags = ComponentFlags::empty(); + pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { + let counters = self.counters.get_mut().iter_mut(); + for component_counters in counters { + component_counters.clear(global_system_counter); } } } @@ -442,9 +443,9 @@ impl SparseSets { self.sets.get_mut(component_id) } - pub(crate) fn clear_flags(&mut self) { + pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { for set in self.sets.values_mut() { - set.clear_flags(); + set.clear_counters(global_system_counter); } } } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 45449ed58a1f0..db058f85f3611 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -1,6 +1,6 @@ use crate::{ archetype::ArchetypeId, - component::{ComponentFlags, ComponentId, ComponentInfo, Components}, + component::{ComponentCounters, ComponentId, ComponentInfo, Components}, entity::Entity, storage::{BlobVec, SparseSet}, }; @@ -34,7 +34,7 @@ impl TableId { pub struct Column { pub(crate) component_id: ComponentId, pub(crate) data: BlobVec, - pub(crate) flags: UnsafeCell>, + pub(crate) counters: UnsafeCell>, } impl Column { @@ -43,7 +43,7 @@ impl Column { Column { component_id: component_info.id(), data: BlobVec::new(component_info.layout(), component_info.drop(), capacity), - flags: UnsafeCell::new(Vec::with_capacity(capacity)), + counters: UnsafeCell::new(Vec::with_capacity(capacity)), } } @@ -67,35 +67,35 @@ impl Column { /// # Safety /// Assumes data has already been allocated for the given row/column. - /// Allows aliased mutable accesses to the row's ComponentFlags. Caller must ensure that this does not happen. + /// Allows aliased mutable accesses to the row's ComponentCounters. Caller must ensure that this does not happen. #[inline] #[allow(clippy::mut_from_ref)] - pub unsafe fn get_flags_unchecked_mut(&self, row: usize) -> &mut ComponentFlags { + pub unsafe fn get_counters_unchecked_mut(&self, row: usize) -> &mut ComponentCounters { debug_assert!(row < self.len()); - (*self.flags.get()).get_unchecked_mut(row) + (*self.counters.get()).get_unchecked_mut(row) } #[inline] pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: usize) { self.data.swap_remove_and_drop_unchecked(row); - (*self.flags.get()).swap_remove(row); + (*self.counters.get()).swap_remove(row); } #[inline] pub(crate) unsafe fn swap_remove_and_forget_unchecked( &mut self, row: usize, - ) -> (*mut u8, ComponentFlags) { + ) -> (*mut u8, ComponentCounters) { let data = self.data.swap_remove_and_forget_unchecked(row); - let flags = (*self.flags.get()).swap_remove(row); - (data, flags) + let counters = (*self.counters.get()).swap_remove(row); + (data, counters) } /// # Safety /// allocated value must be immediately set at the returned row pub(crate) unsafe fn push_uninit(&mut self) -> usize { let row = self.data.push_uninit(); - (*self.flags.get()).push(ComponentFlags::empty()); + (*self.counters.get()).push(ComponentCounters::new(0)); row } @@ -104,8 +104,8 @@ impl Column { self.data.reserve(additional); // SAFE: unique access to self unsafe { - let flags = &mut (*self.flags.get()); - flags.reserve(additional); + let counters = &mut (*self.counters.get()); + counters.reserve(additional); } } @@ -119,8 +119,8 @@ impl Column { /// # Safety /// must ensure rust mutability rules are not violated #[inline] - pub unsafe fn get_flags_mut_ptr(&self) -> *mut ComponentFlags { - (*self.flags.get()).as_mut_ptr() + pub unsafe fn get_counters_mut_ptr(&self) -> *mut ComponentCounters { + (*self.counters.get()).as_mut_ptr() } /// # Safety @@ -134,16 +134,16 @@ impl Column { /// # Safety /// must ensure rust mutability rules are not violated #[inline] - pub unsafe fn get_flags_unchecked(&self, row: usize) -> *mut ComponentFlags { - debug_assert!(row < (*self.flags.get()).len()); - self.get_flags_mut_ptr().add(row) + pub unsafe fn get_counters_unchecked(&self, row: usize) -> *mut ComponentCounters { + debug_assert!(row < (*self.counters.get()).len()); + self.get_counters_mut_ptr().add(row) } #[inline] - pub(crate) fn clear_flags(&mut self) { - let flags = unsafe { (*self.flags.get()).iter_mut() }; - for component_flags in flags { - *component_flags = ComponentFlags::empty(); + pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { + let counters = unsafe { (*self.counters.get()).iter_mut() }; + for component_counters in counters { + component_counters.clear(global_system_counter); } } } @@ -223,10 +223,10 @@ impl Table { let is_last = row == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row)); for column in self.columns.values_mut() { - let (data, flags) = column.swap_remove_and_forget_unchecked(row); + let (data, counters) = column.swap_remove_and_forget_unchecked(row); if let Some(new_column) = new_table.get_column_mut(column.component_id) { new_column.set_unchecked(new_row, data); - *new_column.get_flags_unchecked_mut(new_row) = flags; + *new_column.get_counters_unchecked_mut(new_row) = counters; } } TableMoveResult { @@ -253,9 +253,9 @@ impl Table { let new_row = new_table.allocate(self.entities.swap_remove(row)); for column in self.columns.values_mut() { if let Some(new_column) = new_table.get_column_mut(column.component_id) { - let (data, flags) = column.swap_remove_and_forget_unchecked(row); + let (data, counters) = column.swap_remove_and_forget_unchecked(row); new_column.set_unchecked(new_row, data); - *new_column.get_flags_unchecked_mut(new_row) = flags; + *new_column.get_counters_unchecked_mut(new_row) = counters; } else { column.swap_remove_unchecked(row); } @@ -284,9 +284,9 @@ impl Table { let new_row = new_table.allocate(self.entities.swap_remove(row)); for column in self.columns.values_mut() { let new_column = new_table.get_column_mut(column.component_id).unwrap(); - let (data, flags) = column.swap_remove_and_forget_unchecked(row); + let (data, counters) = column.swap_remove_and_forget_unchecked(row); new_column.set_unchecked(new_row, data); - *new_column.get_flags_unchecked_mut(new_row) = flags; + *new_column.get_counters_unchecked_mut(new_row) = counters; } TableMoveResult { new_row, @@ -334,7 +334,7 @@ impl Table { self.entities.push(entity); for column in self.columns.values_mut() { column.data.set_len(self.entities.len()); - (*column.flags.get()).push(ComponentFlags::empty()); + (*column.counters.get()).push(ComponentCounters::new(0)); } index } @@ -354,9 +354,9 @@ impl Table { self.entities.is_empty() } - pub(crate) fn clear_flags(&mut self) { + pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { for column in self.columns.values_mut() { - column.clear_flags(); + column.clear_counters(global_system_counter); } } @@ -458,9 +458,9 @@ impl Tables { self.tables.iter() } - pub(crate) fn clear_flags(&mut self) { + pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { for table in self.tables.iter_mut() { - table.clear_flags(); + table.clear_counters(global_system_counter); } } } diff --git a/crates/bevy_ecs/src/system/commands.rs b/crates/bevy_ecs/src/system/commands.rs index 8b60b9ada15e3..ad5d17c60b4fe 100644 --- a/crates/bevy_ecs/src/system/commands.rs +++ b/crates/bevy_ecs/src/system/commands.rs @@ -390,7 +390,7 @@ mod tests { assert!(world.entities().len() == 1); let results = world .query::<(&u32, &u64)>() - .iter(&world) + .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results, vec![(1u32, 2u64)]); @@ -401,7 +401,7 @@ mod tests { command_queue.apply(&mut world); let results2 = world .query::<(&u32, &u64)>() - .iter(&world) + .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results2, vec![]); @@ -418,7 +418,7 @@ mod tests { command_queue.apply(&mut world); let results_before = world .query::<(&u32, &u64)>() - .iter(&world) + .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results_before, vec![(1u32, 2u64)]); @@ -430,15 +430,11 @@ mod tests { command_queue.apply(&mut world); let results_after = world .query::<(&u32, &u64)>() - .iter(&world) + .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results_after, vec![]); - let results_after_u64 = world - .query::<&u64>() - .iter(&world) - .copied() - .collect::>(); + let results_after_u64 = world.query::<&u64>().iter().copied().collect::>(); assert_eq!(results_after_u64, vec![]); } diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index 9f67d6c56d528..294a69ac0e38b 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -18,6 +18,7 @@ pub struct ExclusiveSystemFn { func: Box, name: Cow<'static, str>, id: SystemId, + system_counter: Option, } impl ExclusiveSystem for ExclusiveSystemFn { @@ -30,7 +31,17 @@ impl ExclusiveSystem for ExclusiveSystemFn { } fn run(&mut self, world: &mut World) { + // The previous value is saved in case this exclusive system is run by another exclusive system + let saved_counter = world.exclusive_system_counter; + world.exclusive_system_counter = self.system_counter; + (self.func)(world); + + let global_system_counter = world.global_system_counter.get_mut(); + self.system_counter = Some(*global_system_counter); + *global_system_counter += 1; + + world.exclusive_system_counter = saved_counter; } fn initialize(&mut self, _: &mut World) {} @@ -49,6 +60,7 @@ where func: Box::new(self), name: core::any::type_name::().into(), id: SystemId::new(), + system_counter: None, } } } diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 3317fe8a99265..9de4e5e964520 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -15,6 +15,7 @@ pub struct SystemState { pub(crate) archetype_component_access: Access, // NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent SystemParams from overriding each other is_send: bool, + pub(crate) system_counter: Option, } impl SystemState { @@ -25,6 +26,7 @@ impl SystemState { component_access_set: FilteredAccessSet::default(), is_send: true, id: SystemId::new(), + system_counter: None, } } @@ -138,12 +140,16 @@ where #[inline] unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out { - self.func.run( + let global_system_counter = world.increment_global_system_counter(); + let out = self.func.run( input, self.param_state.as_mut().unwrap(), &self.system_state, world, - ) + global_system_counter, + ); + self.system_state.system_counter = Some(global_system_counter); + out } #[inline] @@ -169,6 +175,7 @@ pub trait SystemParamFunction: Send + Sync state: &mut Param::Fetch, system_state: &SystemState, world: &World, + global_system_counter: u32, ) -> Out; } @@ -182,9 +189,9 @@ macro_rules! impl_system_function { FnMut($(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static { #[inline] - fn run(&mut self, _input: (), state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World) -> Out { + fn run(&mut self, _input: (), state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, global_system_counter: u32) -> Out { unsafe { - let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world); + let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, global_system_counter); self($($param),*) } } @@ -198,9 +205,9 @@ macro_rules! impl_system_function { FnMut(In, $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static { #[inline] - fn run(&mut self, input: Input, state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World) -> Out { + fn run(&mut self, input: Input, state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, global_system_counter: u32) -> Out { unsafe { - let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world); + let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, global_system_counter); self(In(input), $($param),*) } } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 74e0311757f05..0d4e6319b4c14 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -162,11 +162,11 @@ mod tests { mut changed: ResMut, mut added: ResMut, ) { - if value.added() { + if value.is_added() { added.0 += 1; } - if value.changed() { + if value.is_changed() { changed.0 += 1; } } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index d19056b86c42f..c35006a6bc2a6 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -17,6 +17,8 @@ where { pub(crate) world: &'w World, pub(crate) state: &'w QueryState, + pub(crate) system_counter: Option, + pub(crate) global_system_counter: u32, } impl<'w, Q: WorldQuery, F: WorldQuery> Query<'w, Q, F> @@ -27,8 +29,18 @@ where /// This will create a Query that could violate memory safety rules. Make sure that this is only called in /// ways that ensure the Queries have unique mutable access. #[inline] - pub(crate) unsafe fn new(world: &'w World, state: &'w QueryState) -> Self { - Self { world, state } + pub(crate) unsafe fn new( + world: &'w World, + state: &'w QueryState, + system_counter: Option, + global_system_counter: u32, + ) -> Self { + Self { + world, + state, + system_counter, + global_system_counter, + } } /// Iterates over the query results. This can only be called for read-only queries @@ -38,14 +50,26 @@ where Q::Fetch: ReadOnlyFetch, { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - unsafe { self.state.iter_unchecked_manual(self.world) } + unsafe { + self.state.iter_unchecked_manual( + self.world, + self.system_counter, + self.global_system_counter, + ) + } } /// Iterates over the query results #[inline] pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - unsafe { self.state.iter_unchecked_manual(self.world) } + unsafe { + self.state.iter_unchecked_manual( + self.world, + self.system_counter, + self.global_system_counter, + ) + } } /// Iterates over the query results @@ -54,7 +78,11 @@ where #[inline] pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> { // SEMI-SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - self.state.iter_unchecked_manual(self.world) + self.state.iter_unchecked_manual( + self.world, + self.system_counter, + self.global_system_counter, + ) } /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot be chained like a normal iterator. @@ -65,14 +93,28 @@ where Q::Fetch: ReadOnlyFetch, { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - unsafe { self.state.for_each_unchecked_manual(self.world, f) }; + unsafe { + self.state.for_each_unchecked_manual( + self.world, + f, + self.system_counter, + self.global_system_counter, + ) + }; } /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot be chained like a normal iterator. #[inline] pub fn for_each_mut(&self, f: impl FnMut(>::Item)) { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - unsafe { self.state.for_each_unchecked_manual(self.world, f) }; + unsafe { + self.state.for_each_unchecked_manual( + self.world, + f, + self.system_counter, + self.global_system_counter, + ) + }; } /// Runs `f` on each query result in parallel using the given task pool. @@ -87,8 +129,14 @@ where { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { - self.state - .par_for_each_unchecked_manual(self.world, task_pool, batch_size, f) + self.state.par_for_each_unchecked_manual( + self.world, + task_pool, + batch_size, + f, + self.system_counter, + self.global_system_counter, + ) }; } @@ -102,8 +150,14 @@ where ) { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { - self.state - .par_for_each_unchecked_manual(self.world, task_pool, batch_size, f) + self.state.par_for_each_unchecked_manual( + self.world, + task_pool, + batch_size, + f, + self.system_counter, + self.global_system_counter, + ) }; } @@ -114,7 +168,14 @@ where Q::Fetch: ReadOnlyFetch, { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - unsafe { self.state.get_unchecked_manual(self.world, entity) } + unsafe { + self.state.get_unchecked_manual( + self.world, + entity, + self.system_counter, + self.global_system_counter, + ) + } } /// Gets the query result for the given `entity` @@ -124,7 +185,14 @@ where entity: Entity, ) -> Result<::Item, QueryEntityError> { // // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - unsafe { self.state.get_unchecked_manual(self.world, entity) } + unsafe { + self.state.get_unchecked_manual( + self.world, + entity, + self.system_counter, + self.global_system_counter, + ) + } } /// Gets the query result for the given `entity` @@ -136,7 +204,12 @@ where entity: Entity, ) -> Result<::Item, QueryEntityError> { // SEMI-SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict - self.state.get_unchecked_manual(self.world, entity) + self.state.get_unchecked_manual( + self.world, + entity, + self.system_counter, + self.global_system_counter, + ) } /// Gets a reference to the entity's component of the given type. This will fail if the entity does not have @@ -207,6 +280,12 @@ where { entity_ref .get_unchecked_mut::() + .map(|component| Mut { + value: component.value, + component_counters: component.component_counters, + system_counter: self.system_counter, + global_system_counter: self.global_system_counter, + }) .ok_or(QueryComponentError::MissingComponent) } else { Err(QueryComponentError::MissingWriteAccess) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 9dcdb9f4d2201..bd643f5edcc42 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, - component::{Component, ComponentFlags, ComponentId, Components}, + component::{Component, ComponentCounters, ComponentId, Components}, entity::{Entities, Entity}, query::{FilterFetch, FilteredAccess, FilteredAccessSet, QueryState, WorldQuery}, system::{CommandQueue, Commands, Query, SystemState}, @@ -58,6 +58,7 @@ pub trait SystemParamFetch<'a>: SystemParamState { state: &'a mut Self, system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item; } @@ -114,10 +115,16 @@ where #[inline] unsafe fn get_param( state: &'a mut Self, - _system_state: &'a SystemState, + system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { - Query::new(world, state) + Query::new( + world, + state, + system_state.system_counter, + global_system_counter, + ) } } @@ -154,24 +161,28 @@ impl_query_set!(); /// Use `Option>` if the resource might not always exist. pub struct Res<'w, T> { value: &'w T, - flags: ComponentFlags, + counters: &'w ComponentCounters, + system_counter: Option, + global_system_counter: u32, } impl<'w, T: Component> Res<'w, T> { /// Returns true if (and only if) this resource been added since the start of the frame. - pub fn added(&self) -> bool { - self.flags.contains(ComponentFlags::ADDED) + pub fn is_added(&self) -> bool { + self.counters + .is_added(self.system_counter, self.global_system_counter) } /// Returns true if (and only if) this resource been mutated since the start of the frame. - pub fn mutated(&self) -> bool { - self.flags.contains(ComponentFlags::MUTATED) + pub fn is_mutated(&self) -> bool { + self.counters + .is_mutated(self.system_counter, self.global_system_counter) } /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. - pub fn changed(&self) -> bool { - self.flags - .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) + pub fn is_changed(&self) -> bool { + self.counters + .is_changed(self.system_counter, self.global_system_counter) } } @@ -227,15 +238,18 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResState { #[inline] unsafe fn get_param( state: &'a mut Self, - _system_state: &'a SystemState, + system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { let column = world .get_populated_resource_column(state.component_id) .expect("Requested resource does not exist"); Res { value: &*column.get_ptr().as_ptr().cast::(), - flags: *column.get_flags_mut_ptr(), + counters: &*column.get_counters_mut_ptr(), + system_counter: system_state.system_counter, + global_system_counter, } } } @@ -278,24 +292,28 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResState { /// Use `Option>` if the resource might not always exist. pub struct ResMut<'w, T> { value: &'w mut T, - flags: &'w mut ComponentFlags, + counters: &'w mut ComponentCounters, + system_counter: Option, + global_system_counter: u32, } impl<'w, T: Component> ResMut<'w, T> { /// Returns true if (and only if) this resource been added since the start of the frame. - pub fn added(&self) -> bool { - self.flags.contains(ComponentFlags::ADDED) + pub fn is_added(&self) -> bool { + self.counters + .is_added(self.system_counter, self.global_system_counter) } /// Returns true if (and only if) this resource been mutated since the start of the frame. - pub fn mutated(&self) -> bool { - self.flags.contains(ComponentFlags::MUTATED) + pub fn is_mutated(&self) -> bool { + self.counters + .is_mutated(self.system_counter, self.global_system_counter) } /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. - pub fn changed(&self) -> bool { - self.flags - .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) + pub fn is_changed(&self) -> bool { + self.counters + .is_changed(self.system_counter, self.global_system_counter) } } @@ -309,7 +327,7 @@ impl<'w, T: Component> Deref for ResMut<'w, T> { impl<'w, T: Component> DerefMut for ResMut<'w, T> { fn deref_mut(&mut self) -> &mut Self::Target { - self.flags.insert(ComponentFlags::MUTATED); + self.counters.set_changed(self.global_system_counter); self.value } } @@ -362,15 +380,18 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResMutState { #[inline] unsafe fn get_param( state: &'a mut Self, - _system_state: &'a SystemState, + system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { let value = world .get_resource_unchecked_mut_with_id(state.component_id) .expect("Requested resource does not exist"); ResMut { value: value.value, - flags: value.flags, + counters: value.component_counters, + system_counter: system_state.system_counter, + global_system_counter, } } } @@ -431,6 +452,7 @@ impl<'a> SystemParamFetch<'a> for CommandQueue { state: &'a mut Self, _system_state: &'a SystemState, world: &'a World, + _global_system_counter: u32, ) -> Self::Item { Commands::new(state, world) } @@ -477,6 +499,7 @@ impl<'a, T: Component + FromWorld> SystemParamFetch<'a> for LocalState { state: &'a mut Self, _system_state: &'a SystemState, _world: &'a World, + _global_system_counter: u32, ) -> Self::Item { Local(&mut state.0) } @@ -524,6 +547,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState { state: &'a mut Self, _system_state: &'a SystemState, world: &'a World, + _global_system_counter: u32, ) -> Self::Item { RemovedComponents { world, @@ -536,6 +560,29 @@ impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState { /// Shared borrow of a NonSend resource pub struct NonSend<'w, T> { pub(crate) value: &'w T, + counters: ComponentCounters, + system_counter: Option, + global_system_counter: u32, +} + +impl<'w, T: Component> NonSend<'w, T> { + /// Returns true if (and only if) this resource been added since the start of the frame. + pub fn is_added(&self) -> bool { + self.counters + .is_added(self.system_counter, self.global_system_counter) + } + + /// Returns true if (and only if) this resource been mutated since the start of the frame. + pub fn is_mutated(&self) -> bool { + self.counters + .is_mutated(self.system_counter, self.global_system_counter) + } + + /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. + pub fn is_changed(&self) -> bool { + self.counters + .is_changed(self.system_counter, self.global_system_counter) + } } impl<'w, T: 'static> Deref for NonSend<'w, T> { @@ -592,13 +639,19 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState { #[inline] unsafe fn get_param( state: &'a mut Self, - _system_state: &'a SystemState, + system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { + world.validate_non_send_access::(); + let column = world + .get_populated_resource_column(state.component_id) + .expect("Requested non-send resource does not exist"); NonSend { - value: world - .get_non_send_with_id::(state.component_id) - .expect("Requested non-send resource does not exist"), + value: &*column.get_ptr().as_ptr().cast::(), + counters: *column.get_counters_mut_ptr(), + system_counter: system_state.system_counter, + global_system_counter, } } } @@ -606,7 +659,29 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState { /// Unique borrow of a NonSend resource pub struct NonSendMut<'a, T: 'static> { pub(crate) value: &'a mut T, - pub(crate) flags: &'a mut ComponentFlags, + counters: &'a mut ComponentCounters, + system_counter: Option, + global_system_counter: u32, +} + +impl<'w, T: Component> NonSendMut<'w, T> { + /// Returns true if (and only if) this resource been added since the start of the frame. + pub fn is_added(&self) -> bool { + self.counters + .is_added(self.system_counter, self.global_system_counter) + } + + /// Returns true if (and only if) this resource been mutated since the start of the frame. + pub fn is_mutated(&self) -> bool { + self.counters + .is_mutated(self.system_counter, self.global_system_counter) + } + + /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. + pub fn is_changed(&self) -> bool { + self.counters + .is_changed(self.system_counter, self.global_system_counter) + } } impl<'a, T: 'static> Deref for NonSendMut<'a, T> { @@ -621,7 +696,7 @@ impl<'a, T: 'static> Deref for NonSendMut<'a, T> { impl<'a, T: 'static> DerefMut for NonSendMut<'a, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - self.flags.insert(ComponentFlags::MUTATED); + self.counters.set_changed(self.global_system_counter); self.value } } @@ -682,15 +757,19 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendMutState { #[inline] unsafe fn get_param( state: &'a mut Self, - _system_state: &'a SystemState, + system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { - let value = world - .get_non_send_unchecked_mut_with_id(state.component_id) + world.validate_non_send_access::(); + let column = world + .get_populated_resource_column(state.component_id) .expect("Requested non-send resource does not exist"); NonSendMut { - value: value.value, - flags: value.flags, + value: &mut *column.get_ptr().as_ptr().cast::(), + counters: &mut *column.get_counters_mut_ptr(), + system_counter: system_state.system_counter, + global_system_counter, } } } @@ -720,6 +799,7 @@ impl<'a> SystemParamFetch<'a> for ArchetypesState { _state: &'a mut Self, _system_state: &'a SystemState, world: &'a World, + _global_system_counter: u32, ) -> Self::Item { world.archetypes() } @@ -748,6 +828,7 @@ impl<'a> SystemParamFetch<'a> for ComponentsState { _state: &'a mut Self, _system_state: &'a SystemState, world: &'a World, + _global_system_counter: u32, ) -> Self::Item { world.components() } @@ -776,6 +857,7 @@ impl<'a> SystemParamFetch<'a> for EntitiesState { _state: &'a mut Self, _system_state: &'a SystemState, world: &'a World, + _global_system_counter: u32, ) -> Self::Item { world.entities() } @@ -804,11 +886,48 @@ impl<'a> SystemParamFetch<'a> for BundlesState { _state: &'a mut Self, _system_state: &'a SystemState, world: &'a World, + _global_system_counter: u32, ) -> Self::Item { world.bundles() } } +#[derive(Debug)] +pub struct SystemCounter { + pub system_counter: Option, + pub global_system_counter: u32, +} + +impl SystemParam for SystemCounter { + type Fetch = SystemCounterState; +} + +pub struct SystemCounterState {} + +unsafe impl SystemParamState for SystemCounterState { + type Config = (); + + fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { + Self {} + } +} + +impl<'a> SystemParamFetch<'a> for SystemCounterState { + type Item = SystemCounter; + + unsafe fn get_param( + _state: &mut Self, + system_state: &SystemState, + _world: &World, + global_system_counter: u32, + ) -> Self::Item { + SystemCounter { + system_counter: system_state.system_counter, + global_system_counter, + } + } +} + macro_rules! impl_system_param_tuple { ($($param: ident),*) => { impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { @@ -824,10 +943,11 @@ macro_rules! impl_system_param_tuple { state: &'a mut Self, system_state: &'a SystemState, world: &'a World, + global_system_counter: u32, ) -> Self::Item { let ($($param,)*) = state; - ($($param::get_param($param, system_state, world),)*) + ($($param::get_param($param, system_state, world, global_system_counter),)*) } } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 9313d0d833fa9..36ffa7cebea44 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1,7 +1,7 @@ use crate::{ - archetype::{Archetype, ArchetypeId, Archetypes}, + archetype::{Archetype, ArchetypeId, Archetypes, ComponentStatus}, bundle::{Bundle, BundleInfo}, - component::{Component, ComponentFlags, ComponentId, Components, StorageType}, + component::{Component, ComponentCounters, ComponentId, Components, StorageType}, entity::{Entity, EntityLocation}, storage::{SparseSet, Storages}, world::{Mut, World}, @@ -79,11 +79,18 @@ impl<'w> EntityRef<'w> { /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component #[inline] pub unsafe fn get_unchecked_mut(&self) -> Option> { - get_component_and_flags_with_type(self.world, TypeId::of::(), self.entity, self.location) - .map(|(value, flags)| Mut { - value: &mut *value.cast::(), - flags: &mut *flags, - }) + get_component_and_counters_with_type( + self.world, + TypeId::of::(), + self.entity, + self.location, + ) + .map(|(value, counters)| Mut { + value: &mut *value.cast::(), + component_counters: &mut *counters, + system_counter: self.world.get_exclusive_system_counter(), + global_system_counter: self.world.get_global_system_counter(), + }) } } @@ -159,15 +166,17 @@ impl<'w> EntityMut<'w> { pub fn get_mut(&mut self) -> Option> { // SAFE: world access is unique, entity location is valid, and returned component is of type T unsafe { - get_component_and_flags_with_type( + get_component_and_counters_with_type( self.world, TypeId::of::(), self.entity, self.location, ) - .map(|(value, flags)| Mut { + .map(|(value, counters)| Mut { value: &mut *value.cast::(), - flags: &mut *flags, + component_counters: &mut *counters, + system_counter: self.world.get_exclusive_system_counter(), + global_system_counter: self.world.get_global_system_counter_unordered(), }) } } @@ -176,17 +185,25 @@ impl<'w> EntityMut<'w> { /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component #[inline] pub unsafe fn get_unchecked_mut(&self) -> Option> { - get_component_and_flags_with_type(self.world, TypeId::of::(), self.entity, self.location) - .map(|(value, flags)| Mut { - value: &mut *value.cast::(), - flags: &mut *flags, - }) + get_component_and_counters_with_type( + self.world, + TypeId::of::(), + self.entity, + self.location, + ) + .map(|(value, counters)| Mut { + value: &mut *value.cast::(), + component_counters: &mut *counters, + system_counter: self.world.get_exclusive_system_counter(), + global_system_counter: self.world.get_global_system_counter(), + }) } // TODO: factor out non-generic part to cut down on monomorphization (just check perf) // TODO: move relevant methods to World (add/remove bundle) pub fn insert_bundle(&mut self, bundle: T) -> &mut Self { let entity = self.entity; + let global_system_counter = self.world.get_global_system_counter_unordered(); let entities = &mut self.world.entities; let archetypes = &mut self.world.archetypes; let components = &mut self.world.components; @@ -262,8 +279,9 @@ impl<'w> EntityMut<'w> { entity, table, table_row, - &from_bundle.bundle_flags, + &from_bundle.bundle_status, bundle, + global_system_counter, ) }; self @@ -551,12 +569,12 @@ unsafe fn get_component( /// # Safety /// Caller must ensure that `component_id` is valid #[inline] -unsafe fn get_component_and_flags( +unsafe fn get_component_and_counters( world: &World, component_id: ComponentId, entity: Entity, location: EntityLocation, -) -> Option<(*mut u8, *mut ComponentFlags)> { +) -> Option<(*mut u8, *mut ComponentCounters)> { let archetype = world.archetypes.get_unchecked(location.archetype_id); let component_info = world.components.get_info_unchecked(component_id); match component_info.storage_type() { @@ -568,14 +586,14 @@ unsafe fn get_component_and_flags( // SAFE: archetypes only store valid table_rows and the stored component type is T Some(( components.get_unchecked(table_row), - components.get_flags_unchecked(table_row), + components.get_counters_unchecked(table_row), )) } StorageType::SparseSet => world .storages .sparse_sets .get(component_id) - .and_then(|sparse_set| sparse_set.get_with_flags(entity)), + .and_then(|sparse_set| sparse_set.get_with_counters(entity)), } } @@ -629,14 +647,14 @@ unsafe fn get_component_with_type( /// # Safety /// `entity_location` must be within bounds of an archetype that exists. -pub(crate) unsafe fn get_component_and_flags_with_type( +pub(crate) unsafe fn get_component_and_counters_with_type( world: &World, type_id: TypeId, entity: Entity, location: EntityLocation, -) -> Option<(*mut u8, *mut ComponentFlags)> { +) -> Option<(*mut u8, *mut ComponentCounters)> { let component_id = world.components.get_id(type_id)?; - get_component_and_flags(world, component_id, entity, location) + get_component_and_counters(world, component_id, entity, location) } /// # Safety @@ -687,14 +705,14 @@ pub(crate) unsafe fn add_bundle_to_archetype( } let mut new_table_components = Vec::new(); let mut new_sparse_set_components = Vec::new(); - let mut tracking_flags = Vec::with_capacity(bundle_info.component_ids.len()); + let mut bundle_status = Vec::with_capacity(bundle_info.component_ids.len()); let current_archetype = archetypes.get_unchecked_mut(archetype_id); for component_id in bundle_info.component_ids.iter().cloned() { if current_archetype.contains(component_id) { - tracking_flags.push(ComponentFlags::MUTATED); + bundle_status.push(ComponentStatus::Mutated); } else { - tracking_flags.push(ComponentFlags::ADDED); + bundle_status.push(ComponentStatus::Added); let component_info = components.get_info_unchecked(component_id); match component_info.storage_type() { StorageType::Table => new_table_components.push(component_id), @@ -710,7 +728,7 @@ pub(crate) unsafe fn add_bundle_to_archetype( let edges = current_archetype.edges_mut(); // the archetype does not change when we add this bundle edges.set_add_bundle(bundle_info.id, archetype_id); - edges.set_from_bundle(bundle_info.id, archetype_id, tracking_flags); + edges.set_from_bundle(bundle_info.id, archetype_id, bundle_status); archetype_id } else { let table_id; @@ -755,7 +773,7 @@ pub(crate) unsafe fn add_bundle_to_archetype( archetypes .get_unchecked_mut(new_archetype_id) .edges_mut() - .set_from_bundle(bundle_info.id, new_archetype_id, tracking_flags); + .set_from_bundle(bundle_info.id, new_archetype_id, bundle_status); new_archetype_id } } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 2a932d83d3c23..112efd3b02471 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -12,14 +12,18 @@ use crate::{ archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes}, bundle::{Bundle, Bundles}, component::{ - Component, ComponentDescriptor, ComponentFlags, ComponentId, Components, ComponentsError, - StorageType, + Component, ComponentCounters, ComponentDescriptor, ComponentId, Components, + ComponentsError, StorageType, }, entity::{Entities, Entity}, - query::{FilterFetch, QueryState, WorldQuery}, + query::{DirectQuery, FilterFetch, QueryState, WorldQuery}, storage::{Column, SparseSet, Storages}, }; -use std::{any::TypeId, fmt}; +use std::{ + any::TypeId, + fmt, + sync::atomic::{AtomicU32, Ordering}, +}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct WorldId(u64); @@ -33,7 +37,6 @@ impl Default for WorldId { /// [World] stores and exposes operations on [entities](Entity), [components](Component), and their associated metadata. /// Each [Entity] has a set of components. Each component can have up to one instance of each component type. /// Entity components can be created, updated, removed, and queried using a given [World]. -#[derive(Default)] pub struct World { id: WorldId, pub(crate) entities: Entities, @@ -45,6 +48,27 @@ pub struct World { /// Access cache used by [WorldCell]. pub(crate) archetype_component_access: ArchetypeComponentAccess, main_thread_validator: MainThreadValidator, + pub(crate) global_system_counter: AtomicU32, + pub(crate) exclusive_system_counter: Option, +} + +impl Default for World { + fn default() -> Self { + Self { + id: Default::default(), + entities: Default::default(), + components: Default::default(), + archetypes: Default::default(), + storages: Default::default(), + bundles: Default::default(), + removed_components: Default::default(), + archetype_component_access: Default::default(), + main_thread_validator: Default::default(), + global_system_counter: Default::default(), + // Default value is -1 so that direct queries outside of exclusive systems properly detect changes + exclusive_system_counter: Some(u32::MAX), + } + } } impl World { @@ -376,15 +400,19 @@ impl World { /// Clears all component tracker state, such as "added", "mutated", and "removed". pub fn clear_trackers(&mut self) { - self.storages.tables.clear_flags(); - self.storages.sparse_sets.clear_flags(); + // PERF: parallelize iterations + let global_system_counter = self.get_global_system_counter_unordered(); + self.storages.tables.clear_counters(global_system_counter); + self.storages + .sparse_sets + .clear_counters(global_system_counter); for entities in self.removed_components.values_mut() { entities.clear(); } let resource_archetype = self.archetypes.resource_mut(); for column in resource_archetype.unique_components.values_mut() { - column.clear_flags(); + column.clear_counters(global_system_counter); } } @@ -411,8 +439,10 @@ impl World { /// (Position { x: 0.0, y: 0.0}, Velocity { x: 0.0, y: 1.0 }), /// ]).collect::>(); /// - /// let mut query = world.query::<(&mut Position, &Velocity)>(); - /// for (mut position, velocity) in query.iter_mut(&mut world) { + /// let mut query = world.query_state::<(&mut Position, &Velocity)>(); + /// let system_counter = world.get_exclusive_system_counter(); + /// let global_system_counter = world.get_global_system_counter(); + /// for (mut position, velocity) in query.iter_mut(&mut world, system_counter, global_system_counter) { /// position.x += velocity.x; /// position.y += velocity.y; /// } @@ -421,7 +451,7 @@ impl World { /// assert_eq!(world.get::(entities[1]).unwrap(), &Position { x: 0.0, y: 1.0 }); /// ``` #[inline] - pub fn query(&mut self) -> QueryState { + pub fn query_state(&mut self) -> QueryState { QueryState::new(self) } @@ -437,19 +467,52 @@ impl World { /// let e1 = world.spawn().insert(A).id(); /// let e2 = world.spawn().insert_bundle((A, B)).id(); /// - /// let mut query = world.query_filtered::>(); - /// let matching_entities = query.iter(&world).collect::>(); + /// let mut query = world.query_state_filtered::>(); + /// let system_counter = world.get_exclusive_system_counter(); + /// let global_system_counter = world.get_global_system_counter(); + /// let matching_entities = query.iter(&world, system_counter, global_system_counter).collect::>(); /// /// assert_eq!(matching_entities, vec![e2]); /// ``` #[inline] - pub fn query_filtered(&mut self) -> QueryState + pub fn query_state_filtered(&mut self) -> QueryState where F::Fetch: FilterFetch, { QueryState::new(self) } + #[inline] + pub fn query(&mut self) -> DirectQuery { + let state = QueryState::new(self); + let global_system_counter = self.get_global_system_counter_unordered(); + unsafe { + DirectQuery::new( + self, + state, + self.get_exclusive_system_counter(), + global_system_counter, + ) + } + } + + #[inline] + pub fn query_filtered(&mut self) -> DirectQuery + where + F::Fetch: FilterFetch, + { + let state = QueryState::new(self); + let global_system_counter = self.get_global_system_counter_unordered(); + unsafe { + DirectQuery::new( + self, + state, + self.get_exclusive_system_counter(), + global_system_counter, + ) + } + } + /// Returns an iterator of entities that had components of type `T` removed since the last call to [World::clear_trackers]. pub fn removed(&self) -> std::iter::Cloned> { if let Some(component_id) = self.components.get_id(TypeId::of::()) { @@ -612,7 +675,7 @@ impl World { .components .get_resource_id(TypeId::of::()) .expect("resource does not exist"); - let (ptr, mut flags) = { + let (ptr, mut counters) = { let resource_archetype = self.archetypes.resource_mut(); let unique_components = resource_archetype.unique_components_mut(); let column = unique_components @@ -628,7 +691,9 @@ impl World { // SAFE: pointer is of type T let value = Mut { value: unsafe { &mut *ptr.cast::() }, - flags: &mut flags, + component_counters: &mut counters, + system_counter: self.get_exclusive_system_counter(), + global_system_counter: self.get_global_system_counter_unordered(), }; let result = f(value, self); let resource_archetype = self.archetypes.resource_mut(); @@ -641,7 +706,7 @@ impl World { // SAFE: row was just allocated above unsafe { column.set_unchecked(row, ptr) }; // SAFE: row was just allocated above - unsafe { *column.get_flags_unchecked_mut(row) = flags }; + unsafe { *column.get_counters_unchecked_mut(row) = counters }; result } @@ -667,7 +732,9 @@ impl World { let column = self.get_populated_resource_column(component_id)?; Some(Mut { value: &mut *column.get_ptr().as_ptr().cast::(), - flags: &mut *column.get_flags_mut_ptr(), + component_counters: &mut *column.get_counters_mut_ptr(), + system_counter: self.get_exclusive_system_counter(), + global_system_counter: self.get_global_system_counter(), }) } @@ -698,6 +765,7 @@ impl World { /// `component_id` must be valid and correspond to a resource component of type T #[inline] unsafe fn insert_resource_with_id(&mut self, component_id: ComponentId, mut value: T) { + let global_system_counter = self.get_global_system_counter_unordered(); let column = self.initialize_resource_internal(component_id); if column.is_empty() { // SAFE: column is of type T and has been allocated above @@ -706,16 +774,17 @@ impl World { let row = column.push_uninit(); // SAFE: index was just allocated above column.set_unchecked(row, data); + // SAFE: index was just allocated above + *column.counters.get_mut().get_unchecked_mut(row) = + ComponentCounters::new(global_system_counter); std::mem::forget(value); - column - .get_flags_unchecked_mut(row) - .set(ComponentFlags::ADDED, true); + *column.get_counters_unchecked_mut(row) = ComponentCounters::new(global_system_counter); } else { // SAFE: column is of type T and has already been allocated *column.get_unchecked(0).cast::() = value; column - .get_flags_unchecked_mut(0) - .set(ComponentFlags::MUTATED, true); + .get_counters_unchecked_mut(0) + .set_changed(global_system_counter); } } @@ -779,7 +848,7 @@ impl World { }) } - fn validate_non_send_access(&self) { + pub(crate) fn validate_non_send_access(&self) { if !self.main_thread_validator.is_main_thread() { panic!( "attempted to access NonSend resource {} off of the main thread", @@ -806,6 +875,22 @@ impl World { }); } } + + pub fn increment_global_system_counter(&self) -> u32 { + self.global_system_counter.fetch_add(1, Ordering::SeqCst) // TODO: can this be relaxed? + } + + pub fn get_global_system_counter(&self) -> u32 { + self.global_system_counter.load(Ordering::Acquire) // FIXME: is this ordering correct? + } + + pub fn get_global_system_counter_unordered(&mut self) -> u32 { + *self.global_system_counter.get_mut() + } + + pub fn get_exclusive_system_counter(&self) -> Option { + self.exclusive_system_counter + } } impl fmt::Debug for World { diff --git a/crates/bevy_ecs/src/world/pointer.rs b/crates/bevy_ecs/src/world/pointer.rs index c857f424a30e2..65f24c278beaf 100644 --- a/crates/bevy_ecs/src/world/pointer.rs +++ b/crates/bevy_ecs/src/world/pointer.rs @@ -1,10 +1,12 @@ -use crate::component::ComponentFlags; +use crate::component::ComponentCounters; use std::ops::{Deref, DerefMut}; /// Unique borrow of an entity's component pub struct Mut<'a, T> { pub(crate) value: &'a mut T, - pub(crate) flags: &'a mut ComponentFlags, + pub(crate) component_counters: &'a mut ComponentCounters, + pub(crate) system_counter: Option, + pub(crate) global_system_counter: u32, } impl<'a, T> Deref for Mut<'a, T> { @@ -19,7 +21,8 @@ impl<'a, T> Deref for Mut<'a, T> { impl<'a, T> DerefMut for Mut<'a, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - self.flags.insert(ComponentFlags::MUTATED); + self.component_counters + .set_changed(self.global_system_counter); self.value } } @@ -32,18 +35,20 @@ impl<'a, T: core::fmt::Debug> core::fmt::Debug for Mut<'a, T> { impl<'w, T> Mut<'w, T> { /// Returns true if (and only if) this component been added since the start of the frame. - pub fn added(&self) -> bool { - self.flags.contains(ComponentFlags::ADDED) + pub fn is_added(&self) -> bool { + self.component_counters + .is_added(self.system_counter, self.global_system_counter) } /// Returns true if (and only if) this component been mutated since the start of the frame. - pub fn mutated(&self) -> bool { - self.flags.contains(ComponentFlags::MUTATED) + pub fn is_mutated(&self) -> bool { + self.component_counters + .is_mutated(self.system_counter, self.global_system_counter) } /// Returns true if (and only if) this component been either mutated or added since the start of the frame. - pub fn changed(&self) -> bool { - self.flags - .intersects(ComponentFlags::ADDED | ComponentFlags::MUTATED) + pub fn is_changed(&self) -> bool { + self.component_counters + .is_changed(self.system_counter, self.global_system_counter) } } diff --git a/crates/bevy_ecs/src/world/spawn_batch.rs b/crates/bevy_ecs/src/world/spawn_batch.rs index 6eeab491b43e4..6fc5b451809be 100644 --- a/crates/bevy_ecs/src/world/spawn_batch.rs +++ b/crates/bevy_ecs/src/world/spawn_batch.rs @@ -17,6 +17,7 @@ where table: &'w mut Table, sparse_sets: &'w mut SparseSets, bundle_info: &'w BundleInfo, + global_system_counter: u32, } impl<'w, I> SpawnBatchIter<'w, I> @@ -64,6 +65,7 @@ where table, sparse_sets: &mut world.storages.sparse_sets, bundle_info, + global_system_counter: *world.global_system_counter.get_mut(), } } } @@ -102,8 +104,9 @@ where entity, self.table, table_row, - &from_bundle.bundle_flags, + &from_bundle.bundle_status, bundle, + self.global_system_counter, ); self.entities.meta[entity.id as usize].location = location; } diff --git a/crates/bevy_render/src/render_graph/nodes/pass_node.rs b/crates/bevy_render/src/render_graph/nodes/pass_node.rs index e105f9bc900b5..21a03e2ded144 100644 --- a/crates/bevy_render/src/render_graph/nodes/pass_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/pass_node.rs @@ -153,7 +153,7 @@ where } fn prepare(&mut self, world: &mut World) { - self.query_state.get_or_insert_with(|| world.query()); + self.query_state.get_or_insert_with(|| world.query_state()); } fn update( @@ -235,7 +235,7 @@ where // attempt to draw each visible entity let mut draw_state = DrawState::default(); for visible_entity in visible_entities.iter() { - if query_state.get(world, visible_entity.entity).is_err() { + if query_state.get(world, visible_entity.entity, world.get_exclusive_system_counter(), world.get_global_system_counter()).is_err() { // visible entity does not match the Pass query continue; } diff --git a/crates/bevy_transform/src/hierarchy/hierarchy.rs b/crates/bevy_transform/src/hierarchy/hierarchy.rs index 47f8db14b33a5..81c44d003ab86 100644 --- a/crates/bevy_transform/src/hierarchy/hierarchy.rs +++ b/crates/bevy_transform/src/hierarchy/hierarchy.rs @@ -112,7 +112,7 @@ mod tests { let mut results = world .query::<(&String, &u32)>() - .iter(&world) + .iter() .map(|(a, b)| (a.clone(), *b)) .collect::>(); results.sort_unstable_by_key(|(_, index)| *index); diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 87aa13f3c768a..e06c01316af1a 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -4,7 +4,7 @@ use crate::{Node, Style}; use bevy_app::EventReader; use bevy_ecs::{ entity::Entity, - query::{Changed, FilterFetch, Flags, With, Without, WorldQuery}, + query::{Changed, Counters, FilterFetch, With, Without, WorldQuery}, system::{Query, Res, ResMut}, }; use bevy_log::warn; @@ -205,8 +205,8 @@ pub fn flex_node_system( Entity, &mut Node, &mut Transform, - Option<(&Parent, Flags)>, - Flags, + Option<(&Parent, Counters)>, + Counters, )>, ) { // update window root nodes @@ -272,7 +272,7 @@ pub fn flex_node_system( let to_logical = |v| (physical_to_logical_factor * v as f64) as f32; - for (entity, mut node, mut transform, parent, transform_flags) in + for (entity, mut node, mut transform, parent, transform_counters) in node_transform_query.iter_mut() { let layout = flex_surface.get_layout(entity).unwrap(); @@ -283,8 +283,8 @@ pub fn flex_node_system( let position = &mut transform.translation; position.x = to_logical(layout.location.x + layout.size.width / 2.0); position.y = to_logical(layout.location.y + layout.size.height / 2.0); - if let Some((parent, parent_flags)) = parent { - if parent_flags.changed() || transform_flags.changed() { + if let Some((parent, parent_counters)) = parent { + if parent_counters.is_changed() || transform_counters.is_changed() { if let Ok(parent_layout) = flex_surface.get_layout(parent.0) { position.x -= to_logical(parent_layout.size.width / 2.0); position.y -= to_logical(parent_layout.size.height / 2.0); diff --git a/crates/bevy_ui/src/update.rs b/crates/bevy_ui/src/update.rs index d478f8b13bd4d..284aed85da265 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -128,7 +128,7 @@ mod tests { let mut actual_result = world .query::<(&String, &Transform)>() - .iter(&world) + .iter() .map(|(name, transform)| (name.clone(), get_steps(transform))) .collect::>(); actual_result.sort_unstable_by_key(|(name, _)| name.clone()); diff --git a/examples/ecs/change_detection.rs b/examples/ecs/change_detection.rs index 2e2f40478869a..ea696e9232e1f 100644 --- a/examples/ecs/change_detection.rs +++ b/examples/ecs/change_detection.rs @@ -8,7 +8,7 @@ fn main() { .add_startup_system(setup.system()) .add_system(change_component.system()) .add_system(change_detection.system()) - .add_system(flags_monitoring.system()) + .add_system(counters_monitoring.system()) .run(); } @@ -37,9 +37,11 @@ fn change_detection(query: Query<(Entity, &MyComponent), Changed>) } } -// By looking at flags, the query is not filtered but the information is available -fn flags_monitoring(query: Query<(Entity, Option<&MyComponent>, Option>)>) { - for (entity, component, flags) in query.iter() { - info!("{:?}: {:?} -> {:?}", entity, component, flags,); +// By looking at counters, the query is not filtered but the information is available +fn counters_monitoring( + query: Query<(Entity, Option<&MyComponent>, Option>)>, +) { + for (entity, component, counters) in query.iter() { + info!("{:?}: {:?} -> {:?}", entity, component, counters,); } } From f31a329c676bff70db0954d668bf9b2e7d811650 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Mon, 8 Mar 2021 22:44:41 +0900 Subject: [PATCH 02/41] Change the system counters from Option to u32 --- crates/bevy_ecs/src/component/mod.rs | 28 ++++++---------- crates/bevy_ecs/src/lib.rs | 30 ++++++++--------- crates/bevy_ecs/src/query/direct.rs | 4 +-- crates/bevy_ecs/src/query/fetch.rs | 20 ++++++------ crates/bevy_ecs/src/query/filter.rs | 12 +++---- crates/bevy_ecs/src/query/iter.rs | 2 +- crates/bevy_ecs/src/query/state.rs | 32 +++++++++---------- crates/bevy_ecs/src/reflect.rs | 2 +- .../bevy_ecs/src/system/exclusive_system.rs | 7 ++-- crates/bevy_ecs/src/system/into_system.rs | 7 ++-- crates/bevy_ecs/src/system/query.rs | 4 +-- crates/bevy_ecs/src/system/system_param.rs | 10 +++--- crates/bevy_ecs/src/world/mod.rs | 7 ++-- crates/bevy_ecs/src/world/pointer.rs | 2 +- crates/bevy_render/src/mesh/mesh.rs | 3 -- 15 files changed, 81 insertions(+), 89 deletions(-) diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index 672d02512554e..81196b06bdace 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -294,29 +294,21 @@ pub struct ComponentCounters { } impl ComponentCounters { - pub fn is_added(&self, system_counter: Option, global_system_counter: u32) -> bool { - if let Some(system_counter) = system_counter { - let component_age = global_system_counter.wrapping_sub(self.added); - let system_age = global_system_counter.wrapping_sub(system_counter); - - component_age < system_age - } else { - true - } + pub fn is_added(&self, system_counter: u32, global_system_counter: u32) -> bool { + let component_age = global_system_counter.wrapping_sub(self.added); + let system_age = global_system_counter.wrapping_sub(system_counter); + + component_age < system_age } - pub fn is_changed(&self, system_counter: Option, global_system_counter: u32) -> bool { - if let Some(system_counter) = system_counter { - let component_age = global_system_counter.wrapping_sub(self.changed); - let system_age = global_system_counter.wrapping_sub(system_counter); + pub fn is_changed(&self, system_counter: u32, global_system_counter: u32) -> bool { + let component_age = global_system_counter.wrapping_sub(self.changed); + let system_age = global_system_counter.wrapping_sub(system_counter); - component_age < system_age - } else { - true - } + component_age < system_age } - pub fn is_mutated(&self, system_counter: Option, global_system_counter: u32) -> bool { + pub fn is_mutated(&self, system_counter: u32, global_system_counter: u32) -> bool { self.is_changed(system_counter, global_system_counter) && !self.is_added(system_counter, global_system_counter) } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 833859ae2429f..8bbb5f66b1e7f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -628,7 +628,7 @@ mod tests { assert!(world.query_filtered::<(), Added>().get(a).is_ok()); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); assert_eq!(world.query::<&i32>().iter().count(), 1); assert_eq!(world.query_filtered::<(), Added>().iter().count(), 0); @@ -658,7 +658,7 @@ mod tests { assert_eq!(get_added::(&mut world), vec![e1]); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); assert!(get_added::(&mut world).is_empty()); let e2 = world.spawn().insert_bundle((A(1), B(1))).id(); assert_eq!(get_added::(&mut world), vec![e2]); @@ -679,7 +679,7 @@ mod tests { let e3 = world.spawn().insert_bundle((A(0), B(0))).id(); world.spawn().insert_bundle((A(0), B)); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); for (i, mut a) in world.query::<&mut A>().iter_mut().enumerate() { if i % 2 == 0 { @@ -729,7 +729,7 @@ mod tests { ); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); assert!(get_filtered::>(&mut world).is_empty()); @@ -743,7 +743,7 @@ mod tests { // assert_eq!(get_filtered::>(&mut world), vec![e4]); // This case is no longer possible to detect world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); // ensure inserting multiple components set mutated state for // already existing components and set added state for @@ -782,7 +782,7 @@ mod tests { let e2 = world.spawn().insert_bundle((A(0), B(0))).id(); world.spawn().insert_bundle((A(0), B(0))); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); for mut a in world.query::<&mut A>().iter_mut() { a.0 += 1; @@ -807,7 +807,7 @@ mod tests { let _e3 = world.spawn().insert_bundle((A(0), B(0))).id(); let e4 = world.spawn().insert(A(0)).id(); // ensure filters work for archetypes with only one of the Or filter items world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); *world.entity_mut(e1).get_mut::().unwrap() = A(1); *world.entity_mut(e2).get_mut::().unwrap() = B(1); @@ -834,7 +834,7 @@ mod tests { } assert_eq!(get_changed(&mut world), vec![e1]); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); assert_eq!(get_changed(&mut world), vec![]); *world.get_mut(e1).unwrap() = A(1); assert_eq!(get_changed(&mut world), vec![e1]); @@ -1054,7 +1054,7 @@ mod tests { assert!(!a_counters.is_mutated()); assert!(a_counters.is_changed()); world.clear_trackers(); - world.exclusive_system_counter = Some(world.increment_global_system_counter()); + world.exclusive_system_counter = world.increment_global_system_counter(); let counters = world .query::>>() .iter() @@ -1113,8 +1113,8 @@ mod tests { let mut world_a = World::new(); let world_b = World::new(); let mut query = world_a.query_state::<&i32>(); - query.iter(&world_a, None, 0); - query.iter(&world_b, None, 0); + query.iter(&world_a, u32::MAX, 0); + query.iter(&world_b, u32::MAX, 0); } #[test] @@ -1123,8 +1123,8 @@ mod tests { let mut world_a = World::new(); let world_b = World::new(); let mut query = world_a.query_state::<&i32>(); - let _ = query.get(&world_a, Entity::new(0), None, 0); - let _ = query.get(&world_b, Entity::new(0), None, 0); + let _ = query.get(&world_a, Entity::new(0), u32::MAX, 0); + let _ = query.get(&world_b, Entity::new(0), u32::MAX, 0); } #[test] @@ -1133,8 +1133,8 @@ mod tests { let mut world_a = World::new(); let world_b = World::new(); let mut query = world_a.query_state::<&i32>(); - query.for_each(&world_a, |_| {}, None, 0); - query.for_each(&world_b, |_| {}, None, 0); + query.for_each(&world_a, |_| {}, u32::MAX, 0); + query.for_each(&world_b, |_| {}, u32::MAX, 0); } #[test] diff --git a/crates/bevy_ecs/src/query/direct.rs b/crates/bevy_ecs/src/query/direct.rs index 1fb10cfa57a4e..f96a8a8431214 100644 --- a/crates/bevy_ecs/src/query/direct.rs +++ b/crates/bevy_ecs/src/query/direct.rs @@ -17,7 +17,7 @@ where { pub(crate) world: &'w World, pub(crate) state: QueryState, - pub(crate) system_counter: Option, + pub(crate) system_counter: u32, pub(crate) global_system_counter: u32, } @@ -32,7 +32,7 @@ where pub(crate) unsafe fn new( world: &'w World, state: QueryState, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self { Self { diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index c9c0cc096128a..44ad1947bf753 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -27,7 +27,7 @@ pub trait Fetch<'w>: Sized { unsafe fn init( world: &World, state: &Self::State, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self; @@ -132,7 +132,7 @@ impl<'w> Fetch<'w> for EntityFetch { unsafe fn init( _world: &World, _state: &Self::State, - _system_counter: Option, + _system_counter: u32, _global_system_counter: u32, ) -> Self { Self { @@ -239,7 +239,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { unsafe fn init( world: &World, state: &Self::State, - _system_counter: Option, + _system_counter: u32, _global_system_counter: u32, ) -> Self { let mut value = Self { @@ -319,7 +319,7 @@ pub struct WriteFetch { entities: *const Entity, entity_table_rows: *const usize, sparse_set: *const ComponentSparseSet, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -384,7 +384,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { unsafe fn init( world: &World, state: &Self::State, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self { let mut value = Self { @@ -532,7 +532,7 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { unsafe fn init( world: &World, state: &Self::State, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self { Self { @@ -585,7 +585,7 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { #[derive(Clone)] pub struct Counters { component_counters: ComponentCounters, - system_counter: Option, + system_counter: u32, global_system_counter: u32, marker: PhantomData, } @@ -673,7 +673,7 @@ pub struct CountersFetch { entities: *const Entity, sparse_set: *const ComponentSparseSet, marker: PhantomData, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -695,7 +695,7 @@ impl<'w, T: Component> Fetch<'w> for CountersFetch { unsafe fn init( world: &World, state: &Self::State, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self { let mut value = Self { @@ -788,7 +788,7 @@ macro_rules! impl_tuple_fetch { type Item = ($($name::Item,)*); type State = ($($name::State,)*); - unsafe fn init(_world: &World, state: &Self::State, _system_counter: Option, _global_system_counter: u32) -> Self { + unsafe fn init(_world: &World, state: &Self::State, _system_counter: u32, _global_system_counter: u32) -> Self { let ($($name,)*) = state; ($($name::init(_world, $name, _system_counter, _global_system_counter),)*) } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index afeea657e39dd..a858f378931d3 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -105,7 +105,7 @@ impl<'a, T: Component> Fetch<'a> for WithFetch { unsafe fn init( _world: &World, state: &Self::State, - _system_counter: Option, + _system_counter: u32, _global_system_counter: u32, ) -> Self { Self { @@ -201,7 +201,7 @@ impl<'a, T: Component> Fetch<'a> for WithoutFetch { unsafe fn init( _world: &World, state: &Self::State, - _system_counter: Option, + _system_counter: u32, _global_system_counter: u32, ) -> Self { Self { @@ -296,7 +296,7 @@ impl<'a, T: Bundle> Fetch<'a> for WithBundleFetch { unsafe fn init( _world: &World, state: &Self::State, - _system_counter: Option, + _system_counter: u32, _global_system_counter: u32, ) -> Self { Self { @@ -371,7 +371,7 @@ macro_rules! impl_query_filter_tuple { type State = Or<($(<$filter as Fetch<'a>>::State,)*)>; type Item = bool; - unsafe fn init(world: &World, state: &Self::State, system_counter: Option, global_system_counter: u32) -> Self { + unsafe fn init(world: &World, state: &Self::State, system_counter: u32, global_system_counter: u32) -> Self { let ($($filter,)*) = &state.0; Or(($(OrFetch { fetch: $filter::init(world, $filter, system_counter, global_system_counter), @@ -469,7 +469,7 @@ macro_rules! impl_counter_filter { marker: PhantomData, entities: *const Entity, sparse_set: *const ComponentSparseSet, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -525,7 +525,7 @@ macro_rules! impl_counter_filter { type State = $state_name; type Item = bool; - unsafe fn init(world: &World, state: &Self::State, system_counter: Option, global_system_counter: u32) -> Self { + unsafe fn init(world: &World, state: &Self::State, system_counter: u32, global_system_counter: u32) -> Self { let mut value = Self { storage_type: state.storage_type, table_counters: ptr::null_mut::(), diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index ce92f7c34114e..b759899c46da4 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -29,7 +29,7 @@ where pub(crate) unsafe fn new( world: &'w World, query_state: &'s QueryState, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self { let fetch = ::init( diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index d64b85fcd9125..07c948d05dddc 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -106,7 +106,7 @@ where &mut self, world: &'w World, entity: Entity, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> where @@ -121,7 +121,7 @@ where &mut self, world: &'w mut World, entity: Entity, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> { // SAFE: query has unique world access @@ -136,7 +136,7 @@ where &mut self, world: &'w World, entity: Entity, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> { self.validate_world_and_update_archetypes(world); @@ -150,7 +150,7 @@ where &self, world: &'w World, entity: Entity, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Result<>::Item, QueryEntityError> { let location = world @@ -191,7 +191,7 @@ where pub fn iter<'w, 's>( &'s mut self, world: &'w World, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> QueryIter<'w, 's, Q, F> where @@ -205,7 +205,7 @@ where pub fn iter_mut<'w, 's>( &'s mut self, world: &'w mut World, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> QueryIter<'w, 's, Q, F> { // SAFE: query has unique world access @@ -219,7 +219,7 @@ where pub unsafe fn iter_unchecked<'w, 's>( &'s mut self, world: &'w World, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> QueryIter<'w, 's, Q, F> { self.validate_world_and_update_archetypes(world); @@ -235,7 +235,7 @@ where pub(crate) unsafe fn iter_unchecked_manual<'w, 's>( &'s self, world: &'w World, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> QueryIter<'w, 's, Q, F> { QueryIter::new(world, self, system_counter, global_system_counter) @@ -246,7 +246,7 @@ where &mut self, world: &'w World, func: impl FnMut(>::Item), - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) where Q::Fetch: ReadOnlyFetch, @@ -262,7 +262,7 @@ where &mut self, world: &'w mut World, func: impl FnMut(>::Item), - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) { // SAFE: query has unique world access @@ -279,7 +279,7 @@ where &mut self, world: &'w World, func: impl FnMut(>::Item), - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) { self.validate_world_and_update_archetypes(world); @@ -293,7 +293,7 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) where Q::Fetch: ReadOnlyFetch, @@ -318,7 +318,7 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) { // SAFE: query has unique world access @@ -344,7 +344,7 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) { self.validate_world_and_update_archetypes(world); @@ -367,7 +367,7 @@ where &'s self, world: &'w World, mut func: impl FnMut(>::Item), - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) { let mut fetch = ::init( @@ -426,7 +426,7 @@ where task_pool: &TaskPool, batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) { task_pool.scope(|scope| { diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 4d6bf4ea40139..c63f41da7c39b 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -115,7 +115,7 @@ impl FromType for ReflectComponent { pub struct ReflectMut<'a> { pub(crate) value: &'a mut dyn Reflect, pub(crate) component_counters: &'a mut ComponentCounters, - pub(crate) system_counter: Option, + pub(crate) system_counter: u32, pub(crate) global_system_counter: u32, } diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index 294a69ac0e38b..aef858938980b 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -18,7 +18,7 @@ pub struct ExclusiveSystemFn { func: Box, name: Cow<'static, str>, id: SystemId, - system_counter: Option, + system_counter: u32, } impl ExclusiveSystem for ExclusiveSystemFn { @@ -38,7 +38,7 @@ impl ExclusiveSystem for ExclusiveSystemFn { (self.func)(world); let global_system_counter = world.global_system_counter.get_mut(); - self.system_counter = Some(*global_system_counter); + self.system_counter = *global_system_counter; *global_system_counter += 1; world.exclusive_system_counter = saved_counter; @@ -60,7 +60,8 @@ where func: Box::new(self), name: core::any::type_name::().into(), id: SystemId::new(), - system_counter: None, + // The value of -1 means that everything should be detected as added/changed + system_counter: u32::MAX, } } } diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 9de4e5e964520..3cbd0ae37f494 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -15,7 +15,7 @@ pub struct SystemState { pub(crate) archetype_component_access: Access, // NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent SystemParams from overriding each other is_send: bool, - pub(crate) system_counter: Option, + pub(crate) system_counter: u32, } impl SystemState { @@ -26,7 +26,8 @@ impl SystemState { component_access_set: FilteredAccessSet::default(), is_send: true, id: SystemId::new(), - system_counter: None, + // The value of -1 means that everything should be detected as added/changed + system_counter: u32::MAX, } } @@ -148,7 +149,7 @@ where world, global_system_counter, ); - self.system_state.system_counter = Some(global_system_counter); + self.system_state.system_counter = global_system_counter; out } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index c35006a6bc2a6..b5d8f1a1bf2bc 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -17,7 +17,7 @@ where { pub(crate) world: &'w World, pub(crate) state: &'w QueryState, - pub(crate) system_counter: Option, + pub(crate) system_counter: u32, pub(crate) global_system_counter: u32, } @@ -32,7 +32,7 @@ where pub(crate) unsafe fn new( world: &'w World, state: &'w QueryState, - system_counter: Option, + system_counter: u32, global_system_counter: u32, ) -> Self { Self { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index bd643f5edcc42..0bb355acb2224 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -162,7 +162,7 @@ impl_query_set!(); pub struct Res<'w, T> { value: &'w T, counters: &'w ComponentCounters, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -293,7 +293,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResState { pub struct ResMut<'w, T> { value: &'w mut T, counters: &'w mut ComponentCounters, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -561,7 +561,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState { pub struct NonSend<'w, T> { pub(crate) value: &'w T, counters: ComponentCounters, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -660,7 +660,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState { pub struct NonSendMut<'a, T: 'static> { pub(crate) value: &'a mut T, counters: &'a mut ComponentCounters, - system_counter: Option, + system_counter: u32, global_system_counter: u32, } @@ -894,7 +894,7 @@ impl<'a> SystemParamFetch<'a> for BundlesState { #[derive(Debug)] pub struct SystemCounter { - pub system_counter: Option, + pub system_counter: u32, pub global_system_counter: u32, } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 112efd3b02471..defcfb0f207c6 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -49,7 +49,7 @@ pub struct World { pub(crate) archetype_component_access: ArchetypeComponentAccess, main_thread_validator: MainThreadValidator, pub(crate) global_system_counter: AtomicU32, - pub(crate) exclusive_system_counter: Option, + pub(crate) exclusive_system_counter: u32, } impl Default for World { @@ -66,7 +66,8 @@ impl Default for World { main_thread_validator: Default::default(), global_system_counter: Default::default(), // Default value is -1 so that direct queries outside of exclusive systems properly detect changes - exclusive_system_counter: Some(u32::MAX), + // That value will not be used in any exclusive system + exclusive_system_counter: u32::MAX, } } } @@ -888,7 +889,7 @@ impl World { *self.global_system_counter.get_mut() } - pub fn get_exclusive_system_counter(&self) -> Option { + pub fn get_exclusive_system_counter(&self) -> u32 { self.exclusive_system_counter } } diff --git a/crates/bevy_ecs/src/world/pointer.rs b/crates/bevy_ecs/src/world/pointer.rs index 65f24c278beaf..fe19f642d0dd1 100644 --- a/crates/bevy_ecs/src/world/pointer.rs +++ b/crates/bevy_ecs/src/world/pointer.rs @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut}; pub struct Mut<'a, T> { pub(crate) value: &'a mut T, pub(crate) component_counters: &'a mut ComponentCounters, - pub(crate) system_counter: Option, + pub(crate) system_counter: u32, pub(crate) global_system_counter: u32, } diff --git a/crates/bevy_render/src/mesh/mesh.rs b/crates/bevy_render/src/mesh/mesh.rs index 95cdeeaf3ba28..73ec6b3b77547 100644 --- a/crates/bevy_render/src/mesh/mesh.rs +++ b/crates/bevy_render/src/mesh/mesh.rs @@ -233,13 +233,10 @@ pub struct Mesh { impl Mesh { /// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`] pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color"; - /// The direction the vertex normal is facing in. Use in conjunction with [`Mesh::set_attribute`] pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal"; - /// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`] pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position"; - /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::set_attribute`] pub const ATTRIBUTE_UV_0: &'static str = "Vertex_Uv"; From 0dcf832a5ae8e36c82b08c1e3c5be395963a7335 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Tue, 9 Mar 2021 00:13:18 +0900 Subject: [PATCH 03/41] Check and fix old counters before they overflow --- crates/bevy_core/src/time/fixed_timestep.rs | 5 ++ crates/bevy_ecs/src/component/mod.rs | 14 ++++- crates/bevy_ecs/src/schedule/mod.rs | 2 + crates/bevy_ecs/src/schedule/stage.rs | 52 +++++++++++++++++++ crates/bevy_ecs/src/storage/sparse_set.rs | 8 +-- crates/bevy_ecs/src/storage/table.rs | 6 +-- .../bevy_ecs/src/system/exclusive_system.rs | 11 ++++ crates/bevy_ecs/src/system/into_system.rs | 7 ++- crates/bevy_ecs/src/system/system.rs | 1 + crates/bevy_ecs/src/system/system_chaining.rs | 5 ++ crates/bevy_ecs/src/world/mod.rs | 27 +++++----- 11 files changed, 116 insertions(+), 22 deletions(-) diff --git a/crates/bevy_core/src/time/fixed_timestep.rs b/crates/bevy_core/src/time/fixed_timestep.rs index befd138892b8d..856e5022428f5 100644 --- a/crates/bevy_core/src/time/fixed_timestep.rs +++ b/crates/bevy_core/src/time/fixed_timestep.rs @@ -196,4 +196,9 @@ impl System for FixedTimestep { ); } } + + fn check_system_counter(&mut self, global_system_counter: u32) { + self.internal_system + .check_system_counter(global_system_counter); + } } diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index 81196b06bdace..ce775e60dbab8 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -320,11 +320,21 @@ impl ComponentCounters { } } - pub(crate) fn clear(&mut self, _global_system_counter: u32) { - // TODO: advance old component counters before they are passed by the global counter + pub(crate) fn check_counters(&mut self, global_system_counter: u32) { + check_counter_impl(&mut self.added, global_system_counter); + check_counter_impl(&mut self.changed, global_system_counter); } pub(crate) fn set_changed(&mut self, global_system_counter: u32) { self.changed = global_system_counter; } } + +pub(crate) fn check_counter_impl(counter: &mut u32, global_system_counter: u32) { + let counter_age = global_system_counter.wrapping_sub(*counter); + let max_age = (u32::MAX / 4) * 3; + // Clamp to max age + if counter_age > max_age { + *counter = global_system_counter.wrapping_sub(max_age); + } +} diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 4339b5446492e..faea06bd02e09 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -326,4 +326,6 @@ impl System for RunOnce { fn apply_buffers(&mut self, _world: &mut World) {} fn initialize(&mut self, _world: &mut World) {} + + fn check_system_counter(&mut self, _global_system_counter: u32) {} } diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index 77caaf8aa1a81..e4e6667c8afd5 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -78,6 +78,8 @@ pub struct SystemStage { uninitialized_at_end: Vec, /// Newly inserted systems that will be initialized at the next opportunity. uninitialized_parallel: Vec, + /// Saves the value of the global system counter during the last counter check + last_counter_check: u32, } impl SystemStage { @@ -100,6 +102,7 @@ impl SystemStage { uninitialized_at_start: vec![], uninitialized_before_commands: vec![], uninitialized_at_end: vec![], + last_counter_check: Default::default(), } } @@ -167,6 +170,15 @@ impl SystemStage { // TODO: consider exposing fn add_system_to_set(&mut self, system: impl Into, set: usize) -> &mut Self { + // This assertion is there to document that a maximum of `u32::MAX / 8` systems should be added to a stage + // to guarantee that change detection has no false positive, but it can be circumvented using exclusive or chained systems + assert!( + self.exclusive_at_start.len() + + self.exclusive_before_commands.len() + + self.exclusive_at_end.len() + + self.parallel.len() + < (u32::MAX / 8) as usize + ); self.systems_modified = true; match system.into() { SystemDescriptor::Exclusive(descriptor) => { @@ -316,6 +328,43 @@ impl SystemStage { info!("{}", string); } } + + /// Checks for old component and system counters + fn check_counters(&mut self, world: &mut World) { + let global_system_counter = world.get_global_system_counter_unordered(); + let time_since_last_check = global_system_counter.wrapping_sub(self.last_counter_check); + // Only check after at least `u32::MAX / 8` counts, and at most `u32::MAX / 4` counts + // since the max number of [System] in a [SystemStage] is limited to `u32::MAX / 8` + // and this function is called at the end of each [SystemStage] loop + if time_since_last_check > (u32::MAX / 8) { + // Check all system counters + for exclusive_system in &mut self.exclusive_at_start { + exclusive_system + .system_mut() + .check_system_counter(global_system_counter); + } + for exclusive_system in &mut self.exclusive_before_commands { + exclusive_system + .system_mut() + .check_system_counter(global_system_counter); + } + for exclusive_system in &mut self.exclusive_at_end { + exclusive_system + .system_mut() + .check_system_counter(global_system_counter); + } + for parallel_system in &mut self.parallel { + parallel_system + .system_mut() + .check_system_counter(global_system_counter); + } + + // Check component counters + world.check_component_counters(); + + self.last_counter_check = global_system_counter; + } + } } enum DependencyGraphError { @@ -601,6 +650,9 @@ impl Stage for SystemStage { } } + // Check for old component and system counters + self.check_counters(world); + // Reevaluate system sets' run criteria. has_work = false; for system_set in self.system_sets.iter_mut() { diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 0c7d8c7920b7a..d2eac5ec0e548 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -213,10 +213,10 @@ impl ComponentSparseSet { } } - pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { + pub(crate) fn check_counters(&mut self, global_system_counter: u32) { let counters = self.counters.get_mut().iter_mut(); for component_counters in counters { - component_counters.clear(global_system_counter); + component_counters.check_counters(global_system_counter); } } } @@ -443,9 +443,9 @@ impl SparseSets { self.sets.get_mut(component_id) } - pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { + pub(crate) fn check_counters(&mut self, global_system_counter: u32) { for set in self.sets.values_mut() { - set.clear_counters(global_system_counter); + set.check_counters(global_system_counter); } } } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index db058f85f3611..52fcf5d8c4c5b 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -140,10 +140,10 @@ impl Column { } #[inline] - pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { + pub(crate) fn check_counters(&mut self, global_system_counter: u32) { let counters = unsafe { (*self.counters.get()).iter_mut() }; for component_counters in counters { - component_counters.clear(global_system_counter); + component_counters.check_counters(global_system_counter); } } } @@ -356,7 +356,7 @@ impl Table { pub(crate) fn clear_counters(&mut self, global_system_counter: u32) { for column in self.columns.values_mut() { - column.clear_counters(global_system_counter); + column.check_counters(global_system_counter); } } diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index aef858938980b..25c32e0088b37 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -1,4 +1,5 @@ use crate::{ + component::check_counter_impl, system::{BoxedSystem, IntoSystem, System, SystemId}, world::World, }; @@ -12,6 +13,8 @@ pub trait ExclusiveSystem: Send + Sync + 'static { fn run(&mut self, world: &mut World); fn initialize(&mut self, world: &mut World); + + fn check_system_counter(&mut self, global_system_counter: u32); } pub struct ExclusiveSystemFn { @@ -45,6 +48,10 @@ impl ExclusiveSystem for ExclusiveSystemFn { } fn initialize(&mut self, _: &mut World) {} + + fn check_system_counter(&mut self, global_system_counter: u32) { + check_counter_impl(&mut self.system_counter, global_system_counter); + } } pub trait IntoExclusiveSystem { @@ -87,6 +94,10 @@ impl ExclusiveSystem for ExclusiveSystemCoerced { fn initialize(&mut self, world: &mut World) { self.system.initialize(world); } + + fn check_system_counter(&mut self, global_system_counter: u32) { + self.system.check_system_counter(global_system_counter); + } } impl IntoExclusiveSystem<(Params, SystemType), ExclusiveSystemCoerced> for S diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 3cbd0ae37f494..b8b3ce4b5e25f 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,6 +1,6 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, - component::ComponentId, + component::{check_counter_impl, ComponentId}, query::{Access, FilteredAccessSet}, system::{System, SystemId, SystemParam, SystemParamFetch, SystemParamState}, world::World, @@ -167,6 +167,11 @@ where self.config.take().unwrap(), )); } + + #[inline] + fn check_system_counter(&mut self, global_system_counter: u32) { + check_counter_impl(&mut self.system_state.system_counter, global_system_counter); + } } pub trait SystemParamFunction: Send + Sync + 'static { diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 5ddfa56e17466..03d619624d025 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -37,6 +37,7 @@ pub trait System: Send + Sync + 'static { } fn apply_buffers(&mut self, world: &mut World); fn initialize(&mut self, _world: &mut World); + fn check_system_counter(&mut self, global_system_counter: u32); } pub type BoxedSystem = Box>; diff --git a/crates/bevy_ecs/src/system/system_chaining.rs b/crates/bevy_ecs/src/system/system_chaining.rs index c620131d98039..21c40509d8f5b 100644 --- a/crates/bevy_ecs/src/system/system_chaining.rs +++ b/crates/bevy_ecs/src/system/system_chaining.rs @@ -68,6 +68,11 @@ impl> System for ChainSystem self.component_access .extend(self.system_b.component_access()); } + + fn check_system_counter(&mut self, global_system_counter: u32) { + self.system_a.check_system_counter(global_system_counter); + self.system_b.check_system_counter(global_system_counter); + } } pub trait IntoChainSystem: System + Sized diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index defcfb0f207c6..35ecf24514c50 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -399,22 +399,11 @@ impl World { .unwrap_or(false) } - /// Clears all component tracker state, such as "added", "mutated", and "removed". + /// Clears removed component tracker state pub fn clear_trackers(&mut self) { - // PERF: parallelize iterations - let global_system_counter = self.get_global_system_counter_unordered(); - self.storages.tables.clear_counters(global_system_counter); - self.storages - .sparse_sets - .clear_counters(global_system_counter); for entities in self.removed_components.values_mut() { entities.clear(); } - - let resource_archetype = self.archetypes.resource_mut(); - for column in resource_archetype.unique_components.values_mut() { - column.clear_counters(global_system_counter); - } } /// Returns [QueryState] for the given [WorldQuery], which is used to efficiently @@ -892,6 +881,20 @@ impl World { pub fn get_exclusive_system_counter(&self) -> u32 { self.exclusive_system_counter } + + pub fn check_component_counters(&mut self) { + // Iterate over all component counters, clamping their age to max age + // PERF: parallelize + let global_system_counter = self.get_global_system_counter_unordered(); + self.storages.tables.clear_counters(global_system_counter); + self.storages + .sparse_sets + .check_counters(global_system_counter); + let resource_archetype = self.archetypes.resource_mut(); + for column in resource_archetype.unique_components.values_mut() { + column.check_counters(global_system_counter); + } + } } impl fmt::Debug for World { From c85cdda2566e3b43c8ff53846476c1edc7cfe5b3 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Tue, 9 Mar 2021 01:53:25 +0900 Subject: [PATCH 04/41] Add Not query transformer --- crates/bevy_ecs/src/query/filter.rs | 100 +++++++++++++++++++++++++++- crates/bevy_ecs/src/query/mod.rs | 15 +++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index a858f378931d3..94c510e28de59 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -47,7 +47,7 @@ where } } -/// Filter that retrieves components of type `T` that have either been mutated or added since the start of the frame. +/// Filter that selects entities with a component `T` pub struct With(PhantomData); impl WorldQuery for With { @@ -142,7 +142,7 @@ impl<'a, T: Component> Fetch<'a> for WithFetch { } } -/// Filter that retrieves components of type `T` that have either been mutated or added since the start of the frame. +/// Filter that selects entities without a component `T` pub struct Without(PhantomData); impl WorldQuery for Without { @@ -455,6 +455,102 @@ macro_rules! impl_query_filter_tuple { all_tuples!(impl_query_filter_tuple, 0, 15, F, S); +/// Query trnasformer that inverses its inner filter. +/// For example, `Not>` is equivalent to `Without` +pub struct Not(pub T); + +pub struct NotFetch { + fetch: T, + matches: bool, +} + +impl WorldQuery for Not +where + T::Fetch: FilterFetch, +{ + type Fetch = Not>; + type State = Not; +} + +impl<'a, T: FilterFetch> Fetch<'a> for Not> { + type Item = bool; + type State = Not<>::State>; + + unsafe fn init( + world: &World, + state: &Self::State, + system_counter: u32, + global_system_counter: u32, + ) -> Self { + Not(NotFetch { + fetch: T::init(world, &state.0, system_counter, global_system_counter), + matches: false, + }) + } + + fn is_dense(&self) -> bool { + self.0.fetch.is_dense() + } + + unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + self.0.matches = state.0.matches_table(table); + if self.0.matches { + self.0.fetch.set_table(&state.0, table); + } + } + + unsafe fn set_archetype( + &mut self, + state: &Self::State, + archetype: &Archetype, + tables: &Tables, + ) { + self.0.matches = state.0.matches_archetype(archetype); + if self.0.matches { + self.0.fetch.set_archetype(&state.0, archetype, tables); + } + } + + unsafe fn table_fetch(&mut self, table_row: usize) -> bool { + dbg!((&self.0.matches, self.0.fetch.table_filter_fetch(table_row))); + !(self.0.matches && self.0.fetch.table_filter_fetch(table_row)) + } + + unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool { + dbg!(( + &self.0.matches, + self.0.fetch.archetype_filter_fetch(archetype_index) + )); + !(self.0.matches && self.0.fetch.archetype_filter_fetch(archetype_index)) + } +} + +unsafe impl FetchState for Not { + fn init(world: &mut World) -> Self { + Not(T::init(world)) + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + self.0.update_component_access(access); + } + + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + self.0.update_archetype_component_access(archetype, access); + } + + fn matches_archetype(&self, _archetype: &Archetype) -> bool { + true + } + + fn matches_table(&self, _table: &Table) -> bool { + true + } +} + macro_rules! impl_counter_filter { ( $(#[$meta:meta])* diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 2ee4493f82b4f..42ac0bd1eba31 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -14,6 +14,7 @@ pub use state::*; #[cfg(test)] mod tests { + use super::{Not, With}; use crate::{ component::{ComponentDescriptor, StorageType}, world::World, @@ -59,4 +60,18 @@ mod tests { let values = world.query::<&B>().iter().collect::>(); assert_eq!(values, vec![&B(3)]); } + + #[test] + fn not_transformer() { + let mut world = World::new(); + + world.spawn().insert_bundle((A(1), B(2))); + world.spawn().insert_bundle((A(2),)); + + let values = world + .query_filtered::<&A, Not>>() + .iter() + .collect::>(); + assert_eq!(values, vec![&A(2)]); + } } From 255d9a6da0993db78c0b987225de0baba7310619 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Tue, 9 Mar 2021 02:08:53 +0900 Subject: [PATCH 05/41] Remove Mutated --- crates/bevy_ecs/src/component/mod.rs | 5 --- crates/bevy_ecs/src/lib.rs | 37 +++++++++++--------- crates/bevy_ecs/src/query/fetch.rs | 10 ++---- crates/bevy_ecs/src/query/filter.rs | 31 ++++------------- crates/bevy_ecs/src/reflect.rs | 10 ++---- crates/bevy_ecs/src/system/mod.rs | 4 +-- crates/bevy_ecs/src/system/system_param.rs | 40 +++++----------------- crates/bevy_ecs/src/world/pointer.rs | 10 ++---- examples/ecs/change_detection.rs | 2 +- examples/ecs/state.rs | 2 +- examples/ui/button.rs | 2 +- 11 files changed, 46 insertions(+), 107 deletions(-) diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index ce775e60dbab8..9a66b1352ae38 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -308,11 +308,6 @@ impl ComponentCounters { component_age < system_age } - pub fn is_mutated(&self, system_counter: u32, global_system_counter: u32) -> bool { - self.is_changed(system_counter, global_system_counter) - && !self.is_added(system_counter, global_system_counter) - } - pub(crate) fn new(global_system_counter: u32) -> Self { Self { added: global_system_counter, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 8bbb5f66b1e7f..94dba3b49217b 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -16,7 +16,7 @@ pub mod prelude { pub use crate::{ bundle::Bundle, entity::Entity, - query::{Added, Changed, Counters, Mutated, Or, QueryState, With, WithBundle, Without}, + query::{Added, Changed, Counters, Or, QueryState, With, WithBundle, Without}, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, Schedule, Stage, StageLabel, State, StateStage, SystemLabel, SystemStage, @@ -35,7 +35,7 @@ mod tests { bundle::Bundle, component::{Component, ComponentDescriptor, StorageType, TypeInfo}, entity::Entity, - query::{Added, Changed, Counters, FilterFetch, Mutated, Or, With, Without, WorldQuery}, + query::{Added, Changed, Counters, FilterFetch, Not, Or, With, Without, WorldQuery}, world::{Mut, World}, }; use bevy_tasks::TaskPool; @@ -697,17 +697,20 @@ mod tests { .collect::>() } - assert_eq!(get_filtered::>(&mut world), vec![e1, e3]); + assert_eq!( + get_filtered::<(Changed, Not>)>(&mut world), + vec![e1, e3] + ); // ensure changing an entity's archetypes also moves its mutated state world.entity_mut(e1).insert(C); - assert_eq!(get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); + assert_eq!(get_filtered::<(Changed, Not>)>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing mutated state world.entity_mut(e1).insert_bundle((A(0), B)); assert_eq!( - get_filtered::>(&mut world), + get_filtered::<(Changed, Not>)>(&mut world), vec![e3, e1], "changed entities list should not change" ); @@ -715,7 +718,7 @@ mod tests { // removing an unchanged entity should not change mutated state assert!(world.despawn(e2)); assert_eq!( - get_filtered::>(&mut world), + get_filtered::<(Changed, Not>)>(&mut world), vec![e3, e1], "changed entities list should not change" ); @@ -723,7 +726,7 @@ mod tests { // removing a changed entity should remove it from enumeration assert!(world.despawn(e1)); assert_eq!( - get_filtered::>(&mut world), + get_filtered::<(Changed, Not>)>(&mut world), vec![e3], "e1 should no longer be returned" ); @@ -731,16 +734,16 @@ mod tests { world.clear_trackers(); world.exclusive_system_counter = world.increment_global_system_counter(); - assert!(get_filtered::>(&mut world).is_empty()); + assert!(get_filtered::<(Changed, Not>)>(&mut world).is_empty()); let e4 = world.spawn().id(); world.entity_mut(e4).insert(A(0)); - assert!(get_filtered::>(&mut world).is_empty()); + assert!(get_filtered::<(Changed, Not>)>(&mut world).is_empty()); assert_eq!(get_filtered::>(&mut world), vec![e4]); world.entity_mut(e4).insert(A(1)); - // assert_eq!(get_filtered::>(&mut world), vec![e4]); // This case is no longer possible to detect + // assert_eq!(get_filtered::<(Changed, Not>)>(&mut world), vec![e4]); // This case is no longer possible to detect world.clear_trackers(); world.exclusive_system_counter = world.increment_global_system_counter(); @@ -751,9 +754,12 @@ mod tests { world.entity_mut(e4).insert_bundle((A(0), B(0))); assert!(get_filtered::>(&mut world).is_empty()); - assert_eq!(get_filtered::>(&mut world), vec![e4]); + assert_eq!( + get_filtered::<(Changed, Not>)>(&mut world), + vec![e4] + ); assert_eq!(get_filtered::>(&mut world), vec![e4]); - assert!(get_filtered::>(&mut world).is_empty()); + assert!(get_filtered::<(Changed, Not>)>(&mut world).is_empty()); } #[test] @@ -793,7 +799,7 @@ mod tests { } let a_b_mutated = world - .query_filtered::, Mutated)>() + .query_filtered::, Not>), (Changed, Not>))>() .iter() .collect::>(); assert_eq!(a_b_mutated, vec![e2]); @@ -814,7 +820,7 @@ mod tests { *world.entity_mut(e4).get_mut::().unwrap() = A(1); let a_b_mutated = world - .query_filtered::, Mutated)>>() + .query_filtered::, Not>), (Changed, Not>))>>() .iter() .collect::>(); // e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component @@ -1051,7 +1057,6 @@ mod tests { let a_counters = counters[0].as_ref().unwrap(); assert!(counters[1].is_none()); assert!(a_counters.is_added()); - assert!(!a_counters.is_mutated()); assert!(a_counters.is_changed()); world.clear_trackers(); world.exclusive_system_counter = world.increment_global_system_counter(); @@ -1061,7 +1066,6 @@ mod tests { .collect::>(); let a_counters = counters[0].as_ref().unwrap(); assert!(!a_counters.is_added()); - assert!(!a_counters.is_mutated()); assert!(!a_counters.is_changed()); *world.get_mut(e1).unwrap() = A(1); let counters = world @@ -1071,7 +1075,6 @@ mod tests { let a_counters = counters[0].as_ref().unwrap(); assert!(!a_counters.is_added()); assert!(a_counters.is_changed()); - assert!(a_counters.is_mutated()); } #[test] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 44ad1947bf753..2f79ff751530d 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -581,7 +581,7 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { } } -/// Flags on component `T` that happened since the start of the frame. +/// Flags on component `T` that happened since the last execution of this system. #[derive(Clone)] pub struct Counters { component_counters: ComponentCounters, @@ -606,13 +606,7 @@ impl Counters { .is_added(self.system_counter, self.global_system_counter) } - /// Has this component been mutated since the last execution of this system. - pub fn is_mutated(&self) -> bool { - self.component_counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Has this component been mutated since the last execution of this system. + /// Has this component been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.component_counters .is_changed(self.system_counter, self.global_system_counter) diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 94c510e28de59..8d02fccf370f1 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -688,13 +688,13 @@ macro_rules! impl_counter_filter { } impl_counter_filter!( - /// Filter that retrieves components of type `T` that have been added since the start of the frame + /// Filter that retrieves components of type `T` that have been added since the last execution of this system /// /// This filter is useful as a performance optimization as it means that the query contains fewer items /// for a system to iterate over. /// /// Because the ordering of systems can change and this filter is only effective on changes before the query executes - /// you need to use explicit dependency ordering or ordered stages for these query filters to be useful. + /// you need to use explicit dependency ordering or ordered stages to avoid frame delays. /// /// /// Example: @@ -719,46 +719,29 @@ impl_counter_filter!( ); impl_counter_filter!( - /// Filter that retrieves components of type `T` that have been mutated since the start of the frame. - /// Added components do not count as mutated. + /// Filter that retrieves components of type `T` that have been changed since the last execution of this system /// /// This filter is useful as a performance optimization as it means that the query contains fewer items /// for a system to iterate over. /// /// Because the ordering of systems can change and this filter is only effective on changes before the query executes - /// you need to use explicit dependency ordering or ordered stages for these query filters to be useful. + /// you need to use explicit dependency ordering or ordered stages to avoid frame delays. /// /// Example: /// ``` /// # use bevy_ecs::system::Query; - /// # use bevy_ecs::query::Mutated; + /// # use bevy_ecs::query::Changed; /// # /// # #[derive(Debug)] /// # struct Name {}; /// # struct Transform {}; /// # - /// fn print_moving_objects_system(query: Query<&Name, Mutated>) { + /// fn print_moving_objects_system(query: Query<&Name, Changed>) { /// for name in query.iter() { - /// println!("Entity Moved: {:?}", name) + /// println!("Entity Moved: {:?}", name); /// } /// } /// ``` - Mutated, - MutatedState, - MutatedFetch, - ComponentCounters::is_mutated -); - -impl_counter_filter!( - /// Filter that retrieves components of type `T` that have been added or mutated since the start of the frame - /// - /// This filter is useful as a performance optimization as it means that the query contains fewer items - /// for a system to iterate over. - /// - /// Because the ordering of systems can change and this filter is only effective on changes before the query executes - /// you need to use explicit dependency ordering or ordered stages for these query filters to be useful. - /// - /// Also see the documentation for [`Mutated`] and [`Added`] as this filter is a logical OR of them. Changed, ChangedState, ChangedFetch, diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index c63f41da7c39b..fbd8a5bd790f6 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -138,19 +138,13 @@ impl<'a> DerefMut for ReflectMut<'a> { } impl<'a> ReflectMut<'a> { - /// Returns true if (and only if) this component been added since the start of the frame. + /// Returns true if (and only if) this component been added since the last execution of this system. pub fn is_added(&self) -> bool { self.component_counters .is_added(self.system_counter, self.global_system_counter) } - /// Returns true if (and only if) this component been mutated since the start of the frame. - pub fn is_mutated(&self) -> bool { - self.component_counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Returns true if (and only if) this component been either mutated or added since the start of the frame. + /// Returns true if (and only if) this component been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.component_counters .is_changed(self.system_counter, self.global_system_counter) diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 0d4e6319b4c14..f8be64bcb5dcb 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -22,7 +22,7 @@ mod tests { bundle::Bundles, component::Components, entity::{Entities, Entity}, - query::{Added, Changed, Mutated, Or, With, Without}, + query::{Added, Changed, Not, Or, With, Without}, schedule::{Schedule, Stage, SystemStage}, system::{ IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet, RemovedComponents, Res, @@ -130,7 +130,7 @@ mod tests { set: QuerySet<( Query<(), Or<(Changed, Changed)>>, Query<(), Or<(Added, Added)>>, - Query<(), Or<(Mutated, Mutated)>>, + Query<(), Or<((Changed, Not>), (Changed, Not>))>>, )>, ) { let changed = set.q0().iter().count(); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0bb355acb2224..73299af2c30c1 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -167,19 +167,13 @@ pub struct Res<'w, T> { } impl<'w, T: Component> Res<'w, T> { - /// Returns true if (and only if) this resource been added since the start of the frame. + /// Returns true if (and only if) this resource been added since the last execution of this system. pub fn is_added(&self) -> bool { self.counters .is_added(self.system_counter, self.global_system_counter) } - /// Returns true if (and only if) this resource been mutated since the start of the frame. - pub fn is_mutated(&self) -> bool { - self.counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. + /// Returns true if (and only if) this resource been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.counters .is_changed(self.system_counter, self.global_system_counter) @@ -298,19 +292,13 @@ pub struct ResMut<'w, T> { } impl<'w, T: Component> ResMut<'w, T> { - /// Returns true if (and only if) this resource been added since the start of the frame. + /// Returns true if (and only if) this resource been added since the last execution of this system. pub fn is_added(&self) -> bool { self.counters .is_added(self.system_counter, self.global_system_counter) } - /// Returns true if (and only if) this resource been mutated since the start of the frame. - pub fn is_mutated(&self) -> bool { - self.counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. + /// Returns true if (and only if) this resource been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.counters .is_changed(self.system_counter, self.global_system_counter) @@ -566,19 +554,13 @@ pub struct NonSend<'w, T> { } impl<'w, T: Component> NonSend<'w, T> { - /// Returns true if (and only if) this resource been added since the start of the frame. + /// Returns true if (and only if) this resource been added since the last execution of this system. pub fn is_added(&self) -> bool { self.counters .is_added(self.system_counter, self.global_system_counter) } - /// Returns true if (and only if) this resource been mutated since the start of the frame. - pub fn is_mutated(&self) -> bool { - self.counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. + /// Returns true if (and only if) this resource been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.counters .is_changed(self.system_counter, self.global_system_counter) @@ -665,19 +647,13 @@ pub struct NonSendMut<'a, T: 'static> { } impl<'w, T: Component> NonSendMut<'w, T> { - /// Returns true if (and only if) this resource been added since the start of the frame. + /// Returns true if (and only if) this resource been added since the last execution of this system. pub fn is_added(&self) -> bool { self.counters .is_added(self.system_counter, self.global_system_counter) } - /// Returns true if (and only if) this resource been mutated since the start of the frame. - pub fn is_mutated(&self) -> bool { - self.counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Returns true if (and only if) this resource been either mutated or added since the start of the frame. + /// Returns true if (and only if) this resource been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.counters .is_changed(self.system_counter, self.global_system_counter) diff --git a/crates/bevy_ecs/src/world/pointer.rs b/crates/bevy_ecs/src/world/pointer.rs index fe19f642d0dd1..1951e5d8ab3c4 100644 --- a/crates/bevy_ecs/src/world/pointer.rs +++ b/crates/bevy_ecs/src/world/pointer.rs @@ -34,19 +34,13 @@ impl<'a, T: core::fmt::Debug> core::fmt::Debug for Mut<'a, T> { } impl<'w, T> Mut<'w, T> { - /// Returns true if (and only if) this component been added since the start of the frame. + /// Returns true if (and only if) this component been added since the last execution of this system. pub fn is_added(&self) -> bool { self.component_counters .is_added(self.system_counter, self.global_system_counter) } - /// Returns true if (and only if) this component been mutated since the start of the frame. - pub fn is_mutated(&self) -> bool { - self.component_counters - .is_mutated(self.system_counter, self.global_system_counter) - } - - /// Returns true if (and only if) this component been either mutated or added since the start of the frame. + /// Returns true if (and only if) this component been changed since the last execution of this system. pub fn is_changed(&self) -> bool { self.component_counters .is_changed(self.system_counter, self.global_system_counter) diff --git a/examples/ecs/change_detection.rs b/examples/ecs/change_detection.rs index ea696e9232e1f..c432e42934ddb 100644 --- a/examples/ecs/change_detection.rs +++ b/examples/ecs/change_detection.rs @@ -29,7 +29,7 @@ fn change_component(time: Res