From 48183c6a3c878cc163214d058b229dc699213ee2 Mon Sep 17 00:00:00 2001 From: Ellen Date: Fri, 30 Jul 2021 01:16:10 +0100 Subject: [PATCH 1/2] Entity Relations :thepr: Co-authored-by: Patrik Buhring --- Cargo.toml | 4 + crates/bevy_ecs/Cargo.toml | 1 + crates/bevy_ecs/macros/src/lib.rs | 19 +- crates/bevy_ecs/src/archetype.rs | 205 ++++-- crates/bevy_ecs/src/bundle.rs | 126 +++- crates/bevy_ecs/src/component.rs | 256 ++++--- crates/bevy_ecs/src/lib.rs | 43 +- crates/bevy_ecs/src/query/fetch.rs | 788 +++++++++++++++++++-- crates/bevy_ecs/src/query/filter.rs | 417 +++++++++-- crates/bevy_ecs/src/query/iter.rs | 78 +- crates/bevy_ecs/src/query/mod.rs | 2 + crates/bevy_ecs/src/query/state.rs | 292 ++++++-- crates/bevy_ecs/src/query/target_filter.rs | 249 +++++++ crates/bevy_ecs/src/schedule/stage.rs | 2 +- crates/bevy_ecs/src/storage/sparse_set.rs | 83 ++- crates/bevy_ecs/src/storage/table.rs | 129 +++- crates/bevy_ecs/src/system/commands/mod.rs | 100 +++ crates/bevy_ecs/src/system/mod.rs | 13 +- crates/bevy_ecs/src/system/query.rs | 94 ++- crates/bevy_ecs/src/system/system_param.rs | 29 +- crates/bevy_ecs/src/world/entity_ref.rs | 449 +++++++++--- crates/bevy_ecs/src/world/mod.rs | 104 ++- crates/bevy_ecs/src/world/spawn_batch.rs | 6 +- crates/bevy_ecs/src/world/tests.rs | 722 +++++++++++++++++++ crates/bevy_ecs/src/world/world_cell.rs | 27 +- crates/bevy_scene/src/dynamic_scene.rs | 6 +- crates/bevy_scene/src/scene_spawner.rs | 4 +- examples/README.md | 1 + examples/ecs/relations_grouping.rs | 209 ++++++ 29 files changed, 3849 insertions(+), 609 deletions(-) create mode 100644 crates/bevy_ecs/src/query/target_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 f95b76f75a9ee..341418058fd20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -261,6 +261,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 = "component_change_detection" path = "examples/ecs/component_change_detection.rs" diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index b7797993bc427..14bcbfa374815 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" fixedbitset = "0.4" fxhash = "0.2" diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index d4d307f8a53e6..849ebd96b437e 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -128,7 +128,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { }); } else { field_component_ids.push(quote! { - component_ids.push(components.get_or_insert_id::<#field_type>()); + component_ids.push( + components + .component_id_or_insert::<#field_type>() + ); }); field_get_components.push(quote! { func((&mut self.#field as *mut #field_type).cast::()); @@ -260,7 +263,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 (target_filter, cache) in #query.target_filter_accesses.iter_mut() { + QueryState::<#query, #filter>::new_archetype( + &#query.fetch_state, + &#query.filter_state, + &mut #query.archetype_component_access, + &*target_filter, + cache, + archetype + ); + } + system_meta .archetype_component_access .extend(&#query.archetype_component_access); @@ -282,7 +295,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..347f083afe752 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}, entity::{Entity, EntityLocation}, - storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId}, + storage::{Column, SparseSet, SparseSetIndex, TableId}, }; use std::{ borrow::Cow, - collections::HashMap, hash::Hash, ops::{Index, IndexMut}, }; @@ -48,15 +50,41 @@ pub struct AddBundle { #[derive(Default)] pub struct Edges { - pub add_bundle: SparseArray, - pub remove_bundle: SparseArray>, - pub remove_bundle_intersection: SparseArray>, + pub add_bundle: HashMap, + pub remove_bundle: HashMap>, + pub remove_bundle_intersection: HashMap>, } impl Edges { + pub fn debug_ram_usage(&self) -> usize { + use std::mem::size_of; + + let mut size = size_of::(); + + let usage_of_hm = |cap: usize, k, v| (cap * 11 / 10).next_power_of_two() * (k + v + 8); + + size += usage_of_hm( + self.add_bundle.capacity(), + size_of::(), + size_of::(), + ); + size += usage_of_hm( + self.remove_bundle.capacity(), + size_of::(), + size_of::>(), + ); + size += usage_of_hm( + self.remove_bundle_intersection.capacity(), + size_of::(), + size_of::>(), + ); + + size + } + #[inline] pub fn get_add_bundle(&self, bundle_id: BundleId) -> Option<&AddBundle> { - self.add_bundle.get(bundle_id) + self.add_bundle.get(&bundle_id) } #[inline] @@ -77,7 +105,7 @@ impl Edges { #[inline] pub fn get_remove_bundle(&self, bundle_id: BundleId) -> Option> { - self.remove_bundle.get(bundle_id).cloned() + self.remove_bundle.get(&bundle_id).cloned() } #[inline] @@ -90,7 +118,7 @@ impl Edges { &self, bundle_id: BundleId, ) -> Option> { - self.remove_bundle_intersection.get(bundle_id).cloned() + self.remove_bundle_intersection.get(&bundle_id).cloned() } #[inline] @@ -124,47 +152,95 @@ pub struct Archetype { entities: Vec, edges: Edges, table_info: TableInfo, - table_components: Cow<'static, [ComponentId]>, - sparse_set_components: Cow<'static, [ComponentId]>, + table_components: Cow<'static, [(ComponentId, Option)]>, + sparse_set_components: Cow<'static, [(ComponentId, Option)]>, pub(crate) unique_components: SparseSet, pub(crate) components: SparseSet, + pub(crate) relations: SparseSet>, } impl Archetype { + pub fn debug_ram_usage(&self) -> (usize, usize) { + use std::mem::size_of; + let mut size = size_of::(); + + if let Cow::Owned(owned) = &self.table_components { + size += size_of::<(ComponentId, Option)>() * owned.len(); + } + if let Cow::Owned(owned) = &self.sparse_set_components { + size += size_of::<(ComponentId, Option)>() * owned.len(); + } + + size += size_of::() * self.components.dense_len(); + size += size_of::() * self.components.indices_len(); + size += size_of::>() * self.components.sparse_len(); + + size += size_of::() * self.relations.indices_len(); + size += size_of::>() * self.relations.sparse_len(); + + for v in self.relations.values() { + let cap = v.capacity(); + let usage = (cap * 11 / 10).next_power_of_two() + * (size_of::() + size_of::() + 8); + size += usage; + } + + size += size_of::() * self.entities.len(); + + (size, self.edges.debug_ram_usage()) + } + pub fn new( id: ArchetypeId, table_id: TableId, - table_components: Cow<'static, [ComponentId]>, - sparse_set_components: Cow<'static, [ComponentId]>, + table_components: Cow<'static, [(ComponentId, Option)]>, + sparse_set_components: Cow<'static, [(ComponentId, 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 ((component_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(*component_id, arch_comp_info); + } + Some(target) => { + let set = relations.get_or_insert_with(*component_id, StableHashMap::default); + set.insert(*target, arch_comp_info); + } + }; } - for (component_id, archetype_component_id) in sparse_set_components + for ((component_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(*component_id, arch_comp_info); + } + Some(target) => { + let set = relations.get_or_insert_with(*component_id, StableHashMap::default); + set.insert(*target, arch_comp_info); + } + }; } + Self { id, table_info: TableInfo { @@ -172,6 +248,7 @@ impl Archetype { entity_rows: Default::default(), }, components, + relations, table_components, sparse_set_components, unique_components: SparseSet::new(), @@ -201,12 +278,12 @@ impl Archetype { } #[inline] - pub fn table_components(&self) -> &[ComponentId] { + pub fn table_components(&self) -> &[(ComponentId, Option)] { &self.table_components } #[inline] - pub fn sparse_set_components(&self) -> &[ComponentId] { + pub fn sparse_set_components(&self) -> &[(ComponentId, Option)] { &self.sparse_set_components } @@ -221,8 +298,17 @@ impl Archetype { } #[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 |component_id| { + self.relations + .get(component_id) + .unwrap() + .keys() + .map(move |target| (component_id, Some(*target))) + })) } #[inline] @@ -289,25 +375,50 @@ impl Archetype { } #[inline] - pub fn contains(&self, component_id: ComponentId) -> bool { - self.components.contains(component_id) + pub fn contains(&self, component_id: ComponentId, target: Option) -> bool { + match target { + None => self.components.contains(component_id), + Some(target) => self + .relations + .get(component_id) + .map(|set| set.contains_key(&target)) + .unwrap_or(false), + } } + // 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, + component_id: ComponentId, + target: Option, + ) -> Option { + match target { + None => self.components.get(component_id), + Some(target) => self + .relations + .get(component_id) + .and_then(|set| set.get(&target)), + } + .map(|info| info.storage_type) } #[inline] pub fn get_archetype_component_id( &self, component_id: ComponentId, + // FIXME(Relationships) treat archetype componnet id the same as component id maybe?? see other fixme + // then we oculd get rid of this `target` arg and same with fn above + target: Option, ) -> Option { - self.components - .get(component_id) - .map(|info| info.archetype_component_id) + match target { + None => self.components.get(component_id), + Some(target) => self + .relations + .get(component_id) + .and_then(|set| set.get(&target)), + } + .map(|info| info.archetype_component_id) } } @@ -329,8 +440,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, [(ComponentId, Option)]>, + sparse_set_components: Cow<'static, [(ComponentId, Option)]>, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -453,6 +564,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 +590,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<(ComponentId, Option)>, + sparse_set_components: Vec<(ComponentId, 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 2c38e249fb5ec..0bea0286002b6 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -2,7 +2,7 @@ pub use bevy_ecs_macros::Bundle; use crate::{ archetype::ComponentStatus, - component::{Component, ComponentId, ComponentTicks, Components, StorageType}, + component::{Component, ComponentId, ComponentInfo, ComponentTicks, Components, StorageType}, entity::Entity, storage::{SparseSetIndex, SparseSets, Table}, }; @@ -68,7 +68,7 @@ macro_rules! tuple_impl { unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { #[allow(unused_variables)] fn component_ids(components: &mut Components) -> Vec { - vec![$(components.get_or_insert_id::<$name>()),*] + vec![$(components.component_id_or_insert::<$name>()),*] } #[allow(unused_variables, unused_mut)] @@ -96,7 +96,7 @@ macro_rules! tuple_impl { all_tuples!(tuple_impl, 0, 15, C); -#[derive(Debug, Clone, Copy)] +#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] pub struct BundleId(usize); impl BundleId { @@ -119,7 +119,7 @@ impl SparseSetIndex for BundleId { pub struct BundleInfo { pub(crate) id: BundleId, - pub(crate) component_ids: Vec, + pub(crate) component_ids: Vec<(ComponentId, Option)>, pub(crate) storage_types: Vec, } @@ -128,7 +128,7 @@ impl BundleInfo { /// table row must exist, entity must be valid #[allow(clippy::too_many_arguments)] #[inline] - pub(crate) unsafe fn write_components( + pub(crate) unsafe fn write_bundle( &self, sparse_sets: &mut SparseSets, entity: Entity, @@ -141,31 +141,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_component( + 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_component( + &self, + sparse_sets: &mut SparseSets, + entity: Entity, + table: &mut Table, + table_row: usize, + bundle_status: &[ComponentStatus], + component_index: usize, + component_ptr: *mut u8, + change_tick: u32, + ) { + let (component_id, target) = self.component_ids[component_index]; + match self.storage_types[component_index] { + StorageType::Table => { + let column = table.get_column_mut(component_id, target).unwrap(); + match bundle_status[component_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(component_id, target).unwrap(); + sparse_set.insert(entity, component_ptr, change_tick); + } + } } #[inline] @@ -174,7 +198,7 @@ impl BundleInfo { } #[inline] - pub fn components(&self) -> &[ComponentId] { + pub fn components(&self) -> &[(ComponentId, Option)] { &self.component_ids } @@ -188,6 +212,8 @@ impl BundleInfo { pub struct Bundles { bundle_infos: Vec, bundle_ids: HashMap, + // FIXME(Relations): Figure out a way to put relations in a bundle and then remove this + relation_bundle_ids: HashMap<(ComponentId, Entity), BundleId>, } impl Bundles { @@ -197,11 +223,43 @@ impl Bundles { } #[inline] - pub fn get_id(&self, type_id: TypeId) -> Option { + pub fn get_bundle_id(&self, type_id: TypeId) -> Option { self.bundle_ids.get(&type_id).cloned() } - pub(crate) fn init_info<'a, T: Bundle>( + pub fn get_relation_bundle_id( + &self, + component_id: ComponentId, + target: Entity, + ) -> Option { + self.relation_bundle_ids + .get(&(component_id, target)) + .copied() + } + + pub(crate) fn init_relation_bundle_info<'a>( + &'a mut self, + component_info: &ComponentInfo, + target: Entity, + ) -> &'a BundleInfo { + let bundle_infos = &mut self.bundle_infos; + let id = self + .relation_bundle_ids + .entry((component_info.id(), target)) + .or_insert_with(|| { + let id = BundleId(bundle_infos.len()); + let bundle_info = BundleInfo { + id, + component_ids: vec![(component_info.id(), Some(target))], + storage_types: vec![component_info.storage_type()], + }; + bundle_infos.push(bundle_info); + id + }); + &self.bundle_infos[id.0] + } + + pub(crate) fn init_bundle_info<'a, T: Bundle>( &'a mut self, components: &mut Components, ) -> &'a BundleInfo { @@ -216,8 +274,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] } } @@ -233,8 +290,7 @@ unsafe fn initialize_bundle( let mut storage_types = Vec::new(); for &component_id in &component_ids { - // SAFE: component_id exists and is therefore valid - let component_info = components.get_info_unchecked(component_id); + let component_info = components.info(component_id).unwrap(); storage_types.push(component_info.storage_type()); } @@ -247,7 +303,7 @@ unsafe fn initialize_bundle( BundleInfo { id, - component_ids, + component_ids: component_ids.iter().map(|&e| (e, None)).collect(), storage_types, } } diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 6e71a1a7d9328..20156a0f814e1 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1,8 +1,9 @@ use crate::storage::SparseSetIndex; use std::{ alloc::Layout, - any::{Any, TypeId}, - collections::hash_map::Entry, + any::TypeId, + borrow::Cow, + collections::{hash_map::Entry, HashMap}, }; use thiserror::Error; @@ -100,22 +101,10 @@ impl ComponentInfo { #[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) - } - - #[inline] - pub fn index(self) -> usize { - self.0 - } -} - impl SparseSetIndex for ComponentId { #[inline] fn sparse_set_index(&self) -> usize { - self.index() + self.0 } fn get_sparse_set_index(value: usize) -> Self { @@ -125,8 +114,9 @@ impl SparseSetIndex for ComponentId { #[derive(Debug)] pub struct ComponentDescriptor { - name: String, + name: Cow<'static, str>, storage_type: StorageType, + target_type: TargetType, // SAFETY: This must remain private. It must only be set to "true" if this component is // actually Send + Sync is_send_and_sync: bool, @@ -141,10 +131,13 @@ impl ComponentDescriptor { x.cast::().drop_in_place() } + // FIXME(Relations) Remove `new` and `new_targeted` methods once we + // rebase ontop of derive(Component) so that this is moved to the type system :) pub fn new(storage_type: StorageType) -> Self { Self { - name: std::any::type_name::().to_string(), + name: std::any::type_name::().into(), storage_type, + target_type: TargetType::None, is_send_and_sync: true, type_id: Some(TypeId::of::()), layout: Layout::new::(), @@ -152,10 +145,27 @@ impl ComponentDescriptor { } } - fn new_non_send(storage_type: StorageType) -> Self { + pub fn default() -> Self { + Self::new::(StorageType::Table) + } + + pub fn new_targeted(storage_type: StorageType) -> Self { Self { - name: std::any::type_name::().to_string(), + name: std::any::type_name::().into(), storage_type, + target_type: TargetType::Entity, + is_send_and_sync: true, + type_id: Some(TypeId::of::()), + layout: Layout::new::(), + drop: Self::drop_ptr::, + } + } + + pub fn new_non_send_sync() -> Self { + Self { + name: std::any::type_name::().into(), + storage_type: StorageType::Table, + target_type: TargetType::None, is_send_and_sync: false, type_id: Some(TypeId::of::()), layout: Layout::new::(), @@ -179,152 +189,190 @@ impl ComponentDescriptor { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum TargetType { + None, + Entity, +} + #[derive(Debug, Default)] pub struct Components { - components: Vec, - indices: std::collections::HashMap, - resource_indices: std::collections::HashMap, + infos: Vec, + // These are only used by bevy. Scripting/dynamic components should + // use their own hashmap to lookup CustomId -> ComponentId + component_indices: HashMap, + resource_indices: HashMap, } #[derive(Debug, Error)] pub enum ComponentsError { #[error("A component of type {name:?} ({type_id:?}) already exists")] - ComponentAlreadyExists { type_id: TypeId, name: String }, + ComponentAlreadyExists { + type_id: TypeId, + name: Cow<'static, str>, + }, + #[error("A resource of type {name:?} ({type_id:?}) already exists")] + ResourceAlreadyExists { + type_id: TypeId, + name: Cow<'static, str>, + }, } impl Components { - pub(crate) fn add( + #[inline] + pub fn new_component( &mut self, descriptor: ComponentDescriptor, - ) -> Result { - let index = self.components.len(); + ) -> Result<&ComponentInfo, ComponentsError> { + let index = self.infos.len(); if let Some(type_id) = descriptor.type_id { - let index_entry = self.indices.entry(type_id); + let index_entry = self.component_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.component_indices.insert(type_id, ComponentId(index)); } - self.components + self.infos .push(ComponentInfo::new(ComponentId(index), descriptor)); - - Ok(ComponentId(index)) + Ok(unsafe { self.infos.get_unchecked(index) }) } #[inline] - pub fn get_or_insert_id(&mut self) -> ComponentId { - // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] - unsafe { - self.get_or_insert_with(TypeId::of::(), || { - ComponentDescriptor::new::(StorageType::default()) - }) + pub fn new_resource( + &mut self, + descriptor: ComponentDescriptor, + ) -> Result<&ComponentInfo, ComponentsError> { + let index = self.infos.len(); + if let Some(type_id) = descriptor.type_id { + let index_entry = self.resource_indices.entry(type_id); + if let Entry::Occupied(_) = index_entry { + return Err(ComponentsError::ResourceAlreadyExists { + type_id, + name: descriptor.name, + }); + } + self.resource_indices.insert(type_id, ComponentId(index)); } - } - - #[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) } + self.infos + .push(ComponentInfo::new(ComponentId(index), descriptor)); + Ok(unsafe { self.infos.get_unchecked(index) }) } #[inline] pub fn len(&self) -> usize { - self.components.len() + self.infos.len() } #[inline] pub fn is_empty(&self) -> bool { - self.components.len() == 0 + self.infos.len() == 0 } #[inline] - pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { - self.components.get(id.0) + pub fn info(&self, id: ComponentId) -> Option<&ComponentInfo> { + self.infos.get(id.0) } - /// # Safety + #[inline] + /// Safety /// - /// `id` must be a valid [ComponentId] + /// `id` must be a valid id + pub unsafe fn info_unchecked(&self, id: ComponentId) -> &ComponentInfo { + debug_assert!(id.0 < self.infos.len()); + self.infos.get_unchecked(id.0) + } + + // #[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 component_info(&self, type_id: TypeId) -> Option<&ComponentInfo> { + let id = self.component_indices.get(&type_id).copied()?; + Some(unsafe { self.infos.get_unchecked(id.0) }) } #[inline] - pub fn get_id(&self, type_id: TypeId) -> Option { - self.indices.get(&type_id).map(|index| ComponentId(*index)) + pub fn component_id(&self, type_id: TypeId) -> Option { + self.component_indices.get(&type_id).copied() } + // #[inline] - pub fn get_resource_id(&self, type_id: TypeId) -> Option { - self.resource_indices - .get(&type_id) - .map(|index| ComponentId(*index)) + pub fn resource_info(&self, type_id: TypeId) -> Option<&ComponentInfo> { + let id = self.resource_indices.get(&type_id).copied()?; + Some(unsafe { self.infos.get_unchecked(id.0) }) } #[inline] - pub fn get_or_insert_resource_id(&mut self) -> ComponentId { - // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] - unsafe { - self.get_or_insert_resource_with(TypeId::of::(), || { - ComponentDescriptor::new::(StorageType::default()) - }) + pub fn resource_id(&self, type_id: TypeId) -> Option { + self.resource_indices.get(&type_id).copied() + } + + // + #[inline] + pub fn component_info_or_insert_from(&mut self, layout: ComponentDescriptor) -> &ComponentInfo { + match self.component_indices.get(&layout.type_id().unwrap()) { + Some(&id) => unsafe { self.infos.get_unchecked(id.0) }, + None => self.new_component(layout).unwrap(), } } #[inline] - pub fn get_or_insert_non_send_resource_id(&mut self) -> ComponentId { - // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] - unsafe { - self.get_or_insert_resource_with(TypeId::of::(), || { - ComponentDescriptor::new_non_send::(StorageType::default()) - }) + pub fn component_info_or_insert(&mut self) -> &ComponentInfo { + self.component_info_or_insert_from(ComponentDescriptor::default::()) + } + + // + #[inline] + pub fn component_id_or_insert_from(&mut self, layout: ComponentDescriptor) -> ComponentId { + self.component_indices + .get(&layout.type_id().unwrap()) + .copied() + .unwrap_or_else(|| self.new_component(layout).unwrap().id) + } + + #[inline] + pub fn component_id_or_insert(&mut self) -> ComponentId { + self.component_id_or_insert_from(ComponentDescriptor::default::()) + } + + // + #[inline] + pub fn resource_info_or_insert_from(&mut self, layout: ComponentDescriptor) -> &ComponentInfo { + match self.resource_indices.get(&layout.type_id().unwrap()) { + Some(&id) => unsafe { self.infos.get_unchecked(id.0) }, + None => self.new_resource(layout).unwrap(), } } - /// # Safety - /// - /// The [`ComponentDescriptor`] must match the [`TypeId`] #[inline] - unsafe fn get_or_insert_resource_with( - &mut self, - type_id: TypeId, - func: impl FnOnce() -> ComponentDescriptor, - ) -> ComponentId { - let components = &mut self.components; - let index = self.resource_indices.entry(type_id).or_insert_with(|| { - let descriptor = func(); - let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), descriptor)); - index - }); + pub fn resource_info_or_insert(&mut self) -> &ComponentInfo { + self.resource_info_or_insert_from(ComponentDescriptor::default::()) + } - ComponentId(*index) + #[inline] + pub fn non_send_resource_info_or_insert(&mut self) -> &ComponentInfo { + self.resource_info_or_insert_from(ComponentDescriptor::new_non_send_sync::()) } - /// # Safety - /// - /// The [`ComponentDescriptor`] must match the [`TypeId`] + // #[inline] - pub(crate) unsafe fn get_or_insert_with( - &mut self, - type_id: TypeId, - func: impl FnOnce() -> ComponentDescriptor, - ) -> ComponentId { - let components = &mut self.components; - let index = self.indices.entry(type_id).or_insert_with(|| { - let descriptor = func(); - let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), descriptor)); - index - }); - - ComponentId(*index) + pub fn resource_id_or_insert_from(&mut self, layout: ComponentDescriptor) -> ComponentId { + self.resource_indices + .get(&layout.type_id().unwrap()) + .copied() + .unwrap_or_else(|| self.new_resource(layout).unwrap().id) + } + + #[inline] + pub fn resource_id_or_insert(&mut self) -> ComponentId { + self.resource_id_or_insert_from(ComponentDescriptor::default::()) + } + + #[inline] + pub fn non_send_resource_id_or_insert(&mut self) -> ComponentId { + self.resource_id_or_insert_from(ComponentDescriptor::new_non_send_sync::()) } } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 988d020aaab4a..8bc3d580b37c3 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, QueryState, + QueryTargetFilters, Relation, TargetFilter, 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}, + component::{Component, ComponentDescriptor, StorageType}, entity::Entity, query::{ Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery, @@ -117,8 +120,10 @@ mod tests { assert_eq!( ::component_ids(world.components_mut()), vec![ - world.components_mut().get_or_insert_id::<&'static str>(), - world.components_mut().get_or_insert_id::(), + world + .components_mut() + .component_id_or_insert::<&'static str>(), + world.components_mut().component_id_or_insert::(), ] ); @@ -153,10 +158,12 @@ mod tests { assert_eq!( ::component_ids(world.components_mut()), vec![ - world.components_mut().get_or_insert_id::(), - world.components_mut().get_or_insert_id::<&'static str>(), - world.components_mut().get_or_insert_id::(), - world.components_mut().get_or_insert_id::(), + world.components_mut().component_id_or_insert::(), + world + .components_mut() + .component_id_or_insert::<&'static str>(), + world.components_mut().component_id_or_insert::(), + world.components_mut().component_id_or_insert::(), ] ); @@ -885,14 +892,11 @@ mod tests { assert!(!world.contains_resource::()); world.insert_resource(123); - let resource_id = world - .components() - .get_resource_id(TypeId::of::()) - .unwrap(); + let resource_id = world.components().resource_id(TypeId::of::()).unwrap(); 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); @@ -948,10 +952,7 @@ mod tests { "other resources are unaffected" ); - let current_resource_id = world - .components() - .get_resource_id(TypeId::of::()) - .unwrap(); + let current_resource_id = world.components().resource_id(TypeId::of::()).unwrap(); assert_eq!( resource_id, current_resource_id, "resource id does not change after removing / re-adding" @@ -960,7 +961,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!( @@ -1161,9 +1162,9 @@ 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.component_id(TypeId::of::()).unwrap(); + let f64_id = world.components.component_id(TypeId::of::()).unwrap(); 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 97bdda0462c45..1136c126c8581 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, ComponentId, ComponentTicks, 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<'world, 'state> Fetch<'world, 'state, State = Self::State>; + type Fetch: for<'world, 'state> Fetch< + 'world, + 'state, + State = Self::State, + TargetFilter = ::TargetFilter, + >; type State: FetchState; } pub trait Fetch<'world, 'state>: Sized { type Item; - type State: FetchState; + type State: FetchState; + type TargetFilter: Clone + std::hash::Hash + PartialEq + Eq + Default + Send + Sync + 'static; /// Creates a new instance of this fetch. /// @@ -58,6 +66,7 @@ pub trait Fetch<'world, 'state>: Sized { unsafe fn init( world: &World, state: &Self::State, + target_filter: &Self::TargetFilter, last_change_tick: u32, change_tick: u32, ) -> Self; @@ -76,7 +85,13 @@ pub trait Fetch<'world, 'state>: 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, + target_filter: &Self::TargetFilter, + 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<'world, 'state>: 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, + target_filter: &Self::TargetFilter, + 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,6 +137,8 @@ pub trait Fetch<'world, 'state>: Sized { /// [`FetchState::matches_archetype`], [`FetchState::matches_table`], [`Fetch::archetype_fetch`], and /// [`Fetch::table_fetch`]. pub unsafe trait FetchState: Send + Sync + Sized { + type TargetFilter: 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_archetype_component_access( @@ -124,8 +146,9 @@ pub unsafe trait FetchState: Send + Sync + Sized { 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, target_filter: &Self::TargetFilter) -> bool; + fn matches_table(&self, table: &Table, target_filter: &Self::TargetFilter) -> bool; + fn deduplicate_targets(target_filter: &mut Self::TargetFilter); } /// A fetch that is read only. This must only be implemented for read-only fetches. @@ -149,6 +172,8 @@ pub struct EntityState; // SAFETY: no component or archetype access unsafe impl FetchState for EntityState { + type TargetFilter = (); + fn init(_world: &mut World) -> Self { Self } @@ -163,19 +188,22 @@ unsafe impl FetchState for EntityState { } #[inline] - fn matches_archetype(&self, _archetype: &Archetype) -> bool { + fn matches_archetype(&self, _archetype: &Archetype, _: &Self::TargetFilter) -> bool { true } #[inline] - fn matches_table(&self, _table: &Table) -> bool { + fn matches_table(&self, _table: &Table, _: &Self::TargetFilter) -> bool { true } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } impl<'w, 's> Fetch<'w, 's> for EntityFetch { type Item = Entity; type State = EntityState; + type TargetFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -185,6 +213,7 @@ impl<'w, 's> Fetch<'w, 's> for EntityFetch { unsafe fn init( _world: &World, _state: &Self::State, + _: &Self::TargetFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -197,6 +226,7 @@ impl<'w, 's> Fetch<'w, 's> for EntityFetch { unsafe fn set_archetype( &mut self, _state: &Self::State, + _: &Self::TargetFilter, archetype: &Archetype, _tables: &Tables, ) { @@ -204,7 +234,7 @@ impl<'w, 's> Fetch<'w, 's> for EntityFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, table: &Table) { + unsafe fn set_table(&mut self, _state: &Self::State, _: &Self::TargetFilter, table: &Table) { self.entities = table.entities().as_ptr(); } @@ -234,8 +264,10 @@ 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 TargetFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world.components.component_info_or_insert::(); ReadState { component_id: component_info.id(), storage_type: component_info.storage_type(), @@ -257,19 +289,21 @@ 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, _: &Self::TargetFilter) -> 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, _: &Self::TargetFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } /// The [`Fetch`] of `&T`. @@ -299,6 +333,7 @@ unsafe impl ReadOnlyFetch for ReadFetch {} impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { type Item = &'w T; type State = ReadState; + type TargetFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -311,6 +346,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { unsafe fn init( world: &World, state: &Self::State, + _: &Self::TargetFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -325,7 +361,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id) + .get(state.component_id, None) .unwrap(); } value @@ -335,6 +371,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + _: &Self::TargetFilter, archetype: &Archetype, tables: &Tables, ) { @@ -342,7 +379,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> 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 +388,9 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + unsafe fn set_table(&mut self, state: &Self::State, _: &Self::TargetFilter, table: &Table) { self.table_components = table - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap() .get_data_ptr() .cast::(); @@ -421,8 +458,10 @@ 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 TargetFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world.components.component_info_or_insert::(); WriteState { component_id: component_info.id(), storage_type: component_info.storage_type(), @@ -444,24 +483,27 @@ 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, _: &Self::TargetFilter) -> 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, _: &Self::TargetFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { type Item = Mut<'w, T>; type State = WriteState; + type TargetFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -474,6 +516,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { unsafe fn init( world: &World, state: &Self::State, + _: &Self::TargetFilter, last_change_tick: u32, change_tick: u32, ) -> Self { @@ -491,7 +534,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id) + .get(state.component_id, None) .unwrap(); } value @@ -501,6 +544,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + _: &Self::TargetFilter, archetype: &Archetype, tables: &Tables, ) { @@ -508,7 +552,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> 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 +562,8 @@ impl<'w, 's, T: Component> Fetch<'w, 's> 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, _: &Self::TargetFilter, 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 +611,582 @@ impl<'w, 's, T: Component> Fetch<'w, 's> 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, + component_id: ComponentId, + storage_type: StorageType, +} + +unsafe impl FetchState for ReadRelationState { + type TargetFilter = smallvec::SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let component_info = + world + .components + .component_info_or_insert_from(ComponentDescriptor::new_targeted::( + StorageType::Table, + )); + + Self { + p: PhantomData, + component_id: component_info.id(), + storage_type: component_info.storage_type(), + } + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + if access.access().has_write(self.component_id) { + 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.component_id); + } + + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + if self.matches_archetype(archetype, &Default::default()) { + // FIXME(Relationships): make `ArchetypeComponentId` work like `ComponentId` and not be + // a fresh ID for every target I think? Need to investigate more what `ArchetypeComponentId` + // is actually used for and if that is even possible to do :) + let targets = archetype.relations.get(self.component_id).unwrap(); + for id in targets.values() { + access.add_read(id.archetype_component_id); + } + } + } + + fn matches_archetype( + &self, + archetype: &Archetype, + target_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + if archetype.relations.get(self.component_id).is_none() { + return false; + } + target_filter + .iter() + .all(|target| archetype.contains(self.component_id, Some(*target))) + } + + fn matches_table(&self, table: &Table, target_filter: &SmallVec<[Entity; 4]>) -> bool { + if table + .targeted_component_columns + .get(self.component_id) + .is_none() + { + return false; + } + target_filter + .iter() + .all(|target| table.has_column(self.component_id, Some(*target))) + } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + target_filter.sort(); + target_filter.dedup(); + } +} + +pub struct ReadRelationFetch { + component_id: ComponentId, + target_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, + .. + }) => 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 `ReadRelationState::deduplicate_targets` + // so this will not lead to aliasing borrows if users insert two identical target filters + 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, + .. + }) => 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 `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, &*ptr)) + }, + } + } +} + +impl<'w, 's, T: Component> Fetch<'w, 's> for ReadRelationFetch { + type Item = RelationAccess<'w, 's, T>; + type State = ReadRelationState; + type TargetFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + world: &World, + state: &Self::State, + target_filter: &Self::TargetFilter, + _last_change_tick: u32, + _change_tick: u32, + ) -> Self { + Self { + component_id: state.component_id, + target_filter_ptr: target_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, + _: &Self::TargetFilter, + 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, _: &Self::TargetFilter, 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.target_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.component_id).unwrap().keys()), + _ => Either::U(target_filters.iter()), + }; + + RelationAccess::Sparse(SparseRelationAccess { + current_entity: (&*self.entities)[archetype_index], + sparse_sets: sparse_sets + .get_sets_of_component_id(self.component_id) + .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.component_id)` instead of this + let table = &*self.table_ptr; + + let target_filters = &*self.target_filter_ptr; + let iter = match target_filters.len() { + 0 => Either::T( + table + .targeted_component_columns + .get(self.component_id) + .unwrap() + .keys(), + ), + _ => Either::U(target_filters.iter()), + }; + + RelationAccess::Table(TableRelationAccess { + columns: table + .targeted_component_columns + .get(self.component_id) + .unwrap(), + current_idx: table_row, + iter, + p: PhantomData, + }) + } +} + +impl WorldQuery for &mut Relation { + type Fetch = WriteRelationFetch; + type State = WriteRelationState; +} + +pub struct WriteRelationState { + p: PhantomData, + component_id: ComponentId, + storage_type: StorageType, +} + +unsafe impl FetchState for WriteRelationState { + type TargetFilter = smallvec::SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let component_info = + world + .components + .component_info_or_insert_from(ComponentDescriptor::new_targeted::( + StorageType::Table, + )); + + Self { + p: PhantomData, + component_id: component_info.id(), + storage_type: component_info.storage_type(), + } + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + if access.access().has_read(self.component_id) { + panic!("&mut Relation<{}> conflicts with a previous access in this query. Mutable access must be exclusive.", + std::any::type_name::()); + } + access.add_write(self.component_id); + } + + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + if self.matches_archetype(archetype, &Default::default()) { + let targets = archetype.relations.get(self.component_id).unwrap(); + for id in targets.values() { + access.add_write(id.archetype_component_id); + } + } + } + + fn matches_archetype( + &self, + archetype: &Archetype, + target_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + if archetype.relations.get(self.component_id).is_none() { + return false; + } + target_filter + .iter() + .all(|target| archetype.contains(self.component_id, Some(*target))) + } + + fn matches_table(&self, table: &Table, target_filter: &SmallVec<[Entity; 4]>) -> bool { + if table + .targeted_component_columns + .get(self.component_id) + .is_none() + { + return false; + } + target_filter + .iter() + .all(|target| table.has_column(self.component_id, Some(*target))) + } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + target_filter.sort(); + target_filter.dedup(); + } +} + +pub struct WriteRelationFetch { + component_id: ComponentId, + target_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 RelationAccessMut<'w, 's, T: Component> { + access: RelationAccess<'w, 's, T>, + change_tick: u32, + last_change_tick: u32, +} + +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 { + let (target, ptr, ticks) = match &mut self.access { + RelationAccess::Table(TableRelationAccess { + current_idx, + columns, + iter, + .. + }) => 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 col = columns.get(target).unwrap(); + let ptr = col.get_data_unchecked(*current_idx) as *mut T; + let ticks = col.get_ticks_mut_ptr_unchecked(*current_idx); + (target, ptr, ticks) + }, + RelationAccess::Sparse(SparseRelationAccess { + current_entity, + sparse_sets, + iter, + .. + }) => 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; + (target, ptr, ticks) + }, + }; + + Some(( + *target, + Mut { + value: unsafe { &mut *ptr }, + ticks: Ticks { + component_ticks: unsafe { &mut *ticks }, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, + }, + )) + } +} + +impl<'w, 's, T: Component> Fetch<'w, 's> for WriteRelationFetch { + type Item = RelationAccessMut<'w, 's, T>; + type State = WriteRelationState; + type TargetFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + world: &World, + state: &Self::State, + target_filter: &Self::TargetFilter, + last_change_tick: u32, + change_tick: u32, + ) -> Self { + Self { + component_id: state.component_id, + target_filter_ptr: target_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, + _: &Self::TargetFilter, + 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, _: &Self::TargetFilter, 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.target_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.component_id).unwrap().keys()), + _ => Either::U(target_filters.iter()), + }; + + RelationAccessMut { + access: RelationAccess::Sparse(SparseRelationAccess { + current_entity: (&*self.entities)[archetype_index], + sparse_sets: sparse_sets + .get_sets_of_component_id(self.component_id) + .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.target_filter_ptr; + let iter = match target_filters.len() { + 0 => Either::T( + table + .targeted_component_columns + .get(self.component_id) + .unwrap() + .keys(), + ), + _ => Either::U(target_filters.iter()), + }; + + RelationAccessMut { + access: RelationAccess::Table(TableRelationAccess { + columns: table + .targeted_component_columns + .get(self.component_id) + .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,6 +1209,8 @@ pub struct OptionState { // SAFETY: component access and archetype component access are properly updated according to the // internal Fetch unsafe impl FetchState for OptionState { + type TargetFilter = T::TargetFilter; + fn init(world: &mut World) -> Self { Self { state: T::init(world), @@ -604,24 +1226,29 @@ 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, _: &Self::TargetFilter) -> bool { true } - fn matches_table(&self, _table: &Table) -> bool { + fn matches_table(&self, _table: &Table, _: &Self::TargetFilter) -> bool { true } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + T::deduplicate_targets(target_filter); + } } impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch { type Item = Option; type State = OptionState; + type TargetFilter = T::TargetFilter; #[inline] fn is_dense(&self) -> bool { @@ -631,11 +1258,18 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch { unsafe fn init( world: &World, state: &Self::State, + target_filter: &Self::TargetFilter, 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, + target_filter, + last_change_tick, + change_tick, + ), matches: false, } } @@ -644,20 +1278,27 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + target_filter: &Self::TargetFilter, archetype: &Archetype, tables: &Tables, ) { - self.matches = state.state.matches_archetype(archetype); + self.matches = state.state.matches_archetype(archetype, target_filter); if self.matches { - self.fetch.set_archetype(&state.state, archetype, tables); + self.fetch + .set_archetype(&state.state, target_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, + target_filter: &Self::TargetFilter, + table: &Table, + ) { + self.matches = state.state.matches_table(table, target_filter); if self.matches { - self.fetch.set_table(&state.state, table); + self.fetch.set_table(&state.state, target_filter, table); } } @@ -757,8 +1398,11 @@ 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 TargetFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world.components.component_info_or_insert::(); + Self { component_id: component_info.id(), storage_type: component_info.storage_type(), @@ -780,19 +1424,21 @@ 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, _: &Self::TargetFilter) -> 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, _: &Self::TargetFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } /// The [`Fetch`] of [`ChangeTrackers`]. @@ -813,6 +1459,7 @@ unsafe impl ReadOnlyFetch for ChangeTrackersFetch {} impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { type Item = ChangeTrackers; type State = ChangeTrackersState; + type TargetFilter = (); #[inline] fn is_dense(&self) -> bool { @@ -825,6 +1472,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { unsafe fn init( world: &World, state: &Self::State, + _: &Self::TargetFilter, last_change_tick: u32, change_tick: u32, ) -> Self { @@ -842,7 +1490,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { value.sparse_set = world .storages() .sparse_sets - .get(state.component_id) + .get(state.component_id, None) .unwrap(); } value @@ -852,6 +1500,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { unsafe fn set_archetype( &mut self, state: &Self::State, + _: &Self::TargetFilter, archetype: &Archetype, tables: &Tables, ) { @@ -859,7 +1508,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> 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 +1517,9 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { } #[inline] - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + unsafe fn set_table(&mut self, state: &Self::State, _: &Self::TargetFilter, table: &Table) { self.table_ticks = table - .get_column(state.component_id) + .get_column(state.component_id, None) .unwrap() .get_ticks_const_ptr(); } @@ -911,16 +1560,18 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { } macro_rules! impl_tuple_fetch { - ($(($name: ident, $state: ident)),*) => { + ($(($name: ident, $state: ident, $target_filter: ident)),*) => { #[allow(non_snake_case)] impl<'w, 's, $($name: Fetch<'w, 's>),*> Fetch<'w, 's> for ($($name,)*) { type Item = ($($name::Item,)*); type State = ($($name::State,)*); + type TargetFilter = ($($name::TargetFilter,)*); #[allow(clippy::unused_unit)] - unsafe fn init(_world: &World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self { + unsafe fn init(_world: &World, state: &Self::State, target_filter: &Self::TargetFilter, _last_change_tick: u32, _change_tick: u32) -> Self { let ($($name,)*) = state; - ($($name::init(_world, $name, _last_change_tick, _change_tick),)*) + let ($($target_filter,)*) = target_filter; + ($($name::init(_world, $name, $target_filter, _last_change_tick, _change_tick),)*) } @@ -931,17 +1582,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, target_filter: &Self::TargetFilter, _archetype: &Archetype, _tables: &Tables) { let ($($name,)*) = self; let ($($state,)*) = _state; - $($name.set_archetype($state, _archetype, _tables);)* + let ($($target_filter,)*) = target_filter; + $($name.set_archetype($state, $target_filter, _archetype, _tables);)* } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) { + unsafe fn set_table(&mut self, _state: &Self::State, _target_filter: &Self::TargetFilter, _table: &Table) { let ($($name,)*) = self; let ($($state,)*) = _state; - $($name.set_table($state, _table);)* + let ($($target_filter,)*) = _target_filter; + $($name.set_table($state, $target_filter, _table);)* } #[inline] @@ -963,6 +1616,8 @@ macro_rules! impl_tuple_fetch { #[allow(non_snake_case)] #[allow(clippy::unused_unit)] unsafe impl<$($name: FetchState),*> FetchState for ($($name,)*) { + type TargetFilter = ($($name::TargetFilter,)*); + fn init(_world: &mut World) -> Self { ($($name::init(_world),)*) } @@ -977,14 +1632,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, _target_filter: &Self::TargetFilter) -> bool { let ($($name,)*) = self; - true $(&& $name.matches_archetype(_archetype))* + let ($($target_filter,)*) = _target_filter; + true $(&& $name.matches_archetype(_archetype, $target_filter))* } - fn matches_table(&self, _table: &Table) -> bool { + fn matches_table(&self, _table: &Table, _target_filter: &Self::TargetFilter) -> bool { let ($($name,)*) = self; - true $(&& $name.matches_table(_table))* + let ($($target_filter,)*) = _target_filter; + true $(&& $name.matches_table(_table, $target_filter))* + } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + let ($($name,)*) = target_filter; + $($name::deduplicate_targets($name);)* } } @@ -999,4 +1661,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 9a46035c62949..113957339da50 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -1,15 +1,18 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, bundle::Bundle, - component::{Component, ComponentId, ComponentTicks, StorageType}, + component::{Component, ComponentDescriptor, ComponentId, ComponentTicks, 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; + /// 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<'w, 's> Fetch<'w, 's> { @@ -66,7 +69,7 @@ where /// } /// # compliment_entity_system.system(); /// ``` -pub struct With(PhantomData); +pub struct With(PhantomData>); impl WorldQuery for With { type Fetch = WithFetch; @@ -88,8 +91,10 @@ pub struct WithState { // SAFETY: no component access or archetype component access unsafe impl FetchState for WithState { + type TargetFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world.components.component_info_or_insert::(); Self { component_id: component_info.id(), storage_type: component_info.storage_type(), @@ -110,22 +115,26 @@ unsafe impl FetchState for WithState { ) { } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - archetype.contains(self.component_id) + fn matches_archetype(&self, archetype: &Archetype, _: &Self::TargetFilter) -> 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, _: &Self::TargetFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch { type Item = bool; type State = WithState; + type TargetFilter = (); unsafe fn init( _world: &World, state: &Self::State, + _: &Self::TargetFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -141,12 +150,13 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} + unsafe fn set_table(&mut self, _state: &Self::State, _: &Self::TargetFilter, _table: &Table) {} #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, + _: &Self::TargetFilter, _archetype: &Archetype, _tables: &Tables, ) { @@ -185,7 +195,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch { /// } /// # no_permit_system.system(); /// ``` -pub struct Without(PhantomData); +pub struct Without(PhantomData>); impl WorldQuery for Without { type Fetch = WithoutFetch; @@ -207,8 +217,10 @@ pub struct WithoutState { // SAFETY: no component access or archetype component access unsafe impl FetchState for WithoutState { + type TargetFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world.components.component_info_or_insert::(); Self { component_id: component_info.id(), storage_type: component_info.storage_type(), @@ -229,22 +241,26 @@ unsafe impl FetchState for WithoutState { ) { } - fn matches_archetype(&self, archetype: &Archetype) -> bool { - !archetype.contains(self.component_id) + fn matches_archetype(&self, archetype: &Archetype, _: &Self::TargetFilter) -> 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, _: &Self::TargetFilter) -> bool { + !table.has_column(self.component_id, None) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch { type Item = bool; type State = WithoutState; + type TargetFilter = (); unsafe fn init( _world: &World, state: &Self::State, + _: &Self::TargetFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -260,12 +276,13 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} + unsafe fn set_table(&mut self, _state: &Self::State, _: &Self::TargetFilter, _table: &Table) {} #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, + _: &Self::TargetFilter, _archetype: &Archetype, _tables: &Tables, ) { @@ -295,20 +312,22 @@ pub struct WithBundleFetch { } pub struct WithBundleState { - component_ids: Vec, + component_ids: Vec<(ComponentId, Option)>, is_dense: bool, marker: PhantomData, } // SAFETY: no component access or archetype component access unsafe impl FetchState for WithBundleState { + type TargetFilter = (); + fn init(world: &mut World) -> Self { - let bundle_info = world.bundles.init_info::(&mut world.components); + let bundle_info = world.bundles.init_bundle_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 + is_dense: bundle_info.component_ids.iter().all(|(component_id, _)| { + components.info(*component_id).unwrap().storage_type() == StorageType::Table }), marker: PhantomData, } @@ -316,7 +335,7 @@ unsafe impl FetchState for WithBundleState { #[inline] fn update_component_access(&self, access: &mut FilteredAccess) { - for component_id in self.component_ids.iter().cloned() { + for (component_id, _) in self.component_ids.iter().cloned() { access.add_with(component_id); } } @@ -329,22 +348,30 @@ 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, _: &Self::TargetFilter) -> bool { + self.component_ids + .iter() + .all(|&(component_id, target)| archetype.contains(component_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, _: &Self::TargetFilter) -> bool { + self.component_ids + .iter() + .all(|&(component_id, target)| table.has_column(component_id, target)) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch { type Item = bool; type State = WithBundleState; + type TargetFilter = (); unsafe fn init( _world: &World, state: &Self::State, + _: &Self::TargetFilter, _last_change_tick: u32, _change_tick: u32, ) -> Self { @@ -360,12 +387,155 @@ impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} + unsafe fn set_table(&mut self, _state: &Self::State, _: &Self::TargetFilter, _table: &Table) {} + + #[inline] + unsafe fn set_archetype( + &mut self, + _state: &Self::State, + _: &Self::TargetFilter, + _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 Without> { + type Fetch = WithoutRelationFetch; + type State = WithoutRelationState; +} + +pub struct WithoutRelationState { + storage_type: StorageType, + component_id: ComponentId, + marker: PhantomData, +} + +unsafe impl FetchState for WithoutRelationState { + type TargetFilter = smallvec::SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let component_info = + world + .components + .component_info_or_insert_from(ComponentDescriptor::new_targeted::( + StorageType::Table, + )); + + Self { + marker: PhantomData, + component_id: component_info.id(), + storage_type: component_info.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, + target_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.component_id).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 target_filter.is_empty() { + return false; + } + target_filter + .iter() + .all(|&target| !archetype.contains(self.component_id, Some(target))) + } + + fn matches_table(&self, table: &Table, target_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 + .targeted_component_columns + .get(self.component_id) + .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 target_filter.is_empty() { + return false; + } + target_filter + .iter() + .all(|&target| !table.has_column(self.component_id, Some(target))) + } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + target_filter.sort(); + target_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 TargetFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + _world: &World, + state: &Self::State, + _: &Self::TargetFilter, + _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, _: &Self::TargetFilter, _table: &Table) {} #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, + _: &Self::TargetFilter, _archetype: &Archetype, _tables: &Tables, ) { @@ -382,6 +552,130 @@ impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch { } } +impl WorldQuery for With> { + type Fetch = WithRelationFetch; + type State = WithRelationState; +} + +pub struct WithRelationState { + storage_type: StorageType, + component_id: ComponentId, + marker: PhantomData, +} + +unsafe impl FetchState for WithRelationState { + type TargetFilter = SmallVec<[Entity; 4]>; + + fn init(world: &mut World) -> Self { + let component_info = + world + .components + .component_info_or_insert_from(ComponentDescriptor::new_targeted::( + StorageType::Table, + )); + + Self { + marker: PhantomData, + component_id: component_info.id(), + storage_type: component_info.storage_type(), + } + } + + fn update_component_access(&self, access: &mut FilteredAccess) { + access.add_with(self.component_id); + } + + fn update_archetype_component_access( + &self, + _archetype: &Archetype, + _access: &mut Access, + ) { + } + + fn matches_archetype( + &self, + archetype: &Archetype, + target_filter: &SmallVec<[Entity; 4]>, + ) -> bool { + if archetype.relations.get(self.component_id).is_none() { + return false; + } + target_filter + .iter() + .all(|&target| archetype.contains(self.component_id, Some(target))) + } + + fn matches_table(&self, table: &Table, target_filter: &SmallVec<[Entity; 4]>) -> bool { + if table + .targeted_component_columns + .get(self.component_id) + .is_none() + { + return false; + } + target_filter + .iter() + .all(|&target| table.has_column(self.component_id, Some(target))) + } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + target_filter.sort(); + target_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 TargetFilter = SmallVec<[Entity; 4]>; + + unsafe fn init( + _world: &World, + state: &Self::State, + _: &Self::TargetFilter, + _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, _: &Self::TargetFilter, _table: &Table) {} + + #[inline] + unsafe fn set_archetype( + &mut self, + _state: &Self::State, + _: &Self::TargetFilter, + _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 + } +} + /// A filter that tests if any of the given filters apply. /// /// This is useful for example if a system with multiple components in a query only wants to run @@ -419,7 +713,7 @@ pub struct OrFetch { } macro_rules! impl_query_filter_tuple { - ($(($filter: ident, $state: ident)),*) => { + ($(($filter: ident, $state: ident, $target_filter: ident)),*) => { #[allow(unused_variables)] #[allow(non_snake_case)] impl<'a, $($filter: FilterFetch),*> FilterFetch for ($($filter,)*) { @@ -449,11 +743,13 @@ macro_rules! impl_query_filter_tuple { 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 TargetFilter = ($(<$filter as Fetch<'w, 's>>::TargetFilter,)*); - unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self { + unsafe fn init(world: &World, state: &Self::State, target_filter: &Self::TargetFilter, last_change_tick: u32, change_tick: u32) -> Self { let ($($filter,)*) = &state.0; + let ($($target_filter,)*) = target_filter; Or(($(OrFetch { - fetch: $filter::init(world, $filter, last_change_tick, change_tick), + fetch: $filter::init(world, $filter, $target_filter, last_change_tick, change_tick), matches: false, },)*)) } @@ -465,25 +761,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, target_filter: &Self::TargetFilter, table: &Table) { let ($($filter,)*) = &mut self.0; let ($($state,)*) = &state.0; + let ($($target_filter,)*) = target_filter; $( - $filter.matches = $state.matches_table(table); + $filter.matches = $state.matches_table(table, $target_filter); if $filter.matches { - $filter.fetch.set_table($state, table); + $filter.fetch.set_table($state, $target_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, target_filter: &Self::TargetFilter, archetype: &Archetype, tables: &Tables) { let ($($filter,)*) = &mut self.0; let ($($state,)*) = &state.0; + let ($($target_filter,)*) = target_filter; $( - $filter.matches = $state.matches_archetype(archetype); + $filter.matches = $state.matches_archetype(archetype, $target_filter); if $filter.matches { - $filter.fetch.set_archetype($state, archetype, tables); + $filter.fetch.set_archetype($state, $target_filter, archetype, tables); } )* } @@ -505,6 +803,8 @@ macro_rules! impl_query_filter_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] unsafe impl<$($filter: FetchState),*> FetchState for Or<($($filter,)*)> { + type TargetFilter = ($($filter::TargetFilter,)*); + fn init(world: &mut World) -> Self { Or(($($filter::init(world),)*)) } @@ -519,20 +819,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, target_filter: &Self::TargetFilter) -> bool { let ($($filter,)*) = &self.0; - false $(|| $filter.matches_archetype(archetype))* + let ($($target_filter,)*) = target_filter; + false $(|| $filter.matches_archetype(archetype, $target_filter))* } - fn matches_table(&self, table: &Table) -> bool { + fn matches_table(&self, table: &Table, target_filter: &Self::TargetFilter,) -> bool { let ($($filter,)*) = &self.0; - false $(|| $filter.matches_table(table))* + let ($($target_filter,)*) = target_filter; + false $(|| $filter.matches_table(table, $target_filter))* + } + + fn deduplicate_targets(target_filter: &mut Self::TargetFilter) { + let ($($filter,)*) = target_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 { ( @@ -574,8 +881,13 @@ 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 TargetFilter = (); + fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_info = world + .components + .component_info_or_insert::(); + Self { component_id: component_info.id(), storage_type: component_info.storage_type(), @@ -598,25 +910,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, _: &Self::TargetFilter) -> 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, _: &Self::TargetFilter) -> bool { + table.has_column(self.component_id, None) } + + fn deduplicate_targets(_: &mut Self::TargetFilter) {} } impl<'w, 's, T: Component> Fetch<'w, 's> for $fetch_name { type State = $state_name; type Item = bool; + type TargetFilter = (); - unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self { + unsafe fn init(world: &World, state: &Self::State, _: &Self::TargetFilter, last_change_tick: u32, change_tick: u32) -> Self { let mut value = Self { storage_type: state.storage_type, table_ticks: ptr::null::>(), @@ -631,7 +946,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 +956,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, _: &Self::TargetFilter, 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, _: &Self::TargetFilter, 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 7585e150878d8..c1e80eaac25c7 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_.0, last_change_tick, change_tick, ); let filter = ::init( world, &query_state.filter_state, + &query_state.current_.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_.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_.1, archetype, self.tables, ); @@ -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_.0, + table, + ); + self.filter.set_table( + &self.query_state.filter_state, + &self.query_state.current_.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_.0, archetype, self.tables, ); self.filter.set_archetype( &self.query_state.filter_state, + &self.query_state.current_.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()) @@ -364,6 +386,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()) @@ -392,7 +415,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()) @@ -401,6 +424,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, @@ -417,6 +441,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(), @@ -454,21 +479,25 @@ where let fetch = ::init( world, &query_state.fetch_state, + &query_state.current_.0, last_change_tick, change_tick, ); let filter = ::init( world, &query_state.filter_state, + &query_state.current_.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, } @@ -503,8 +532,13 @@ where 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_.0, table); + self.filter.set_table( + &query_state.filter_state, + &query_state.current_.1, + table, + ); self.current_len = table.len(); self.current_index = 0; continue; @@ -525,10 +559,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_.0, + archetype, + tables, + ); + self.filter.set_archetype( + &query_state.filter_state, + &query_state.current_.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..eaf30f2e2d685 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -3,12 +3,14 @@ mod fetch; mod filter; mod iter; mod state; +mod target_filter; pub use access::*; pub use fetch::*; pub use filter::*; pub use iter::*; pub use state::*; +pub use target_filter::*; #[cfg(test)] mod tests { diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 6349d3101b2a0..32640efd6d2b7 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, ComponentId}, entity::Entity, query::{ Access, Fetch, FetchState, FilterFetch, FilteredAccess, QueryCombinationIter, QueryIter, @@ -13,20 +15,65 @@ use bevy_tasks::TaskPool; use fixedbitset::FixedBitSet; use thiserror::Error; -pub struct QueryState +use super::{QueryTargetFilters, SpecifiesRelation, TargetFilter}; + +pub struct QueryStateTargetFiltersBuilder<'a, 'b, Q: WorldQuery, F: WorldQuery> where F::Fetch: FilterFetch, { - world_id: WorldId, + state: &'a mut QueryState, + world: &'b World, + filters: QueryTargetFilters, +} + +impl<'a, 'b, Q: WorldQuery, F: WorldQuery> QueryStateTargetFiltersBuilder<'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. + #[must_use = "target filters will be unchanged if you do not call `apply_filters`"] + pub fn add_(mut self, filter: TargetFilter) -> Self + where + QueryTargetFilters: + SpecifiesRelation>, + { + self.filters.add_filter_relation(filter); + self + } + + pub fn apply_filters(self) -> &'a mut QueryState { + let Self { + state, + world, + filters, + } = self; + state.set_target_filters(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_: QueryTargetFilters, + pub(crate) target_filter_accesses: HashMap, QueryAccessCache>, + pub(crate) fetch_state: Q::State, pub(crate) filter_state: F::State, } @@ -54,16 +101,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_: Default::default(), + target_filter_accesses: HashMap::new(), + archetype_component_access: Default::default(), }; + state.set_target_filters(world, QueryTargetFilters::default()); state.validate_world_and_update_archetypes(world); state } @@ -78,40 +125,108 @@ where } } + pub fn current_query_access_cache(&self) -> &QueryAccessCache { + self.target_filter_accesses.get(&self.current_).unwrap() + } + + pub fn clear_target_filters(&mut self, world: &World) -> &mut Self { + self.set_target_filters(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 = "target filters will be unchanged if you do not call `apply_filters`"] + pub fn new_target_filters<'a, K: Component, Path>( + &mut self, + world: &'a World, + target_filter: TargetFilter, + ) -> QueryStateTargetFiltersBuilder<'_, 'a, Q, F> + where + QueryTargetFilters: + SpecifiesRelation>, + { + QueryStateTargetFiltersBuilder { + state: self, + world, + filters: QueryTargetFilters::new(), + } + .add_(target_filter) + } + + pub(crate) fn set_target_filters( + &mut self, + world: &World, + mut target_filter: QueryTargetFilters, + ) { + // We deduplicate targets so that `RelationAccess` and `RelationAccessMut` + // dont yield aliasing borrows when we have two identical target filters + target_filter.deduplicate_targets(); + self.current_ = target_filter.clone(); + self.target_filter_accesses + .entry(target_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 (target_filter, cache) in self.target_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, + &*target_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, + target_filter: &QueryTargetFilters, + cache: &mut QueryAccessCache, + archetype: &Archetype, + ) { + if fetch_state.matches_archetype(archetype, &target_filter.0) + && filter_state.matches_archetype(archetype, &target_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()); } } } @@ -173,19 +288,40 @@ where .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_.0, + last_change_tick, + change_tick, + ); + let mut filter = ::init( + world, + &self.filter_state, + &self.current_.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_.0, + archetype, + &world.storages().tables, + ); + filter.set_archetype( + &self.filter_state, + &self.current_.1, + archetype, + &world.storages().tables, + ); if filter.archetype_filter_fetch(location.index) { Ok(fetch.archetype_fetch(location.index)) } else { @@ -402,16 +538,26 @@ where ) { // 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_.0, + last_change_tick, + change_tick, + ); + let mut filter = ::init( + world, + &self.filter_state, + &self.current_.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_.0, table); + filter.set_table(&self.filter_state, &self.current_.1, table); for table_index in 0..table.len() { if !filter.table_filter_fetch(table_index) { @@ -424,10 +570,14 @@ 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_.0, archetype, tables); + filter.set_archetype(&self.filter_state, &self.current_.1, archetype, tables); for archetype_index in 0..archetype.len() { if !filter.archetype_filter_fetch(archetype_index) { @@ -457,14 +607,24 @@ where // 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_.0, + last_change_tick, + change_tick, + ); + let filter = ::init( + world, + &self.filter_state, + &self.current_.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 +633,21 @@ where let mut fetch = ::init( world, &self.fetch_state, + &self.current_.0, last_change_tick, change_tick, ); let mut filter = ::init( world, &self.filter_state, + &self.current_.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_.0, table); + filter.set_table(&self.filter_state, &self.current_.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 +662,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 +675,31 @@ where let mut fetch = ::init( world, &self.fetch_state, + &self.current_.0, last_change_tick, change_tick, ); let mut filter = ::init( world, &self.filter_state, + &self.current_.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_.0, + archetype, + tables, + ); + filter.set_archetype( + &self.filter_state, + &self.current_.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/query/target_filter.rs b/crates/bevy_ecs/src/query/target_filter.rs new file mode 100644 index 0000000000000..5be58e88a724a --- /dev/null +++ b/crates/bevy_ecs/src/query/target_filter.rs @@ -0,0 +1,249 @@ +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 QueryTargetFilters( + pub ::TargetFilter, + pub ::TargetFilter, + PhantomData (Q, F)>, +); + +macro_rules! impl_trait { + ($trait:ident, $($body:tt)*) => { + impl $trait for QueryTargetFilters + where + ::TargetFilter: $trait, + ::TargetFilter: $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 TargetFilter(SmallVec<[Entity; 4]>, PhantomData); + +impl TargetFilter { + pub fn new() -> Self { + Self(SmallVec::new(), PhantomData) + } + + pub fn target(mut self, target: Entity) -> Self { + self.0.push(target); + self + } +} + +impl QueryTargetFilters { + pub fn new() -> Self { + Self::default() + } + + pub fn add_filter_relation(&mut self, filter: TargetFilter) + where + Self: SpecifiesRelation, + { + Self::__add_(filter, self); + } + + pub fn deduplicate_targets(&mut self) { + ::deduplicate_targets(&mut self.0); + ::deduplicate_targets(&mut self.1); + } +} + +pub trait SpecifiesRelation { + type TargetFilter; + fn __add_(entity: TargetFilter, target_filter: &mut Self::TargetFilter); +} + +pub struct Intrinsic; +pub struct InData(PhantomData); +pub struct InFilter(PhantomData); +pub struct InTuple(PhantomData); + +impl SpecifiesRelation for &Relation { + type TargetFilter = <::State as FetchState>::TargetFilter; + fn __add_(filter: TargetFilter, target_filter: &mut smallvec::SmallVec<[Entity; 4]>) { + target_filter.extend(filter.0.into_iter()); + } +} +impl SpecifiesRelation for &mut Relation { + type TargetFilter = <::State as FetchState>::TargetFilter; + fn __add_(filter: TargetFilter, target_filter: &mut smallvec::SmallVec<[Entity; 4]>) { + target_filter.extend(filter.0.into_iter()); + } +} +impl SpecifiesRelation + for crate::prelude::Without> +{ + type TargetFilter = <::State as FetchState>::TargetFilter; + fn __add_(filter: TargetFilter, target_filter: &mut smallvec::SmallVec<[Entity; 4]>) { + target_filter.extend(filter.0.into_iter()); + } +} +impl SpecifiesRelation for crate::prelude::With> { + type TargetFilter = <::State as FetchState>::TargetFilter; + fn __add_(filter: TargetFilter, target_filter: &mut smallvec::SmallVec<[Entity; 4]>) { + target_filter.extend(filter.0.into_iter()); + } +} + +impl SpecifiesRelation> + for QueryTargetFilters +where + Q: SpecifiesRelation< + Kind, + Path, + TargetFilter = <::State as FetchState>::TargetFilter, + >, +{ + type TargetFilter = Self; + fn __add_(entity: TargetFilter, target_filter: &mut Self::TargetFilter) { + Q::__add_(entity, &mut target_filter.0); + } +} +impl SpecifiesRelation> + for QueryTargetFilters +where + F: SpecifiesRelation< + Kind, + Path, + TargetFilter = <::State as FetchState>::TargetFilter, + >, +{ + type TargetFilter = Self; + fn __add_(entity: TargetFilter, target_filter: &mut Self::TargetFilter) { + F::__add_(entity, &mut target_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, + TargetFilter = <::State as FetchState>::TargetFilter, + >, + { + type TargetFilter = ( + $(<<$head as WorldQuery>::State as FetchState>::TargetFilter,)* + ::TargetFilter, + $(<<$tail as WorldQuery>::State as FetchState>::TargetFilter,)* + ); + + #[allow(non_snake_case, unused)] + fn __add_(entity: TargetFilter, target_filter: &mut Self::TargetFilter) { + let ( + $($head,)* + my_thing, + $($tail,)* + ) = target_filter; + Selected::__add_(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: QueryTargetFilters<&Relation, ()> = Default::default(); + filter.add_filter_relation(TargetFilter::::new().target(Entity::new(1))); + dbg!(&filter.0); + + let mut filter: QueryTargetFilters<(&Relation, &Relation), ()> = Default::default(); + filter.add_filter_relation(TargetFilter::::new().target(Entity::new(1))); + filter.add_filter_relation(TargetFilter::::new().target(Entity::new(12))); + dbg!(&filter.0); +} diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index 9d5dda4bdf385..63d4494a2ba6a 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -490,7 +490,7 @@ impl SystemStage { if !conflicts.is_empty() { let names = conflicts .iter() - .map(|id| world.components().get_info(*id).unwrap().name()) + .map(|id| world.components().info(*id).unwrap().name()) .collect::>(); writeln!(string, " conflicts: {:?}", names).unwrap(); } diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 6d0b85ffbbe90..a5172e7a21d3a 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -1,3 +1,5 @@ +use bevy_utils::HashMap; + use crate::{ component::{ComponentId, ComponentInfo, ComponentTicks}, entity::Entity, @@ -235,6 +237,18 @@ impl SparseSet { } impl SparseSet { + pub fn dense_len(&self) -> usize { + self.dense.len() + } + + pub fn indices_len(&self) -> usize { + self.indices.len() + } + + pub fn sparse_len(&self) -> usize { + self.sparse.values.len() + } + pub fn with_capacity(capacity: usize) -> Self { Self { dense: Vec::with_capacity(capacity), @@ -377,33 +391,72 @@ impl_sparse_set_index!(u8, u16, u32, u64, usize); #[derive(Default)] pub struct SparseSets { - sets: SparseSet, + component_sets: SparseSet, + targeted_component_sets: SparseSet>, } impl SparseSets { - pub fn get_or_insert(&mut self, component_info: &ComponentInfo) -> &mut ComponentSparseSet { - if !self.sets.contains(component_info.id()) { - self.sets.insert( - component_info.id(), - ComponentSparseSet::new(component_info, 64), - ); + pub fn get_sets_of_component_id( + &self, + component_id: ComponentId, + ) -> Option<&HashMap> { + self.targeted_component_sets.get(component_id) + } + + // FIXME(Relationships): https://discord.com/channels/691052431525675048/749335865876021248/862199702825205760 + // FIXME(Relationships): Deal with the ability to register components with a target, and relations without one + pub fn get_or_insert( + &mut self, + component_info: &ComponentInfo, + target: Option, + ) -> &mut ComponentSparseSet { + match target { + None => self + .component_sets + .get_or_insert_with(component_info.id(), || { + ComponentSparseSet::new(component_info, 64) + }), + Some(target) => self + .targeted_component_sets + .get_or_insert_with(component_info.id(), HashMap::default) + .entry(target) + .or_insert_with(|| ComponentSparseSet::new(component_info, 64)), } - - self.sets.get_mut(component_info.id()).unwrap() } - pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> { - self.sets.get(component_id) + pub fn get( + &self, + component_id: ComponentId, + target: Option, + ) -> Option<&ComponentSparseSet> { + match &target { + Some(target) => self.targeted_component_sets.get(component_id)?.get(target), + None => self.component_sets.get(component_id), + } } - pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> { - self.sets.get_mut(component_id) + pub fn get_mut( + &mut self, + component_id: ComponentId, + target: Option, + ) -> Option<&mut ComponentSparseSet> { + match &target { + Some(target) => self + .targeted_component_sets + .get_mut(component_id)? + .get_mut(target), + None => self.component_sets.get_mut(component_id), + } } - pub(crate) fn check_change_ticks(&mut self, change_tick: u32) { - for set in self.sets.values_mut() { + for set in self.component_sets.values_mut() { set.check_change_ticks(change_tick); } + for targeted_sets in self.targeted_component_sets.values_mut() { + for set in targeted_sets.values_mut() { + set.check_change_ticks(change_tick); + } + } } } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 3b015076132ec..e447788ca25ce 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -3,7 +3,7 @@ use crate::{ entity::Entity, storage::{BlobVec, SparseSet}, }; -use bevy_utils::{AHasher, HashMap}; +use bevy_utils::{AHasher, HashMap, StableHashMap}; use std::{ cell::UnsafeCell, hash::{Hash, Hasher}, @@ -31,17 +31,22 @@ impl TableId { } } +#[derive(Debug)] pub struct Column { - pub(crate) component_id: ComponentId, + pub(crate) component_id: (ComponentId, Option), pub(crate) data: BlobVec, pub(crate) ticks: Vec>, } impl Column { #[inline] - pub fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self { + pub fn with_capacity( + component_info: &ComponentInfo, + target: Option, + capacity: usize, + ) -> Self { Column { - component_id: component_info.id(), + component_id: (component_info.id(), target), data: BlobVec::new(component_info.layout(), component_info.drop(), capacity), ticks: Vec::with_capacity(capacity), } @@ -188,21 +193,24 @@ impl Column { } pub struct Table { - columns: SparseSet, + pub(crate) component_columns: SparseSet, + pub(crate) targeted_component_columns: SparseSet>, entities: Vec, } impl Table { pub const fn new() -> Table { Self { - columns: SparseSet::new(), + component_columns: SparseSet::new(), + targeted_component_columns: SparseSet::new(), entities: Vec::new(), } } pub fn with_capacity(capacity: usize, column_capacity: usize) -> Table { Self { - columns: SparseSet::with_capacity(column_capacity), + component_columns: SparseSet::with_capacity(column_capacity), + targeted_component_columns: SparseSet::with_capacity(column_capacity), entities: Vec::with_capacity(capacity), } } @@ -212,20 +220,39 @@ impl Table { &self.entities } - pub fn add_column(&mut self, component_info: &ComponentInfo) { - self.columns.insert( - component_info.id(), - Column::with_capacity(component_info, self.entities.capacity()), + fn columns_mut(&mut self) -> impl Iterator { + self.component_columns.values_mut().chain( + self.targeted_component_columns + .values_mut() + .flat_map(|map| map.values_mut()), ) } + pub fn add_column(&mut self, component_info: &ComponentInfo, target: Option) { + let capacity = self.capacity(); + match target { + None => self.component_columns.insert( + component_info.id(), + Column::with_capacity(component_info, None, capacity), + ), + Some(target) => { + self.targeted_component_columns + .get_or_insert_with(component_info.id(), StableHashMap::default) + .insert( + target, + Column::with_capacity(component_info, Some(target), capacity), + ); + } + } + } + /// Removes the entity at the given row and returns the entity swapped in to replace it (if an /// entity was swapped in) /// /// # Safety /// `row` must be in-bounds pub unsafe fn swap_remove_unchecked(&mut self, row: usize) -> Option { - for column in self.columns.values_mut() { + for column in self.columns_mut() { column.swap_remove_unchecked(row); } let is_last = row == self.entities.len() - 1; @@ -252,9 +279,11 @@ impl Table { debug_assert!(row < self.len()); let is_last = row == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row)); - for column in self.columns.values_mut() { + for column in self.columns_mut() { let (data, ticks) = column.swap_remove_and_forget_unchecked(row); - if let Some(new_column) = new_table.get_column_mut(column.component_id) { + if let Some(new_column) = + new_table.get_column_mut(column.component_id.0, column.component_id.1) + { new_column.initialize(new_row, data, ticks); } } @@ -282,8 +311,10 @@ impl Table { debug_assert!(row < self.len()); let is_last = row == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row)); - for column in self.columns.values_mut() { - if let Some(new_column) = new_table.get_column_mut(column.component_id) { + for column in self.columns_mut() { + if let Some(new_column) = + new_table.get_column_mut(column.component_id.0, column.component_id.1) + { let (data, ticks) = column.swap_remove_and_forget_unchecked(row); new_column.initialize(new_row, data, ticks); } else { @@ -314,8 +345,10 @@ impl Table { debug_assert!(row < self.len()); let is_last = row == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row)); - for column in self.columns.values_mut() { - let new_column = new_table.get_column_mut(column.component_id).unwrap(); + for column in self.columns_mut() { + let new_column = new_table + .get_column_mut(column.component_id.0, column.component_id.1) + .unwrap(); let (data, ticks) = column.swap_remove_and_forget_unchecked(row); new_column.initialize(new_row, data, ticks); } @@ -330,18 +363,41 @@ impl Table { } #[inline] - pub fn get_column(&self, component_id: ComponentId) -> Option<&Column> { - self.columns.get(component_id) + pub fn get_column(&self, component_id: ComponentId, target: Option) -> Option<&Column> { + match target { + Some(target) => self + .targeted_component_columns + .get(component_id) + .and_then(|map| map.get(&target)), + None => self.component_columns.get(component_id), + } } #[inline] - pub fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut Column> { - self.columns.get_mut(component_id) + pub fn get_column_mut( + &mut self, + component_id: ComponentId, + target: Option, + ) -> Option<&mut Column> { + match target { + Some(target) => self + .targeted_component_columns + .get_mut(component_id) + .and_then(|map| map.get_mut(&target)), + None => self.component_columns.get_mut(component_id), + } } #[inline] - pub fn has_column(&self, component_id: ComponentId) -> bool { - self.columns.contains(component_id) + pub fn has_column(&self, component_id: ComponentId, target: Option) -> bool { + match target { + Some(target) => self + .targeted_component_columns + .get(component_id) + .and_then(|map| map.get(&target)) + .is_some(), + None => self.component_columns.contains(component_id), + } } pub fn reserve(&mut self, additional: usize) { @@ -351,7 +407,7 @@ impl Table { // use entities vector capacity as driving capacity for all related allocations let new_capacity = self.entities.capacity(); - for column in self.columns.values_mut() { + for column in self.columns_mut() { column.reserve_exact(new_capacity - column.len()); } } @@ -365,8 +421,8 @@ impl Table { self.reserve(1); let index = self.entities.len(); self.entities.push(entity); - for column in self.columns.values_mut() { - column.data.set_len(self.entities.len()); + for column in self.columns_mut() { + column.data.set_len(index + 1); column.ticks.push(UnsafeCell::new(ComponentTicks::new(0))); } index @@ -388,14 +444,10 @@ impl Table { } pub(crate) fn check_change_ticks(&mut self, change_tick: u32) { - for column in self.columns.values_mut() { + for column in self.columns_mut() { column.check_change_ticks(change_tick); } } - - pub fn iter(&self) -> impl Iterator { - self.columns.values() - } } pub struct Tables { @@ -454,7 +506,7 @@ impl Tables { /// `component_ids` must contain components that exist in `components` pub unsafe fn get_id_or_insert( &mut self, - component_ids: &[ComponentId], + component_ids: &[(ComponentId, Option)], components: &Components, ) -> TableId { let mut hasher = AHasher::default(); @@ -464,7 +516,7 @@ impl Tables { *self.table_ids.entry(hash).or_insert_with(move || { let mut table = Table::with_capacity(0, component_ids.len()); for component_id in component_ids.iter() { - table.add_column(components.get_info_unchecked(*component_id)); + table.add_column(components.info(component_id.0).unwrap(), component_id.1); } tables.push(table); TableId(tables.len() - 1) @@ -505,10 +557,9 @@ mod tests { #[test] fn table() { let mut components = Components::default(); - let component_id = components.get_or_insert_id::(); - let columns = &[component_id]; - let mut table = Table::with_capacity(0, columns.len()); - table.add_column(components.get_info(component_id).unwrap()); + let component_info = components.component_info_or_insert::(); + let mut table = Table::with_capacity(0, 1); + table.add_column(component_info, None); let entities = (0..200).map(Entity::new).collect::>(); for entity in entities.iter() { // SAFE: we allocate and immediately set data afterwards @@ -517,7 +568,7 @@ mod tests { let mut value = row; let value_ptr = ((&mut value) as *mut usize).cast::(); table - .get_column_mut(component_id) + .get_column_mut(component_info.id(), None) .unwrap() .initialize_data(row, value_ptr); }; diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 0488b09e4f770..642fc93df4f05 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -255,6 +255,24 @@ impl<'a, 'b> EntityCommands<'a, 'b> { pub fn commands(&mut self) -> &mut Commands<'a> { self.commands } + + pub fn insert_relation(&mut self, relation: T, target: Entity) -> &mut Self { + self.commands.add(InsertRelation { + this: self.entity, + relation, + target, + }); + self + } + + pub fn remove_relation(&mut self, target: Entity) -> &mut Self { + self.commands.add(RemoveRelation:: { + this: self.entity, + target, + p: PhantomData, + }); + self + } } #[derive(Debug)] @@ -387,6 +405,34 @@ impl Command for RemoveResource { } } +pub struct InsertRelation { + this: Entity, + relation: T, + target: Entity, +} + +impl Command for InsertRelation { + fn write(self, world: &mut World) { + world + .entity_mut(self.this) + .insert_relation(self.relation, self.target); + } +} + +pub struct RemoveRelation { + this: Entity, + target: Entity, + p: PhantomData, +} + +impl Command for RemoveRelation { + fn write(self, world: &mut World) { + world + .entity_mut(self.this) + .remove_relation::(self.target); + } +} + #[cfg(test)] #[allow(clippy::float_cmp, clippy::approx_constant)] mod tests { @@ -520,4 +566,58 @@ mod tests { assert!(!world.contains_resource::()); assert!(world.contains_resource::()); } + + #[test] + #[allow(clippy::bool_assert_comparison)] + fn relations() { + struct MyRelation(bool); + struct MyRelationTwo(u32); + + let mut world = World::default(); + let mut queue = CommandQueue::default(); + + let t1 = world.spawn().id(); + let t2 = world.spawn().id(); + let t3 = world.spawn().id(); + + let mut commands = Commands::new(&mut queue, &world); + let e1 = commands + .spawn() + .insert_relation(MyRelation(true), t1) + .insert_relation(MyRelation(false), t2) + .insert_relation(MyRelationTwo(18), t3) + .id(); + let e2 = commands + .spawn() + .insert_relation(MyRelation(true), t3) + .insert_relation(MyRelationTwo(10), t1) + .insert_relation(MyRelation(false), t1) + .insert_relation(MyRelation(false), t3) + .id(); + drop(commands); + queue.apply(&mut world); + + let e1 = world.entity(e1); + assert_eq!(e1.get_relation::(t1).unwrap().0, true); + assert_eq!(e1.get_relation::(t2).unwrap().0, false); + assert_eq!(e1.get_relation::(t3).unwrap().0, 18); + let e1 = e1.id(); + + let e2 = world.entity(e2); + assert_eq!(e2.get_relation::(t3).unwrap().0, false); + assert_eq!(e2.get_relation::(t1).unwrap().0, 10); + assert_eq!(e2.get_relation::(t1).unwrap().0, false); + let e2 = e2.id(); + + let mut commands = Commands::new(&mut queue, &world); + + commands.entity(e1).remove_relation::(t2); + commands.entity(e2).remove_relation::(t3); + + drop(commands); + queue.apply(&mut world); + + assert!(world.entity(e1).get_relation::(t2).is_none()); + assert!(world.entity(e2).get_relation::(t3).is_none()); + } } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 92b03c1f4e223..07a6691e334aa 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -390,14 +390,14 @@ mod tests { let archetype = archetypes.get(location.archetype_id).unwrap(); let archetype_components = archetype.components().collect::>(); let bundle_id = bundles - .get_id(std::any::TypeId::of::<(i32, bool)>()) + .get_bundle_id(std::any::TypeId::of::<(i32, bool)>()) .expect("Bundle used to spawn entity should exist"); let bundle_info = bundles.get(bundle_id).unwrap(); let mut bundle_components = bundle_info.components().to_vec(); bundle_components.sort(); - for component_id in bundle_components.iter() { + for (component_id, _) in bundle_components.iter() { assert!( - components.get_info(*component_id).is_some(), + components.info(*component_id).is_some(), "every bundle component exists in Components" ); } @@ -428,11 +428,8 @@ mod tests { y.initialize(&mut world); let conflicts = x.component_access().get_conflicts(y.component_access()); - let b_id = world - .components() - .get_resource_id(TypeId::of::()) - .unwrap(); - let d_id = world.components().get_id(TypeId::of::()).unwrap(); + let b_id = world.components().resource_id(TypeId::of::()).unwrap(); + let d_id = world.components().component_id(TypeId::of::()).unwrap(); assert_eq!(conflicts, vec![b_id, d_id]); } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 05ff84a798ed6..2180ddd503fe9 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1,9 +1,10 @@ use crate::{ component::Component, entity::Entity, + prelude::QueryTargetFilters, query::{ Fetch, FilterFetch, QueryCombinationIter, QueryEntityError, QueryIter, QueryState, - ReadOnlyFetch, WorldQuery, + ReadOnlyFetch, SpecifiesRelation, TargetFilter, WorldQuery, }, world::{Mut, World}, }; @@ -107,12 +108,12 @@ use thiserror::Error; /// /// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery) /// bevy has to offer. -pub struct Query<'w, Q: WorldQuery, F: WorldQuery = ()> +pub struct Query<'w, Q: WorldQuery + 'static, F: WorldQuery + 'static = ()> where F::Fetch: FilterFetch, { pub(crate) world: &'w World, - pub(crate) state: &'w QueryState, + pub(crate) state: &'w mut QueryState, pub(crate) last_change_tick: u32, pub(crate) change_tick: u32, } @@ -130,7 +131,7 @@ where #[inline] pub(crate) unsafe fn new( world: &'w World, - state: &'w QueryState, + state: &'w mut QueryState, last_change_tick: u32, change_tick: u32, ) -> Self { @@ -285,7 +286,7 @@ where /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot /// be chained like a normal [`Iterator`]. #[inline] - pub fn for_each_mut<'s>(&'s mut self, f: impl FnMut(>::Item)) { + pub fn for_each_mut(&mut self, f: impl FnMut(>::Item)) { // SAFE: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict unsafe { @@ -402,6 +403,8 @@ where .get_unchecked_manual(self.world, entity, self.last_change_tick, self.change_tick) } + // FIXME(Relationships) implement get_relation methods for both `T *` and `T some_entity` + /// Gets a reference to the [`Entity`]'s [`Component`] of the given type. This will fail if the /// entity does not have the given component type or if the given component type does not match /// this query. @@ -413,11 +416,11 @@ where .ok_or(QueryComponentError::NoSuchEntity)?; let component_id = world .components() - .get_id(TypeId::of::()) + .component_id(TypeId::of::()) .ok_or(QueryComponentError::MissingComponent)?; let archetype_component = entity_ref .archetype() - .get_archetype_component_id(component_id) + .get_archetype_component_id(component_id, None) .ok_or(QueryComponentError::MissingComponent)?; if self .state @@ -463,11 +466,11 @@ where .ok_or(QueryComponentError::NoSuchEntity)?; let component_id = world .components() - .get_id(TypeId::of::()) + .component_id(TypeId::of::()) .ok_or(QueryComponentError::MissingComponent)?; let archetype_component = entity_ref .archetype() - .get_archetype_component_id(component_id) + .get_archetype_component_id(component_id, None) .ok_or(QueryComponentError::MissingComponent)?; if self .state @@ -552,6 +555,79 @@ where self.state .is_empty(self.world, self.last_change_tick, self.change_tick) } + + pub fn clear_target_filters(&mut self) -> &mut Self { + self.set_target_filters(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_target_filters( + &mut self, + filter: TargetFilter, + ) -> QueryTargetFiltersBuilder<'_, 'w, Q, F> + where + QueryTargetFilters: + SpecifiesRelation>, + { + QueryTargetFiltersBuilder { + query: self, + filters: QueryTargetFilters::new(), + } + .add_target_filter(filter) + } + + /// Overwrites current relation filters with the provided relation filters + /// + /// You should prefer `new_filter_relation` over this method + fn set_target_filters(&mut self, target_filter: QueryTargetFilters) -> &mut Self { + self.state.set_target_filters(self.world, target_filter); + self + } +} + +impl<'w, Q: WorldQuery, F: WorldQuery> Drop for Query<'w, Q, F> +where + F::Fetch: FilterFetch, +{ + fn drop(&mut self) { + self.set_target_filters(QueryTargetFilters::new()); + } +} + +pub struct QueryTargetFiltersBuilder<'a, 'b: 'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> +where + F::Fetch: FilterFetch, +{ + query: &'a mut Query<'b, Q, F>, + filters: QueryTargetFilters, +} + +impl<'a, 'b: 'a, Q: WorldQuery, F: WorldQuery> QueryTargetFiltersBuilder<'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. + #[must_use = "target filters will be unchanged if you do not call `apply_filters`"] + pub fn add_target_filter(mut self, filter: TargetFilter) -> Self + where + QueryTargetFilters: + SpecifiesRelation>, + { + self.filters.add_filter_relation(filter); + self + } + + pub fn apply_filters(self) -> &'a mut Query<'b, Q, F> { + let Self { query, filters } = self; + query.state.set_target_filters(query.world, filters); + query + } } /// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`] diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0499948755a93..5700f9a0d7bb5 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -137,7 +137,17 @@ where } fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { - self.new_archetype(archetype); + for (target_filter, cache) in self.target_filter_accesses.iter_mut() { + Self::new_archetype( + &self.fetch_state, + &self.filter_state, + &mut self.archetype_component_access, + &*target_filter, + cache, + archetype, + ); + } + system_meta .archetype_component_access .extend(&self.archetype_component_access); @@ -177,7 +187,7 @@ fn assert_component_access_compatibility( } let conflicting_components = conflicts .drain(..) - .map(|component_id| world.components.get_info(component_id).unwrap().name()) + .map(|component_id| world.components.info(component_id).unwrap().name()) .collect::>(); let accesses = conflicting_components.join(", "); panic!("Query<{}, {}> in system {} accesses component(s) {} in a way that conflicts with a previous system parameter. Allowing this would break Rust's mutability rules. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `QuerySet`.", @@ -272,7 +282,7 @@ unsafe impl SystemParamState for ResState { let resource_archetype = world.archetypes.resource(); let archetype_component_id = resource_archetype - .get_archetype_component_id(component_id) + .get_archetype_component_id(component_id, None) .unwrap(); system_meta .archetype_component_access @@ -386,7 +396,7 @@ unsafe impl SystemParamState for ResMutState { let resource_archetype = world.archetypes.resource(); let archetype_component_id = resource_archetype - .get_archetype_component_id(component_id) + .get_archetype_component_id(component_id, None) .unwrap(); system_meta .archetype_component_access @@ -623,7 +633,7 @@ pub struct RemovedComponents<'a, T> { impl<'a, T> RemovedComponents<'a, T> { /// Returns an iterator over the entities that had their `T` [`Component`] removed. pub fn iter(&self) -> std::iter::Cloned> { - self.world.removed_with_id(self.component_id) + self.world.removed_with_id(self.component_id, None) } } @@ -647,7 +657,7 @@ unsafe impl SystemParamState for RemovedComponentsState { fn init(world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self { Self { - component_id: world.components.get_or_insert_id::(), + component_id: world.components.component_id_or_insert::(), marker: PhantomData, } } @@ -756,7 +766,7 @@ unsafe impl SystemParamState for NonSendState { let resource_archetype = world.archetypes.resource(); let archetype_component_id = resource_archetype - .get_archetype_component_id(component_id) + .get_archetype_component_id(component_id, None) .unwrap(); system_meta .archetype_component_access @@ -860,7 +870,8 @@ unsafe impl SystemParamState for NonSendMutState { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { system_meta.set_non_send(); - let component_id = world.components.get_or_insert_non_send_resource_id::(); + let component_id = world.components.non_send_resource_id_or_insert::(); + let combined_access = system_meta.component_access_set.combined_access_mut(); if combined_access.has_write(component_id) { panic!( @@ -875,7 +886,7 @@ unsafe impl SystemParamState for NonSendMutState { let resource_archetype = world.archetypes.resource(); let archetype_component_id = resource_archetype - .get_archetype_component_id(component_id) + .get_archetype_component_id(component_id, None) .unwrap(); system_meta .archetype_component_access diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 6e7185dc5b625..1fe6d11cd8293 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -7,6 +7,7 @@ use crate::{ storage::{SparseSet, Storages}, world::{Mut, World}, }; +use bevy_utils::HashMap; use std::any::TypeId; pub struct EntityRef<'w> { @@ -45,27 +46,53 @@ impl<'w> EntityRef<'w> { self.world } + #[inline] + pub fn contains_relation(&self, target: Entity) -> bool { + self.contains_type_id(TypeId::of::(), Some(target)) + } + #[inline] pub fn contains(&self) -> bool { - self.contains_type_id(TypeId::of::()) + self.contains_type_id(TypeId::of::(), None) } #[inline] - pub fn contains_id(&self, component_id: ComponentId) -> bool { - contains_component_with_id(self.world, component_id, self.location) + pub fn contains_id(&self, component_id: ComponentId, target: Option) -> bool { + contains_component(self.world, component_id, target, self.location) } #[inline] - pub fn contains_type_id(&self, type_id: TypeId) -> bool { - contains_component_with_type(self.world, type_id, self.location) + pub fn contains_type_id(&self, type_id: TypeId, target: Option) -> bool { + contains_component_with_type(self.world, type_id, target, self.location) } #[inline] pub fn get(&self) -> Option<&'w T> { // SAFE: entity location is valid and returned component is of type T unsafe { - get_component_with_type(self.world, TypeId::of::(), self.entity, self.location) - .map(|value| &*value.cast::()) + get_component_with_type( + self.world, + TypeId::of::(), + None, + self.entity, + self.location, + ) + .map(|value| &*value.cast::()) + } + } + + #[inline] + pub fn get_relation(&self, target: Entity) -> Option<&'w T> { + // SAFE: entity location is valid and returned component is of type T + unsafe { + get_component_with_type( + self.world, + TypeId::of::(), + Some(target), + self.entity, + self.location, + ) + .map(|value| &*value.cast::()) } } @@ -78,15 +105,49 @@ impl<'w> EntityRef<'w> { last_change_tick: u32, change_tick: u32, ) -> Option> { - get_component_and_ticks_with_type(self.world, TypeId::of::(), self.entity, self.location) - .map(|(value, ticks)| Mut { - value: &mut *value.cast::(), - ticks: Ticks { - component_ticks: &mut *ticks, - last_change_tick, - change_tick, - }, - }) + // SAFETY: Caller + get_component_and_ticks_with_type( + self.world, + TypeId::of::(), + None, + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + value: &mut *value.cast::(), + ticks: Ticks { + component_ticks: &mut *ticks, + last_change_tick, + change_tick, + }, + }) + } + + /// # Safety + /// This allows aliased mutability. You must make sure this call does not result in multiple + /// mutable references to the same component + #[inline] + pub unsafe fn get_relation_unchecked_mut( + &self, + target: Entity, + last_change_tick: u32, + change_tick: u32, + ) -> Option> { + get_component_and_ticks_with_type( + self.world, + TypeId::of::(), + Some(target), + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + value: &mut *value.cast::(), + ticks: Ticks { + component_ticks: &mut *ticks, + last_change_tick, + change_tick, + }, + }) } } @@ -127,27 +188,53 @@ impl<'w> EntityMut<'w> { &self.world.archetypes[self.location.archetype_id] } + #[inline] + pub fn contains_relation(&self, target: Entity) -> bool { + self.contains_type_id(TypeId::of::(), Some(target)) + } + #[inline] pub fn contains(&self) -> bool { - self.contains_type_id(TypeId::of::()) + self.contains_type_id(TypeId::of::(), None) } #[inline] - pub fn contains_id(&self, component_id: ComponentId) -> bool { - contains_component_with_id(self.world, component_id, self.location) + pub fn contains_id(&self, component_id: ComponentId, target: Option) -> bool { + contains_component(self.world, component_id, target, self.location) } #[inline] - pub fn contains_type_id(&self, type_id: TypeId) -> bool { - contains_component_with_type(self.world, type_id, self.location) + pub fn contains_type_id(&self, type_id: TypeId, target: Option) -> bool { + contains_component_with_type(self.world, type_id, target, self.location) } #[inline] pub fn get(&self) -> Option<&'w T> { // SAFE: entity location is valid and returned component is of type T unsafe { - get_component_with_type(self.world, TypeId::of::(), self.entity, self.location) - .map(|value| &*value.cast::()) + get_component_with_type( + self.world, + TypeId::of::(), + None, + self.entity, + self.location, + ) + .map(|value| &*value.cast::()) + } + } + + #[inline] + pub fn get_relation(&self, target: Entity) -> Option<&'w T> { + // SAFE: entity location is valid and returned component is of type T + unsafe { + get_component_with_type( + self.world, + TypeId::of::(), + Some(target), + self.entity, + self.location, + ) + .map(|value| &*value.cast::()) } } @@ -159,6 +246,7 @@ impl<'w> EntityMut<'w> { get_component_and_ticks_with_type( self.world, TypeId::of::(), + None, self.entity, self.location, ) @@ -178,7 +266,35 @@ impl<'w> EntityMut<'w> { /// mutable references to the same component #[inline] pub unsafe fn get_unchecked_mut(&self) -> Option> { - get_component_and_ticks_with_type(self.world, TypeId::of::(), self.entity, self.location) + get_component_and_ticks_with_type( + self.world, + TypeId::of::(), + None, + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + value: &mut *value.cast::(), + ticks: Ticks { + component_ticks: &mut *ticks, + last_change_tick: self.world.last_change_tick(), + change_tick: self.world.read_change_tick(), + }, + }) + } + + #[inline] + pub fn get_relation_mut(&mut self, target: Entity) -> Option> { + // SAFE: world access is unique, entity location is valid, and returned component is of type + // T + unsafe { + get_component_and_ticks_with_type( + self.world, + TypeId::of::(), + Some(target), + self.entity, + self.location, + ) .map(|(value, ticks)| Mut { value: &mut *value.cast::(), ticks: Ticks { @@ -187,6 +303,32 @@ impl<'w> EntityMut<'w> { change_tick: self.world.read_change_tick(), }, }) + } + } + + /// # Safety + /// This allows aliased mutability. You must make sure this call does not result in multiple + /// mutable references to the same component + #[inline] + pub unsafe fn get_relation_unchecked_mut( + &self, + target: Entity, + ) -> Option> { + get_component_and_ticks_with_type( + self.world, + TypeId::of::(), + Some(target), + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + value: &mut *value.cast::(), + ticks: Ticks { + component_ticks: &mut *ticks, + last_change_tick: self.world.last_change_tick(), + change_tick: self.world.read_change_tick(), + }, + }) } /// # Safety: @@ -263,8 +405,7 @@ impl<'w> EntityMut<'w> { let bundle_info = self .world .bundles - .init_info::(&mut self.world.components); - + .init_bundle_info::(&mut self.world.components); let (archetype, bundle_status, new_location) = unsafe { Self::get_insert_bundle_info( &mut self.world.entities, @@ -282,7 +423,7 @@ impl<'w> EntityMut<'w> { let table_row = archetype.entity_table_row(new_location.index); // SAFE: table row is valid unsafe { - bundle_info.write_components( + bundle_info.write_bundle( &mut self.world.storages.sparse_sets, self.entity, table, @@ -295,6 +436,48 @@ impl<'w> EntityMut<'w> { self } + pub fn insert_relation(&mut self, data: T, target: Entity) -> &mut Self { + let mut data = core::mem::ManuallyDrop::new(data); + let change_tick = self.world.change_tick(); + + let bundle_info = { + let component_info = self.world.components.component_info_or_insert::(); + self.world + .bundles + .init_relation_bundle_info(component_info, target) + }; + + let (archetype, bundle_status, new_location) = unsafe { + Self::get_insert_bundle_info( + &mut self.world.entities, + &mut self.world.archetypes, + &mut self.world.components, + &mut self.world.storages, + bundle_info, + self.location, + self.entity, + ) + }; + self.location = new_location; + + let table = &mut self.world.storages.tables[archetype.table_id()]; + let table_row = archetype.entity_table_row(new_location.index); + unsafe { + bundle_info.write_component( + &mut self.world.storages.sparse_sets, + self.entity, + table, + table_row, + bundle_status, + 0, + &mut data as *mut core::mem::ManuallyDrop as *mut u8, + change_tick, + ); + } + + self + } + pub fn remove_bundle(&mut self) -> Option { let archetypes = &mut self.world.archetypes; let storages = &mut self.world.storages; @@ -302,7 +485,7 @@ impl<'w> EntityMut<'w> { let entities = &mut self.world.entities; let removed_components = &mut self.world.removed_components; - let bundle_info = self.world.bundles.init_info::(components); + let bundle_info = self.world.bundles.init_bundle_info::(components); let old_location = self.location; let new_archetype_id = unsafe { remove_bundle_from_archetype( @@ -333,7 +516,8 @@ impl<'w> EntityMut<'w> { storages, old_archetype, removed_components, - component_id, + component_id.0, + component_id.1, entity, old_location, ) @@ -415,6 +599,70 @@ impl<'w> EntityMut<'w> { entities.meta[entity.id as usize].location = new_location; } + pub fn remove_relation(&mut self, target: Entity) -> Option { + let component_info = self.world.components.component_info(TypeId::of::())?; + let bundle_info = self + .world + .bundles + .init_relation_bundle_info(component_info, target); + + let component_id = component_info.id(); + let archetypes = &mut self.world.archetypes; + let storages = &mut self.world.storages; + let components = &mut self.world.components; + let entities = &mut self.world.entities; + let removed_components = &mut self.world.removed_components; + + let old_location = self.location; + let new_archetype_id = unsafe { + remove_bundle_from_archetype( + archetypes, + storages, + components, + old_location.archetype_id, + bundle_info, + false, + )? + }; + + if new_archetype_id == old_location.archetype_id { + return None; + } + + // SAFE: current entity archetype is valid + let old_archetype = &mut archetypes[old_location.archetype_id]; + let entity = self.entity; + // SAFE: bundle components are iterated in order, which guarantees that the component type matches + let result = unsafe { + // SAFE: entity location is valid and table row is removed below + core::ptr::read(take_component( + components, + storages, + old_archetype, + removed_components, + component_id, + Some(target), + entity, + old_location, + ) as *mut T) + }; + + unsafe { + Self::move_entity_from_remove::( + entity, + &mut self.location, + old_location.archetype_id, + old_location, + entities, + archetypes, + storages, + new_archetype_id, + ); + } + + Some(result) + } + /// Remove any components in the bundle that the entity has. pub fn remove_bundle_intersection(&mut self) { let archetypes = &mut self.world.archetypes; @@ -423,7 +671,7 @@ impl<'w> EntityMut<'w> { let entities = &mut self.world.entities; let removed_components = &mut self.world.removed_components; - let bundle_info = self.world.bundles.init_info::(components); + let bundle_info = self.world.bundles.init_bundle_info::(components); let old_location = self.location; let new_archetype_id = unsafe { remove_bundle_from_archetype( @@ -443,18 +691,26 @@ impl<'w> EntityMut<'w> { let old_archetype = &mut archetypes[old_location.archetype_id]; let entity = self.entity; - for component_id in bundle_info.component_ids.iter().cloned() { - if old_archetype.contains(component_id) { - removed_components - .get_or_insert_with(component_id, Vec::new) - .push(entity); - + for (component_id, target) in bundle_info.component_ids.iter().cloned() { + if old_archetype.contains(component_id, target) { + let (none_remove, target_removed) = removed_components + .get_or_insert_with(component_id, || (Vec::new(), HashMap::default())); + + match target { + None => none_remove.push(entity), + Some(target) => target_removed + .entry(target) + .or_insert_with(Vec::new) + .push(entity), + } // Make sure to drop components stored in sparse sets. // Dense components are dropped later in `move_to_and_drop_missing_unchecked`. - if let Some(StorageType::SparseSet) = old_archetype.get_storage_type(component_id) { + if let Some(StorageType::SparseSet) = + old_archetype.get_storage_type(component_id, target) + { storages .sparse_sets - .get_mut(component_id) + .get_mut(component_id, target) .unwrap() .remove(entity); } @@ -494,11 +750,19 @@ impl<'w> EntityMut<'w> { let moved_entity; { let archetype = &mut world.archetypes[location.archetype_id]; - for component_id in archetype.components() { + for (component_id, target) in archetype.components() { let removed_components = world .removed_components - .get_or_insert_with(component_id, Vec::new); - removed_components.push(self.entity); + .get_or_insert_with(component_id, Default::default); + + match target { + None => removed_components.0.push(self.entity), + Some(target) => removed_components + .1 + .entry(target) + .or_insert_with(Vec::new) + .push(self.entity), + } } let remove_result = archetype.swap_remove(location.index); if let Some(swapped_entity) = remove_result.swapped_entity { @@ -506,8 +770,12 @@ impl<'w> EntityMut<'w> { } table_row = remove_result.table_row; - for component_id in archetype.sparse_set_components() { - let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap(); + for &(component_id, target) in archetype.sparse_set_components() { + let sparse_set = world + .storages + .sparse_sets + .get_mut(component_id, target) + .unwrap(); sparse_set.remove(self.entity); } // SAFE: table rows stored in archetypes always exist @@ -552,16 +820,16 @@ impl<'w> EntityMut<'w> { unsafe fn get_component( world: &World, component_id: ComponentId, + target: Option, entity: Entity, location: EntityLocation, ) -> Option<*mut u8> { let archetype = &world.archetypes[location.archetype_id]; - // SAFE: component_id exists and is therefore valid - let component_info = world.components.get_info_unchecked(component_id); + let component_info = world.components.info(component_id).unwrap(); match component_info.storage_type() { StorageType::Table => { let table = &world.storages.tables[archetype.table_id()]; - let components = table.get_column(component_id)?; + let components = table.get_column(component_id, target)?; let table_row = archetype.entity_table_row(location.index); // SAFE: archetypes only store valid table_rows and the stored component type is T Some(components.get_data_unchecked(table_row)) @@ -569,7 +837,7 @@ unsafe fn get_component( StorageType::SparseSet => world .storages .sparse_sets - .get(component_id) + .get(component_id, target) .and_then(|sparse_set| sparse_set.get(entity)), } } @@ -580,15 +848,16 @@ unsafe fn get_component( unsafe fn get_component_and_ticks( world: &World, component_id: ComponentId, + target: Option, entity: Entity, location: EntityLocation, ) -> Option<(*mut u8, *mut ComponentTicks)> { let archetype = &world.archetypes[location.archetype_id]; - let component_info = world.components.get_info_unchecked(component_id); + let component_info = world.components.info(component_id).unwrap(); match component_info.storage_type() { StorageType::Table => { let table = &world.storages.tables[archetype.table_id()]; - let components = table.get_column(component_id)?; + let components = table.get_column(component_id, target)?; let table_row = archetype.entity_table_row(location.index); // SAFE: archetypes only store valid table_rows and the stored component type is T Some(( @@ -599,7 +868,7 @@ unsafe fn get_component_and_ticks( StorageType::SparseSet => world .storages .sparse_sets - .get(component_id) + .get(component_id, target) .and_then(|sparse_set| sparse_set.get_with_ticks(entity)), } } @@ -615,30 +884,40 @@ unsafe fn get_component_and_ticks( /// - `component_id` must be valid /// - The relevant table row **must be removed** by the caller once all components are taken #[inline] +#[allow(clippy::too_many_arguments)] unsafe fn take_component( components: &Components, storages: &mut Storages, archetype: &Archetype, - removed_components: &mut SparseSet>, + removed_components: &mut SparseSet, HashMap>)>, component_id: ComponentId, + target: Option, entity: Entity, location: EntityLocation, ) -> *mut u8 { - let component_info = components.get_info_unchecked(component_id); - let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); - removed_components.push(entity); - match component_info.storage_type() { + let targets = removed_components.get_or_insert_with(component_id, Default::default); + match target { + None => targets.0.push(entity), + Some(target) => targets + .1 + .entry(target) + .or_insert_with(Vec::new) + .push(entity), + } + + let storage_type = components.info(component_id).unwrap().storage_type(); + match storage_type { StorageType::Table => { let table = &storages.tables[archetype.table_id()]; // SAFE: archetypes will always point to valid columns - let components = table.get_column(component_id).unwrap(); + let components = table.get_column(component_id, target).unwrap(); let table_row = archetype.entity_table_row(location.index); // SAFE: archetypes only store valid table_rows and the stored component type is T components.get_data_unchecked(table_row) } StorageType::SparseSet => storages .sparse_sets - .get_mut(component_id) + .get_mut(component_id, target) .unwrap() .remove_and_forget(entity) .unwrap(), @@ -646,43 +925,51 @@ unsafe fn take_component( } /// # Safety -/// `entity_location` must be within bounds of an archetype that exists. +/// `entity_location` must be within bounds of an archetype that exists unsafe fn get_component_with_type( world: &World, type_id: TypeId, + target: Option, entity: Entity, location: EntityLocation, ) -> Option<*mut u8> { - let component_id = world.components.get_id(type_id)?; - get_component(world, component_id, entity, location) + let component_id = world.components.component_id(type_id)?; + get_component(world, component_id, target, entity, location) } /// # Safety -/// `entity_location` must be within bounds of an archetype that exists. +/// `entity_location` must be within bounds of an archetype that exists pub(crate) unsafe fn get_component_and_ticks_with_type( world: &World, type_id: TypeId, + target: Option, entity: Entity, location: EntityLocation, ) -> Option<(*mut u8, *mut ComponentTicks)> { - let component_id = world.components.get_id(type_id)?; - get_component_and_ticks(world, component_id, entity, location) + let component_id = world.components.component_id(type_id)?; + get_component_and_ticks(world, component_id, target, entity, location) } -fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool { - if let Some(component_id) = world.components.get_id(type_id) { - contains_component_with_id(world, component_id, location) +fn contains_component_with_type( + world: &World, + type_id: TypeId, + target: Option, + location: EntityLocation, +) -> bool { + if let Some(component_id) = world.components.component_id(type_id) { + contains_component(world, component_id, target, location) } else { false } } -fn contains_component_with_id( +fn contains_component( world: &World, component_id: ComponentId, + target: Option, location: EntityLocation, ) -> bool { - world.archetypes[location.archetype_id].contains(component_id) + world.archetypes[location.archetype_id].contains(component_id, target) } /// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same @@ -709,17 +996,17 @@ pub(crate) unsafe fn add_bundle_to_archetype( let mut bundle_status = Vec::with_capacity(bundle_info.component_ids.len()); let current_archetype = &mut archetypes[archetype_id]; - for component_id in bundle_info.component_ids.iter().cloned() { - if current_archetype.contains(component_id) { + for (component_id, target) in bundle_info.component_ids.iter().cloned() { + if current_archetype.contains(component_id, target) { bundle_status.push(ComponentStatus::Mutated); } else { bundle_status.push(ComponentStatus::Added); - let component_info = components.get_info_unchecked(component_id); + let component_info = components.info(component_id).unwrap(); match component_info.storage_type() { - StorageType::Table => new_table_components.push(component_id), + StorageType::Table => new_table_components.push((component_id, target)), StorageType::SparseSet => { - storages.sparse_sets.get_or_insert(component_info); - new_sparse_set_components.push(component_id) + storages.sparse_sets.get_or_insert(component_info, target); + new_sparse_set_components.push((component_id, target)) } } } @@ -762,6 +1049,7 @@ pub(crate) unsafe fn add_bundle_to_archetype( new_sparse_set_components }; }; + let new_archetype_id = archetypes.get_id_or_insert(table_id, table_components, sparse_set_components); // add an edge from the old archetype to the new archetype @@ -814,13 +1102,14 @@ unsafe fn remove_bundle_from_archetype( let current_archetype = &mut archetypes[archetype_id]; let mut removed_table_components = Vec::new(); let mut removed_sparse_set_components = Vec::new(); - for component_id in bundle_info.component_ids.iter().cloned() { - if current_archetype.contains(component_id) { - // SAFE: bundle components were already initialized by bundles.get_info - let component_info = components.get_info_unchecked(component_id); + for (component_id, target) in bundle_info.component_ids.iter().cloned() { + if current_archetype.contains(component_id, target) { + let component_info = components.info(component_id).unwrap(); match component_info.storage_type() { - StorageType::Table => removed_table_components.push(component_id), - StorageType::SparseSet => removed_sparse_set_components.push(component_id), + StorageType::Table => removed_table_components.push((component_id, target)), + StorageType::SparseSet => { + removed_sparse_set_components.push((component_id, target)) + } } } else if !intersection { // a component in the bundle was not present in the entity's archetype, so this diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 862181538328b..a59fe888d437d 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1,5 +1,7 @@ mod entity_ref; mod spawn_batch; +#[cfg(any(doc, test))] +mod tests; mod world_cell; pub use crate::change_detection::Mut; @@ -19,6 +21,7 @@ use crate::{ query::{FilterFetch, QueryState, WorldQuery}, storage::{Column, SparseSet, Storages}, }; +use bevy_utils::HashMap; use std::{ any::TypeId, fmt, @@ -46,7 +49,8 @@ pub struct World { pub(crate) archetypes: Archetypes, pub(crate) storages: Storages, pub(crate) bundles: Bundles, - pub(crate) removed_components: SparseSet>, + pub(crate) removed_components: + SparseSet, HashMap>)>, /// Access cache used by [WorldCell]. pub(crate) archetype_component_access: ArchetypeComponentAccess, main_thread_validator: MainThreadValidator, @@ -54,6 +58,33 @@ pub struct World { pub(crate) last_change_tick: u32, } +impl World { + pub fn debug_ram_usage(&self) { + let mut archetype_usage = 0; + let mut edges_usage = 0; + + let num_archetypes = self.archetypes.archetypes.len(); + for archetype in self.archetypes.archetypes.iter() { + let (arch, edges) = archetype.debug_ram_usage(); + archetype_usage += arch; + edges_usage += edges; + } + println!("total usage: {}", archetype_usage + edges_usage); + println!("num archetypes: {}", num_archetypes); + + println!("archetypes usage: {}", archetype_usage); + println!( + "average usage per archetype: {}", + archetype_usage / num_archetypes + ); + + println!("edges usage: {}", edges_usage); + println!("average edges usage: {}", edges_usage / num_archetypes); + + println!("\n\n"); + } +} + impl Default for World { fn default() -> Self { Self { @@ -153,15 +184,15 @@ impl World { descriptor: ComponentDescriptor, ) -> Result { let storage_type = descriptor.storage_type(); - let component_id = self.components.add(descriptor)?; + let component_id = self.components.new_component(descriptor)?; + + // FIXME(Relationships): i still dont like this // ensure sparse set is created for SparseSet components if storage_type == StorageType::SparseSet { - // SAFE: just created - let info = unsafe { self.components.get_info_unchecked(component_id) }; - self.storages.sparse_sets.get_or_insert(info); + self.storages.sparse_sets.get_or_insert(component_id, None); } - Ok(component_id) + Ok(component_id.id()) } /// Retrieves an [EntityRef] that exposes read-only operations for the given `entity`. @@ -407,7 +438,10 @@ impl World { /// Clears component tracker state pub fn clear_trackers(&mut self) { for entities in self.removed_components.values_mut() { - entities.clear(); + entities.0.clear(); + for entities in entities.1.values_mut() { + entities.clear(); + } } self.last_change_tick = self.increment_change_tick(); @@ -497,20 +531,31 @@ impl World { /// Returns an iterator of entities that had components of type `T` removed /// since the last call to [World::clear_trackers]. pub fn removed(&self) -> std::iter::Cloned> { - if let Some(component_id) = self.components.get_id(TypeId::of::()) { - self.removed_with_id(component_id) + if let Some(component_id) = self.components.component_id(TypeId::of::()) { + self.removed_with_id(component_id, None) } else { [].iter().cloned() } } + // FIXME(Relationships) implement a way to set `*` for the target /// Returns an iterator of entities that had components with the given `component_id` removed /// since the last call to [World::clear_trackers]. + /// + /// Set [target] to None if you don't care about relations pub fn removed_with_id( &self, component_id: ComponentId, + target: Option, ) -> std::iter::Cloned> { - if let Some(removed) = self.removed_components.get(component_id) { + if let Some(removed) = self + .removed_components + .get(component_id) + .and_then(|targets| match target { + None => Some(&targets.0), + Some(target) => targets.1.get(&target), + }) + { removed.iter().cloned() } else { [].iter().cloned() @@ -521,7 +566,7 @@ impl World { /// Resources are "unique" data of a given type. #[inline] pub fn insert_resource(&mut self, value: T) { - let component_id = self.components.get_or_insert_resource_id::(); + let component_id = self.components.resource_id_or_insert::(); // SAFE: component_id just initialized and corresponds to resource of type T unsafe { self.insert_resource_with_id(component_id, value) }; } @@ -531,7 +576,7 @@ impl World { #[inline] pub fn insert_non_send(&mut self, value: T) { self.validate_non_send_access::(); - let component_id = self.components.get_or_insert_non_send_resource_id::(); + let component_id = self.components.non_send_resource_id_or_insert::(); // SAFE: component_id just initialized and corresponds to resource of type T unsafe { self.insert_resource_with_id(component_id, value) }; } @@ -556,7 +601,7 @@ impl World { /// make sure you're on main thread if T isn't Send + Sync #[allow(unused_unsafe)] pub unsafe fn remove_resource_unchecked(&mut self) -> Option { - let component_id = self.components.get_resource_id(TypeId::of::())?; + let component_id = self.components.resource_id(TypeId::of::())?; let resource_archetype = self.archetypes.resource_mut(); let unique_components = resource_archetype.unique_components_mut(); let column = unique_components.get_mut(component_id)?; @@ -573,25 +618,22 @@ impl World { /// Returns `true` if a resource of type `T` exists. Otherwise returns `false`. #[inline] pub fn contains_resource(&self) -> bool { - let component_id = - if let Some(component_id) = self.components.get_resource_id(TypeId::of::()) { - component_id - } else { - return false; - }; - self.get_populated_resource_column(component_id).is_some() + self.components + .resource_id(TypeId::of::()) + .and_then(|component_id| self.get_populated_resource_column(component_id)) + .is_some() } /// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None] /// Resources are "unique" data of a given type. #[inline] pub fn get_resource(&self) -> Option<&T> { - let component_id = self.components.get_resource_id(TypeId::of::())?; + let component_id = self.components.resource_id(TypeId::of::())?; unsafe { self.get_resource_with_id(component_id) } } pub fn is_resource_added(&self) -> bool { - let component_id = self.components.get_resource_id(TypeId::of::()).unwrap(); + let component_id = self.components.resource_id(TypeId::of::()).unwrap(); let column = self.get_populated_resource_column(component_id).unwrap(); // SAFE: resources table always have row 0 let ticks = unsafe { column.get_ticks_unchecked(0) }; @@ -599,7 +641,7 @@ impl World { } pub fn is_resource_changed(&self) -> bool { - let component_id = self.components.get_resource_id(TypeId::of::()).unwrap(); + let component_id = self.components.resource_id(TypeId::of::()).unwrap(); let column = self.get_populated_resource_column(component_id).unwrap(); // SAFE: resources table always have row 0 let ticks = unsafe { column.get_ticks_unchecked(0) }; @@ -636,7 +678,7 @@ impl World { /// that only one mutable access exists at a time. #[inline] pub unsafe fn get_resource_unchecked_mut(&self) -> Option> { - let component_id = self.components.get_resource_id(TypeId::of::())?; + let component_id = self.components.resource_id(TypeId::of::())?; self.get_resource_unchecked_mut_with_id(component_id) } @@ -644,7 +686,7 @@ impl World { /// [None] Resources are "unique" data of a given type. #[inline] pub fn get_non_send_resource(&self) -> Option<&T> { - let component_id = self.components.get_resource_id(TypeId::of::())?; + let component_id = self.components.resource_id(TypeId::of::())?; // SAFE: component id matches type T unsafe { self.get_non_send_with_id(component_id) } } @@ -665,7 +707,7 @@ impl World { /// ensure that only one mutable access exists at a time. #[inline] pub unsafe fn get_non_send_resource_unchecked_mut(&self) -> Option> { - let component_id = self.components.get_resource_id(TypeId::of::())?; + let component_id = self.components.resource_id(TypeId::of::())?; self.get_non_send_unchecked_mut_with_id(component_id) } @@ -692,7 +734,7 @@ impl World { ) -> U { let component_id = self .components - .get_resource_id(TypeId::of::()) + .resource_id(TypeId::of::()) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); let (ptr, mut ticks) = { let resource_archetype = self.archetypes.resource_mut(); @@ -825,20 +867,20 @@ impl World { }, ); *archetype_component_count += 1; - let component_info = components.get_info_unchecked(component_id); - Column::with_capacity(component_info, 1) + let component_info = components.info(component_id).unwrap(); + Column::with_capacity(component_info, None, 1) }) } pub(crate) fn initialize_resource(&mut self) -> ComponentId { - let component_id = self.components.get_or_insert_resource_id::(); + let component_id = self.components.resource_id_or_insert::(); // SAFE: resource initialized above unsafe { self.initialize_resource_internal(component_id) }; component_id } pub(crate) fn initialize_non_send_resource(&mut self) -> ComponentId { - let component_id = self.components.get_or_insert_non_send_resource_id::(); + let component_id = self.components.non_send_resource_id_or_insert::(); // SAFE: resource initialized above unsafe { self.initialize_resource_internal(component_id) }; component_id diff --git a/crates/bevy_ecs/src/world/spawn_batch.rs b/crates/bevy_ecs/src/world/spawn_batch.rs index 95830dc72b696..8b4caa824b204 100644 --- a/crates/bevy_ecs/src/world/spawn_batch.rs +++ b/crates/bevy_ecs/src/world/spawn_batch.rs @@ -34,7 +34,9 @@ where let (lower, upper) = iter.size_hint(); - let bundle_info = world.bundles.init_info::(&mut world.components); + let bundle_info = world + .bundles + .init_bundle_info::(&mut world.components); let length = upper.unwrap_or(lower); // SAFE: empty archetype exists and bundle components were initialized above @@ -96,7 +98,7 @@ where unsafe { let table_row = self.table.allocate(entity); let location = self.archetype.allocate(entity, table_row); - self.bundle_info.write_components( + self.bundle_info.write_bundle( self.sparse_sets, entity, self.table, diff --git a/crates/bevy_ecs/src/world/tests.rs b/crates/bevy_ecs/src/world/tests.rs new file mode 100644 index 0000000000000..e752ec391cab4 --- /dev/null +++ b/crates/bevy_ecs/src/world/tests.rs @@ -0,0 +1,722 @@ +// FIXME(Relationships) add a .len() method to `RelationAccess` and `RelationAccessMut` maybe also implement ExactSizeIterator? + +use std::convert::TryInto; + +use crate::{ + component::{ComponentDescriptor, StorageType}, + query::TargetFilter, +}; +use crate::{prelude::*, query::RelationAccess}; + +#[test] +fn relation_spawn() { + relation_spawn_raw(StorageType::Table); + relation_spawn_raw(StorageType::SparseSet); +} +#[allow(clippy::bool_assert_comparison)] +fn relation_spawn_raw(storage_type: StorageType) { + let mut world = World::new(); + + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + struct ChildOf; + + let parent = world.spawn().id(); + let not_parent = world.spawn().id(); + + let mut child = world.spawn(); + let child = child.insert_relation(ChildOf, parent); + + assert!(child.contains_relation::(parent)); + assert_eq!(child.contains_relation::(not_parent), false); + assert_eq!(child.contains_relation::(parent), false); + + assert!(child.remove_relation::(parent).is_some()); + assert!(child.remove_relation::(parent).is_none()); + assert!(child.remove_relation::(parent).is_none()); + assert!(child.remove_relation::(not_parent).is_none()); +} + +#[test] +fn relation_query() { + relation_query_raw(StorageType::Table); + relation_query_raw(StorageType::SparseSet); +} +fn relation_query_raw(storage_type: StorageType) { + struct ChildOf; + + let mut world = World::new(); + + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + let parent1 = world.spawn().id(); + let child1 = world.spawn().insert_relation(ChildOf, parent1).id(); + let parent2 = world.spawn().id(); + let child2 = world.spawn().insert_relation(ChildOf, parent2).id(); + + let mut query = world.query::<(Entity, &Relation)>(); + let mut iter = query.iter_mut(&mut world); + assert!(iter.next().unwrap().0 == child1); + assert!(iter.next().unwrap().0 == child2); + assert!(matches!(iter.next(), None)); + + query + .new_target_filters(&world, TargetFilter::::new().target(parent1)) + .apply_filters(); + let mut iter = query.iter_mut(&mut world); + assert!(iter.next().unwrap().0 == child1); + assert!(matches!(iter.next(), None)); + + query + .new_target_filters(&world, TargetFilter::::new().target(parent2)) + .apply_filters(); + let mut iter = query.iter_mut(&mut world); + assert!(iter.next().unwrap().0 == child2); + assert!(matches!(iter.next(), None)); + + query + .new_target_filters( + &world, + TargetFilter::::new() + .target(parent1) + .target(parent2), + ) + .apply_filters(); + let mut iter = query.iter_mut(&mut world); + assert!(matches!(iter.next(), None)); +} + +#[test] +fn relation_access() { + relation_access_raw(StorageType::Table); + relation_access_raw(StorageType::SparseSet); +} +fn relation_access_raw(storage_type: StorageType) { + #[derive(Debug, PartialEq, Eq)] + struct ChildOf { + despawn_recursive: bool, + } + let mut world = World::new(); + + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + let random_parent = world.spawn().id(); + let parent1 = world.spawn().id(); + let parent2 = world.spawn().id(); + let child1 = world + .spawn() + .insert_relation( + ChildOf { + despawn_recursive: true, + }, + parent1, + ) + .insert_relation( + ChildOf { + despawn_recursive: false, + }, + random_parent, + ) + .id(); + let child2 = world + .spawn() + .insert_relation( + ChildOf { + despawn_recursive: false, + }, + parent2, + ) + .insert_relation( + ChildOf { + despawn_recursive: true, + }, + random_parent, + ) + .id(); + + let mut query = world.query::<(Entity, &Relation)>(); + + query + .new_target_filters(&world, TargetFilter::::new().target(parent1)) + .apply_filters(); + let mut iter = query.iter(&world); + let (child, mut accessor) = iter.next().unwrap(); + assert!(child == child1); + assert_eq!( + accessor.next().unwrap(), + ( + parent1, + &ChildOf { + despawn_recursive: true + } + ) + ); + assert!(matches!(accessor.next(), None)); + assert!(matches!(iter.next(), None)); + + query + .new_target_filters(&world, TargetFilter::::new().target(parent2)) + .apply_filters(); + let mut iter = query.iter(&world); + let (child, mut accessor) = iter.next().unwrap(); + assert!(child == child2); + assert_eq!( + accessor.next().unwrap(), + ( + parent2, + &ChildOf { + despawn_recursive: false + } + ) + ); + assert!(matches!(accessor.next(), None)); + assert!(matches!(iter.next(), None)); + + query.clear_target_filters(&world); + let mut iter = query.iter(&world); + // + let (child, mut accessor) = iter.next().unwrap(); + assert!(child == child1); + assert_eq!( + accessor.next().unwrap(), + ( + random_parent, + &ChildOf { + despawn_recursive: false + } + ) + ); + assert_eq!( + accessor.next().unwrap(), + ( + parent1, + &ChildOf { + despawn_recursive: true + } + ) + ); + assert!(matches!(accessor.next(), None)); + // + let (child, mut accessor) = iter.next().unwrap(); + assert!(child == child2); + assert_eq!( + accessor.next().unwrap(), + ( + random_parent, + &ChildOf { + despawn_recursive: true + } + ) + ); + assert_eq!( + accessor.next().unwrap(), + ( + parent2, + &ChildOf { + despawn_recursive: false + } + ) + ); + assert!(matches!(accessor.next(), None)); + assert!(matches!(iter.next(), None)); +} + +#[test] +fn relation_query_mut() { + relation_query_mut_raw(StorageType::Table); + relation_query_mut_raw(StorageType::SparseSet); +} + +fn relation_query_mut_raw(storage_type: StorageType) { + #[derive(Eq, PartialEq, Debug, Copy, Clone)] + struct MyRelation(bool, u32); + + struct Fragment; + + let mut world = World::new(); + world + .register_component(ComponentDescriptor::new_targeted::( + storage_type, + )) + .unwrap(); + + let target1 = world.spawn().insert(Fragment::<1>).id(); + let target2 = world.spawn().insert(Fragment::<1>).id(); + let target3 = world.spawn().id(); + + let targeter1 = world + .spawn() + .insert(Fragment::<0>) + .insert("targeter1") + .insert_relation(MyRelation(true, 10), target1) + .insert_relation(MyRelation(false, 48), target2) + .insert_relation(MyRelation(false, 14), target3) + .id(); + let targeter2 = world + .spawn() + .insert("targeter2") + .insert_relation(MyRelation(false, 75), target1) + .insert_relation(MyRelation(true, 22), target2) + .id(); + let targeter3 = world + .spawn() + .insert(Fragment::<0>) + .insert("targeter3") + .insert_relation(MyRelation(true, 839), target2) + .insert_relation(MyRelation(true, 3), target3) + .id(); + + let mut query = world.query::<(Entity, &mut Relation, &&str)>(); + + query + .new_target_filters(&world, TargetFilter::::new().target(target2)) + .apply_filters(); + for (_, mut accessor, _) in query.iter_mut(&mut world) { + let (_, mut rel) = accessor.single(); + rel.0 = !rel.0; + rel.1 += 10; + } + + query + .new_target_filters( + &world, + TargetFilter::::new() + .target(target1) + .target(target2), + ) + .apply_filters(); + let mut was_targeter1 = false; + let mut was_targeter2 = false; + for (targeter, accessor, name) in query.iter_mut(&mut world) { + match () { + _ if targeter == targeter1 => { + was_targeter1 = true; + assert_eq!(*name, "targeter1"); + let targets = accessor.map(|(t, rel)| (t, *rel)).collect::>(); + assert_eq!(&targets[0], &(target1, MyRelation(true, 10))); + assert_eq!(&targets[1], &(target2, MyRelation(true, 58))); + assert_eq!(targets.len(), 2); + } + _ if targeter == targeter2 => { + was_targeter2 = true; + assert_eq!(*name, "targeter2"); + let targets = accessor.map(|(t, rel)| (t, *rel)).collect::>(); + assert_eq!(&targets[0], &(target1, MyRelation(false, 75))); + assert_eq!(&targets[1], &(target2, MyRelation(false, 32))); + assert_eq!(targets.len(), 2); + } + _ => panic!(), + } + } + assert!(was_targeter1 && was_targeter2); + + query.clear_target_filters(&world); + for (_, accessor, _) in query.iter_mut(&mut world) { + for (_, mut rel) in accessor { + rel.0 = !rel.0; + rel.1 *= 2; + } + } + + let mut was_targeter1 = false; + let mut was_targeter2 = false; + let mut was_targeter3 = false; + for (targeter, accessor, name) in query.iter_mut(&mut world) { + match () { + _ if targeter == targeter1 => { + was_targeter1 = true; + assert_eq!(*name, "targeter1"); + let targets = accessor.map(|(t, rel)| (t, *rel)).collect::>(); + assert_eq!(&targets[0], &(target1, MyRelation(false, 20))); + assert_eq!(&targets[1], &(target2, MyRelation(false, 116))); + assert_eq!(&targets[2], &(target3, MyRelation(true, 28))); + assert_eq!(targets.len(), 3); + } + _ if targeter == targeter2 => { + was_targeter2 = true; + assert_eq!(*name, "targeter2"); + let targets = accessor.map(|(t, rel)| (t, *rel)).collect::>(); + assert_eq!(&targets[0], &(target1, MyRelation(true, 150))); + assert_eq!(&targets[1], &(target2, MyRelation(true, 64))); + assert_eq!(targets.len(), 2); + } + _ if targeter == targeter3 => { + was_targeter3 = true; + assert_eq!(*name, "targeter3"); + let targets = accessor.map(|(t, rel)| (t, *rel)).collect::>(); + assert_eq!(&targets[0], &(target2, MyRelation(true, 849 * 2))); + assert_eq!(&targets[1], &(target3, MyRelation(false, 6))); + assert_eq!(targets.len(), 2); + } + _ => panic!(), + } + } + assert!(was_targeter1 && was_targeter2 && was_targeter3); +} + +#[test] +fn some_example_code() { + #[derive(PartialEq, Eq, Debug)] + struct MyRelation; + + let mut world = World::new(); + + let target1 = world.spawn().id(); + let target2 = world.spawn().id(); + let my_entity = world + .spawn() + .insert_relation(MyRelation, target1) + .insert_relation(MyRelation, target2) + .id(); + + let mut iterated_entities = Vec::new(); + let mut query = world.query::<(Entity, &Relation)>(); + for (entity, relations) in query.iter_mut(&mut world) { + iterated_entities.push(entity); + assert_eq!( + &relations.collect::>(), + &[(target1, &MyRelation), (target2, &MyRelation)], + ); + } + + assert_eq!(&iterated_entities, &[my_entity]); +} + +macro_rules! self_query_conflict_tests { + ($($name:ident => <$param:ty>)*) => { + $( + #[test] + #[should_panic] + fn $name() { + let mut world = World::new(); + world.query::<$param>(); + } + )* + }; +} + +self_query_conflict_tests!( + mut_and_mut => <(&mut Relation, &mut Relation)> + mut_and_ref => <(&mut Relation, &Relation)> + ref_and_mut => <(&Relation, &mut Relation)> + rel_and_mut => <(&Relation, &mut u32)> + rel_mut_and_ref => <(&mut Relation, &u32)> + mut_and_rel => <(&mut u32, &Relation)> + ref_and_rel_mut => <(&u32, &mut Relation)> + mut_and_rel_mut => <(&mut u32, &mut Relation)> + rel_mut_and_mut => <(&mut Relation, &mut u32)> +); + +macro_rules! no_self_query_conflict_tests { + ($($name:ident => <$param:ty>)*) => { + $( + #[test] + fn $name() { + let mut world = World::new(); + world.query::<$param>(); + } + )* + }; +} + +no_self_query_conflict_tests!( + rel_and_rel => <(&Relation, &Relation)> + rel_and_diff_rel => <(&Relation, &Relation)> + rel_mut_and_diff_rel_mut => <(&mut Relation, &mut Relation)> + rel_and_diff_rel_mut => <(&Relation, &mut Relation)> + rel_mut_and_diff_rel => <(&mut Relation, &Relation)> + rel_and_ref => <(&Relation, &u32)> + ref_and_rel => <(&u32, &Relation)> + rel_mut_and_diff_ref => <(&mut Relation, &u64)> + rel_and_diff_mut => <(&Relation, &mut u64)> + ref_and_diff_rel_mut => <(&u64, &mut Relation)> + mut_and_diff_rel => <(&mut u64, &Relation)> + mut_and_diff_rel_mut => <(&mut u64, &mut Relation)> + rel_mut_and_diff_mut => <(&mut Relation, &mut u64)> +); + +#[test] +fn compiles() { + let mut world = World::new(); + + let mut query = world.query::<&u32>(); + + let borrows = query.iter(&world).collect::>(); + query.clear_target_filters(&world); + let _borrows2 = query.iter(&world).collect::>(); + dbg!(borrows); +} + +/** +```compile_fail +use bevy_ecs::prelude::*; + +let mut world = World::new(); +let mut query = world.query::<&Relation>(); +let _borrows = query.iter(&world).collect::>(); +query.clear_target_filters(&world); +let _borrows2 = query.iter(&world).collect::>(); +drop(_borrows); // If this doesn't fail to compile we have unsoundness - Boxy +``` +*/ +pub fn _compile_fail() {} + +#[test] +fn explicit_path() { + let mut world = World::new(); + let mut query = world.query::<(&Relation, &Relation)>(); + let target = world.spawn().id(); + + query + .new_target_filters::>>( + &world, + TargetFilter::new().target(target), + ) + .apply_filters(); +} + +#[test] +fn foo() { + let mut world = World::new(); + let mut query = world.query::<&Relation>(); + let target = world.spawn().id(); + + query + .new_target_filters(&world, TargetFilter::::new().target(target)) + .apply_filters() + .for_each(&world, |_| ()) +} + +#[test] +#[allow(clippy::bool_assert_comparison)] +fn conflict_without_relation() { + let mut world = World::new(); + let q1 = world.query::<(&mut u32, &Relation)>(); + let q2 = world.query_filtered::<&mut u32, Without>>(); + assert_eq!( + q1.component_access.is_compatible(&q2.component_access), + false + ); +} + +#[test] +fn without_filter() { + without_filter_raw(StorageType::Table); + without_filter_raw(StorageType::SparseSet); +} + +fn without_filter_raw(storage_type: StorageType) { + let mut world = World::new(); + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + struct MyRelation; + + let target1 = world.spawn().id(); + let target2 = world.spawn().id(); + world + .spawn() + .insert(String::from("blah")) + .insert_relation(MyRelation, target1) + .id(); + let source2 = world + .spawn() + .insert(String::from("OwO")) + .insert_relation(MyRelation, target2) + .id(); + + let no_relation1 = world.spawn().insert(String::from("hiiii")).id(); + + let data = world + .query_filtered::<(Entity, &String), Without>>() + .iter(&world) + .collect::>(); + assert_eq!(&data, &[(no_relation1, &String::from("hiiii"))]); + + let data = world + .query_filtered::<(Entity, &String), Without>>() + .new_target_filters(&world, TargetFilter::::new().target(target1)) + .apply_filters() + .iter(&world) + .collect::>(); + assert_eq!( + &data, + &[ + (no_relation1, &String::from("hiiii")), + (source2, &String::from("OwO")) + ] + ); +} + +#[test] +fn relations_dont_yield_components() { + relations_dont_yield_components_raw(StorageType::SparseSet); + relations_dont_yield_components_raw(StorageType::Table); +} + +fn relations_dont_yield_components_raw(storage_type: StorageType) { + let mut world = World::new(); + + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + let _has_component = world.spawn().insert(10_u32).id(); + let target1 = world.spawn().id(); + let _has_both = world + .spawn() + .insert_relation(12_u32, target1) + .insert(14_u32) + .id(); + let target2 = world.spawn().id(); + let _has_relation = world.spawn().insert_relation(16_u32, target2).id(); + + let mut q = world.query::<&Relation>(); + let [first, second]: [RelationAccess<_>; 2] = + q.iter(&world).collect::>().try_into().unwrap(); + + assert_eq!(&first.collect::>(), &[(target1, &12_u32)]); + assert_eq!(&second.collect::>(), &[(target2, &16_u32)]); + + let [first, second]: [&u32; 2] = world + .query::<&u32>() + .iter(&world) + .collect::>() + .try_into() + .unwrap(); + assert_eq!(first, &10_u32); + assert_eq!(second, &14_u32); +} + +#[test] +fn duplicated_target_filters() { + duplicated_target_filters_raw(StorageType::SparseSet); + duplicated_target_filters_raw(StorageType::Table); +} + +fn duplicated_target_filters_raw(storage_type: StorageType) { + let mut world = World::new(); + + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + let target = world.spawn().id(); + let _source = world.spawn().insert_relation(10_u32, target).id(); + + let mut q = world.query::<&Relation>(); + let [relations]: [RelationAccess<_>; 1] = q + .new_target_filters( + &world, + TargetFilter::::new().target(target).target(target), + ) + .apply_filters() + .iter(&world) + .collect::>() + .try_into() + .unwrap(); + let [(rel_target, rel_data)]: [(Entity, &u32); 1] = + relations.collect::>().try_into().unwrap(); + assert_eq!(rel_target, target); + assert_eq!(rel_data, &10); +} + +#[test] +fn with_filter() { + with_filter_raw(StorageType::Table); + with_filter_raw(StorageType::SparseSet); +} + +fn with_filter_raw(storage_type: StorageType) { + let mut world = World::new(); + world + .register_component(ComponentDescriptor::new_targeted::(storage_type)) + .unwrap(); + + let no_relation = world.spawn().insert(10_u32).id(); + let target1 = world.spawn().id(); + let has_relation = world.spawn().insert_relation(12_u32, target1).id(); + let target2 = world.spawn().id(); + let has_both = world + .spawn() + .insert_relation(14_u32, target2) + .insert(16_u32) + .id(); + let many_relations = world + .spawn() + .insert_relation(18_u32, target1) + .insert_relation(20_u32, target2) + .id(); + + let mut q = world.query_filtered::>>(); + let [e1, e2, e3]: [Entity; 3] = q.iter(&world).collect::>().try_into().unwrap(); + assert_eq!(e1, has_relation); + assert_eq!(e2, has_both); + assert_eq!(e3, many_relations); + let [e1, e2]: [Entity; 2] = q + .new_target_filters(&world, TargetFilter::::new().target(target1)) + .apply_filters() + .iter(&world) + .collect::>() + .try_into() + .unwrap(); + assert_eq!(e1, has_relation); + assert_eq!(e2, many_relations); + let [e1, e2]: [Entity; 2] = q + .new_target_filters(&world, TargetFilter::::new().target(target2)) + .apply_filters() + .iter(&world) + .collect::>() + .try_into() + .unwrap(); + assert_eq!(e1, has_both); + assert_eq!(e2, many_relations); + let []: [Entity; 0] = q + .new_target_filters(&world, TargetFilter::::new().target(no_relation)) + .apply_filters() + .iter(&world) + .collect::>() + .try_into() + .unwrap(); +} + +#[test] +pub fn sparse_set_relation_registration() { + let mut world = World::new(); + world + .register_component(ComponentDescriptor::new_targeted::( + StorageType::SparseSet, + )) + .unwrap(); + let mut q = world.query::<&Relation>(); + assert!(q.iter(&world).next().is_none()); + + let target = world.spawn().id(); + world + .spawn() + .insert_relation(String::from("UwowU"), target) + .id(); + + use std::any::TypeId; + let ty_id = world + .components + .component_id(TypeId::of::()) + .unwrap(); + + assert_eq!( + world + .storages + .sparse_sets + .get(ty_id, Some(target)) + .unwrap() + .len(), + 1 + ); +} diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index e128167d5610d..3109c5a8d1793 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -183,9 +183,10 @@ impl<'w> WorldCell<'w> { } pub fn get_resource(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; + let component_id = self.world.components.resource_id(TypeId::of::())?; let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; + let archetype_component_id = + resource_archetype.get_archetype_component_id(component_id, None)?; Some(WorldBorrow::new( // SAFE: ComponentId matches TypeId unsafe { self.world.get_resource_with_id(component_id)? }, @@ -195,9 +196,10 @@ impl<'w> WorldCell<'w> { } pub fn get_resource_mut(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; + let component_id = self.world.components.resource_id(TypeId::of::())?; let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; + let archetype_component_id = + resource_archetype.get_archetype_component_id(component_id, None)?; Some(WorldBorrowMut::new( // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut unsafe { @@ -210,9 +212,10 @@ impl<'w> WorldCell<'w> { } pub fn get_non_send(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; + let component_id = self.world.components.resource_id(TypeId::of::())?; let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; + let archetype_component_id = + resource_archetype.get_archetype_component_id(component_id, None)?; Some(WorldBorrow::new( // SAFE: ComponentId matches TypeId unsafe { self.world.get_non_send_with_id(component_id)? }, @@ -222,9 +225,10 @@ impl<'w> WorldCell<'w> { } pub fn get_non_send_mut(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; + let component_id = self.world.components.resource_id(TypeId::of::())?; let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; + let archetype_component_id = + resource_archetype.get_archetype_component_id(component_id, None)?; Some(WorldBorrowMut::new( // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut unsafe { @@ -292,13 +296,10 @@ mod tests { } } - let u32_component_id = world - .components - .get_resource_id(TypeId::of::()) - .unwrap(); + let u32_component_id = world.components.resource_id(TypeId::of::()).unwrap(); let resource_archetype = world.archetypes.get(ArchetypeId::resource()).unwrap(); let u32_archetype_component_id = resource_archetype - .get_archetype_component_id(u32_component_id) + .get_archetype_component_id(u32_component_id, None) .unwrap(); assert_eq!(world.archetype_component_access.access.len(), 1); assert_eq!( diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 82d015ec3e3bc..51c4116d00052 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -36,10 +36,10 @@ impl DynamicScene { }); } - for component_id in archetype.components() { + for (component_id, _) in archetype.components() { let reflect_component = world .components() - .get_info(component_id) + .info(component_id) .and_then(|info| type_registry.get(info.type_id().unwrap())) .and_then(|registration| registration.data::()); if let Some(reflect_component) = reflect_component { @@ -83,7 +83,7 @@ impl DynamicScene { })?; if world .entity(entity) - .contains_type_id(registration.type_id()) + .contains_type_id(registration.type_id(), None) { reflect_component.apply_component(world, entity, &**component); } else { diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index e6570a20f9ed2..2c22759d5983d 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -159,11 +159,11 @@ impl SceneSpawner { .entity_map .entry(*scene_entity) .or_insert_with(|| world.spawn().id()); - for component_id in archetype.components() { + for (component_id, _) in archetype.components() { let component_info = scene .world .components() - .get_info(component_id) + .info(component_id) .expect("component_ids in archetypes should have ComponentInfo"); let reflect_component = type_registry diff --git a/examples/README.md b/examples/README.md index 37b76a3a34830..a48cf3600f535 100644 --- a/examples/README.md +++ b/examples/README.md @@ -163,6 +163,7 @@ Example | File | Description `iter_combinations` | [`ecs/iter_combinations.rs`](./ecs/iter_combinations.rs) | Shows how to iterate over combinations of query results. `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` `query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` +`relations_grouping` | [`ecs/relations_grouping.rs`](./ecs/relations_grouping.rs) | Group entities using `Relations` and then query for entities in those groups `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) `state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state diff --git a/examples/ecs/relations_grouping.rs b/examples/ecs/relations_grouping.rs new file mode 100644 index 0000000000000..9451852c93212 --- /dev/null +++ b/examples/ecs/relations_grouping.rs @@ -0,0 +1,209 @@ +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + prelude::*, + render::camera::Camera, + sprite::SpriteSettings, +}; +use rand::Rng; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + // Adds frame time diagnostics + .add_plugin(FrameTimeDiagnosticsPlugin::default()) + // Adds a system that prints diagnostics to the console + .add_plugin(LogDiagnosticsPlugin::default()) + .insert_resource(GroupSet(Vec::new())) + .insert_resource(SpriteSettings { + // NOTE: this is an experimental feature that doesn't work in all cases + frustum_culling_enabled: true, + }) + .add_startup_system(create_groups.system()) + .add_system(move_camera.system()) + // + .add_system_to_stage(CoreStage::PreUpdate, remove_group_targets.system()) + // + .add_stage_after(CoreStage::PreUpdate, "foo", SystemStage::parallel()) + .add_system_to_stage("foo", pick_group_targets.system()) + // + .add_system(move_bevys.system().label("a")) + .add_system(set_group_position.system().after("a")) + .run() +} + +struct InGroup; +struct MoveToGroup; + +struct Group; + +struct TargetOffset(Vec3); +struct GroupPosition(Vec3); + +struct GroupSet(Vec); + +const NUM_GROUPS: u32 = 30; +const MAP_BOUNDS: (f32, f32) = (1500., 750.); +const GROUP_RANGE: (f32, f32) = (100., 100.); + +fn create_groups( + mut commands: Commands, + mut groups: ResMut, + assets: ResMut, + mut materials: ResMut>, +) { + let mut rng = rand::thread_rng(); + + commands + .spawn() + .insert_bundle(OrthographicCameraBundle::new_2d()); + + for _ in 0..NUM_GROUPS { + let sprite_handle = materials.add(assets.load("branding/icon.png").into()); + materials.get_mut(&sprite_handle).unwrap().color = + Color::rgb(rng.gen(), rng.gen(), rng.gen()); + + let group_area = Vec3::new( + rng.gen_range(-MAP_BOUNDS.0..MAP_BOUNDS.0), + rng.gen_range(-MAP_BOUNDS.1..MAP_BOUNDS.1), + 0., + ); + + let group_id = commands + .spawn() + .insert(Group) + .insert(GroupPosition(group_area)) + .id(); + groups.0.push(group_id); + + for _ in 0..rng.gen_range(50..150) { + commands + .spawn() + .insert_relation(InGroup, group_id) + .insert(TargetOffset(Vec3::new( + rng.gen_range(-GROUP_RANGE.0..GROUP_RANGE.0), + rng.gen_range(-GROUP_RANGE.1..GROUP_RANGE.1), + 0., + ))) + .insert_bundle(SpriteBundle { + material: sprite_handle.clone(), + transform: Transform::from_translation( + Vec3::new( + rng.gen_range(-GROUP_RANGE.0..GROUP_RANGE.0), + rng.gen_range(-GROUP_RANGE.1..GROUP_RANGE.1), + 0., + ) + group_area, + ), + sprite: Sprite::new(Vec2::new(48., 48.)), + ..Default::default() + }); + } + } +} + +fn move_camera(keys: Res>, mut query: Query<&mut Transform, With>) { + let mut transform = query.single_mut().unwrap(); + if keys.pressed(KeyCode::A) { + transform.translation -= Vec3::new(10., 0., 0.); + } + if keys.pressed(KeyCode::D) { + transform.translation += Vec3::new(10., 0., 0.); + } + if keys.pressed(KeyCode::W) { + transform.translation += Vec3::new(0., 10., 0.); + } + if keys.pressed(KeyCode::S) { + transform.translation -= Vec3::new(0., 10., 0.); + } + transform.translation.x = transform.translation.x.clamp(-MAP_BOUNDS.0, MAP_BOUNDS.0); + transform.translation.y = transform.translation.y.clamp(-MAP_BOUNDS.1, MAP_BOUNDS.1); +} + +fn set_group_position( + mut bevys: Query<&Transform, With>>, + mut groups: Query<&mut GroupPosition>, + group_set: Res, +) { + for &group_entity in group_set.0.iter() { + let mut iter = bevys + .new_target_filters(TargetFilter::::new().target(group_entity)) + .apply_filters() + .iter(); + + let mut average_pos = + (iter.next().unwrap().translation + iter.next().unwrap().translation) / 2.0; + + for pos in iter { + average_pos += pos.translation; + average_pos /= 2.0; + } + + let mut group_pos = groups.get_mut(group_entity).unwrap(); + group_pos.0 = average_pos; + } +} + +fn remove_group_targets( + mut commands: Commands, + mut groups: Query<(&mut GroupPosition, &Relation), With>, + group_set: Res, +) { + for &group_entity in group_set.0.iter() { + let (group_pos, move_to) = match groups.get_mut(group_entity) { + Ok(mut components) => ((*components.0).0, components.1.single().0), + Err(_) => continue, + }; + let target_pos = match groups.get_mut(move_to) { + Ok((target_pos, _)) => (*target_pos).0, + Err(_) => continue, + }; + + if (target_pos - group_pos).abs().length() < 100.0 { + commands + .entity(group_entity) + .remove_relation::(move_to); + } + } +} + +fn pick_group_targets( + mut commands: Commands, + groups: Query, Without>)>, + group_set: Res, +) { + let mut rng = rand::thread_rng(); + for group in groups.iter() { + let group_target = loop { + let group_target = group_set.0[rng.gen_range::(0..group_set.0.len())]; + if group_target != group { + break group_target; + } + }; + + commands + .entity(group) + .insert_relation(MoveToGroup, group_target); + } +} + +fn move_bevys( + mut bevys: Query<(&mut Transform, &TargetOffset, &Relation)>, + mut groups: Query<(&mut GroupPosition, &Relation)>, + group_set: Res, +) { + for &group_entity in group_set.0.iter() { + let (move_to, _) = match groups.get_mut(group_entity) { + Ok(mut x) => x.1.single(), + Err(_) => continue, + }; + let GroupPosition(target_pos) = *groups.get_mut(move_to).unwrap().0; + + bevys + .new_target_filters(TargetFilter::::new().target(group_entity)) + .apply_filters() + .iter_mut() + .for_each(|(mut transform, target_offset, _)| { + let velocity = ((target_pos + target_offset.0) - transform.translation).normalize(); + transform.translation += velocity; + }); + } +} From 593f49fdb10b200a463fecfb6a07cd20be50344e Mon Sep 17 00:00:00 2001 From: Ellen Date: Fri, 30 Jul 2021 01:22:27 +0100 Subject: [PATCH 2/2] awd --- crates/bevy_ecs/src/component.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 20156a0f814e1..69acbc2b030a3 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -277,7 +277,7 @@ impl Components { } #[inline] - /// Safety + /// # Safety /// /// `id` must be a valid id pub unsafe fn info_unchecked(&self, id: ComponentId) -> &ComponentInfo {