diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 27383ff71ee551..d4d307f8a53e65 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -110,15 +110,15 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { .map(|field| &field.ty) .collect::>(); - let mut field_type_infos = Vec::new(); + let mut field_component_ids = Vec::new(); let mut field_get_components = Vec::new(); let mut field_from_components = Vec::new(); for ((field_type, is_bundle), field) in field_type.iter().zip(is_bundle.iter()).zip(field.iter()) { if *is_bundle { - field_type_infos.push(quote! { - type_info.extend(<#field_type as #ecs_path::bundle::Bundle>::type_info()); + field_component_ids.push(quote! { + component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components)); }); field_get_components.push(quote! { self.#field.get_components(&mut func); @@ -127,8 +127,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func), }); } else { - field_type_infos.push(quote! { - type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>()); + field_component_ids.push(quote! { + component_ids.push(components.get_or_insert_id::<#field_type>()); }); field_get_components.push(quote! { func((&mut self.#field as *mut #field_type).cast::()); @@ -147,10 +147,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { TokenStream::from(quote! { /// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause { - fn type_info() -> Vec<#ecs_path::component::TypeInfo> { - let mut type_info = Vec::with_capacity(#field_len); - #(#field_type_infos)* - type_info + fn component_ids( + components: &mut #ecs_path::component::Components, + ) -> Vec<#ecs_path::component::ComponentId> { + let mut component_ids = Vec::with_capacity(#field_len); + #(#field_component_ids)* + component_ids } #[allow(unused_variables, unused_mut, non_snake_case)] diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 00f3bac9e527e4..ab8f346a551164 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, TypeInfo}, + component::{Component, ComponentId, ComponentTicks, Components, StorageType}, entity::Entity, storage::{SparseSetIndex, SparseSets, Table}, }; @@ -38,13 +38,13 @@ use std::{any::TypeId, collections::HashMap}; /// ``` /// /// # Safety -/// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the +/// [Bundle::component_id] must return the ComponentId for each component type in the bundle, in the /// _exact_ order that [Bundle::get_components] is called. -/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by -/// [Bundle::type_info] +/// [Bundle::from_components] must call `func` exactly once for each [ComponentId] returned by +/// [Bundle::component_id] pub unsafe trait Bundle: Send + Sync + 'static { - /// Gets this [Bundle]'s components type info, in the order of this bundle's Components - fn type_info() -> Vec; + /// Gets this [Bundle]'s component ids, in the order of this bundle's Components + fn component_ids(components: &mut Components) -> Vec; /// Calls `func`, which should return data for each component in the bundle, in the order of /// this bundle's Components @@ -66,8 +66,9 @@ macro_rules! tuple_impl { ($($name: ident),*) => { /// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { - fn type_info() -> Vec { - vec![$(TypeInfo::of::<$name>()),*] + #[allow(unused_variables)] + fn component_ids(components: &mut Components) -> Vec { + vec![$(components.get_or_insert_id::<$name>()),*] } #[allow(unused_variables, unused_mut)] @@ -205,10 +206,12 @@ impl Bundles { ) -> &'a BundleInfo { let bundle_infos = &mut self.bundle_infos; let id = self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { - let type_info = T::type_info(); + let component_ids = T::component_ids(components); let id = BundleId(bundle_infos.len()); - let bundle_info = - initialize_bundle(std::any::type_name::(), &type_info, id, components); + // SAFE: T::component_id ensures info was created + let bundle_info = unsafe { + initialize_bundle(std::any::type_name::(), component_ids, id, components) + }; bundle_infos.push(bundle_info); id }); @@ -217,21 +220,21 @@ impl Bundles { } } -fn initialize_bundle( +/// # Safety +/// +/// `component_id` must be valid [ComponentId]'s +unsafe fn initialize_bundle( bundle_type_name: &'static str, - type_info: &[TypeInfo], + component_ids: Vec, id: BundleId, components: &mut Components, ) -> BundleInfo { - let mut component_ids = Vec::new(); let mut storage_types = Vec::new(); - for type_info in type_info { - let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone()); - // SAFE: get_with_type_info ensures info was created - let info = unsafe { components.get_info_unchecked(component_id) }; - component_ids.push(component_id); - storage_types.push(info.storage_type()); + for &component_id in &component_ids { + // SAFE: component_id exists and is therefore valid + let component_info = components.get_info_unchecked(component_id); + storage_types.push(component_info.storage_type()); } let mut deduped = component_ids.clone(); diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component.rs similarity index 82% rename from crates/bevy_ecs/src/component/mod.rs rename to crates/bevy_ecs/src/component.rs index ccb8a6f993e6db..6e71a1a7d93280 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1,7 +1,3 @@ -mod type_info; - -pub use type_info::*; - use crate::storage::SparseSetIndex; use std::{ alloc::Layout, @@ -56,15 +52,8 @@ impl Default for StorageType { #[derive(Debug)] pub struct ComponentInfo { - name: String, id: ComponentId, - type_id: Option, - // SAFETY: This must remain private. It must only be set to "true" if this component is - // actually Send + Sync - is_send_and_sync: bool, - layout: Layout, - drop: unsafe fn(*mut u8), - storage_type: StorageType, + descriptor: ComponentDescriptor, } impl ComponentInfo { @@ -75,44 +64,36 @@ impl ComponentInfo { #[inline] pub fn name(&self) -> &str { - &self.name + &self.descriptor.name } #[inline] pub fn type_id(&self) -> Option { - self.type_id + self.descriptor.type_id } #[inline] pub fn layout(&self) -> Layout { - self.layout + self.descriptor.layout } #[inline] pub fn drop(&self) -> unsafe fn(*mut u8) { - self.drop + self.descriptor.drop } #[inline] pub fn storage_type(&self) -> StorageType { - self.storage_type + self.descriptor.storage_type } #[inline] pub fn is_send_and_sync(&self) -> bool { - self.is_send_and_sync + self.descriptor.is_send_and_sync } fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self { - ComponentInfo { - id, - name: descriptor.name, - storage_type: descriptor.storage_type, - type_id: descriptor.type_id, - is_send_and_sync: descriptor.is_send_and_sync, - drop: descriptor.drop, - layout: descriptor.layout, - } + ComponentInfo { id, descriptor } } } @@ -142,6 +123,7 @@ impl SparseSetIndex for ComponentId { } } +#[derive(Debug)] pub struct ComponentDescriptor { name: String, storage_type: StorageType, @@ -154,6 +136,11 @@ pub struct ComponentDescriptor { } impl ComponentDescriptor { + // SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value. + unsafe fn drop_ptr(x: *mut u8) { + x.cast::().drop_in_place() + } + pub fn new(storage_type: StorageType) -> Self { Self { name: std::any::type_name::().to_string(), @@ -161,7 +148,18 @@ impl ComponentDescriptor { is_send_and_sync: true, type_id: Some(TypeId::of::()), layout: Layout::new::(), - drop: TypeInfo::drop_ptr::, + drop: Self::drop_ptr::, + } + } + + fn new_non_send(storage_type: StorageType) -> Self { + Self { + name: std::any::type_name::().to_string(), + storage_type, + is_send_and_sync: false, + type_id: Some(TypeId::of::()), + layout: Layout::new::(), + drop: Self::drop_ptr::, } } @@ -181,19 +179,6 @@ impl ComponentDescriptor { } } -impl From for ComponentDescriptor { - fn from(type_info: TypeInfo) -> Self { - Self { - name: type_info.type_name().to_string(), - storage_type: StorageType::default(), - is_send_and_sync: type_info.is_send_and_sync(), - type_id: Some(type_info.type_id()), - drop: type_info.drop(), - layout: type_info.layout(), - } - } -} - #[derive(Debug, Default)] pub struct Components { components: Vec, @@ -231,7 +216,12 @@ impl Components { #[inline] pub fn get_or_insert_id(&mut self) -> ComponentId { - self.get_or_insert_with(TypeId::of::(), TypeInfo::of::) + // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.get_or_insert_with(TypeId::of::(), || { + ComponentDescriptor::new::(StorageType::default()) + }) + } } #[inline] @@ -279,42 +269,58 @@ impl Components { #[inline] pub fn get_or_insert_resource_id(&mut self) -> ComponentId { - self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of::) + // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.get_or_insert_resource_with(TypeId::of::(), || { + ComponentDescriptor::new::(StorageType::default()) + }) + } } #[inline] pub fn get_or_insert_non_send_resource_id(&mut self) -> ComponentId { - self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of_non_send_and_sync::) + // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.get_or_insert_resource_with(TypeId::of::(), || { + ComponentDescriptor::new_non_send::(StorageType::default()) + }) + } } + /// # Safety + /// + /// The [`ComponentDescriptor`] must match the [`TypeId`] #[inline] - fn get_or_insert_resource_with( + unsafe fn get_or_insert_resource_with( &mut self, type_id: TypeId, - func: impl FnOnce() -> TypeInfo, + func: impl FnOnce() -> ComponentDescriptor, ) -> ComponentId { let components = &mut self.components; let index = self.resource_indices.entry(type_id).or_insert_with(|| { - let type_info = func(); + let descriptor = func(); let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), type_info.into())); + components.push(ComponentInfo::new(ComponentId(index), descriptor)); index }); ComponentId(*index) } + /// # Safety + /// + /// The [`ComponentDescriptor`] must match the [`TypeId`] #[inline] - pub(crate) fn get_or_insert_with( + pub(crate) unsafe fn get_or_insert_with( &mut self, type_id: TypeId, - func: impl FnOnce() -> TypeInfo, + func: impl FnOnce() -> ComponentDescriptor, ) -> ComponentId { let components = &mut self.components; let index = self.indices.entry(type_id).or_insert_with(|| { - let type_info = func(); + let descriptor = func(); let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), type_info.into())); + components.push(ComponentInfo::new(ComponentId(index), descriptor)); index }); diff --git a/crates/bevy_ecs/src/component/type_info.rs b/crates/bevy_ecs/src/component/type_info.rs deleted file mode 100644 index 58839632f438ff..00000000000000 --- a/crates/bevy_ecs/src/component/type_info.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::{alloc::Layout, any::TypeId}; - -/// Metadata required to store a component. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TypeInfo { - type_id: TypeId, - layout: Layout, - drop: unsafe fn(*mut u8), - type_name: &'static str, - is_send_and_sync: bool, -} - -impl TypeInfo { - /// Metadata for `T`. - pub fn of() -> Self { - Self { - type_id: TypeId::of::(), - layout: Layout::new::(), - is_send_and_sync: true, - drop: Self::drop_ptr::, - type_name: core::any::type_name::(), - } - } - - pub fn of_non_send_and_sync() -> Self { - Self { - type_id: TypeId::of::(), - layout: Layout::new::(), - is_send_and_sync: false, - drop: Self::drop_ptr::, - type_name: core::any::type_name::(), - } - } - - #[inline] - pub fn type_id(&self) -> TypeId { - self.type_id - } - - #[inline] - pub fn layout(&self) -> Layout { - self.layout - } - - #[inline] - pub fn drop(&self) -> unsafe fn(*mut u8) { - self.drop - } - - #[inline] - pub fn is_send_and_sync(&self) -> bool { - self.is_send_and_sync - } - - #[inline] - pub fn type_name(&self) -> &'static str { - self.type_name - } - - pub(crate) unsafe fn drop_ptr(x: *mut u8) { - x.cast::().drop_in_place() - } -} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index d95a15bd0804b2..fa50ae8efd22af 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -41,7 +41,7 @@ mod tests { use crate as bevy_ecs; use crate::{ bundle::Bundle, - component::{Component, ComponentDescriptor, ComponentId, StorageType, TypeInfo}, + component::{Component, ComponentDescriptor, ComponentId, StorageType}, entity::Entity, query::{ Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery, @@ -102,21 +102,26 @@ mod tests { #[test] fn bundle_derive() { + let mut world = World::new(); + #[derive(Bundle, PartialEq, Debug)] struct Foo { x: &'static str, y: i32, } - assert_eq!( - ::type_info(), - vec![TypeInfo::of::<&'static str>(), TypeInfo::of::(),] - ); - - let mut world = World::new(); world .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) .unwrap(); + + assert_eq!( + ::component_ids(world.components_mut()), + vec![ + world.components_mut().get_or_insert_id::<&'static str>(), + world.components_mut().get_or_insert_id::(), + ] + ); + let e1 = world.spawn().insert_bundle(Foo { x: "abc", y: 123 }).id(); let e2 = world.spawn().insert_bundle(("def", 456, true)).id(); assert_eq!(*world.get::<&str>(e1).unwrap(), "abc"); @@ -146,12 +151,12 @@ mod tests { } assert_eq!( - ::type_info(), + ::component_ids(world.components_mut()), vec![ - TypeInfo::of::(), - TypeInfo::of::<&'static str>(), - TypeInfo::of::(), - TypeInfo::of::(), + 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::(), ] ); diff --git a/crates/bevy_ecs/src/storage/blob_vec.rs b/crates/bevy_ecs/src/storage/blob_vec.rs index d724728c177b28..49c92d92d1eb90 100644 --- a/crates/bevy_ecs/src/storage/blob_vec.rs +++ b/crates/bevy_ecs/src/storage/blob_vec.rs @@ -267,9 +267,13 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize { #[cfg(test)] mod tests { use super::BlobVec; - use crate::component::TypeInfo; use std::{alloc::Layout, cell::RefCell, rc::Rc}; + // SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value. + unsafe fn drop_ptr(x: *mut u8) { + x.cast::().drop_in_place() + } + /// # Safety /// /// `blob_vec` must have a layout that matches Layout::new::() @@ -300,7 +304,7 @@ mod tests { #[test] fn resize_test() { let item_layout = Layout::new::(); - let drop = TypeInfo::drop_ptr::; + let drop = drop_ptr::; let mut blob_vec = BlobVec::new(item_layout, drop, 64); unsafe { for i in 0..1_000 { @@ -330,7 +334,7 @@ mod tests { let drop_counter = Rc::new(RefCell::new(0)); { let item_layout = Layout::new::(); - let drop = TypeInfo::drop_ptr::; + let drop = drop_ptr::; let mut blob_vec = BlobVec::new(item_layout, drop, 2); assert_eq!(blob_vec.capacity(), 2); unsafe { @@ -390,7 +394,7 @@ mod tests { #[test] fn blob_vec_drop_empty_capacity() { let item_layout = Layout::new::(); - let drop = TypeInfo::drop_ptr::; + let drop = drop_ptr::; let _ = BlobVec::new(item_layout, drop, 0); } } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 8fc334b56975a3..3b015076132ecf 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -500,17 +500,12 @@ impl IndexMut for Tables { #[cfg(test)] mod tests { - use crate::{ - component::{Components, TypeInfo}, - entity::Entity, - storage::Table, - }; + use crate::{component::Components, entity::Entity, storage::Table}; #[test] fn table() { let mut components = Components::default(); - let type_info = TypeInfo::of::(); - let component_id = components.get_or_insert_with(type_info.type_id(), || type_info); + 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());