-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Refactor ECS to reduce the dependency on a 1-to-1 mapping between components and real rust types #2490
Changes from all commits
8bcdc16
b3993ca
12fcaf6
222d674
e222fbd
721071d
db6db96
edbc8bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<TypeId>, | ||
// 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<TypeId> { | ||
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,14 +136,30 @@ 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<T>(x: *mut u8) { | ||
x.cast::<T>().drop_in_place() | ||
} | ||
|
||
pub fn new<T: Component>(storage_type: StorageType) -> Self { | ||
Self { | ||
name: std::any::type_name::<T>().to_string(), | ||
storage_type, | ||
is_send_and_sync: true, | ||
type_id: Some(TypeId::of::<T>()), | ||
layout: Layout::new::<T>(), | ||
drop: TypeInfo::drop_ptr::<T>, | ||
drop: Self::drop_ptr::<T>, | ||
} | ||
} | ||
|
||
fn new_non_send<T: Any>(storage_type: StorageType) -> Self { | ||
Self { | ||
name: std::any::type_name::<T>().to_string(), | ||
storage_type, | ||
is_send_and_sync: false, | ||
type_id: Some(TypeId::of::<T>()), | ||
layout: Layout::new::<T>(), | ||
drop: Self::drop_ptr::<T>, | ||
} | ||
} | ||
|
||
|
@@ -181,19 +179,6 @@ impl ComponentDescriptor { | |
} | ||
} | ||
|
||
impl From<TypeInfo> 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<ComponentInfo>, | ||
|
@@ -231,7 +216,12 @@ impl Components { | |
|
||
#[inline] | ||
pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId { | ||
self.get_or_insert_with(TypeId::of::<T>(), TypeInfo::of::<T>) | ||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] | ||
unsafe { | ||
self.get_or_insert_with(TypeId::of::<T>(), || { | ||
ComponentDescriptor::new::<T>(StorageType::default()) | ||
}) | ||
} | ||
} | ||
|
||
#[inline] | ||
|
@@ -279,42 +269,58 @@ impl Components { | |
|
||
#[inline] | ||
pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId { | ||
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of::<T>) | ||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] | ||
unsafe { | ||
self.get_or_insert_resource_with(TypeId::of::<T>(), || { | ||
ComponentDescriptor::new::<T>(StorageType::default()) | ||
}) | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId { | ||
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of_non_send_and_sync::<T>) | ||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] | ||
unsafe { | ||
self.get_or_insert_resource_with(TypeId::of::<T>(), || { | ||
ComponentDescriptor::new_non_send::<T>(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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we know if perf is actually worse without this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah right, the string... rip There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type_name returns a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think personally I'd rather drop the unsafe, use a Cow and then always take a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont know if that actually tells us anything about the performance here though? Can we run benches or whatever and see |
||
) -> 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( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method was already de-facto unsafe. Nobody checked that the |
||
&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 | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making this public may help with #2486, but there are probably other places that need to be changed too for it to work.