Skip to content

Commit

Permalink
Implement Custom Component Ids
Browse files Browse the repository at this point in the history
This replaces the use of TypeId for identifying components and instead
uses ComponentId which is an enum that can represent either a Rust
TypeId or an external id represented by a u64.
  • Loading branch information
zicklag committed Oct 6, 2020
1 parent 4c753e2 commit d6c3844
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 88 deletions.
22 changes: 11 additions & 11 deletions crates/bevy_ecs/hecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
let n = tys.len();
let code = quote! {
impl #path::DynamicBundle for #ident {
fn with_ids<T>(&self, f: impl FnOnce(&[std::any::TypeId]) -> T) -> T {
fn with_ids<T>(&self, f: impl FnOnce(&[#path::ComponentId]) -> T) -> T {
Self::with_static_ids(f)
}

fn type_info(&self) -> Vec<#path::TypeInfo> {
Self::static_type_info()
}

unsafe fn put(mut self, mut f: impl FnMut(*mut u8, std::any::TypeId, usize) -> bool) {
unsafe fn put(mut self, mut f: impl FnMut(*mut u8, #path::ComponentId, usize) -> bool) {
#(
if f((&mut self.#fields as *mut #tys).cast::<u8>(), std::any::TypeId::of::<#tys>(), std::mem::size_of::<#tys>()) {
if f((&mut self.#fields as *mut #tys).cast::<u8>(), std::any::TypeId::of::<#tys>().into(), std::mem::size_of::<#tys>()) {
#[allow(clippy::forget_copy)]
std::mem::forget(self.#fields);
}
Expand All @@ -77,22 +77,22 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
}

impl #path::Bundle for #ident {
fn with_static_ids<T>(f: impl FnOnce(&[std::any::TypeId]) -> T) -> T {
fn with_static_ids<T>(f: impl FnOnce(&[#path::ComponentId]) -> T) -> T {
use std::any::TypeId;
use std::mem;

#path::lazy_static::lazy_static! {
static ref ELEMENTS: [TypeId; #n] = {
static ref ELEMENTS: [#path::ComponentId; #n] = {
let mut dedup = #path::bevy_utils::HashSet::default();
for &(ty, name) in [#((std::any::TypeId::of::<#tys>(), std::any::type_name::<#tys>())),*].iter() {
for &(ty, name) in [#((#path::ComponentId::from(std::any::TypeId::of::<#tys>()), std::any::type_name::<#tys>())),*].iter() {
if !dedup.insert(ty) {
panic!("{} has multiple {} fields; each type must occur at most once!", stringify!(#ident), name);
}
}

let mut tys = [#((mem::align_of::<#tys>(), TypeId::of::<#tys>())),*];
let mut tys = [#((mem::align_of::<#tys>(), #path::ComponentId::from(TypeId::of::<#tys>()))),*];
tys.sort_unstable_by(|x, y| x.0.cmp(&y.0).reverse().then(x.1.cmp(&y.1)));
let mut ids = [TypeId::of::<()>(); #n];
let mut ids: [#path::ComponentId; #n] = [TypeId::of::<()>().into(); #n];
for (id, info) in ids.iter_mut().zip(tys.iter()) {
*id = info.1;
}
Expand All @@ -104,16 +104,16 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
}

fn static_type_info() -> Vec<#path::TypeInfo> {
let mut info = vec![#(#path::TypeInfo::of::<#tys>()),*];
let mut info = vec![#(#path::TypeInfo::of::<#tys>().into()),*];
info.sort_unstable();
info
}

unsafe fn get(
mut f: impl FnMut(std::any::TypeId, usize) -> Option<std::ptr::NonNull<u8>>,
mut f: impl FnMut(#path::ComponentId, usize) -> Option<std::ptr::NonNull<u8>>,
) -> Result<Self, #path::MissingComponent> {
#(
let #fields = f(std::any::TypeId::of::<#tys>(), std::mem::size_of::<#tys>())
let #fields = f(std::any::TypeId::of::<#tys>().into(), std::mem::size_of::<#tys>())
.ok_or_else(#path::MissingComponent::new::<#tys>)?
.cast::<#tys>()
.as_ptr();
Expand Down
73 changes: 47 additions & 26 deletions crates/bevy_ecs/hecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ use crate::{
alloc::{alloc, dealloc, Layout},
vec::Vec,
},
world::ComponentId,
Entity,
};
use bevy_utils::{HashMap, HashMapExt};
use core::{
any::{type_name, TypeId},
any::TypeId,
cell::UnsafeCell,
mem,
ptr::{self, NonNull},
Expand All @@ -38,7 +39,7 @@ use crate::{borrow::AtomicBorrow, query::Fetch, Access, Component, Query};
#[derive(Debug)]
pub struct Archetype {
types: Vec<TypeInfo>,
state: HashMap<TypeId, TypeState>,
state: HashMap<ComponentId, TypeState>,
len: usize,
entities: Vec<Entity>,
// UnsafeCell allows unique references into `data` to be constructed while shared references
Expand Down Expand Up @@ -93,23 +94,23 @@ impl Archetype {
#[allow(missing_docs)]
#[inline]
pub fn has<T: Component>(&self) -> bool {
self.has_dynamic(TypeId::of::<T>())
self.has_dynamic(TypeId::of::<T>().into())
}

#[allow(missing_docs)]
#[inline]
pub fn has_type(&self, ty: TypeId) -> bool {
pub fn has_component(&self, ty: ComponentId) -> bool {
self.has_dynamic(ty)
}

pub(crate) fn has_dynamic(&self, id: TypeId) -> bool {
pub(crate) fn has_dynamic(&self, id: ComponentId) -> bool {
self.state.contains_key(&id)
}

#[allow(missing_docs)]
#[inline]
pub fn get<T: Component>(&self) -> Option<NonNull<T>> {
let state = self.state.get(&TypeId::of::<T>())?;
let state = self.state.get(&TypeId::of::<T>().into())?;
Some(unsafe {
NonNull::new_unchecked(
(*self.data.get()).as_ptr().add(state.offset).cast::<T>() as *mut T
Expand All @@ -120,7 +121,7 @@ impl Archetype {
#[allow(missing_docs)]
#[inline]
pub fn get_with_type_state<T: Component>(&self) -> Option<(NonNull<T>, &TypeState)> {
let state = self.state.get(&TypeId::of::<T>())?;
let state = self.state.get(&TypeId::of::<T>().into())?;
Some(unsafe {
(
NonNull::new_unchecked(
Expand All @@ -132,51 +133,71 @@ impl Archetype {
}

#[allow(missing_docs)]
pub fn get_type_state(&self, ty: TypeId) -> Option<&TypeState> {
pub fn get_type_state(&self, ty: ComponentId) -> Option<&TypeState> {
self.state.get(&ty)
}

#[allow(missing_docs)]
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
pub fn get_type_state_mut(&mut self, ty: ComponentId) -> Option<&mut TypeState> {
self.state.get_mut(&ty)
}

#[allow(missing_docs)]
#[inline]
pub fn borrow<T: Component>(&self) {
if self
.state
.get(&TypeId::of::<T>())
.map_or(false, |x| !x.borrow.borrow())
{
panic!("{} already borrowed uniquely", type_name::<T>());
self.borrow_component(std::any::TypeId::of::<T>().into())
}

#[allow(missing_docs)]
#[inline]
pub fn borrow_component(&self, id: ComponentId) {
if self.state.get(&id).map_or(false, |x| !x.borrow.borrow()) {
panic!("{:?} already borrowed uniquely", id);
}
}

#[allow(missing_docs)]
#[inline]
pub fn borrow_mut<T: Component>(&self) {
self.borrow_component_mut(std::any::TypeId::of::<T>().into())
}

#[allow(missing_docs)]
#[inline]
pub fn borrow_component_mut(&self, id: ComponentId) {
if self
.state
.get(&TypeId::of::<T>())
.get(&id)
.map_or(false, |x| !x.borrow.borrow_mut())
{
panic!("{} already borrowed", type_name::<T>());
panic!("{:?} already borrowed", id);
}
}

#[allow(missing_docs)]
#[inline]
pub fn release<T: Component>(&self) {
if let Some(x) = self.state.get(&TypeId::of::<T>()) {
self.release_component(std::any::TypeId::of::<T>().into());
}

#[allow(missing_docs)]
#[inline]
pub fn release_component(&self, id: ComponentId) {
if let Some(x) = self.state.get(&id) {
x.borrow.release();
}
}

#[allow(missing_docs)]
#[inline]
pub fn release_mut<T: Component>(&self) {
if let Some(x) = self.state.get(&TypeId::of::<T>()) {
self.release_component_mut(std::any::TypeId::of::<T>().into())
}

#[allow(missing_docs)]
#[inline]
pub fn release_component_mut(&self, id: ComponentId) {
if let Some(x) = self.state.get(&id) {
x.borrow.release_mut();
}
}
Expand Down Expand Up @@ -216,7 +237,7 @@ impl Archetype {
/// `index` must be in-bounds
pub(crate) unsafe fn get_dynamic(
&self,
ty: TypeId,
ty: ComponentId,
size: usize,
index: usize,
) -> Option<NonNull<u8>> {
Expand Down Expand Up @@ -356,7 +377,7 @@ impl Archetype {
pub(crate) unsafe fn move_to(
&mut self,
index: usize,
mut f: impl FnMut(*mut u8, TypeId, usize, bool, bool),
mut f: impl FnMut(*mut u8, ComponentId, usize, bool, bool),
) -> Option<Entity> {
let last = self.len - 1;
for ty in &self.types {
Expand Down Expand Up @@ -400,7 +421,7 @@ impl Archetype {
pub unsafe fn put_dynamic(
&mut self,
component: *mut u8,
ty: TypeId,
ty: ComponentId,
size: usize,
index: usize,
added: bool,
Expand Down Expand Up @@ -484,7 +505,7 @@ impl TypeState {
/// Metadata required to store a component
#[derive(Debug, Copy, Clone)]
pub struct TypeInfo {
id: TypeId,
id: ComponentId,
layout: Layout,
drop: unsafe fn(*mut u8),
}
Expand All @@ -497,15 +518,15 @@ impl TypeInfo {
}

Self {
id: TypeId::of::<T>(),
id: TypeId::of::<T>().into(),
layout: Layout::new::<T>(),
drop: drop_ptr::<T>,
}
}

#[allow(missing_docs)]
#[inline]
pub fn id(&self) -> TypeId {
pub fn id(&self) -> ComponentId {
self.id
}

Expand All @@ -527,7 +548,7 @@ impl PartialOrd for TypeInfo {
}

impl Ord for TypeInfo {
/// Order by alignment, descending. Ties broken with TypeId.
/// Order by alignment, descending. Ties broken with ComponentId.
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.layout
.align()
Expand Down
29 changes: 16 additions & 13 deletions crates/bevy_ecs/hecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

// modified by Bevy contributors

use crate::alloc::{vec, vec::Vec};
use crate::{
alloc::{vec, vec::Vec},
world::ComponentId,
};
use core::{
any::{type_name, TypeId},
fmt, mem,
Expand All @@ -27,7 +30,7 @@ use crate::{archetype::TypeInfo, Component};
pub trait DynamicBundle {
/// Invoke a callback on the fields' type IDs, sorted by descending alignment then id
#[doc(hidden)]
fn with_ids<T>(&self, f: impl FnOnce(&[TypeId]) -> T) -> T;
fn with_ids<T>(&self, f: impl FnOnce(&[ComponentId]) -> T) -> T;
/// Obtain the fields' TypeInfos, sorted by descending alignment then id
#[doc(hidden)]
fn type_info(&self) -> Vec<TypeInfo>;
Expand All @@ -36,13 +39,13 @@ pub trait DynamicBundle {
/// Must invoke `f` only with a valid pointer, its type, and the pointee's size. A `false`
/// return value indicates that the value was not moved and should be dropped.
#[doc(hidden)]
unsafe fn put(self, f: impl FnMut(*mut u8, TypeId, usize) -> bool);
unsafe fn put(self, f: impl FnMut(*mut u8, ComponentId, usize) -> bool);
}

/// A statically typed collection of components
pub trait Bundle: DynamicBundle {
#[doc(hidden)]
fn with_static_ids<T>(f: impl FnOnce(&[TypeId]) -> T) -> T;
fn with_static_ids<T>(f: impl FnOnce(&[ComponentId]) -> T) -> T;

/// Obtain the fields' TypeInfos, sorted by descending alignment then id
#[doc(hidden)]
Expand All @@ -56,7 +59,7 @@ pub trait Bundle: DynamicBundle {
/// pointers if any call to `f` returns `None`.
#[doc(hidden)]
unsafe fn get(
f: impl FnMut(TypeId, usize) -> Option<NonNull<u8>>,
f: impl FnMut(ComponentId, usize) -> Option<NonNull<u8>>,
) -> Result<Self, MissingComponent>
where
Self: Sized;
Expand Down Expand Up @@ -85,7 +88,7 @@ impl std::error::Error for MissingComponent {}
macro_rules! tuple_impl {
($($name: ident),*) => {
impl<$($name: Component),*> DynamicBundle for ($($name,)*) {
fn with_ids<T>(&self, f: impl FnOnce(&[TypeId]) -> T) -> T {
fn with_ids<T>(&self, f: impl FnOnce(&[ComponentId]) -> T) -> T {
Self::with_static_ids(f)
}

Expand All @@ -94,13 +97,13 @@ macro_rules! tuple_impl {
}

#[allow(unused_variables, unused_mut)]
unsafe fn put(self, mut f: impl FnMut(*mut u8, TypeId, usize) -> bool) {
unsafe fn put(self, mut f: impl FnMut(*mut u8, ComponentId, usize) -> bool) {
#[allow(non_snake_case)]
let ($(mut $name,)*) = self;
$(
if f(
(&mut $name as *mut $name).cast::<u8>(),
TypeId::of::<$name>(),
TypeId::of::<$name>().into(),
mem::size_of::<$name>()
) {
mem::forget($name)
Expand All @@ -110,11 +113,11 @@ macro_rules! tuple_impl {
}

impl<$($name: Component),*> Bundle for ($($name,)*) {
fn with_static_ids<T>(f: impl FnOnce(&[TypeId]) -> T) -> T {
fn with_static_ids<T>(f: impl FnOnce(&[ComponentId]) -> T) -> T {
const N: usize = count!($($name),*);
let mut xs: [(usize, TypeId); N] = [$((mem::align_of::<$name>(), TypeId::of::<$name>())),*];
let mut xs: [(usize, ComponentId); N] = [$((mem::align_of::<$name>(), TypeId::of::<$name>().into())),*];
xs.sort_unstable_by(|x, y| x.0.cmp(&y.0).reverse().then(x.1.cmp(&y.1)));
let mut ids = [TypeId::of::<()>(); N];
let mut ids = [TypeId::of::<()>().into(); N];
for (slot, &(_, id)) in ids.iter_mut().zip(xs.iter()) {
*slot = id;
}
Expand All @@ -128,10 +131,10 @@ macro_rules! tuple_impl {
}

#[allow(unused_variables, unused_mut)]
unsafe fn get(mut f: impl FnMut(TypeId, usize) -> Option<NonNull<u8>>) -> Result<Self, MissingComponent> {
unsafe fn get(mut f: impl FnMut(ComponentId, usize) -> Option<NonNull<u8>>) -> Result<Self, MissingComponent> {
#[allow(non_snake_case)]
let ($(mut $name,)*) = ($(
f(TypeId::of::<$name>(), mem::size_of::<$name>()).ok_or_else(MissingComponent::new::<$name>)?
f(TypeId::of::<$name>().into(), mem::size_of::<$name>()).ok_or_else(MissingComponent::new::<$name>)?
.as_ptr()
.cast::<$name>(),)*
);
Expand Down
Loading

0 comments on commit d6c3844

Please sign in to comment.