Skip to content

Commit 86cc70b

Browse files
committed
Refactor ECS to reduce the dependency on a 1-to-1 mapping between components and real rust types (#2490)
# Objective There is currently a 1-to-1 mapping between components and real rust types. This means that it is impossible for multiple components to be represented by the same rust type or for a component to not have a rust type at all. This means that component types can't be defined in languages other than rust like necessary for scripting or sandboxed (wasm?) plugins. ## Solution Refactor `ComponentDescriptor` and `Bundle` to remove `TypeInfo`. `Bundle` now uses `ComponentId` instead. `ComponentDescriptor` is now always created from a rust type instead of through the `TypeInfo` indirection. A future PR may make it possible to construct a `ComponentDescriptor` from it's fields without a rust type being involved.
1 parent 4b6238d commit 86cc70b

File tree

7 files changed

+119
-167
lines changed

7 files changed

+119
-167
lines changed

crates/bevy_ecs/macros/src/lib.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
110110
.map(|field| &field.ty)
111111
.collect::<Vec<_>>();
112112

113-
let mut field_type_infos = Vec::new();
113+
let mut field_component_ids = Vec::new();
114114
let mut field_get_components = Vec::new();
115115
let mut field_from_components = Vec::new();
116116
for ((field_type, is_bundle), field) in
117117
field_type.iter().zip(is_bundle.iter()).zip(field.iter())
118118
{
119119
if *is_bundle {
120-
field_type_infos.push(quote! {
121-
type_info.extend(<#field_type as #ecs_path::bundle::Bundle>::type_info());
120+
field_component_ids.push(quote! {
121+
component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components));
122122
});
123123
field_get_components.push(quote! {
124124
self.#field.get_components(&mut func);
@@ -127,8 +127,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
127127
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func),
128128
});
129129
} else {
130-
field_type_infos.push(quote! {
131-
type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>());
130+
field_component_ids.push(quote! {
131+
component_ids.push(components.get_or_insert_id::<#field_type>());
132132
});
133133
field_get_components.push(quote! {
134134
func((&mut self.#field as *mut #field_type).cast::<u8>());
@@ -147,10 +147,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
147147
TokenStream::from(quote! {
148148
/// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
149149
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause {
150-
fn type_info() -> Vec<#ecs_path::component::TypeInfo> {
151-
let mut type_info = Vec::with_capacity(#field_len);
152-
#(#field_type_infos)*
153-
type_info
150+
fn component_ids(
151+
components: &mut #ecs_path::component::Components,
152+
) -> Vec<#ecs_path::component::ComponentId> {
153+
let mut component_ids = Vec::with_capacity(#field_len);
154+
#(#field_component_ids)*
155+
component_ids
154156
}
155157

156158
#[allow(unused_variables, unused_mut, non_snake_case)]

crates/bevy_ecs/src/bundle.rs

+23-20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pub use bevy_ecs_macros::Bundle;
22

33
use crate::{
44
archetype::ComponentStatus,
5-
component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo},
5+
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
66
entity::Entity,
77
storage::{SparseSetIndex, SparseSets, Table},
88
};
@@ -38,13 +38,13 @@ use std::{any::TypeId, collections::HashMap};
3838
/// ```
3939
///
4040
/// # Safety
41-
/// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the
41+
/// [Bundle::component_id] must return the ComponentId for each component type in the bundle, in the
4242
/// _exact_ order that [Bundle::get_components] is called.
43-
/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by
44-
/// [Bundle::type_info]
43+
/// [Bundle::from_components] must call `func` exactly once for each [ComponentId] returned by
44+
/// [Bundle::component_id]
4545
pub unsafe trait Bundle: Send + Sync + 'static {
46-
/// Gets this [Bundle]'s components type info, in the order of this bundle's Components
47-
fn type_info() -> Vec<TypeInfo>;
46+
/// Gets this [Bundle]'s component ids, in the order of this bundle's Components
47+
fn component_ids(components: &mut Components) -> Vec<ComponentId>;
4848

4949
/// Calls `func`, which should return data for each component in the bundle, in the order of
5050
/// this bundle's Components
@@ -66,8 +66,9 @@ macro_rules! tuple_impl {
6666
($($name: ident),*) => {
6767
/// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
6868
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
69-
fn type_info() -> Vec<TypeInfo> {
70-
vec![$(TypeInfo::of::<$name>()),*]
69+
#[allow(unused_variables)]
70+
fn component_ids(components: &mut Components) -> Vec<ComponentId> {
71+
vec![$(components.get_or_insert_id::<$name>()),*]
7172
}
7273

7374
#[allow(unused_variables, unused_mut)]
@@ -205,10 +206,12 @@ impl Bundles {
205206
) -> &'a BundleInfo {
206207
let bundle_infos = &mut self.bundle_infos;
207208
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
208-
let type_info = T::type_info();
209+
let component_ids = T::component_ids(components);
209210
let id = BundleId(bundle_infos.len());
210-
let bundle_info =
211-
initialize_bundle(std::any::type_name::<T>(), &type_info, id, components);
211+
// SAFE: T::component_id ensures info was created
212+
let bundle_info = unsafe {
213+
initialize_bundle(std::any::type_name::<T>(), component_ids, id, components)
214+
};
212215
bundle_infos.push(bundle_info);
213216
id
214217
});
@@ -217,21 +220,21 @@ impl Bundles {
217220
}
218221
}
219222

220-
fn initialize_bundle(
223+
/// # Safety
224+
///
225+
/// `component_id` must be valid [ComponentId]'s
226+
unsafe fn initialize_bundle(
221227
bundle_type_name: &'static str,
222-
type_info: &[TypeInfo],
228+
component_ids: Vec<ComponentId>,
223229
id: BundleId,
224230
components: &mut Components,
225231
) -> BundleInfo {
226-
let mut component_ids = Vec::new();
227232
let mut storage_types = Vec::new();
228233

229-
for type_info in type_info {
230-
let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone());
231-
// SAFE: get_with_type_info ensures info was created
232-
let info = unsafe { components.get_info_unchecked(component_id) };
233-
component_ids.push(component_id);
234-
storage_types.push(info.storage_type());
234+
for &component_id in &component_ids {
235+
// SAFE: component_id exists and is therefore valid
236+
let component_info = components.get_info_unchecked(component_id);
237+
storage_types.push(component_info.storage_type());
235238
}
236239

237240
let mut deduped = component_ids.clone();

crates/bevy_ecs/src/component/mod.rs renamed to crates/bevy_ecs/src/component.rs

+58-52
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
mod type_info;
2-
3-
pub use type_info::*;
4-
51
use crate::storage::SparseSetIndex;
62
use std::{
73
alloc::Layout,
@@ -56,15 +52,8 @@ impl Default for StorageType {
5652

5753
#[derive(Debug)]
5854
pub struct ComponentInfo {
59-
name: String,
6055
id: ComponentId,
61-
type_id: Option<TypeId>,
62-
// SAFETY: This must remain private. It must only be set to "true" if this component is
63-
// actually Send + Sync
64-
is_send_and_sync: bool,
65-
layout: Layout,
66-
drop: unsafe fn(*mut u8),
67-
storage_type: StorageType,
56+
descriptor: ComponentDescriptor,
6857
}
6958

7059
impl ComponentInfo {
@@ -75,44 +64,36 @@ impl ComponentInfo {
7564

7665
#[inline]
7766
pub fn name(&self) -> &str {
78-
&self.name
67+
&self.descriptor.name
7968
}
8069

8170
#[inline]
8271
pub fn type_id(&self) -> Option<TypeId> {
83-
self.type_id
72+
self.descriptor.type_id
8473
}
8574

8675
#[inline]
8776
pub fn layout(&self) -> Layout {
88-
self.layout
77+
self.descriptor.layout
8978
}
9079

9180
#[inline]
9281
pub fn drop(&self) -> unsafe fn(*mut u8) {
93-
self.drop
82+
self.descriptor.drop
9483
}
9584

9685
#[inline]
9786
pub fn storage_type(&self) -> StorageType {
98-
self.storage_type
87+
self.descriptor.storage_type
9988
}
10089

10190
#[inline]
10291
pub fn is_send_and_sync(&self) -> bool {
103-
self.is_send_and_sync
92+
self.descriptor.is_send_and_sync
10493
}
10594

10695
fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
107-
ComponentInfo {
108-
id,
109-
name: descriptor.name,
110-
storage_type: descriptor.storage_type,
111-
type_id: descriptor.type_id,
112-
is_send_and_sync: descriptor.is_send_and_sync,
113-
drop: descriptor.drop,
114-
layout: descriptor.layout,
115-
}
96+
ComponentInfo { id, descriptor }
11697
}
11798
}
11899

@@ -142,6 +123,7 @@ impl SparseSetIndex for ComponentId {
142123
}
143124
}
144125

126+
#[derive(Debug)]
145127
pub struct ComponentDescriptor {
146128
name: String,
147129
storage_type: StorageType,
@@ -154,14 +136,30 @@ pub struct ComponentDescriptor {
154136
}
155137

156138
impl ComponentDescriptor {
139+
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
140+
unsafe fn drop_ptr<T>(x: *mut u8) {
141+
x.cast::<T>().drop_in_place()
142+
}
143+
157144
pub fn new<T: Component>(storage_type: StorageType) -> Self {
158145
Self {
159146
name: std::any::type_name::<T>().to_string(),
160147
storage_type,
161148
is_send_and_sync: true,
162149
type_id: Some(TypeId::of::<T>()),
163150
layout: Layout::new::<T>(),
164-
drop: TypeInfo::drop_ptr::<T>,
151+
drop: Self::drop_ptr::<T>,
152+
}
153+
}
154+
155+
fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
156+
Self {
157+
name: std::any::type_name::<T>().to_string(),
158+
storage_type,
159+
is_send_and_sync: false,
160+
type_id: Some(TypeId::of::<T>()),
161+
layout: Layout::new::<T>(),
162+
drop: Self::drop_ptr::<T>,
165163
}
166164
}
167165

@@ -181,19 +179,6 @@ impl ComponentDescriptor {
181179
}
182180
}
183181

184-
impl From<TypeInfo> for ComponentDescriptor {
185-
fn from(type_info: TypeInfo) -> Self {
186-
Self {
187-
name: type_info.type_name().to_string(),
188-
storage_type: StorageType::default(),
189-
is_send_and_sync: type_info.is_send_and_sync(),
190-
type_id: Some(type_info.type_id()),
191-
drop: type_info.drop(),
192-
layout: type_info.layout(),
193-
}
194-
}
195-
}
196-
197182
#[derive(Debug, Default)]
198183
pub struct Components {
199184
components: Vec<ComponentInfo>,
@@ -231,7 +216,12 @@ impl Components {
231216

232217
#[inline]
233218
pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId {
234-
self.get_or_insert_with(TypeId::of::<T>(), TypeInfo::of::<T>)
219+
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
220+
unsafe {
221+
self.get_or_insert_with(TypeId::of::<T>(), || {
222+
ComponentDescriptor::new::<T>(StorageType::default())
223+
})
224+
}
235225
}
236226

237227
#[inline]
@@ -279,42 +269,58 @@ impl Components {
279269

280270
#[inline]
281271
pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId {
282-
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of::<T>)
272+
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
273+
unsafe {
274+
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
275+
ComponentDescriptor::new::<T>(StorageType::default())
276+
})
277+
}
283278
}
284279

285280
#[inline]
286281
pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId {
287-
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of_non_send_and_sync::<T>)
282+
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
283+
unsafe {
284+
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
285+
ComponentDescriptor::new_non_send::<T>(StorageType::default())
286+
})
287+
}
288288
}
289289

290+
/// # Safety
291+
///
292+
/// The [`ComponentDescriptor`] must match the [`TypeId`]
290293
#[inline]
291-
fn get_or_insert_resource_with(
294+
unsafe fn get_or_insert_resource_with(
292295
&mut self,
293296
type_id: TypeId,
294-
func: impl FnOnce() -> TypeInfo,
297+
func: impl FnOnce() -> ComponentDescriptor,
295298
) -> ComponentId {
296299
let components = &mut self.components;
297300
let index = self.resource_indices.entry(type_id).or_insert_with(|| {
298-
let type_info = func();
301+
let descriptor = func();
299302
let index = components.len();
300-
components.push(ComponentInfo::new(ComponentId(index), type_info.into()));
303+
components.push(ComponentInfo::new(ComponentId(index), descriptor));
301304
index
302305
});
303306

304307
ComponentId(*index)
305308
}
306309

310+
/// # Safety
311+
///
312+
/// The [`ComponentDescriptor`] must match the [`TypeId`]
307313
#[inline]
308-
pub(crate) fn get_or_insert_with(
314+
pub(crate) unsafe fn get_or_insert_with(
309315
&mut self,
310316
type_id: TypeId,
311-
func: impl FnOnce() -> TypeInfo,
317+
func: impl FnOnce() -> ComponentDescriptor,
312318
) -> ComponentId {
313319
let components = &mut self.components;
314320
let index = self.indices.entry(type_id).or_insert_with(|| {
315-
let type_info = func();
321+
let descriptor = func();
316322
let index = components.len();
317-
components.push(ComponentInfo::new(ComponentId(index), type_info.into()));
323+
components.push(ComponentInfo::new(ComponentId(index), descriptor));
318324
index
319325
});
320326

0 commit comments

Comments
 (0)