From ee941502837d220f469ed081f52bc5400930b68b Mon Sep 17 00:00:00 2001 From: Ellen Date: Sun, 20 Jun 2021 02:49:10 +0100 Subject: [PATCH] relations --- Cargo.toml | 4 + crates/bevy_core/src/time/fixed_timestep.rs | 4 +- crates/bevy_ecs/Cargo.toml | 1 + crates/bevy_ecs/macros/src/lib.rs | 14 +- crates/bevy_ecs/src/archetype.rs | 149 ++- crates/bevy_ecs/src/bundle.rs | 125 ++- crates/bevy_ecs/src/component/mod.rs | 333 +++---- crates/bevy_ecs/src/lib.rs | 35 +- crates/bevy_ecs/src/query/fetch.rs | 921 ++++++++++++++++-- crates/bevy_ecs/src/query/filter.rs | 506 ++++++++-- crates/bevy_ecs/src/query/iter.rs | 99 +- crates/bevy_ecs/src/query/mod.rs | 2 + crates/bevy_ecs/src/query/relation_filter.rs | 270 +++++ crates/bevy_ecs/src/query/state.rs | 362 +++++-- crates/bevy_ecs/src/schedule/run_criteria.rs | 6 +- crates/bevy_ecs/src/schedule/stage.rs | 14 +- .../bevy_ecs/src/schedule/system_container.rs | 8 +- crates/bevy_ecs/src/storage/sparse_set.rs | 70 +- crates/bevy_ecs/src/storage/table.rs | 145 ++- crates/bevy_ecs/src/system/commands.rs | 106 ++ crates/bevy_ecs/src/system/function_system.rs | 6 +- crates/bevy_ecs/src/system/mod.rs | 19 +- crates/bevy_ecs/src/system/query.rs | 111 ++- crates/bevy_ecs/src/system/system.rs | 4 +- crates/bevy_ecs/src/system/system_chaining.rs | 6 +- crates/bevy_ecs/src/system/system_param.rs | 63 +- crates/bevy_ecs/src/world/entity_ref.rs | 595 +++++++---- crates/bevy_ecs/src/world/mod.rs | 128 ++- crates/bevy_ecs/src/world/tests.rs | 685 +++++++++++++ crates/bevy_ecs/src/world/world_cell.rs | 43 +- crates/bevy_scene/src/dynamic_scene.rs | 9 +- crates/bevy_scene/src/scene_spawner.rs | 14 +- examples/README.md | 1 + examples/ecs/relations_grouping.rs | 209 ++++ 34 files changed, 4169 insertions(+), 898 deletions(-) create mode 100644 crates/bevy_ecs/src/query/relation_filter.rs create mode 100644 crates/bevy_ecs/src/world/tests.rs create mode 100644 examples/ecs/relations_grouping.rs diff --git a/Cargo.toml b/Cargo.toml index 9e8551ffc3daf..9d6a4d2de4215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -258,6 +258,10 @@ path = "examples/diagnostics/custom_diagnostic.rs" name = "ecs_guide" path = "examples/ecs/ecs_guide.rs" +[[example]] +name = "relations_grouping" +path = "examples/ecs/relations_grouping.rs" + [[example]] name = "change_detection" path = "examples/ecs/change_detection.rs" diff --git a/crates/bevy_core/src/time/fixed_timestep.rs b/crates/bevy_core/src/time/fixed_timestep.rs index c03d87296fa50..c3f1e72ffdf21 100644 --- a/crates/bevy_core/src/time/fixed_timestep.rs +++ b/crates/bevy_core/src/time/fixed_timestep.rs @@ -1,7 +1,7 @@ use crate::Time; use bevy_ecs::{ archetype::{Archetype, ArchetypeComponentId}, - component::ComponentId, + component::RelationKindId, query::Access, schedule::ShouldRun, system::{IntoSystem, Local, Res, ResMut, System, SystemId}, @@ -160,7 +160,7 @@ impl System for FixedTimestep { self.internal_system.archetype_component_access() } - fn component_access(&self) -> &Access { + fn component_access(&self) -> &Access { self.internal_system.component_access() } diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 18593fbe8dda8..e81b72d88f779 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -23,6 +23,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.5.0" } bevy_utils = { path = "../bevy_utils", version = "0.5.0" } bevy_ecs_macros = { path = "macros", version = "0.5.0" } +smallvec = { version = "1.4", features = ["serde"] } async-channel = "1.4" bitflags = "1.2" fixedbitset = "0.4" diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 27383ff71ee55..0cef397999abb 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -258,7 +258,17 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { let (#(#query,)*) = &mut self.0; #( - #query.new_archetype(archetype); + for (relation_filter, cache) in #query.relation_filter_accesses.iter_mut() { + QueryState::<#query, #filter>::new_archetype( + &#query.fetch_state, + &#query.filter_state, + &mut #query.archetype_component_access, + &*relation_filter, + cache, + archetype + ); + } + system_meta .archetype_component_access .extend(&#query.archetype_component_access); @@ -280,7 +290,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { world: &'a World, change_tick: u32, ) -> Self::Item { - let (#(#query,)*) = &state.0; + let (#(#query,)*) = &mut state.0; QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*)) } } diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 5a718bd6baf8d..f70e73c9db230 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -1,12 +1,14 @@ +use bevy_utils::HashMap; +use bevy_utils::StableHashMap; + use crate::{ bundle::BundleId, - component::{ComponentId, StorageType}, + component::{RelationKindId, StorageType}, entity::{Entity, EntityLocation}, storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId}, }; use std::{ borrow::Cow, - collections::HashMap, hash::Hash, ops::{Index, IndexMut}, }; @@ -124,47 +126,65 @@ pub struct Archetype { entities: Vec, edges: Edges, table_info: TableInfo, - table_components: Cow<'static, [ComponentId]>, - sparse_set_components: Cow<'static, [ComponentId]>, - pub(crate) unique_components: SparseSet, - pub(crate) components: SparseSet, + table_components: Cow<'static, [(RelationKindId, Option)]>, + sparse_set_components: Cow<'static, [(RelationKindId, Option)]>, + pub(crate) unique_components: SparseSet, + pub(crate) components: SparseSet, + pub(crate) relations: SparseSet>, } impl Archetype { pub fn new( id: ArchetypeId, table_id: TableId, - table_components: Cow<'static, [ComponentId]>, - sparse_set_components: Cow<'static, [ComponentId]>, + table_components: Cow<'static, [(RelationKindId, Option)]>, + sparse_set_components: Cow<'static, [(RelationKindId, Option)]>, table_archetype_components: Vec, sparse_set_archetype_components: Vec, ) -> Self { + // FIXME(Relationships) sort out this capacity weirdness let mut components = SparseSet::with_capacity(table_components.len() + sparse_set_components.len()); - for (component_id, archetype_component_id) in + let mut relations = SparseSet::new(); + for ((kind_id, target), archetype_component_id) in table_components.iter().zip(table_archetype_components) { - components.insert( - *component_id, - ArchetypeComponentInfo { - storage_type: StorageType::Table, - archetype_component_id, - }, - ); + let arch_comp_info = ArchetypeComponentInfo { + storage_type: StorageType::Table, + archetype_component_id, + }; + + match target { + None => { + components.insert(*kind_id, arch_comp_info); + } + Some(target) => { + let set = relations.get_or_insert_with(*kind_id, StableHashMap::default); + set.insert(*target, arch_comp_info); + } + }; } - for (component_id, archetype_component_id) in sparse_set_components + for ((kind_id, target), archetype_component_id) in sparse_set_components .iter() .zip(sparse_set_archetype_components) { - components.insert( - *component_id, - ArchetypeComponentInfo { - storage_type: StorageType::SparseSet, - archetype_component_id, - }, - ); + let arch_comp_info = ArchetypeComponentInfo { + storage_type: StorageType::SparseSet, + archetype_component_id, + }; + + match target { + None => { + components.insert(*kind_id, arch_comp_info); + } + Some(target) => { + let set = relations.get_or_insert_with(*kind_id, StableHashMap::default); + set.insert(*target, arch_comp_info); + } + }; } + Self { id, table_info: TableInfo { @@ -172,6 +192,7 @@ impl Archetype { entity_rows: Default::default(), }, components, + relations, table_components, sparse_set_components, unique_components: SparseSet::new(), @@ -201,28 +222,38 @@ impl Archetype { } #[inline] - pub fn table_components(&self) -> &[ComponentId] { + pub fn table_components(&self) -> &[(RelationKindId, Option)] { &self.table_components } #[inline] - pub fn sparse_set_components(&self) -> &[ComponentId] { + pub fn sparse_set_components(&self) -> &[(RelationKindId, Option)] { &self.sparse_set_components } #[inline] - pub fn unique_components(&self) -> &SparseSet { + pub fn unique_components(&self) -> &SparseSet { &self.unique_components } #[inline] - pub fn unique_components_mut(&mut self) -> &mut SparseSet { + pub fn unique_components_mut(&mut self) -> &mut SparseSet { &mut self.unique_components } + // FIXME(Relationships) this also yields relations which feels weird but also needed #[inline] - pub fn components(&self) -> impl Iterator + '_ { - self.components.indices() + pub fn components(&self) -> impl Iterator)> + '_ { + self.components + .indices() + .map(|kind| (kind, None)) + .chain(self.relations.indices().flat_map(move |kind_id| { + self.relations + .get(kind_id) + .unwrap() + .keys() + .map(move |target| (kind_id, Some(*target))) + })) } #[inline] @@ -289,25 +320,51 @@ impl Archetype { } #[inline] - pub fn contains(&self, component_id: ComponentId) -> bool { - self.components.contains(component_id) + pub fn contains(&self, relation_kind: RelationKindId, relation_target: Option) -> bool { + match relation_target { + None => self.components.contains(relation_kind), + Some(target) => self + .relations + .get(relation_kind) + .map(|set| set.get(&target)) + .flatten() + .is_some(), + } } + // FIXME(Relationships) technically the target is unnecessary here as all `KindId` have the same storage type #[inline] - pub fn get_storage_type(&self, component_id: ComponentId) -> Option { - self.components - .get(component_id) - .map(|info| info.storage_type) + pub fn get_storage_type( + &self, + relation_kind: RelationKindId, + relation_target: Option, + ) -> Option { + match relation_target { + None => self.components.get(relation_kind), + Some(target) => self + .relations + .get(relation_kind) + .map(|set| set.get(&target)) + .flatten(), + } + .map(|info| info.storage_type) } #[inline] pub fn get_archetype_component_id( &self, - component_id: ComponentId, + relation_kind: RelationKindId, + relation_target: Option, ) -> Option { - self.components - .get(component_id) - .map(|info| info.archetype_component_id) + match relation_target { + None => self.components.get(relation_kind), + Some(target) => self + .relations + .get(relation_kind) + .map(|set| set.get(&target)) + .flatten(), + } + .map(|info| info.archetype_component_id) } } @@ -329,8 +386,8 @@ impl ArchetypeGeneration { #[derive(Hash, PartialEq, Eq)] pub struct ArchetypeIdentity { - table_components: Cow<'static, [ComponentId]>, - sparse_set_components: Cow<'static, [ComponentId]>, + table_components: Cow<'static, [(RelationKindId, Option)]>, + sparse_set_components: Cow<'static, [(RelationKindId, Option)]>, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -453,6 +510,10 @@ impl Archetypes { a: ArchetypeId, b: ArchetypeId, ) -> (&mut Archetype, &mut Archetype) { + if a.0 == b.0 { + panic!("both indexes were the same"); + } + if a.index() > b.index() { let (b_slice, a_slice) = self.archetypes.split_at_mut(a.index()); (&mut a_slice[0], &mut b_slice[b.index()]) @@ -475,8 +536,8 @@ impl Archetypes { pub(crate) fn get_id_or_insert( &mut self, table_id: TableId, - table_components: Vec, - sparse_set_components: Vec, + table_components: Vec<(RelationKindId, Option)>, + sparse_set_components: Vec<(RelationKindId, Option)>, ) -> ArchetypeId { let table_components = Cow::from(table_components); let sparse_set_components = Cow::from(sparse_set_components); diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 00f3bac9e527e..424d34f4e7054 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -2,7 +2,10 @@ pub use bevy_ecs_macros::Bundle; use crate::{ archetype::ComponentStatus, - component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo}, + component::{ + Component, ComponentTicks, Components, RelationKindId, RelationKindInfo, StorageType, + TypeInfo, + }, entity::Entity, storage::{SparseSetIndex, SparseSets, Table}, }; @@ -117,7 +120,7 @@ impl SparseSetIndex for BundleId { pub struct BundleInfo { pub(crate) id: BundleId, - pub(crate) component_ids: Vec, + pub(crate) relationship_ids: Vec<(RelationKindId, Option)>, pub(crate) storage_types: Vec, } @@ -139,31 +142,55 @@ impl BundleInfo { // 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| { - let component_id = *self.component_ids.get_unchecked(bundle_component); - match self.storage_types[bundle_component] { - StorageType::Table => { - let column = table.get_column_mut(component_id).unwrap(); - match bundle_status.get_unchecked(bundle_component) { - ComponentStatus::Added => { - column.initialize( - table_row, - component_ptr, - ComponentTicks::new(change_tick), - ); - } - ComponentStatus::Mutated => { - column.replace(table_row, component_ptr, change_tick); - } + bundle.get_components(&mut |component_ptr| { + self.write_relationship( + sparse_sets, + entity, + table, + table_row, + bundle_status, + bundle_component, + component_ptr, + change_tick, + ); + bundle_component += 1; + }); + } + + #[allow(clippy::too_many_arguments)] + pub(crate) unsafe fn write_relationship( + &self, + sparse_sets: &mut SparseSets, + entity: Entity, + table: &mut Table, + table_row: usize, + bundle_status: &[ComponentStatus], + relationship_index: usize, + component_ptr: *mut u8, + change_tick: u32, + ) { + let (kind_id, target) = self.relationship_ids[relationship_index]; + match self.storage_types[relationship_index] { + StorageType::Table => { + let column = table.get_column_mut(kind_id, target).unwrap(); + match bundle_status[relationship_index] { + ComponentStatus::Added => { + column.initialize( + table_row, + component_ptr, + ComponentTicks::new(change_tick), + ); + } + ComponentStatus::Mutated => { + column.replace(table_row, component_ptr, change_tick); } - } - StorageType::SparseSet => { - let sparse_set = sparse_sets.get_mut(component_id).unwrap(); - sparse_set.insert(entity, component_ptr, change_tick); } } - bundle_component += 1; - }); + StorageType::SparseSet => { + let sparse_set = sparse_sets.get_mut(kind_id, target).unwrap(); + sparse_set.insert(entity, component_ptr, change_tick); + } + } } #[inline] @@ -172,8 +199,8 @@ impl BundleInfo { } #[inline] - pub fn components(&self) -> &[ComponentId] { - &self.component_ids + pub fn components(&self) -> &[(RelationKindId, Option)] { + &self.relationship_ids } #[inline] @@ -186,6 +213,7 @@ impl BundleInfo { pub struct Bundles { bundle_infos: Vec, bundle_ids: HashMap, + relationship_bundle_ids: HashMap<(RelationKindId, Option), BundleId>, } impl Bundles { @@ -199,6 +227,38 @@ impl Bundles { self.bundle_ids.get(&type_id).cloned() } + pub fn get_relationship_bundle_id( + &self, + relation_kind: RelationKindId, + relation_target: Option, + ) -> Option { + self.relationship_bundle_ids + .get(&(relation_kind, relation_target)) + .copied() + } + + pub(crate) fn init_relationship_info<'a>( + &'a mut self, + relation_kind: &RelationKindInfo, + relation_target: Option, + ) -> &'a BundleInfo { + let bundle_infos = &mut self.bundle_infos; + let id = self + .relationship_bundle_ids + .entry((relation_kind.id(), relation_target)) + .or_insert_with(|| { + let id = BundleId(bundle_infos.len()); + let bundle_info = BundleInfo { + id, + relationship_ids: vec![(relation_kind.id(), relation_target)], + storage_types: vec![relation_kind.data_layout().storage_type()], + }; + bundle_infos.push(bundle_info); + id + }); + &self.bundle_infos[id.0] + } + pub(crate) fn init_info<'a, T: Bundle>( &'a mut self, components: &mut Components, @@ -212,8 +272,7 @@ impl Bundles { bundle_infos.push(bundle_info); id }); - // SAFE: index either exists, or was initialized - unsafe { self.bundle_infos.get_unchecked(id.0) } + &self.bundle_infos[id.0] } } @@ -227,11 +286,9 @@ fn initialize_bundle( let mut storage_types = Vec::new(); for type_info in type_info { - let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone()); - // SAFE: get_with_type_info ensures info was created - let info = unsafe { components.get_info_unchecked(component_id) }; - component_ids.push(component_id); - storage_types.push(info.storage_type()); + let kind_info = components.get_component_kind_or_insert(type_info.clone().into()); + component_ids.push((kind_info.id(), None)); + storage_types.push(kind_info.data_layout().storage_type()); } let mut deduped = component_ids.clone(); @@ -243,7 +300,7 @@ fn initialize_bundle( BundleInfo { id, - component_ids, + relationship_ids: component_ids, storage_types, } } diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index 4a392bbe3a898..53957e22cbefc 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -2,12 +2,10 @@ mod type_info; pub use type_info::*; +use std::collections::HashMap; + use crate::storage::SparseSetIndex; -use std::{ - alloc::Layout, - any::{Any, TypeId}, - collections::hash_map::Entry, -}; +use std::{alloc::Layout, any::TypeId}; use thiserror::Error; /// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have @@ -39,22 +37,56 @@ impl Default for StorageType { } #[derive(Debug)] -pub struct ComponentInfo { +pub struct ComponentDescriptor { name: String, - id: ComponentId, - type_id: Option, - // SAFETY: This must remain private. It must only be set to "true" if this component is - // actually Send + Sync + storage_type: StorageType, + // SAFETY: This must remain private. It must only be set to "true" if this component is actually Send + Sync is_send_and_sync: bool, + type_id: Option, layout: Layout, drop: unsafe fn(*mut u8), - storage_type: StorageType, } -impl ComponentInfo { - #[inline] - pub fn id(&self) -> ComponentId { - self.id +impl ComponentDescriptor { + /// # Safety + /// Must be a valid drop pointer + pub unsafe fn new_dynamic( + name: Option, + storage_type: StorageType, + is_send_and_sync: bool, + layout: Layout, + drop: unsafe fn(*mut u8), + ) -> Self { + Self { + name: name.unwrap_or_default(), + storage_type, + is_send_and_sync, + type_id: None, + layout, + drop, + } + } + + pub fn new(storage_type: StorageType) -> Self { + Self { + name: std::any::type_name::().to_string(), + storage_type, + is_send_and_sync: true, + type_id: Some(TypeId::of::()), + layout: Layout::new::(), + drop: TypeInfo::drop_ptr::, + } + } + + pub fn new_non_send_sync(storage_type: StorageType) -> Self { + Self { + name: std::any::type_name::().to_string(), + storage_type, + is_send_and_sync: false, + type_id: Some(TypeId::of::()), + layout: Layout::new::(), + drop: TypeInfo::drop_ptr::, + } } #[inline] @@ -86,39 +118,28 @@ impl ComponentInfo { pub fn is_send_and_sync(&self) -> bool { self.is_send_and_sync } +} - fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self { - ComponentInfo { - id, - name: descriptor.name, - storage_type: descriptor.storage_type, - type_id: descriptor.type_id, - is_send_and_sync: descriptor.is_send_and_sync, - drop: descriptor.drop, - layout: descriptor.layout, +impl From for ComponentDescriptor { + fn from(type_info: TypeInfo) -> Self { + Self { + name: type_info.type_name().to_string(), + storage_type: StorageType::default(), + is_send_and_sync: type_info.is_send_and_sync(), + type_id: Some(type_info.type_id()), + drop: type_info.drop(), + layout: type_info.layout(), } } } -#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct ComponentId(usize); - -impl ComponentId { - #[inline] - pub const fn new(index: usize) -> ComponentId { - ComponentId(index) - } +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] +pub struct RelationKindId(usize); - #[inline] - pub fn index(self) -> usize { - self.0 - } -} - -impl SparseSetIndex for ComponentId { +impl SparseSetIndex for RelationKindId { #[inline] fn sparse_set_index(&self) -> usize { - self.index() + self.0 } fn get_sparse_set_index(value: usize) -> Self { @@ -126,183 +147,135 @@ impl SparseSetIndex for ComponentId { } } -pub struct ComponentDescriptor { - name: String, - storage_type: StorageType, - // SAFETY: This must remain private. It must only be set to "true" if this component is - // actually Send + Sync - is_send_and_sync: bool, - type_id: Option, - layout: Layout, - drop: unsafe fn(*mut u8), +#[derive(Debug)] +pub struct RelationKindInfo { + data: ComponentDescriptor, + id: RelationKindId, } -impl ComponentDescriptor { - pub fn new(storage_type: StorageType) -> Self { - Self { - name: std::any::type_name::().to_string(), - storage_type, - is_send_and_sync: true, - type_id: Some(TypeId::of::()), - layout: Layout::new::(), - drop: TypeInfo::drop_ptr::, - } - } - - #[inline] - pub fn storage_type(&self) -> StorageType { - self.storage_type +impl RelationKindInfo { + pub fn data_layout(&self) -> &ComponentDescriptor { + &self.data } - #[inline] - pub fn type_id(&self) -> Option { - self.type_id - } - - #[inline] - pub fn name(&self) -> &str { - &self.name - } -} - -impl From for ComponentDescriptor { - fn from(type_info: TypeInfo) -> Self { - Self { - name: type_info.type_name().to_string(), - storage_type: StorageType::default(), - is_send_and_sync: type_info.is_send_and_sync(), - type_id: Some(type_info.type_id()), - drop: type_info.drop(), - layout: type_info.layout(), - } + pub fn id(&self) -> RelationKindId { + self.id } } #[derive(Debug, Default)] pub struct Components { - components: Vec, - indices: std::collections::HashMap, - resource_indices: std::collections::HashMap, + kinds: Vec, + // These are only used by bevy. Scripting/dynamic components should + // use their own hashmap to lookup CustomId -> RelationKindId + component_indices: HashMap, + resource_indices: HashMap, } #[derive(Debug, Error)] -pub enum ComponentsError { +pub enum RelationsError { #[error("A component of type {name:?} ({type_id:?}) already exists")] ComponentAlreadyExists { type_id: TypeId, name: String }, + #[error("A resource of type {name:?} ({type_id:?}) already exists")] + ResourceAlreadyExists { type_id: TypeId, name: String }, } impl Components { - pub(crate) fn add( - &mut self, - descriptor: ComponentDescriptor, - ) -> Result { - let index = self.components.len(); - if let Some(type_id) = descriptor.type_id { - let index_entry = self.indices.entry(type_id); - if let Entry::Occupied(_) = index_entry { - return Err(ComponentsError::ComponentAlreadyExists { - type_id, - name: descriptor.name, - }); - } - self.indices.insert(type_id, index); - } - self.components - .push(ComponentInfo::new(ComponentId(index), descriptor)); - - Ok(ComponentId(index)) + pub fn new_relation_kind(&mut self, layout: ComponentDescriptor) -> &RelationKindInfo { + assert!(layout.type_id.is_none()); + let id = RelationKindId(self.kinds.len()); + self.kinds.push(RelationKindInfo { data: layout, id }); + self.kinds.last().unwrap() } - #[inline] - pub fn get_or_insert_id(&mut self) -> ComponentId { - self.get_or_insert_with(TypeId::of::(), TypeInfo::of::) - } - - #[inline] - pub fn get_or_insert_info(&mut self) -> &ComponentInfo { - let id = self.get_or_insert_id::(); - // SAFE: component_info with the given `id` initialized above - unsafe { self.get_info_unchecked(id) } - } - - #[inline] - pub fn len(&self) -> usize { - self.components.len() - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.components.len() == 0 + pub fn new_component_kind( + &mut self, + layout: ComponentDescriptor, + ) -> Result<&RelationKindInfo, RelationsError> { + let id = RelationKindId(self.kinds.len()); + if self + .component_indices + .contains_key(&layout.type_id().unwrap()) + { + return Err(RelationsError::ComponentAlreadyExists { + type_id: layout.type_id().unwrap(), + name: layout.name, + }); + } + self.component_indices.insert(layout.type_id().unwrap(), id); + self.kinds.push(RelationKindInfo { data: layout, id }); + Ok(self.kinds.last().unwrap()) } - #[inline] - pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { - self.components.get(id.0) + pub fn new_resource_kind( + &mut self, + layout: ComponentDescriptor, + ) -> Result<&RelationKindInfo, RelationsError> { + let id = RelationKindId(self.kinds.len()); + if self + .resource_indices + .contains_key(&layout.type_id().unwrap()) + { + return Err(RelationsError::ResourceAlreadyExists { + type_id: layout.type_id().unwrap(), + name: layout.name, + }); + } + self.resource_indices.insert(layout.type_id().unwrap(), id); + self.kinds.push(RelationKindInfo { data: layout, id }); + Ok(self.kinds.last().unwrap()) } - /// # Safety - /// - /// `id` must be a valid [ComponentId] - #[inline] - pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo { - debug_assert!(id.index() < self.components.len()); - self.components.get_unchecked(id.0) + pub fn get_relation_kind(&self, id: RelationKindId) -> &RelationKindInfo { + self.kinds.get(id.0).unwrap() } - #[inline] - pub fn get_id(&self, type_id: TypeId) -> Option { - self.indices.get(&type_id).map(|index| ComponentId(*index)) + pub fn get_component_kind(&self, type_id: TypeId) -> Option<&RelationKindInfo> { + let id = self.component_indices.get(&type_id).copied()?; + Some(&self.kinds[id.0]) } - #[inline] - pub fn get_resource_id(&self, type_id: TypeId) -> Option { - self.resource_indices - .get(&type_id) - .map(|index| ComponentId(*index)) + pub fn get_resource_kind(&self, type_id: TypeId) -> Option<&RelationKindInfo> { + let id = self.resource_indices.get(&type_id).copied()?; + Some(&self.kinds[id.0]) } - #[inline] - pub fn get_or_insert_resource_id(&mut self) -> ComponentId { - self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of::) + pub fn get_component_kind_or_insert( + &mut self, + layout: ComponentDescriptor, + ) -> &RelationKindInfo { + match self + .component_indices + .get(&layout.type_id().unwrap()) + .copied() + { + Some(kind) => &self.kinds[kind.0], + None => self.new_component_kind(layout).unwrap(), + } } - #[inline] - pub fn get_or_insert_non_send_resource_id(&mut self) -> ComponentId { - self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of_non_send_and_sync::) + pub fn get_resource_kind_or_insert( + &mut self, + layout: ComponentDescriptor, + ) -> &RelationKindInfo { + match self + .resource_indices + .get(&layout.type_id().unwrap()) + .copied() + { + Some(kind) => &self.kinds[kind.0], + None => self.new_resource_kind(layout).unwrap(), + } } #[inline] - fn get_or_insert_resource_with( - &mut self, - type_id: TypeId, - func: impl FnOnce() -> TypeInfo, - ) -> ComponentId { - let components = &mut self.components; - let index = self.resource_indices.entry(type_id).or_insert_with(|| { - let type_info = func(); - let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), type_info.into())); - index - }); - - ComponentId(*index) + pub fn len(&self) -> usize { + self.kinds.len() } #[inline] - pub(crate) fn get_or_insert_with( - &mut self, - type_id: TypeId, - func: impl FnOnce() -> TypeInfo, - ) -> ComponentId { - let components = &mut self.components; - let index = self.indices.entry(type_id).or_insert_with(|| { - let type_info = func(); - let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), type_info.into())); - index - }); - - ComponentId(*index) + pub fn is_empty(&self) -> bool { + self.kinds.is_empty() } } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index a6fdbced0894d..1395448b7098e 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -22,7 +22,10 @@ pub mod prelude { change_detection::DetectChanges, entity::Entity, event::{EventReader, EventWriter}, - query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without}, + query::{ + Added, ChangeTrackers, Changed, InData, InFilter, InTuple, Or, QueryRelationFilter, + QueryState, Relation, RelationFilter, With, WithBundle, Without, + }, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping, @@ -41,7 +44,7 @@ mod tests { use crate as bevy_ecs; use crate::{ bundle::Bundle, - component::{Component, ComponentDescriptor, ComponentId, StorageType, TypeInfo}, + component::{Component, ComponentDescriptor, StorageType, TypeInfo}, entity::Entity, query::{ Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery, @@ -882,12 +885,13 @@ mod tests { world.insert_resource(123); let resource_id = world .components() - .get_resource_id(TypeId::of::()) - .unwrap(); + .get_resource_kind(TypeId::of::()) + .unwrap() + .id(); let archetype_component_id = world .archetypes() .resource() - .get_archetype_component_id(resource_id) + .get_archetype_component_id(resource_id, None) .unwrap(); assert_eq!(*world.get_resource::().expect("resource exists"), 123); @@ -945,8 +949,9 @@ mod tests { let current_resource_id = world .components() - .get_resource_id(TypeId::of::()) - .unwrap(); + .get_resource_kind(TypeId::of::()) + .unwrap() + .id(); assert_eq!( resource_id, current_resource_id, "resource id does not change after removing / re-adding" @@ -955,7 +960,7 @@ mod tests { let current_archetype_component_id = world .archetypes() .resource() - .get_archetype_component_id(current_resource_id) + .get_archetype_component_id(current_resource_id, None) .unwrap(); assert_eq!( @@ -1156,9 +1161,17 @@ mod tests { let mut world = World::new(); let query = world.query_filtered::<&mut i32, Changed>(); - let mut expected = FilteredAccess::::default(); - let i32_id = world.components.get_id(TypeId::of::()).unwrap(); - let f64_id = world.components.get_id(TypeId::of::()).unwrap(); + let mut expected = FilteredAccess::default(); + let i32_id = world + .components + .get_component_kind(TypeId::of::()) + .unwrap() + .id(); + let f64_id = world + .components + .get_component_kind(TypeId::of::()) + .unwrap() + .id(); expected.add_write(i32_id); expected.add_read(f64_id); assert!( diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 2764f897310f2..f354af560899a 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1,13 +1,15 @@ use crate::{ - archetype::{Archetype, ArchetypeComponentId}, + archetype::{Archetype, ArchetypeComponentId, ArchetypeComponentInfo}, change_detection::Ticks, - component::{Component, ComponentId, ComponentTicks, StorageType}, + component::{Component, ComponentDescriptor, ComponentTicks, RelationKindId, StorageType}, entity::Entity, query::{Access, FilteredAccess}, - storage::{ComponentSparseSet, Table, Tables}, + storage::{Column, ComponentSparseSet, SparseSets, Table, Tables}, world::{Mut, World}, }; use bevy_ecs_macros::all_tuples; +use bevy_utils::{HashMap, StableHashMap}; +use smallvec::SmallVec; use std::{ cell::UnsafeCell, marker::PhantomData, @@ -41,13 +43,19 @@ use std::{ /// /// [`Or`]: crate::query::Or pub trait WorldQuery { - type Fetch: for<'a> Fetch<'a, State = Self::State>; + type Fetch: for<'w, 's> Fetch< + 'w, + 's, + State = Self::State, + RelationFilter = ::RelationFilter, + >; type State: FetchState; } -pub trait Fetch<'w>: Sized { +pub trait Fetch<'w, 's>: Sized { type Item; - type State: FetchState; + type State: FetchState; + type RelationFilter: Clone + std::hash::Hash + PartialEq + Eq + Default + Send + Sync + 'static; /// Creates a new instance of this fetch. /// @@ -58,6 +66,7 @@ pub trait Fetch<'w>: Sized { unsafe fn init( world: &World, state: &Self::State, + relation_filter: &Self::RelationFilter, last_change_tick: u32, change_tick: u32, ) -> Self; @@ -76,7 +85,13 @@ pub trait Fetch<'w>: Sized { /// /// `archetype` and `tables` must be from the [`World`] [`Fetch::init`] was called on. `state` must /// be the [Self::State] this was initialized with. - unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables); + unsafe fn set_archetype( + &mut self, + state: &Self::State, + relation_filter: &Self::RelationFilter, + archetype: &Archetype, + tables: &Tables, + ); /// Adjusts internal state to account for the next [`Table`]. This will always be called on tables /// that match this [`Fetch`]. @@ -85,7 +100,12 @@ pub trait Fetch<'w>: Sized { /// /// `table` must be from the [`World`] [`Fetch::init`] was called on. `state` must be the /// [Self::State] this was initialized with. - unsafe fn set_table(&mut self, state: &Self::State, table: &Table); + unsafe fn set_table( + &mut self, + state: &Self::State, + relation_filter: &Self::RelationFilter, + table: &Table, + ); /// Fetch [`Self::Item`] for the given `archetype_index` in the current [`Archetype`]. This must /// always be called after [`Fetch::set_archetype`] with an `archetype_index` in the range of @@ -117,15 +137,22 @@ pub trait Fetch<'w>: Sized { /// [`FetchState::matches_archetype`], [`FetchState::matches_table`], [`Fetch::archetype_fetch`], and /// [`Fetch::table_fetch`]. pub unsafe trait FetchState: Send + Sync + Sized { + type RelationFilter: Clone + std::hash::Hash + PartialEq + Eq + Default + Send + Sync + 'static; + fn init(world: &mut World) -> Self; - fn update_component_access(&self, access: &mut FilteredAccess); + fn update_component_access(&self, access: &mut FilteredAccess); fn update_archetype_component_access( &self, archetype: &Archetype, access: &mut Access, ); - fn matches_archetype(&self, archetype: &Archetype) -> bool; - fn matches_table(&self, table: &Table) -> bool; + fn matches_archetype( + &self, + archetype: &Archetype, + relation_filter: &Self::RelationFilter, + ) -> bool; + fn matches_table(&self, table: &Table, relation_filter: &Self::RelationFilter) -> bool; + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter); } /// A fetch that is read only. This must only be implemented for read-only fetches. @@ -149,11 +176,13 @@ pub struct EntityState; // SAFETY: no component or archetype access unsafe impl FetchState for EntityState { + type RelationFilter = (); + fn init(_world: &mut World) -> Self { Self } - fn update_component_access(&self, _access: &mut FilteredAccess) {} + fn update_component_access(&self, _access: &mut FilteredAccess) {} fn update_archetype_component_access( &self, @@ -163,19 +192,26 @@ unsafe impl FetchState for EntityState { } #[inline] - fn matches_archetype(&self, _archetype: &Archetype) -> bool { + fn matches_archetype( + &self, + _archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { true } #[inline] - fn matches_table(&self, _table: &Table) -> bool { + fn matches_table(&self, _table: &Table, _relation_filter: &Self::RelationFilter) -> bool { true } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } -impl<'w> Fetch<'w> for EntityFetch { +impl<'w, 's> Fetch<'w, 's> for EntityFetch { type Item = Entity; type State = EntityState; + type RelationFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -185,6 +221,7 @@ impl<'w> Fetch<'w> for EntityFetch { unsafe fn init( _world: &World, _state: &Self::State, + _relation_filter: &Self::RelationFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -197,6 +234,7 @@ impl<'w> Fetch<'w> for EntityFetch { unsafe fn set_archetype( &mut self, _state: &Self::State, + _relation_filter: &Self::RelationFilter, archetype: &Archetype, _tables: &Tables, ) { @@ -204,7 +242,12 @@ impl<'w> Fetch<'w> for EntityFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, table: &Table) { + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + table: &Table, + ) { self.entities = table.entities().as_ptr(); } @@ -226,7 +269,7 @@ impl WorldQuery for &T { /// The [`FetchState`] of `&T`. pub struct ReadState { - component_id: ComponentId, + component_id: RelationKindId, storage_type: StorageType, marker: PhantomData, } @@ -234,16 +277,20 @@ pub struct ReadState { // SAFETY: component access and archetype component access are properly updated to reflect that T is // read unsafe impl FetchState for ReadState { + type RelationFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); ReadState { component_id: component_info.id(), - storage_type: component_info.storage_type(), + storage_type: component_info.data_layout().storage_type(), marker: PhantomData, } } - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { if access.access().has_write(self.component_id) { panic!("&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", std::any::type_name::()); @@ -257,19 +304,25 @@ unsafe impl FetchState for ReadState { access: &mut Access, ) { if let Some(archetype_component_id) = - archetype.get_archetype_component_id(self.component_id) + archetype.get_archetype_component_id(self.component_id, None) { access.add_read(archetype_component_id); } } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - archetype.contains(self.component_id) + fn matches_archetype( + &self, + archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { + archetype.contains(self.component_id, None) } - fn matches_table(&self, table: &Table) -> bool { - table.has_column(self.component_id) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } /// The [`Fetch`] of `&T`. @@ -296,9 +349,10 @@ impl Clone for ReadFetch { /// SAFETY: access is read only unsafe impl ReadOnlyFetch for ReadFetch {} -impl<'w, T: Component> Fetch<'w> for ReadFetch { +impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { type Item = &'w T; type State = ReadState; + type RelationFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -311,6 +365,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { unsafe fn init( world: &World, state: &Self::State, + _relation_filter: &Self::RelationFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -325,7 +380,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id) + .get(state.component_id, None) .unwrap(); } value @@ -335,6 +390,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + _relation_filter: &Self::RelationFilter, archetype: &Archetype, tables: &Tables, ) { @@ -342,7 +398,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let column = tables[archetype.table_id()] - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap(); self.table_components = column.get_data_ptr().cast::(); } @@ -351,9 +407,14 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch { } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + unsafe fn set_table( + &mut self, + state: &Self::State, + _relation_filter: &Self::RelationFilter, + table: &Table, + ) { self.table_components = table - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap() .get_data_ptr() .cast::(); @@ -413,7 +474,7 @@ impl Clone for WriteFetch { /// The [`FetchState`] of `&mut T`. pub struct WriteState { - component_id: ComponentId, + component_id: RelationKindId, storage_type: StorageType, marker: PhantomData, } @@ -421,16 +482,20 @@ pub struct WriteState { // SAFETY: component access and archetype component access are properly updated to reflect that T is // written unsafe impl FetchState for WriteState { + type RelationFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); WriteState { component_id: component_info.id(), - storage_type: component_info.storage_type(), + storage_type: component_info.data_layout().storage_type(), marker: PhantomData, } } - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { if access.access().has_read(self.component_id) { panic!("&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", std::any::type_name::()); @@ -444,24 +509,31 @@ unsafe impl FetchState for WriteState { access: &mut Access, ) { if let Some(archetype_component_id) = - archetype.get_archetype_component_id(self.component_id) + archetype.get_archetype_component_id(self.component_id, None) { access.add_write(archetype_component_id); } } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - archetype.contains(self.component_id) + fn matches_archetype( + &self, + archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { + archetype.contains(self.component_id, None) } - fn matches_table(&self, table: &Table) -> bool { - table.has_column(self.component_id) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } -impl<'w, T: Component> Fetch<'w> for WriteFetch { +impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { type Item = Mut<'w, T>; type State = WriteState; + type RelationFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -474,6 +546,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { unsafe fn init( world: &World, state: &Self::State, + _relation_filter: &Self::RelationFilter, last_change_tick: u32, change_tick: u32, ) -> Self { @@ -491,7 +564,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id) + .get(state.component_id, None) .unwrap(); } value @@ -501,6 +574,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + _relation_filter: &Self::RelationFilter, archetype: &Archetype, tables: &Tables, ) { @@ -508,7 +582,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let column = tables[archetype.table_id()] - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap(); self.table_components = column.get_data_ptr().cast::(); self.table_ticks = column.get_ticks_ptr(); @@ -518,8 +592,13 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { - let column = table.get_column(state.component_id).unwrap(); + unsafe fn set_table( + &mut self, + state: &Self::State, + _relation_filter: &Self::RelationFilter, + table: &Table, + ) { + let column = table.get_column(state.component_id, None).unwrap(); self.table_components = column.get_data_ptr().cast::(); self.table_ticks = column.get_ticks_ptr(); } @@ -567,6 +646,625 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch { } } +#[derive(Debug)] +pub enum Either { + T(T), + U(U), +} + +pub struct Relation(std::marker::PhantomData, [u8]); + +impl WorldQuery for &Relation { + type Fetch = ReadRelationFetch; + type State = ReadRelationState; +} + +pub struct ReadRelationState { + p: PhantomData, + relation_kind: RelationKindId, + storage_type: StorageType, +} + +unsafe impl FetchState for ReadRelationState { + type RelationFilter = smallvec::SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let kind_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); + + Self { + p: PhantomData, + relation_kind: kind_info.id(), + storage_type: kind_info.data_layout().storage_type(), + } + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + if access.access().has_write(self.relation_kind) { + panic!("&Relation<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::()); + } + access.add_read(self.relation_kind); + } + + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + if self.matches_archetype(archetype, &Default::default()) { + let targets = archetype.relations.get(self.relation_kind).unwrap(); + for id in targets.values() { + access.add_read(id.archetype_component_id); + } + } + } + + fn matches_archetype( + &self, + archetype: &Archetype, + relation_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + if archetype.relations.get(self.relation_kind).is_none() { + return false; + } + relation_filter + .iter() + .all(|target| archetype.contains(self.relation_kind, Some(*target))) + } + + fn matches_table(&self, table: &Table, relation_filter: &SmallVec<[Entity; 4]>) -> bool { + if table.relation_columns.get(self.relation_kind).is_none() { + return false; + } + relation_filter + .iter() + .all(|target| table.has_column(self.relation_kind, Some(*target))) + } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + relation_filter.sort(); + relation_filter.dedup(); + } +} + +pub struct ReadRelationFetch { + relation_kind: RelationKindId, + relation_filter_ptr: *const [Entity], + + table_ptr: *const Table, + archetype_ptr: *const Archetype, + entity_table_rows: *const [usize], + entities: *const [Entity], + sparse_sets: *const SparseSets, + + storage_type: StorageType, + p: PhantomData, +} + +unsafe impl ReadOnlyFetch for ReadRelationFetch {} + +#[derive(Debug)] +pub struct TableRelationAccess<'w, 's, T: Component> { + current_idx: usize, + columns: &'w StableHashMap, + iter: + Either, std::slice::Iter<'s, Entity>>, + p: PhantomData<&'w T>, +} + +#[derive(Debug)] +pub struct SparseRelationAccess<'w, 's, T: Component> { + current_entity: Entity, + sparse_sets: &'w HashMap, + iter: Either< + std::collections::hash_map::Keys<'w, Entity, ArchetypeComponentInfo>, + std::slice::Iter<'s, Entity>, + >, + p: PhantomData<&'w T>, +} + +// We split these out to separate structs so that the fields are private +#[derive(Debug)] +pub enum RelationAccess<'w, 's, T: Component> { + Table(TableRelationAccess<'w, 's, T>), + Sparse(SparseRelationAccess<'w, 's, T>), +} + +impl<'w, 's, T: Component> RelationAccess<'w, 's, T> { + pub fn single(&mut self) -> ::Item { + let ret = self.next().unwrap(); + assert!(matches!(self.next(), None)); + ret + } +} + +impl<'w, 's, T: Component> Iterator for RelationAccess<'w, 's, T> { + type Item = (Entity, &'w T); + + fn next(&mut self) -> Option { + match self { + Self::Table(TableRelationAccess { + current_idx, + columns, + iter, + .. + }) => match iter { + Either::T(col_iter) => unsafe { + let (target, col) = col_iter.next()?; + let ptr = col.get_data_unchecked(*current_idx) as *mut T; + Some((*target, &*ptr)) + }, + Either::U(target_iter) => unsafe { + // SAFETY: we remove duplicate target filters in `ReadRelationState::deduplicate_targets` + // so this will not lead to aliasing borrows if users insert two identical target filters + let target = target_iter.next()?; + let col = columns.get(target).unwrap(); + let ptr = col.get_data_unchecked(*current_idx) as *mut T; + Some((*target, &*ptr)) + }, + }, + Self::Sparse(SparseRelationAccess { + current_entity, + sparse_sets, + iter, + .. + }) => { + let target = match iter { + Either::T(target_iter) => target_iter.next()?, + Either::U(target_iter) => target_iter.next()?, + }; + + // SAFETY: we remove duplicate target filters in `ReadRelationState::deduplicate_targets` + // so this will not lead to aliasing borrows if users insert two identical target filters + let set = sparse_sets.get(target).unwrap(); + let ptr = set.get(*current_entity).unwrap() as *mut T; + Some((*target, unsafe { &*ptr })) + } + } + } +} + +impl<'w, 's, T: Component> Fetch<'w, 's> for ReadRelationFetch { + type Item = RelationAccess<'w, 's, T>; + type State = ReadRelationState; + type RelationFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + world: &World, + state: &Self::State, + relation_filter: &Self::RelationFilter, + _last_change_tick: u32, + _change_tick: u32, + ) -> Self { + Self { + relation_kind: state.relation_kind, + relation_filter_ptr: relation_filter.as_slice(), + + table_ptr: 0x0 as _, + archetype_ptr: 0x0 as _, + entity_table_rows: &[], + entities: &[], + sparse_sets: &world.storages.sparse_sets, + + storage_type: state.storage_type, + p: PhantomData, + } + } + + fn is_dense(&self) -> bool { + match self.storage_type { + StorageType::Table => true, + StorageType::SparseSet => false, + } + } + + unsafe fn set_archetype( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + archetype: &Archetype, + tables: &Tables, + ) { + self.entity_table_rows = archetype.entity_table_rows(); + self.archetype_ptr = archetype; + self.table_ptr = &tables[archetype.table_id()]; + self.entities = archetype.entities(); + } + + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + table: &Table, + ) { + self.table_ptr = table; + } + + unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + match self.storage_type { + StorageType::Table => { + let table_row = (&*self.entity_table_rows)[archetype_index]; + self.table_fetch(table_row) + } + StorageType::SparseSet => { + let target_filters = &*self.relation_filter_ptr; + let sparse_sets = &*self.sparse_sets; + let archetype = &*self.archetype_ptr; + + let iter = match target_filters.len() { + 0 => Either::T(archetype.relations.get(self.relation_kind).unwrap().keys()), + _ => Either::U(target_filters.iter()), + }; + + RelationAccess::Sparse(SparseRelationAccess { + current_entity: (&*self.entities)[archetype_index], + sparse_sets: sparse_sets + .get_sets_of_relation_kind(self.relation_kind) + .unwrap(), + iter, + p: PhantomData, + }) + } + } + } + + unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { + // FIXME(Relationships) store a ptr to `table.relation_columns.get(self.relation_kind)` instead of this + let table = &*self.table_ptr; + + let target_filters = &*self.relation_filter_ptr; + let iter = match target_filters.len() { + 0 => Either::T( + table + .relation_columns + .get(self.relation_kind) + .unwrap() + .iter(), + ), + _ => Either::U(target_filters.iter()), + }; + + RelationAccess::Table(TableRelationAccess { + columns: table.relation_columns.get(self.relation_kind).unwrap(), + current_idx: table_row, + iter, + p: PhantomData, + }) + } +} + +impl WorldQuery for &mut Relation { + type Fetch = WriteRelationFetch; + type State = WriteRelationState; +} + +pub struct WriteRelationState { + p: PhantomData, + relation_kind: RelationKindId, + storage_type: StorageType, +} + +unsafe impl FetchState for WriteRelationState { + type RelationFilter = smallvec::SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let kind_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); + + Self { + p: PhantomData, + relation_kind: kind_info.id(), + storage_type: kind_info.data_layout().storage_type(), + } + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + if access.access().has_read(self.relation_kind) { + panic!("&mut Relation<{}> conflicts with a previous access in this query. Mutable access must be exclusive.", + std::any::type_name::()); + } + access.add_write(self.relation_kind); + } + + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + if self.matches_archetype(archetype, &Default::default()) { + let targets = archetype.relations.get(self.relation_kind).unwrap(); + for id in targets.values() { + access.add_write(id.archetype_component_id); + } + } + } + + fn matches_archetype( + &self, + archetype: &Archetype, + relation_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + if archetype.relations.get(self.relation_kind).is_none() { + return false; + } + relation_filter + .iter() + .all(|target| archetype.contains(self.relation_kind, Some(*target))) + } + + fn matches_table(&self, table: &Table, relation_filter: &SmallVec<[Entity; 4]>) -> bool { + if table.relation_columns.get(self.relation_kind).is_none() { + return false; + } + relation_filter + .iter() + .all(|target| table.has_column(self.relation_kind, Some(*target))) + } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + relation_filter.sort(); + relation_filter.dedup(); + } +} + +pub struct WriteRelationFetch { + relation_kind: RelationKindId, + relation_filter_ptr: *const [Entity], + last_change_tick: u32, + change_tick: u32, + + table_ptr: *const Table, + archetype_ptr: *const Archetype, + entity_table_rows: *const [usize], + entities: *const [Entity], + sparse_sets: *const SparseSets, + + storage_type: StorageType, + p: PhantomData, +} + +#[derive(Debug)] +pub struct TableRelationAccessMut<'w, 's, T: Component> { + last_change_tick: u32, + change_tick: u32, + inner: TableRelationAccess<'w, 's, T>, +} + +#[derive(Debug)] +pub struct SparseRelationAccessMut<'w, 's, T: Component> { + last_change_tick: u32, + change_tick: u32, + inner: SparseRelationAccess<'w, 's, T>, +} + +#[derive(Debug)] +pub enum RelationAccessMut<'w, 's, T: Component> { + Table(TableRelationAccessMut<'w, 's, T>), + Sparse(SparseRelationAccessMut<'w, 's, T>), +} + +impl<'w, 's, T: Component> RelationAccessMut<'w, 's, T> { + pub fn single(&mut self) -> ::Item { + let ret = self.next().unwrap(); + assert!(matches!(self.next(), None)); + ret + } +} + +impl<'w, 's, T: Component> Iterator for RelationAccessMut<'w, 's, T> { + type Item = (Entity, Mut<'w, T>); + + fn next(&mut self) -> Option { + match self { + Self::Table(TableRelationAccessMut { + inner: + TableRelationAccess { + current_idx, + columns, + iter, + .. + }, + last_change_tick, + change_tick, + }) => match iter { + Either::T(col_iter) => unsafe { + let (target, col) = col_iter.next()?; + let ptr = col.get_data_unchecked(*current_idx) as *mut T; + Some(( + *target, + Mut { + value: &mut *ptr, + // FIXME(Relationships) once we implement Changed> we should test + // that this works, until then I have no idea if i've hooked up all the ticks + // stuff correctly... Oh well.... + ticks: Ticks { + component_ticks: &mut *col + .get_ticks_mut_ptr_unchecked(*current_idx), + last_change_tick: *last_change_tick, + change_tick: *change_tick, + }, + }, + )) + }, + Either::U(target_iter) => unsafe { + // SAFETY: we remove duplicate target filters in `WriteRelationState::deduplicate_targets` + // so this will not lead to aliasing borrows if users insert two identical target filters + let target = target_iter.next()?; + let col = columns.get(target).unwrap(); + let ptr = col.get_data_unchecked(*current_idx) as *mut T; + Some(( + *target, + Mut { + value: &mut *ptr, + ticks: Ticks { + component_ticks: &mut *col + .get_ticks_mut_ptr_unchecked(*current_idx), + last_change_tick: *last_change_tick, + change_tick: *change_tick, + }, + }, + )) + }, + }, + Self::Sparse(SparseRelationAccessMut { + inner: + SparseRelationAccess { + current_entity, + sparse_sets, + iter, + .. + }, + last_change_tick, + change_tick, + }) => unsafe { + let target = match iter { + Either::T(target_iter) => target_iter.next()?, + Either::U(target_iter) => target_iter.next()?, + }; + + // SAFETY: we remove duplicate target filters in `WriteRelationState::deduplicate_targets` + // so this will not lead to aliasing borrows if users insert two identical target filters + let set = sparse_sets.get(&target).unwrap(); + let (ptr, ticks) = set.get_with_ticks(*current_entity).unwrap(); + let ptr = ptr as *mut T; + + Some(( + *target, + Mut { + value: &mut *ptr, + ticks: Ticks { + component_ticks: &mut *ticks, + last_change_tick: *last_change_tick, + change_tick: *change_tick, + }, + }, + )) + }, + } + } +} + +impl<'w, 's, T: Component> Fetch<'w, 's> for WriteRelationFetch { + type Item = RelationAccessMut<'w, 's, T>; + type State = WriteRelationState; + type RelationFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + world: &World, + state: &Self::State, + relation_filter: &Self::RelationFilter, + last_change_tick: u32, + change_tick: u32, + ) -> Self { + Self { + relation_kind: state.relation_kind, + relation_filter_ptr: relation_filter.as_slice(), + last_change_tick, + change_tick, + + table_ptr: 0x0 as _, + archetype_ptr: 0x0 as _, + entity_table_rows: &[], + entities: &[], + sparse_sets: &world.storages.sparse_sets, + + storage_type: state.storage_type, + p: PhantomData, + } + } + + fn is_dense(&self) -> bool { + match self.storage_type { + StorageType::Table => true, + StorageType::SparseSet => false, + } + } + + unsafe fn set_archetype( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + archetype: &Archetype, + tables: &Tables, + ) { + self.entity_table_rows = archetype.entity_table_rows(); + self.archetype_ptr = archetype; + self.table_ptr = &tables[archetype.table_id()]; + self.entities = archetype.entities(); + } + + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + table: &Table, + ) { + self.table_ptr = table; + } + + unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + match self.storage_type { + StorageType::Table => { + let table_row = (&*self.entity_table_rows)[archetype_index]; + self.table_fetch(table_row) + } + StorageType::SparseSet => { + let target_filters = &*self.relation_filter_ptr; + let sparse_sets = &*self.sparse_sets; + let archetype = &*self.archetype_ptr; + + let iter = match target_filters.len() { + 0 => Either::T(archetype.relations.get(self.relation_kind).unwrap().keys()), + _ => Either::U(target_filters.iter()), + }; + + RelationAccessMut::Sparse(SparseRelationAccessMut { + inner: SparseRelationAccess { + current_entity: (&*self.entities)[archetype_index], + sparse_sets: sparse_sets + .get_sets_of_relation_kind(self.relation_kind) + .unwrap(), + iter, + p: PhantomData, + }, + change_tick: self.change_tick, + last_change_tick: self.last_change_tick, + }) + } + } + } + + unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { + let table = &*self.table_ptr; + + let target_filters = &*self.relation_filter_ptr; + let iter = match target_filters.len() { + 0 => Either::T( + table + .relation_columns + .get(self.relation_kind) + .unwrap() + .iter(), + ), + _ => Either::U(target_filters.iter()), + }; + + RelationAccessMut::Table(TableRelationAccessMut { + inner: TableRelationAccess { + columns: table.relation_columns.get(self.relation_kind).unwrap(), + current_idx: table_row, + iter, + p: PhantomData, + }, + change_tick: self.change_tick, + last_change_tick: self.last_change_tick, + }) + } +} + impl WorldQuery for Option { type Fetch = OptionFetch; type State = OptionState; @@ -589,13 +1287,15 @@ pub struct OptionState { // SAFETY: component access and archetype component access are properly updated according to the // internal Fetch unsafe impl FetchState for OptionState { + type RelationFilter = T::RelationFilter; + fn init(world: &mut World) -> Self { Self { state: T::init(world), } } - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { self.state.update_component_access(access); } @@ -604,24 +1304,33 @@ unsafe impl FetchState for OptionState { archetype: &Archetype, access: &mut Access, ) { - if self.state.matches_archetype(archetype) { + if self.state.matches_archetype(archetype, &Default::default()) { self.state .update_archetype_component_access(archetype, access) } } - fn matches_archetype(&self, _archetype: &Archetype) -> bool { + fn matches_archetype( + &self, + _archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { true } - fn matches_table(&self, _table: &Table) -> bool { + fn matches_table(&self, _table: &Table, _relation_filter: &Self::RelationFilter) -> bool { true } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + T::deduplicate_targets(relation_filter); + } } -impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { +impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch { type Item = Option; type State = OptionState; + type RelationFilter = T::RelationFilter; #[inline] fn is_dense(&self) -> bool { @@ -631,11 +1340,18 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { unsafe fn init( world: &World, state: &Self::State, + relation_filter: &Self::RelationFilter, last_change_tick: u32, change_tick: u32, ) -> Self { Self { - fetch: T::init(world, &state.state, last_change_tick, change_tick), + fetch: T::init( + world, + &state.state, + &relation_filter, + last_change_tick, + change_tick, + ), matches: false, } } @@ -644,20 +1360,27 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + relation_filter: &Self::RelationFilter, archetype: &Archetype, tables: &Tables, ) { - self.matches = state.state.matches_archetype(archetype); + self.matches = state.state.matches_archetype(archetype, relation_filter); if self.matches { - self.fetch.set_archetype(&state.state, archetype, tables); + self.fetch + .set_archetype(&state.state, relation_filter, archetype, tables); } } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { - self.matches = state.state.matches_table(table); + unsafe fn set_table( + &mut self, + state: &Self::State, + relation_filter: &Self::RelationFilter, + table: &Table, + ) { + self.matches = state.state.matches_table(table, relation_filter); if self.matches { - self.fetch.set_table(&state.state, table); + self.fetch.set_table(&state.state, relation_filter, table); } } @@ -749,7 +1472,7 @@ impl WorldQuery for ChangeTrackers { /// The [`FetchState`] of [`ChangeTrackers`]. pub struct ChangeTrackersState { - component_id: ComponentId, + component_id: RelationKindId, storage_type: StorageType, marker: PhantomData, } @@ -757,16 +1480,21 @@ pub struct ChangeTrackersState { // SAFETY: component access and archetype component access are properly updated to reflect that T is // read unsafe impl FetchState for ChangeTrackersState { + type RelationFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); + Self { component_id: component_info.id(), - storage_type: component_info.storage_type(), + storage_type: component_info.data_layout().storage_type(), marker: PhantomData, } } - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { if access.access().has_write(self.component_id) { panic!("ChangeTrackers<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", std::any::type_name::()); @@ -780,19 +1508,25 @@ unsafe impl FetchState for ChangeTrackersState { access: &mut Access, ) { if let Some(archetype_component_id) = - archetype.get_archetype_component_id(self.component_id) + archetype.get_archetype_component_id(self.component_id, None) { access.add_read(archetype_component_id); } } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - archetype.contains(self.component_id) + fn matches_archetype( + &self, + archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { + archetype.contains(self.component_id, None) } - fn matches_table(&self, table: &Table) -> bool { - table.has_column(self.component_id) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } /// The [`Fetch`] of [`ChangeTrackers`]. @@ -810,9 +1544,10 @@ pub struct ChangeTrackersFetch { /// SAFETY: access is read only unsafe impl ReadOnlyFetch for ChangeTrackersFetch {} -impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { +impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { type Item = ChangeTrackers; type State = ChangeTrackersState; + type RelationFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -825,6 +1560,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { unsafe fn init( world: &World, state: &Self::State, + _relation_filter: &Self::RelationFilter, last_change_tick: u32, change_tick: u32, ) -> Self { @@ -842,7 +1578,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id) + .get(state.component_id, None) .unwrap(); } value @@ -852,6 +1588,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + _relation_filter: &Self::RelationFilter, archetype: &Archetype, tables: &Tables, ) { @@ -859,7 +1596,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let column = tables[archetype.table_id()] - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap(); self.table_ticks = column.get_ticks_const_ptr(); } @@ -868,9 +1605,14 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + unsafe fn set_table( + &mut self, + state: &Self::State, + _relation_filter: &Self::RelationFilter, + table: &Table, + ) { self.table_ticks = table - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap() .get_ticks_const_ptr(); } @@ -911,15 +1653,17 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch { } macro_rules! impl_tuple_fetch { - ($(($name: ident, $state: ident)),*) => { + ($(($name: ident, $state: ident, $relation_filter: ident)),*) => { #[allow(non_snake_case)] - impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) { + impl<'w, 's, $($name: Fetch<'w, 's>),*> Fetch<'w, 's> for ($($name,)*) { type Item = ($($name::Item,)*); type State = ($($name::State,)*); + type RelationFilter = ($($name::RelationFilter,)*); - unsafe fn init(_world: &World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self { + unsafe fn init(_world: &World, state: &Self::State, relation_filter: &Self::RelationFilter, _last_change_tick: u32, _change_tick: u32) -> Self { let ($($name,)*) = state; - ($($name::init(_world, $name, _last_change_tick, _change_tick),)*) + let ($($relation_filter,)*) = relation_filter; + ($($name::init(_world, $name, $relation_filter, _last_change_tick, _change_tick),)*) } @@ -930,17 +1674,19 @@ macro_rules! impl_tuple_fetch { } #[inline] - unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) { + unsafe fn set_archetype(&mut self, _state: &Self::State, relation_filter: &Self::RelationFilter, _archetype: &Archetype, _tables: &Tables) { let ($($name,)*) = self; let ($($state,)*) = _state; - $($name.set_archetype($state, _archetype, _tables);)* + let ($($relation_filter,)*) = relation_filter; + $($name.set_archetype($state, $relation_filter, _archetype, _tables);)* } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) { + unsafe fn set_table(&mut self, _state: &Self::State, _relation_filter: &Self::RelationFilter, _table: &Table) { let ($($name,)*) = self; let ($($state,)*) = _state; - $($name.set_table($state, _table);)* + let ($($relation_filter,)*) = _relation_filter; + $($name.set_table($state, $relation_filter, _table);)* } #[inline] @@ -959,11 +1705,13 @@ macro_rules! impl_tuple_fetch { // SAFETY: update_component_access and update_archetype_component_access are called for each item in the tuple #[allow(non_snake_case)] unsafe impl<$($name: FetchState),*> FetchState for ($($name,)*) { + type RelationFilter = ($($name::RelationFilter,)*); + fn init(_world: &mut World) -> Self { ($($name::init(_world),)*) } - fn update_component_access(&self, _access: &mut FilteredAccess) { + fn update_component_access(&self, _access: &mut FilteredAccess) { let ($($name,)*) = self; $($name.update_component_access(_access);)* } @@ -973,14 +1721,21 @@ macro_rules! impl_tuple_fetch { $($name.update_archetype_component_access(_archetype, _access);)* } - fn matches_archetype(&self, _archetype: &Archetype) -> bool { + fn matches_archetype(&self, _archetype: &Archetype, _relation_filter: &Self::RelationFilter) -> bool { let ($($name,)*) = self; - true $(&& $name.matches_archetype(_archetype))* + let ($($relation_filter,)*) = _relation_filter; + true $(&& $name.matches_archetype(_archetype, $relation_filter))* } - fn matches_table(&self, _table: &Table) -> bool { + fn matches_table(&self, _table: &Table, _relation_filter: &Self::RelationFilter) -> bool { let ($($name,)*) = self; - true $(&& $name.matches_table(_table))* + let ($($relation_filter,)*) = _relation_filter; + true $(&& $name.matches_table(_table, $relation_filter))* + } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + let ($($name,)*) = relation_filter; + $($name::deduplicate_targets($name);)* } } @@ -995,4 +1750,4 @@ macro_rules! impl_tuple_fetch { }; } -all_tuples!(impl_tuple_fetch, 0, 15, F, S); +all_tuples!(impl_tuple_fetch, 0, 11, F, S, R); diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index cd3d2c9c801c9..c38995e602928 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -1,18 +1,32 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, bundle::Bundle, - component::{Component, ComponentId, ComponentTicks, StorageType}, + component::{Component, ComponentDescriptor, ComponentTicks, RelationKindId, StorageType}, entity::Entity, query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, Tables}, world::World, }; use bevy_ecs_macros::all_tuples; +use smallvec::SmallVec; use std::{cell::UnsafeCell, marker::PhantomData, ptr}; +use super::Relation; + +// TODO: uncomment this and use as shorthand (remove where F::Fetch: FilterFetch everywhere) when +// this bug is fixed in Rust 1.51: https://github.com/rust-lang/rust/pull/81671 +// pub trait QueryFilter: WorldQuery +// where +// Self::Fetch: FilterFetch, +// { +// } + +// impl QueryFilter for T where T::Fetch: FilterFetch { +// } + /// Extension trait for [`Fetch`] containing methods used by query filters. /// This trait exists to allow "short circuit" behaviors for relevant query filter fetches. -pub trait FilterFetch: for<'a> Fetch<'a> { +pub trait FilterFetch: for<'w, 's> Fetch<'w, 's> { /// # Safety /// /// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range @@ -28,7 +42,7 @@ pub trait FilterFetch: for<'a> Fetch<'a> { impl FilterFetch for T where - T: for<'a> Fetch<'a, Item = bool>, + T: for<'w, 's> Fetch<'w, 's, Item = bool>, { #[inline] unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool { @@ -66,7 +80,7 @@ where /// } /// # compliment_entity_system.system(); /// ``` -pub struct With(PhantomData); +pub struct With(PhantomData); impl WorldQuery for With { type Fetch = WithFetch; @@ -81,24 +95,28 @@ pub struct WithFetch { /// The [`FetchState`] of [`With`]. pub struct WithState { - component_id: ComponentId, + component_id: RelationKindId, storage_type: StorageType, marker: PhantomData, } // SAFETY: no component access or archetype component access unsafe impl FetchState for WithState { + type RelationFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); Self { component_id: component_info.id(), - storage_type: component_info.storage_type(), + storage_type: component_info.data_layout().storage_type(), marker: PhantomData, } } #[inline] - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { access.add_with(self.component_id); } @@ -110,22 +128,30 @@ unsafe impl FetchState for WithState { ) { } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - archetype.contains(self.component_id) + fn matches_archetype( + &self, + archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { + archetype.contains(self.component_id, None) } - fn matches_table(&self, table: &Table) -> bool { - table.has_column(self.component_id) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } -impl<'a, T: Component> Fetch<'a> for WithFetch { +impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch { type Item = bool; type State = WithState; + type RelationFilter = (); unsafe fn init( _world: &World, state: &Self::State, + _relation_filter: &Self::RelationFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -141,12 +167,19 @@ impl<'a, T: Component> Fetch<'a> for WithFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _table: &Table, + ) { + } #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, + _relation_filter: &Self::RelationFilter, _archetype: &Archetype, _tables: &Tables, ) { @@ -185,7 +218,7 @@ impl<'a, T: Component> Fetch<'a> for WithFetch { /// } /// # no_permit_system.system(); /// ``` -pub struct Without(PhantomData); +pub struct Without(PhantomData>); impl WorldQuery for Without { type Fetch = WithoutFetch; @@ -200,24 +233,28 @@ pub struct WithoutFetch { /// The [`FetchState`] of [`Without`]. pub struct WithoutState { - component_id: ComponentId, + component_id: RelationKindId, storage_type: StorageType, marker: PhantomData, } // SAFETY: no component access or archetype component access unsafe impl FetchState for WithoutState { + type RelationFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); Self { component_id: component_info.id(), - storage_type: component_info.storage_type(), + storage_type: component_info.data_layout().storage_type(), marker: PhantomData, } } #[inline] - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { access.add_without(self.component_id); } @@ -229,22 +266,30 @@ unsafe impl FetchState for WithoutState { ) { } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - !archetype.contains(self.component_id) + fn matches_archetype( + &self, + archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { + !archetype.contains(self.component_id, None) } - fn matches_table(&self, table: &Table) -> bool { - !table.has_column(self.component_id) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + !table.has_column(self.component_id, None) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } -impl<'a, T: Component> Fetch<'a> for WithoutFetch { +impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch { type Item = bool; type State = WithoutState; + type RelationFilter = (); unsafe fn init( _world: &World, state: &Self::State, + _relation_filter: &Self::RelationFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -260,12 +305,19 @@ impl<'a, T: Component> Fetch<'a> for WithoutFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _table: &Table, + ) { + } #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, + _relation_filter: &Self::RelationFilter, _archetype: &Archetype, _tables: &Tables, ) { @@ -282,6 +334,270 @@ impl<'a, T: Component> Fetch<'a> for WithoutFetch { } } +impl WorldQuery for Without> { + type Fetch = WithoutRelationFetch; + type State = WithoutRelationState; +} + +pub struct WithoutRelationState { + storage_type: StorageType, + relation_kind: RelationKindId, + marker: PhantomData, +} + +unsafe impl FetchState for WithoutRelationState { + type RelationFilter = smallvec::SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let kind_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); + + Self { + marker: PhantomData, + relation_kind: kind_info.id(), + storage_type: kind_info.data_layout().storage_type(), + } + } + + fn update_component_access(&self, _access: &mut FilteredAccess) { + // Note: relations dont add a without access as `Query<&mut T, Without>>` + // and `Query<&mut T, With>>` can access the same entities if the targets + // specified for the relations are different - Boxy + } + + fn update_archetype_component_access( + &self, + _archetype: &Archetype, + _access: &mut Access, + ) { + } + + fn matches_archetype( + &self, + archetype: &Archetype, + relation_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + // If there are no relations of this kind then the archetype matches regardless of + // what relation filters we have + if archetype.relations.get(self.relation_kind).is_none() { + return true; + } + // No relation filters means there shouldn't be *any* relations of the kind but we + // already returned true if that is the case which means this archetype doesn't fit + if relation_filter.is_empty() { + return false; + } + relation_filter + .iter() + .all(|&target| !archetype.contains(self.relation_kind, Some(target))) + } + + fn matches_table(&self, table: &Table, relation_filter: &SmallVec<[Entity; 4]>) -> bool { + // If there are no relations of this kind then the table matches regardless of + // what relation filters we have + if table.relation_columns.get(self.relation_kind).is_none() { + return true; + } + // No relation filters means there shouldn't be *any* relations of the kind but we + // already returned true if that is the case which means this table doesn't fit + if relation_filter.is_empty() { + return false; + } + relation_filter + .iter() + .all(|&target| !table.has_column(self.relation_kind, Some(target))) + } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + relation_filter.sort(); + relation_filter.dedup(); + } +} + +pub struct WithoutRelationFetch { + storage_type: StorageType, + marker: PhantomData, +} + +impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutRelationFetch { + type Item = bool; + type State = WithoutRelationState; + type RelationFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + _world: &World, + state: &Self::State, + _relation_filter: &Self::RelationFilter, + _last_change_tick: u32, + _change_tick: u32, + ) -> Self { + Self { + storage_type: state.storage_type, + marker: PhantomData, + } + } + + fn is_dense(&self) -> bool { + match self.storage_type { + StorageType::Table => true, + StorageType::SparseSet => false, + } + } + + #[inline] + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _table: &Table, + ) { + } + + #[inline] + unsafe fn set_archetype( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _archetype: &Archetype, + _tables: &Tables, + ) { + } + + #[inline] + unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> bool { + true + } + + #[inline] + unsafe fn table_fetch(&mut self, _table_row: usize) -> bool { + true + } +} + +impl WorldQuery for With> { + type Fetch = WithRelationFetch; + type State = WithRelationState; +} + +pub struct WithRelationState { + storage_type: StorageType, + relation_kind: RelationKindId, + marker: PhantomData, +} + +unsafe impl FetchState for WithRelationState { + type RelationFilter = SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let kind_info = world + .components + .get_component_kind_or_insert(ComponentDescriptor::new::(StorageType::Table)); + + Self { + marker: PhantomData, + relation_kind: kind_info.id(), + storage_type: kind_info.data_layout().storage_type(), + } + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + access.add_with(self.relation_kind); + } + + fn update_archetype_component_access( + &self, + _archetype: &Archetype, + _access: &mut Access, + ) { + } + + fn matches_archetype( + &self, + archetype: &Archetype, + relation_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + if archetype.relations.get(self.relation_kind).is_none() { + return false; + } + relation_filter + .iter() + .all(|&target| archetype.contains(self.relation_kind, Some(target))) + } + + fn matches_table(&self, table: &Table, relation_filter: &SmallVec<[Entity; 4]>) -> bool { + if table.relation_columns.get(self.relation_kind).is_none() { + return false; + } + relation_filter + .iter() + .all(|&target| table.has_column(self.relation_kind, Some(target))) + } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + relation_filter.sort(); + relation_filter.dedup(); + } +} + +pub struct WithRelationFetch { + storage_type: StorageType, + marker: PhantomData, +} + +impl<'w, 's, T: Component> Fetch<'w, 's> for WithRelationFetch { + type Item = bool; + type State = WithRelationState; + type RelationFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + _world: &World, + state: &Self::State, + _relation_filter: &Self::RelationFilter, + _last_change_tick: u32, + _change_tick: u32, + ) -> Self { + Self { + storage_type: state.storage_type, + marker: PhantomData, + } + } + + #[inline] + fn is_dense(&self) -> bool { + self.storage_type == StorageType::Table + } + + #[inline] + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _table: &Table, + ) { + } + + #[inline] + unsafe fn set_archetype( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _archetype: &Archetype, + _tables: &Tables, + ) { + } + + #[inline] + unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item { + true + } + + #[inline] + unsafe fn table_fetch(&mut self, _table_row: usize) -> bool { + true + } +} + pub struct WithBundle(PhantomData); impl WorldQuery for WithBundle { @@ -295,29 +611,35 @@ pub struct WithBundleFetch { } pub struct WithBundleState { - component_ids: Vec, + relation_kind_ids: Vec<(RelationKindId, Option)>, is_dense: bool, marker: PhantomData, } // SAFETY: no component access or archetype component access unsafe impl FetchState for WithBundleState { + type RelationFilter = (); + fn init(world: &mut World) -> Self { let bundle_info = world.bundles.init_info::(&mut world.components); let components = &world.components; Self { - component_ids: bundle_info.component_ids.clone(), - is_dense: !bundle_info.component_ids.iter().any(|id| unsafe { - components.get_info_unchecked(*id).storage_type() != StorageType::Table + relation_kind_ids: bundle_info.relationship_ids.clone(), + is_dense: bundle_info.relationship_ids.iter().all(|(kind_id, _)| { + components + .get_relation_kind(*kind_id) + .data_layout() + .storage_type() + == StorageType::Table }), marker: PhantomData, } } #[inline] - fn update_component_access(&self, access: &mut FilteredAccess) { - for component_id in self.component_ids.iter().cloned() { - access.add_with(component_id); + fn update_component_access(&self, access: &mut FilteredAccess) { + for (kind_id, _) in self.relation_kind_ids.iter().cloned() { + access.add_with(kind_id); } } @@ -329,22 +651,34 @@ unsafe impl FetchState for WithBundleState { ) { } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - self.component_ids.iter().all(|id| archetype.contains(*id)) + fn matches_archetype( + &self, + archetype: &Archetype, + _relation_filter: &Self::RelationFilter, + ) -> bool { + self.relation_kind_ids + .iter() + .all(|&(kind_id, target)| archetype.contains(kind_id, target)) } - fn matches_table(&self, table: &Table) -> bool { - self.component_ids.iter().all(|id| table.has_column(*id)) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + self.relation_kind_ids + .iter() + .all(|&(kind_id, target)| table.has_column(kind_id, target)) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } -impl<'a, T: Bundle> Fetch<'a> for WithBundleFetch { +impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch { type Item = bool; type State = WithBundleState; + type RelationFilter = (); unsafe fn init( _world: &World, state: &Self::State, + _relation_filter: &Self::RelationFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -360,12 +694,19 @@ impl<'a, T: Bundle> Fetch<'a> for WithBundleFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} + unsafe fn set_table( + &mut self, + _state: &Self::State, + _relation_filter: &Self::RelationFilter, + _table: &Table, + ) { + } #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, + _relation_filter: &Self::RelationFilter, _archetype: &Archetype, _tables: &Tables, ) { @@ -419,7 +760,7 @@ pub struct OrFetch { } macro_rules! impl_query_filter_tuple { - ($(($filter: ident, $state: ident)),*) => { + ($(($filter: ident, $state: ident, $relation_filter: ident)),*) => { #[allow(unused_variables)] #[allow(non_snake_case)] impl<'a, $($filter: FilterFetch),*> FilterFetch for ($($filter,)*) { @@ -446,14 +787,16 @@ macro_rules! impl_query_filter_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] - impl<'a, $($filter: FilterFetch),*> Fetch<'a> for Or<($(OrFetch<$filter>,)*)> { - type State = Or<($(<$filter as Fetch<'a>>::State,)*)>; + impl<'w, 's, $($filter: FilterFetch),*> Fetch<'w, 's> for Or<($(OrFetch<$filter>,)*)> { + type State = Or<($(<$filter as Fetch<'w, 's>>::State,)*)>; type Item = bool; + type RelationFilter = ($(<$filter as Fetch<'w, 's>>::RelationFilter,)*); - unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self { + unsafe fn init(world: &World, state: &Self::State, relation_filter: &Self::RelationFilter, last_change_tick: u32, change_tick: u32) -> Self { let ($($filter,)*) = &state.0; + let ($($relation_filter,)*) = relation_filter; Or(($(OrFetch { - fetch: $filter::init(world, $filter, last_change_tick, change_tick), + fetch: $filter::init(world, $filter, $relation_filter, last_change_tick, change_tick), matches: false, },)*)) } @@ -465,25 +808,27 @@ macro_rules! impl_query_filter_tuple { } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + unsafe fn set_table(&mut self, state: &Self::State, relation_filter: &Self::RelationFilter, table: &Table) { let ($($filter,)*) = &mut self.0; let ($($state,)*) = &state.0; + let ($($relation_filter,)*) = relation_filter; $( - $filter.matches = $state.matches_table(table); + $filter.matches = $state.matches_table(table, $relation_filter); if $filter.matches { - $filter.fetch.set_table($state, table); + $filter.fetch.set_table($state, $relation_filter, table); } )* } #[inline] - unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) { + unsafe fn set_archetype(&mut self, state: &Self::State, relation_filter: &Self::RelationFilter, archetype: &Archetype, tables: &Tables) { let ($($filter,)*) = &mut self.0; let ($($state,)*) = &state.0; + let ($($relation_filter,)*) = relation_filter; $( - $filter.matches = $state.matches_archetype(archetype); + $filter.matches = $state.matches_archetype(archetype, $relation_filter); if $filter.matches { - $filter.fetch.set_archetype($state, archetype, tables); + $filter.fetch.set_archetype($state, $relation_filter, archetype, tables); } )* } @@ -505,11 +850,13 @@ macro_rules! impl_query_filter_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] unsafe impl<$($filter: FetchState),*> FetchState for Or<($($filter,)*)> { + type RelationFilter = ($($filter::RelationFilter,)*); + fn init(world: &mut World) -> Self { Or(($($filter::init(world),)*)) } - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { let ($($filter,)*) = &self.0; $($filter.update_component_access(access);)* } @@ -519,20 +866,27 @@ macro_rules! impl_query_filter_tuple { $($filter.update_archetype_component_access(archetype, access);)* } - fn matches_archetype(&self, archetype: &Archetype) -> bool { + fn matches_archetype(&self, archetype: &Archetype, relation_filter: &Self::RelationFilter) -> bool { let ($($filter,)*) = &self.0; - false $(|| $filter.matches_archetype(archetype))* + let ($($relation_filter,)*) = relation_filter; + false $(|| $filter.matches_archetype(archetype, $relation_filter))* } - fn matches_table(&self, table: &Table) -> bool { + fn matches_table(&self, table: &Table, relation_filter: &Self::RelationFilter,) -> bool { let ($($filter,)*) = &self.0; - false $(|| $filter.matches_table(table))* + let ($($relation_filter,)*) = relation_filter; + false $(|| $filter.matches_table(table, $relation_filter))* + } + + fn deduplicate_targets(relation_filter: &mut Self::RelationFilter) { + let ($($filter,)*) = relation_filter; + $($filter::deduplicate_targets($filter);)* } } }; } -all_tuples!(impl_query_filter_tuple, 0, 15, F, S); +all_tuples!(impl_query_filter_tuple, 0, 11, F, S, R); macro_rules! impl_tick_filter { ( @@ -561,7 +915,7 @@ macro_rules! impl_tick_filter { $(#[$state_meta])* pub struct $state_name { - component_id: ComponentId, + component_id: RelationKindId, storage_type: StorageType, marker: PhantomData, } @@ -574,17 +928,24 @@ macro_rules! impl_tick_filter { // SAFETY: this reads the T component. archetype component access and component access are updated to reflect that unsafe impl FetchState for $state_name { + type RelationFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .get_component_kind_or_insert( + ComponentDescriptor::new::(StorageType::Table) + ); + Self { component_id: component_info.id(), - storage_type: component_info.storage_type(), + storage_type: component_info.data_layout().storage_type(), marker: PhantomData, } } #[inline] - fn update_component_access(&self, access: &mut FilteredAccess) { + fn update_component_access(&self, access: &mut FilteredAccess) { if access.access().has_write(self.component_id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", std::any::type_name::()); @@ -598,25 +959,28 @@ macro_rules! impl_tick_filter { archetype: &Archetype, access: &mut Access, ) { - if let Some(archetype_component_id) = archetype.get_archetype_component_id(self.component_id) { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(self.component_id, None) { access.add_read(archetype_component_id); } } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - archetype.contains(self.component_id) + fn matches_archetype(&self, archetype: &Archetype, _relation_filter: &Self::RelationFilter) -> bool { + archetype.contains(self.component_id, None) } - fn matches_table(&self, table: &Table) -> bool { - table.has_column(self.component_id) + fn matches_table(&self, table: &Table, _relation_filter: &Self::RelationFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_relation_filter: &mut Self::RelationFilter) {} } - impl<'a, T: Component> Fetch<'a> for $fetch_name { + impl<'w, 's, T: Component> Fetch<'w, 's> for $fetch_name { type State = $state_name; type Item = bool; + type RelationFilter = (); - unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self { + unsafe fn init(world: &World, state: &Self::State, _relation_filter: &Self::RelationFilter, last_change_tick: u32, change_tick: u32) -> Self { let mut value = Self { storage_type: state.storage_type, table_ticks: ptr::null::>(), @@ -631,7 +995,7 @@ macro_rules! impl_tick_filter { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id).unwrap(); + .get(state.component_id, None).unwrap(); } value } @@ -641,19 +1005,19 @@ macro_rules! impl_tick_filter { self.storage_type == StorageType::Table } - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + unsafe fn set_table(&mut self, state: &Self::State, _relation_filter: &Self::RelationFilter, table: &Table) { self.table_ticks = table - .get_column(state.component_id).unwrap() + .get_column(state.component_id, None).unwrap() .get_ticks_ptr(); } - unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) { + unsafe fn set_archetype(&mut self, state: &Self::State, _relation_filter: &Self::RelationFilter, archetype: &Archetype, tables: &Tables) { match state.storage_type { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let table = &tables[archetype.table_id()]; self.table_ticks = table - .get_column(state.component_id).unwrap() + .get_column(state.component_id, None).unwrap() .get_ticks_ptr(); } StorageType::SparseSet => self.entities = archetype.entities().as_ptr(), diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 82ddf458cb40f..f5fd1f8652cd9 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -6,6 +6,8 @@ use crate::{ }; use std::mem::MaybeUninit; +use super::QueryAccessCache; + /// An [`Iterator`] over query results of a [`Query`](crate::system::Query). /// /// This struct is created by the [`Query::iter`](crate::system::Query::iter) and @@ -17,6 +19,7 @@ where tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, + query_access_cache: &'s QueryAccessCache, world: &'w World, table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, @@ -45,26 +48,29 @@ where let fetch = ::init( world, &query_state.fetch_state, + &query_state.current_relation_filter.0, last_change_tick, change_tick, ); let filter = ::init( world, &query_state.filter_state, + &query_state.current_relation_filter.1, last_change_tick, change_tick, ); - + let query_access_cache = query_state.current_query_access_cache(); QueryIter { world, query_state, - tables: &world.storages().tables, - archetypes: &world.archetypes, + query_access_cache, is_dense: fetch.is_dense() && filter.is_dense(), fetch, filter, - table_id_iter: query_state.matched_table_ids.iter(), - archetype_id_iter: query_state.matched_archetype_ids.iter(), + tables: &world.storages().tables, + archetypes: &world.archetypes, + table_id_iter: query_access_cache.matched_table_ids.iter(), + archetype_id_iter: query_access_cache.matched_archetype_ids.iter(), current_len: 0, current_index: 0, } @@ -84,7 +90,12 @@ where None => return true, }; let table = &self.tables[*table_id]; - self.filter.set_table(&self.query_state.filter_state, table); + + self.filter.set_table( + &self.query_state.filter_state, + &self.query_state.current_relation_filter.1, + table, + ); self.current_len = table.len(); self.current_index = 0; continue; @@ -107,6 +118,7 @@ where let archetype = &self.archetypes[*archetype_id]; self.filter.set_archetype( &self.query_state.filter_state, + &self.query_state.current_relation_filter.1, archetype, self.tables, ); @@ -131,7 +143,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Iterator for QueryIter<'w, 's, Q, F> where F::Fetch: FilterFetch, { - type Item = >::Item; + type Item = >::Item; // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual @@ -144,8 +156,16 @@ where if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &self.tables[*table_id]; - self.fetch.set_table(&self.query_state.fetch_state, table); - self.filter.set_table(&self.query_state.filter_state, table); + self.fetch.set_table( + &self.query_state.fetch_state, + &self.query_state.current_relation_filter.0, + table, + ); + self.filter.set_table( + &self.query_state.filter_state, + &self.query_state.current_relation_filter.1, + table, + ); self.current_len = table.len(); self.current_index = 0; continue; @@ -157,7 +177,6 @@ where } let item = self.fetch.table_fetch(self.current_index); - self.current_index += 1; return Some(item); } @@ -168,11 +187,13 @@ where let archetype = &self.archetypes[*archetype_id]; self.fetch.set_archetype( &self.query_state.fetch_state, + &self.query_state.current_relation_filter.0, archetype, self.tables, ); self.filter.set_archetype( &self.query_state.filter_state, + &self.query_state.current_relation_filter.1, archetype, self.tables, ); @@ -200,6 +221,7 @@ where fn size_hint(&self) -> (usize, Option) { let max_size = self .query_state + .current_query_access_cache() .matched_archetypes .ones() .map(|index| self.world.archetypes[ArchetypeId::new(index)].len()) @@ -279,7 +301,7 @@ where /// It is always safe for shared access. unsafe fn fetch_next_aliased_unchecked<'a>( &mut self, - ) -> Option<[>::Item; K]> + ) -> Option<[>::Item; K]> where Q::Fetch: Clone, F::Fetch: Clone, @@ -313,7 +335,7 @@ where } // TODO: use MaybeUninit::uninit_array if it stabilizes - let mut values: [MaybeUninit<>::Item>; K] = + let mut values: [MaybeUninit<>::Item>; K] = MaybeUninit::uninit().assume_init(); for (value, cursor) in values.iter_mut().zip(&mut self.cursors) { @@ -321,15 +343,15 @@ where } // TODO: use MaybeUninit::array_assume_init if it stabilizes - let values: [>::Item; K] = - (&values as *const _ as *const [>::Item; K]).read(); + let values: [>::Item; K] = + (&values as *const _ as *const [>::Item; K]).read(); Some(values) } /// Get next combination of queried components #[inline] - pub fn fetch_next(&mut self) -> Option<[>::Item; K]> + pub fn fetch_next(&mut self) -> Option<[>::Item; K]> where Q::Fetch: Clone, F::Fetch: Clone, @@ -350,7 +372,7 @@ where Q::Fetch: Clone + ReadOnlyFetch, F::Fetch: Clone + FilterFetch + ReadOnlyFetch, { - type Item = [>::Item; K]; + type Item = [>::Item; K]; #[inline] fn next(&mut self) -> Option { @@ -368,6 +390,7 @@ where let max_size: usize = self .query_state + .current_query_access_cache() .matched_archetypes .ones() .map(|index| self.world.archetypes[ArchetypeId::new(index)].len()) @@ -396,7 +419,7 @@ where // TODO: add an ArchetypeOnlyFilter that enables us to implement this for filters like With impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> { fn len(&self) -> usize { - self.query_state + self.query_access_cache .matched_archetypes .ones() .map(|index| self.world.archetypes[ArchetypeId::new(index)].len()) @@ -405,6 +428,7 @@ impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> { } struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> { + query_access_cache: &'s QueryAccessCache, table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, fetch: Q::Fetch, @@ -421,6 +445,7 @@ where { fn clone(&self) -> Self { Self { + query_access_cache: self.query_access_cache, table_id_iter: self.table_id_iter.clone(), archetype_id_iter: self.archetype_id_iter.clone(), fetch: self.fetch.clone(), @@ -458,21 +483,25 @@ where let fetch = ::init( world, &query_state.fetch_state, + &query_state.current_relation_filter.0, last_change_tick, change_tick, ); let filter = ::init( world, &query_state.filter_state, + &query_state.current_relation_filter.1, last_change_tick, change_tick, ); + let query_access_cache = query_state.current_query_access_cache(); QueryIterationCursor { + query_access_cache, is_dense: fetch.is_dense() && filter.is_dense(), fetch, filter, - table_id_iter: query_state.matched_table_ids.iter(), - archetype_id_iter: query_state.matched_archetype_ids.iter(), + table_id_iter: query_access_cache.matched_table_ids.iter(), + archetype_id_iter: query_access_cache.matched_archetype_ids.iter(), current_len: 0, current_index: 0, } @@ -480,7 +509,7 @@ where /// retrieve item returned from most recent `next` call again. #[inline] - unsafe fn peek_last<'w>(&mut self) -> Option<>::Item> { + unsafe fn peek_last<'w>(&mut self) -> Option<>::Item> { if self.current_index > 0 { if self.is_dense { Some(self.fetch.table_fetch(self.current_index - 1)) @@ -501,14 +530,22 @@ where tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, - ) -> Option<>::Item> { + ) -> Option<>::Item> { if self.is_dense { loop { if self.current_index == self.current_len { let table_id = self.table_id_iter.next()?; let table = &tables[*table_id]; - self.fetch.set_table(&query_state.fetch_state, table); - self.filter.set_table(&query_state.filter_state, table); + self.fetch.set_table( + &query_state.fetch_state, + &query_state.current_relation_filter.0, + table, + ); + self.filter.set_table( + &query_state.filter_state, + &query_state.current_relation_filter.1, + table, + ); self.current_len = table.len(); self.current_index = 0; continue; @@ -529,10 +566,18 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; - self.fetch - .set_archetype(&query_state.fetch_state, archetype, tables); - self.filter - .set_archetype(&query_state.filter_state, archetype, tables); + self.fetch.set_archetype( + &query_state.fetch_state, + &query_state.current_relation_filter.0, + archetype, + tables, + ); + self.filter.set_archetype( + &query_state.filter_state, + &query_state.current_relation_filter.1, + archetype, + tables, + ); self.current_len = archetype.len(); self.current_index = 0; continue; diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 99e140846073c..16b05578165e5 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -2,12 +2,14 @@ mod access; mod fetch; mod filter; mod iter; +mod relation_filter; mod state; pub use access::*; pub use fetch::*; pub use filter::*; pub use iter::*; +pub use relation_filter::*; pub use state::*; #[cfg(test)] diff --git a/crates/bevy_ecs/src/query/relation_filter.rs b/crates/bevy_ecs/src/query/relation_filter.rs new file mode 100644 index 0000000000000..77a7e593bd385 --- /dev/null +++ b/crates/bevy_ecs/src/query/relation_filter.rs @@ -0,0 +1,270 @@ +use smallvec::SmallVec; + +use crate::{component::Component, prelude::Entity}; + +use super::{FetchState, Relation, WorldQuery}; +use std::{ + hash::{Hash, Hasher}, + marker::PhantomData, +}; + +// NOTE: This whole file is ~~hilarious~~ elegant type system hacking- thanks to @TheRawMeatball for coming up with this :) + +pub struct QueryRelationFilter( + pub ::RelationFilter, + pub ::RelationFilter, + PhantomData (Q, F)>, +); + +macro_rules! impl_trait { + ($trait:ident, $($body:tt)*) => { + impl $trait for QueryRelationFilter + where + ::RelationFilter: $trait, + ::RelationFilter: $trait { + $($body)* + } + }; +} + +impl_trait!( + Clone, + fn clone(&self) -> Self { + Self(self.0.clone(), self.1.clone(), PhantomData) + } +); + +impl_trait!( + Default, + fn default() -> Self { + Self(Default::default(), Default::default(), PhantomData) + } +); + +impl_trait!( + PartialEq, + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +); + +impl_trait!(Eq,); + +impl_trait!( + Hash, + fn hash(&self, state: &mut H) { + self.0.hash(state); + self.1.hash(state); + } +); + +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct RelationFilter(SmallVec<[Entity; 4]>, PhantomData); + +impl RelationFilter { + pub fn new() -> Self { + Self(SmallVec::new(), PhantomData) + } + + pub fn target(mut self, target: Entity) -> Self { + self.0.push(target); + self + } +} + +impl QueryRelationFilter { + pub fn new() -> Self { + Self::default() + } + + pub fn add_filter_relation(&mut self, filter: RelationFilter) + where + Self: SpecifiesRelation, + { + Self::__add_target_filter(filter, self); + } + + pub fn deduplicate_targets(&mut self) { + ::deduplicate_targets(&mut self.0); + ::deduplicate_targets(&mut self.1); + } +} + +pub trait SpecifiesRelation { + type RelationFilter; + fn __add_target_filter( + entity: RelationFilter, + relation_filter: &mut Self::RelationFilter, + ); +} + +pub struct Intrinsic; +pub struct InData(PhantomData); +pub struct InFilter(PhantomData); +pub struct InTuple(PhantomData); + +impl SpecifiesRelation for &Relation { + type RelationFilter = <::State as FetchState>::RelationFilter; + fn __add_target_filter( + filter: RelationFilter, + relation_filter: &mut smallvec::SmallVec<[Entity; 4]>, + ) { + relation_filter.extend(filter.0.into_iter()); + } +} +impl SpecifiesRelation for &mut Relation { + type RelationFilter = <::State as FetchState>::RelationFilter; + fn __add_target_filter( + filter: RelationFilter, + relation_filter: &mut smallvec::SmallVec<[Entity; 4]>, + ) { + relation_filter.extend(filter.0.into_iter()); + } +} +impl SpecifiesRelation + for crate::prelude::Without> +{ + type RelationFilter = <::State as FetchState>::RelationFilter; + fn __add_target_filter( + filter: RelationFilter, + relation_filter: &mut smallvec::SmallVec<[Entity; 4]>, + ) { + relation_filter.extend(filter.0.into_iter()); + } +} +impl SpecifiesRelation for crate::prelude::With> { + type RelationFilter = <::State as FetchState>::RelationFilter; + fn __add_target_filter( + filter: RelationFilter, + relation_filter: &mut smallvec::SmallVec<[Entity; 4]>, + ) { + relation_filter.extend(filter.0.into_iter()); + } +} + +impl SpecifiesRelation> + for QueryRelationFilter +where + Q: SpecifiesRelation< + Kind, + Path, + RelationFilter = <::State as FetchState>::RelationFilter, + >, +{ + type RelationFilter = Self; + fn __add_target_filter( + entity: RelationFilter, + relation_filter: &mut Self::RelationFilter, + ) { + Q::__add_target_filter(entity, &mut relation_filter.0); + } +} +impl SpecifiesRelation> + for QueryRelationFilter +where + F: SpecifiesRelation< + Kind, + Path, + RelationFilter = <::State as FetchState>::RelationFilter, + >, +{ + type RelationFilter = Self; + fn __add_target_filter( + entity: RelationFilter, + relation_filter: &mut Self::RelationFilter, + ) { + F::__add_target_filter(entity, &mut relation_filter.1); + } +} + +macro_rules! replace_expr { + ($_t:tt $sub:expr) => { + $sub + }; +} + +macro_rules! count_tts { + ($($tts:tt)*) => {0usize $(+ replace_expr!($tts 1usize))*}; +} + +macro_rules! impl_tuple_inner { + ([$($head: ident),*], [$($tail: ident),*]) => { + impl + SpecifiesRelation> + for + ($($head,)* Selected, $($tail,)*) + where + $($head: WorldQuery,)* + $($tail: WorldQuery,)* + Selected: WorldQuery + + SpecifiesRelation< + Kind, + Inner, + RelationFilter = <::State as FetchState>::RelationFilter, + >, + { + type RelationFilter = ( + $(<<$head as WorldQuery>::State as FetchState>::RelationFilter,)* + ::RelationFilter, + $(<<$tail as WorldQuery>::State as FetchState>::RelationFilter,)* + ); + + #[allow(non_snake_case, unused)] + fn __add_target_filter(entity: RelationFilter, relation_filter: &mut Self::RelationFilter) { + let ( + $($head,)* + my_thing, + $($tail,)* + ) = relation_filter; + Selected::__add_target_filter(entity, my_thing); + } + } + }; +} + +macro_rules! impl_tuple { + ($($idents: ident),*) => { + impl_tuple!([], [$($idents),*]); + }; + ([$($head: ident),*], []) => { + impl_tuple_inner!([$($head),*], []); + }; + ([$($head: ident),*], [$last: ident]) => { + impl_tuple_inner!([$($head),*], [$last]); + impl_tuple!([$($head,)* $last], []); + }; + ([$($head: ident),*], [$transfer: ident, $($tail: ident),*]) => { + impl_tuple_inner!([$($head),*], [$($tail,)* $transfer]); + impl_tuple!([$($head,)* $transfer], [$($tail),*]); + }; +} + +impl_tuple!(); +impl_tuple!(A); +impl_tuple!(A, B); +impl_tuple!(A, B, C); +impl_tuple!(A, B, C, D); +impl_tuple!(A, B, C, D, E); +impl_tuple!(A, B, C, D, E, F); +impl_tuple!(A, B, C, D, E, F, G); +impl_tuple!(A, B, C, D, E, F, G, H); +impl_tuple!(A, B, C, D, E, F, G, H, I); +impl_tuple!(A, B, C, D, E, F, G, H, I, J); +impl_tuple!(A, B, C, D, E, F, G, H, I, J, K); + +#[cfg(test)] +#[test] +fn target_filter_tests() { + fn assert_impl + ?Sized>() {} + assert_impl::, &Relation), ()>>(); + assert_impl::, &Relation), ()>>(); + + let mut filter: QueryRelationFilter<&Relation, ()> = Default::default(); + filter.add_filter_relation(RelationFilter::::new().target(Entity::new(1))); + dbg!(&filter.0); + + let mut filter: QueryRelationFilter<(&Relation, &Relation), ()> = Default::default(); + filter.add_filter_relation(RelationFilter::::new().target(Entity::new(1))); + filter.add_filter_relation(RelationFilter::::new().target(Entity::new(12))); + dbg!(&filter.0); +} diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 05c009c6e82db..5296ec73f9aaa 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1,6 +1,8 @@ +use std::collections::HashMap; + use crate::{ archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId}, - component::ComponentId, + component::{Component, RelationKindId}, entity::Entity, query::{ Access, Fetch, FetchState, FilterFetch, FilteredAccess, QueryCombinationIter, QueryIter, @@ -13,20 +15,64 @@ use bevy_tasks::TaskPool; use fixedbitset::FixedBitSet; use thiserror::Error; -pub struct QueryState +use super::{QueryRelationFilter, RelationFilter, SpecifiesRelation}; + +pub struct QueryStateRelationFilterBuilder<'a, 'b, Q: WorldQuery, F: WorldQuery> where F::Fetch: FilterFetch, { - world_id: WorldId, + state: &'a mut QueryState, + world: &'b World, + filters: QueryRelationFilter, +} + +impl<'a, 'b, Q: WorldQuery, F: WorldQuery> QueryStateRelationFilterBuilder<'a, 'b, Q, F> +where + F::Fetch: FilterFetch, +{ + /// If filters have already been added for the relation kind they will be merged with + /// the provided filters. + pub fn add_filter_relation(mut self, filter: RelationFilter) -> Self + where + QueryRelationFilter: + SpecifiesRelation>, + { + self.filters.add_filter_relation(filter); + self + } + + pub fn apply_filters(self) -> &'a mut QueryState { + let Self { + state, + world, + filters, + } = self; + state.set_relation_filter(world, filters); + state + } +} + +pub struct QueryAccessCache { pub(crate) archetype_generation: ArchetypeGeneration, pub(crate) matched_tables: FixedBitSet, pub(crate) matched_archetypes: FixedBitSet, - pub(crate) archetype_component_access: Access, - pub(crate) component_access: FilteredAccess, // NOTE: we maintain both a TableId bitset and a vec because iterating the vec is faster pub(crate) matched_table_ids: Vec, // NOTE: we maintain both a ArchetypeId bitset and a vec because iterating the vec is faster pub(crate) matched_archetype_ids: Vec, +} + +pub struct QueryState +where + F::Fetch: FilterFetch, +{ + world_id: WorldId, + pub(crate) archetype_component_access: Access, + pub(crate) component_access: FilteredAccess, + + pub(crate) current_relation_filter: QueryRelationFilter, + pub(crate) relation_filter_accesses: HashMap, QueryAccessCache>, + pub(crate) fetch_state: Q::State, pub(crate) filter_state: F::State, } @@ -54,16 +100,16 @@ where let mut state = Self { world_id: world.id(), - archetype_generation: ArchetypeGeneration::initial(), - matched_table_ids: Vec::new(), - matched_archetype_ids: Vec::new(), fetch_state, filter_state, component_access, - matched_tables: Default::default(), - matched_archetypes: Default::default(), + + current_relation_filter: Default::default(), + relation_filter_accesses: HashMap::new(), + archetype_component_access: Default::default(), }; + state.set_relation_filter(world, QueryRelationFilter::default()); state.validate_world_and_update_archetypes(world); state } @@ -78,40 +124,113 @@ where } } + pub fn current_query_access_cache(&self) -> &QueryAccessCache { + self.relation_filter_accesses + .get(&self.current_relation_filter) + .unwrap() + } + + pub fn clear_relation_filters(&mut self, world: &World) -> &mut Self { + self.set_relation_filter(world, Default::default()); + self + } + + /// Starts building a new set of relation filters for the query, changes will not take + /// place if `apply_filters` is not called on the returned builder struct. + /// + /// You should call `clear_filter_relations` if you want to reset filters. + #[must_use = "relation filters will be unchanged if you do not call `apply_filters`"] + pub fn new_filter_relation<'a, K: Component, Path>( + &mut self, + world: &'a World, + relation_filter: RelationFilter, + ) -> QueryStateRelationFilterBuilder<'_, 'a, Q, F> + where + QueryRelationFilter: + SpecifiesRelation>, + { + QueryStateRelationFilterBuilder { + state: self, + world, + filters: QueryRelationFilter::new(), + } + .add_filter_relation(relation_filter) + } + + /// Overwrites the existing relation filters + /// + /// You should prefer `new_filter_relation` over this method + pub fn set_relation_filter( + &mut self, + world: &World, + mut relation_filter: QueryRelationFilter, + ) { + // We deduplicate targets so that `RelationAccess` and `RelationAccessMut` + // dont yield aliasing borrows when we have two identical target filters + relation_filter.deduplicate_targets(); + self.current_relation_filter = relation_filter.clone(); + self.relation_filter_accesses + .entry(relation_filter) + .or_insert(QueryAccessCache { + archetype_generation: ArchetypeGeneration::initial(), + matched_table_ids: Vec::new(), + matched_archetype_ids: Vec::new(), + matched_tables: Default::default(), + matched_archetypes: Default::default(), + }); + self.validate_world_and_update_archetypes(world); + } + pub fn validate_world_and_update_archetypes(&mut self, world: &World) { if world.id() != self.world_id { panic!("Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.", std::any::type_name::()); } let archetypes = world.archetypes(); - let new_generation = archetypes.generation(); - let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); - let archetype_index_range = old_generation.value()..new_generation.value(); - - for archetype_index in archetype_index_range { - self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]); + for (relation_filter, cache) in self.relation_filter_accesses.iter_mut() { + let new_generation = archetypes.generation(); + let old_generation = std::mem::replace(&mut cache.archetype_generation, new_generation); + let archetype_index_range = old_generation.value()..new_generation.value(); + + for archetype_index in archetype_index_range { + let archetype = &archetypes[ArchetypeId::new(archetype_index)]; + Self::new_archetype( + &self.fetch_state, + &self.filter_state, + &mut self.archetype_component_access, + &*relation_filter, + cache, + archetype, + ); + } } } - pub fn new_archetype(&mut self, archetype: &Archetype) { - if self.fetch_state.matches_archetype(archetype) - && self.filter_state.matches_archetype(archetype) + pub fn new_archetype( + fetch_state: &Q::State, + filter_state: &F::State, + access: &mut Access, + relation_filter: &QueryRelationFilter, + cache: &mut QueryAccessCache, + archetype: &Archetype, + ) { + if fetch_state.matches_archetype(archetype, &relation_filter.0) + && filter_state.matches_archetype(archetype, &relation_filter.1) { - self.fetch_state - .update_archetype_component_access(archetype, &mut self.archetype_component_access); - self.filter_state - .update_archetype_component_access(archetype, &mut self.archetype_component_access); + fetch_state.update_archetype_component_access(archetype, access); + filter_state.update_archetype_component_access(archetype, access); + let archetype_index = archetype.id().index(); - if !self.matched_archetypes.contains(archetype_index) { - self.matched_archetypes.grow(archetype_index + 1); - self.matched_archetypes.set(archetype_index, true); - self.matched_archetype_ids.push(archetype.id()); + if !cache.matched_archetypes.contains(archetype_index) { + cache.matched_archetypes.grow(archetype.id().index() + 1); + cache.matched_archetypes.set(archetype.id().index(), true); + cache.matched_archetype_ids.push(archetype.id()); } let table_index = archetype.table_id().index(); - if !self.matched_tables.contains(table_index) { - self.matched_tables.grow(table_index + 1); - self.matched_tables.set(table_index, true); - self.matched_table_ids.push(archetype.table_id()); + if !cache.matched_tables.contains(table_index) { + cache.matched_tables.grow(table_index + 1); + cache.matched_tables.set(table_index, true); + cache.matched_table_ids.push(archetype.table_id()); } } } @@ -121,7 +240,7 @@ where &mut self, world: &'w World, entity: Entity, - ) -> Result<>::Item, QueryEntityError> + ) -> Result<>::Item, QueryEntityError> where Q::Fetch: ReadOnlyFetch, { @@ -134,7 +253,7 @@ where &mut self, world: &'w mut World, entity: Entity, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<>::Item, QueryEntityError> { // SAFETY: query has unique world access unsafe { self.get_unchecked(world, entity) } } @@ -148,7 +267,7 @@ where &mut self, world: &'w World, entity: Entity, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<>::Item, QueryEntityError> { self.validate_world_and_update_archetypes(world); self.get_unchecked_manual( world, @@ -167,25 +286,46 @@ where entity: Entity, last_change_tick: u32, change_tick: u32, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<>::Item, QueryEntityError> { let location = world .entities .get(entity) .ok_or(QueryEntityError::NoSuchEntity)?; if !self + .current_query_access_cache() .matched_archetypes .contains(location.archetype_id.index()) { return Err(QueryEntityError::QueryDoesNotMatch); } let archetype = &world.archetypes[location.archetype_id]; - let mut fetch = - ::init(world, &self.fetch_state, last_change_tick, change_tick); - let mut filter = - ::init(world, &self.filter_state, last_change_tick, change_tick); + let mut fetch = ::init( + world, + &self.fetch_state, + &self.current_relation_filter.0, + last_change_tick, + change_tick, + ); + let mut filter = ::init( + world, + &self.filter_state, + &self.current_relation_filter.1, + last_change_tick, + change_tick, + ); - fetch.set_archetype(&self.fetch_state, archetype, &world.storages().tables); - filter.set_archetype(&self.filter_state, archetype, &world.storages().tables); + fetch.set_archetype( + &self.fetch_state, + &self.current_relation_filter.0, + archetype, + &world.storages().tables, + ); + filter.set_archetype( + &self.filter_state, + &self.current_relation_filter.1, + archetype, + &world.storages().tables, + ); if filter.archetype_filter_fetch(location.index) { Ok(fetch.archetype_fetch(location.index)) } else { @@ -290,10 +430,10 @@ where } #[inline] - pub fn for_each<'w>( - &mut self, + pub fn for_each<'s, 'w>( + &'s mut self, world: &'w World, - func: impl FnMut(>::Item), + func: impl FnMut(>::Item), ) where Q::Fetch: ReadOnlyFetch, { @@ -304,10 +444,10 @@ where } #[inline] - pub fn for_each_mut<'w>( - &mut self, + pub fn for_each_mut<'w, 's>( + &'s mut self, world: &'w mut World, - func: impl FnMut(>::Item), + func: impl FnMut(>::Item), ) { // SAFETY: query has unique world access unsafe { @@ -320,10 +460,10 @@ where /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn for_each_unchecked<'w>( - &mut self, + pub unsafe fn for_each_unchecked<'w, 's>( + &'s mut self, world: &'w World, - func: impl FnMut(>::Item), + func: impl FnMut(>::Item), ) { self.validate_world_and_update_archetypes(world); self.for_each_unchecked_manual( @@ -335,12 +475,12 @@ where } #[inline] - pub fn par_for_each<'w>( - &mut self, + pub fn par_for_each<'w, 's>( + &'s mut self, world: &'w World, task_pool: &TaskPool, batch_size: usize, - func: impl Fn(>::Item) + Send + Sync + Clone, + func: impl Fn(>::Item) + Send + Sync + Clone, ) where Q::Fetch: ReadOnlyFetch, { @@ -351,12 +491,12 @@ where } #[inline] - pub fn par_for_each_mut<'w>( - &mut self, + pub fn par_for_each_mut<'w, 's>( + &'s mut self, world: &'w mut World, task_pool: &TaskPool, batch_size: usize, - func: impl Fn(>::Item) + Send + Sync + Clone, + func: impl Fn(>::Item) + Send + Sync + Clone, ) { // SAFETY: query has unique world access unsafe { @@ -369,12 +509,12 @@ where /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn par_for_each_unchecked<'w>( - &mut self, + pub unsafe fn par_for_each_unchecked<'w, 's>( + &'s mut self, world: &'w World, task_pool: &TaskPool, batch_size: usize, - func: impl Fn(>::Item) + Send + Sync + Clone, + func: impl Fn(>::Item) + Send + Sync + Clone, ) { self.validate_world_and_update_archetypes(world); self.par_for_each_unchecked_manual( @@ -396,22 +536,32 @@ where pub(crate) unsafe fn for_each_unchecked_manual<'w, 's>( &'s self, world: &'w World, - mut func: impl FnMut(>::Item), + mut func: impl FnMut(>::Item), last_change_tick: u32, change_tick: u32, ) { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual - let mut fetch = - ::init(world, &self.fetch_state, last_change_tick, change_tick); - let mut filter = - ::init(world, &self.filter_state, last_change_tick, change_tick); + let mut fetch = ::init( + world, + &self.fetch_state, + &self.current_relation_filter.0, + last_change_tick, + change_tick, + ); + let mut filter = ::init( + world, + &self.filter_state, + &self.current_relation_filter.1, + last_change_tick, + change_tick, + ); if fetch.is_dense() && filter.is_dense() { let tables = &world.storages().tables; - for table_id in self.matched_table_ids.iter() { + for table_id in self.current_query_access_cache().matched_table_ids.iter() { let table = &tables[*table_id]; - fetch.set_table(&self.fetch_state, table); - filter.set_table(&self.filter_state, table); + fetch.set_table(&self.fetch_state, &self.current_relation_filter.0, table); + filter.set_table(&self.filter_state, &self.current_relation_filter.1, table); for table_index in 0..table.len() { if !filter.table_filter_fetch(table_index) { @@ -424,10 +574,24 @@ where } else { let archetypes = &world.archetypes; let tables = &world.storages().tables; - for archetype_id in self.matched_archetype_ids.iter() { + for archetype_id in self + .current_query_access_cache() + .matched_archetype_ids + .iter() + { let archetype = &archetypes[*archetype_id]; - fetch.set_archetype(&self.fetch_state, archetype, tables); - filter.set_archetype(&self.filter_state, archetype, tables); + fetch.set_archetype( + &self.fetch_state, + &self.current_relation_filter.0, + archetype, + tables, + ); + filter.set_archetype( + &self.filter_state, + &self.current_relation_filter.1, + archetype, + tables, + ); for archetype_index in 0..archetype.len() { if !filter.archetype_filter_fetch(archetype_index) { @@ -450,21 +614,31 @@ where world: &'w World, task_pool: &TaskPool, batch_size: usize, - func: impl Fn(>::Item) + Send + Sync + Clone, + func: impl Fn(>::Item) + Send + Sync + Clone, last_change_tick: u32, change_tick: u32, ) { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual task_pool.scope(|scope| { - let fetch = - ::init(world, &self.fetch_state, last_change_tick, change_tick); - let filter = - ::init(world, &self.filter_state, last_change_tick, change_tick); + let fetch = ::init( + world, + &self.fetch_state, + &self.current_relation_filter.0, + last_change_tick, + change_tick, + ); + let filter = ::init( + world, + &self.filter_state, + &self.current_relation_filter.1, + last_change_tick, + change_tick, + ); if fetch.is_dense() && filter.is_dense() { let tables = &world.storages().tables; - for table_id in self.matched_table_ids.iter() { + for table_id in self.current_query_access_cache().matched_table_ids.iter() { let table = &tables[*table_id]; let mut offset = 0; while offset < table.len() { @@ -473,19 +647,29 @@ where let mut fetch = ::init( world, &self.fetch_state, + &self.current_relation_filter.0, last_change_tick, change_tick, ); let mut filter = ::init( world, &self.filter_state, + &self.current_relation_filter.1, last_change_tick, change_tick, ); let tables = &world.storages().tables; let table = &tables[*table_id]; - fetch.set_table(&self.fetch_state, table); - filter.set_table(&self.filter_state, table); + fetch.set_table( + &self.fetch_state, + &self.current_relation_filter.0, + table, + ); + filter.set_table( + &self.filter_state, + &self.current_relation_filter.1, + table, + ); let len = batch_size.min(table.len() - offset); for table_index in offset..offset + len { if !filter.table_filter_fetch(table_index) { @@ -500,7 +684,11 @@ where } } else { let archetypes = &world.archetypes; - for archetype_id in self.matched_archetype_ids.iter() { + for archetype_id in self + .current_query_access_cache() + .matched_archetype_ids + .iter() + { let mut offset = 0; let archetype = &archetypes[*archetype_id]; while offset < archetype.len() { @@ -509,19 +697,31 @@ where let mut fetch = ::init( world, &self.fetch_state, + &self.current_relation_filter.0, last_change_tick, change_tick, ); let mut filter = ::init( world, &self.filter_state, + &self.current_relation_filter.1, last_change_tick, change_tick, ); let tables = &world.storages().tables; let archetype = &world.archetypes[*archetype_id]; - fetch.set_archetype(&self.fetch_state, archetype, tables); - filter.set_archetype(&self.filter_state, archetype, tables); + fetch.set_archetype( + &self.fetch_state, + &self.current_relation_filter.0, + archetype, + tables, + ); + filter.set_archetype( + &self.filter_state, + &self.current_relation_filter.1, + archetype, + tables, + ); let len = batch_size.min(archetype.len() - offset); for archetype_index in offset..offset + len { diff --git a/crates/bevy_ecs/src/schedule/run_criteria.rs b/crates/bevy_ecs/src/schedule/run_criteria.rs index 5ea8fab4d1e5f..a7f38d4498844 100644 --- a/crates/bevy_ecs/src/schedule/run_criteria.rs +++ b/crates/bevy_ecs/src/schedule/run_criteria.rs @@ -1,6 +1,6 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, - component::ComponentId, + component::RelationKindId, query::Access, schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel}, system::{BoxedSystem, System, SystemId}, @@ -366,7 +366,7 @@ pub struct RunOnce { ran: bool, system_id: SystemId, archetype_component_access: Access, - component_access: Access, + component_access: Access, } impl Default for RunOnce { @@ -394,7 +394,7 @@ impl System for RunOnce { fn new_archetype(&mut self, _archetype: &Archetype) {} - fn component_access(&self) -> &Access { + fn component_access(&self) -> &Access { &self.component_access } diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index a92ffe7d67475..0f2abff227320 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -1,5 +1,5 @@ use crate::{ - component::ComponentId, + component::RelationKindId, schedule::{ graph_utils::{self, DependencyGraphError}, BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy, @@ -471,7 +471,7 @@ impl SystemStage { fn write_display_names_of_pairs( string: &mut String, systems: &[impl SystemContainer], - mut ambiguities: Vec<(usize, usize, Vec)>, + mut ambiguities: Vec<(usize, usize, Vec)>, world: &World, ) { for (index_a, index_b, conflicts) in ambiguities.drain(..) { @@ -485,7 +485,13 @@ impl SystemStage { if !conflicts.is_empty() { let names = conflicts .iter() - .map(|id| world.components().get_info(*id).unwrap().name()) + .map(|id| { + world + .components() + .get_relation_kind(*id) + .data_layout() + .name() + }) .collect::>(); writeln!(string, " conflicts: {:?}", names).unwrap(); } @@ -659,7 +665,7 @@ fn process_systems( /// Returns vector containing all pairs of indices of systems with ambiguous execution order, /// along with specific components that have triggered the warning. /// Systems must be topologically sorted beforehand. -fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec)> { +fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec)> { let mut ambiguity_set_labels = HashMap::default(); for set in systems.iter().flat_map(|c| c.ambiguity_sets()) { let len = ambiguity_set_labels.len(); diff --git a/crates/bevy_ecs/src/schedule/system_container.rs b/crates/bevy_ecs/src/schedule/system_container.rs index 727c007e68b39..7f6d573bf4ef0 100644 --- a/crates/bevy_ecs/src/schedule/system_container.rs +++ b/crates/bevy_ecs/src/schedule/system_container.rs @@ -1,5 +1,5 @@ use crate::{ - component::ComponentId, + component::RelationKindId, query::Access, schedule::{ BoxedAmbiguitySetLabel, BoxedRunCriteriaLabel, BoxedSystemLabel, ExclusiveSystemDescriptor, @@ -21,7 +21,7 @@ pub trait SystemContainer: GraphNode