Skip to content

Commit c529535

Browse files
committed
statically defined storage type
1 parent aafa863 commit c529535

File tree

11 files changed

+154
-93
lines changed

11 files changed

+154
-93
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ bevy_audio = ["bevy_internal/bevy_audio"]
4545
bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"]
4646
bevy_gilrs = ["bevy_internal/bevy_gilrs"]
4747
bevy_gltf = ["bevy_internal/bevy_gltf"]
48-
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
48+
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
4949
bevy_winit = ["bevy_internal/bevy_winit"]
5050

5151
trace_chrome = ["bevy_internal/trace_chrome"]
Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
use proc_macro::TokenStream;
2+
use proc_macro2::{Span, TokenStream as TokenStream2};
23
use quote::quote;
3-
use syn::{parse_macro_input, parse_quote, DeriveInput, Path};
4+
use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput, Error, Ident, Path, Result};
45

56
pub fn derive_component(input: TokenStream) -> TokenStream {
67
let mut ast = parse_macro_input!(input as DeriveInput);
78
let bevy_ecs_path: Path = crate::bevy_ecs_path();
89

10+
let storage = ast
11+
.attrs
12+
.iter()
13+
.find(|attr| attr.path.is_ident("storage"))
14+
.map_or(Ok(StorageTy::Table), parse_storage_attribute)
15+
.map(|ty| storage_path(bevy_ecs_path.clone(), ty))
16+
.unwrap_or_else(|err| err.to_compile_error());
17+
918
ast.generics
1019
.make_where_clause()
1120
.predicates
@@ -15,6 +24,31 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
1524
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
1625

1726
TokenStream::from(quote! {
18-
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {}
27+
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
28+
type Storage = #storage;
29+
}
1930
})
2031
}
32+
33+
enum StorageTy {
34+
Table,
35+
Sparse,
36+
}
37+
38+
fn parse_storage_attribute(attr: &Attribute) -> Result<StorageTy> {
39+
let ident = attr.parse_args::<Ident>()?;
40+
match ident.to_string().as_str() {
41+
"table" => Ok(StorageTy::Table),
42+
"sparse" => Ok(StorageTy::Sparse),
43+
_ => Err(Error::new(ident.span(), "Invalid storage type, expected 'table' or 'sparse'.")),
44+
}
45+
}
46+
47+
fn storage_path(bevy_ecs_path: Path, ty: StorageTy) -> TokenStream2 {
48+
let typename = match ty {
49+
StorageTy::Table => Ident::new("TableStorage", Span::call_site()),
50+
StorageTy::Sparse => Ident::new("SparseStorage", Span::call_site()),
51+
};
52+
53+
quote! { #bevy_ecs_path::component::#typename }
54+
}

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
115115
let mut field_type_infos = Vec::new();
116116
let mut field_get_components = Vec::new();
117117
let mut field_from_components = Vec::new();
118+
let mut is_dense_const_exprs = Vec::new();
119+
let mut is_dense_fn_exprs = Vec::new();
118120
for ((field_type, is_bundle), field) in
119121
field_type.iter().zip(is_bundle.iter()).zip(field.iter())
120122
{
@@ -128,6 +130,9 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
128130
field_from_components.push(quote! {
129131
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func),
130132
});
133+
is_dense_fn_exprs.push(quote! {
134+
<#field_type as #ecs_path::bundle::Bundle>::is_dense()
135+
});
131136
} else {
132137
field_type_infos.push(quote! {
133138
type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>());
@@ -139,6 +144,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
139144
field_from_components.push(quote! {
140145
#field: func().cast::<#field_type>().read(),
141146
});
147+
is_dense_const_exprs.push(quote! {
148+
<<#field_type as #ecs_path::component::Component>::Storage as #ecs_path::component::ComponentStorage>::STORAGE_TYPE
149+
== #ecs_path::component::StorageType::Table
150+
});
142151
}
143152
}
144153
let field_len = field.len();
@@ -155,6 +164,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
155164
type_info
156165
}
157166

167+
#[inline(always)]
168+
fn is_dense() -> bool {
169+
// insert const expressions in front to avoid evaluating non-const functions if possible
170+
true #(&& (#is_dense_const_exprs))* #(&& #is_dense_fn_exprs)*
171+
}
172+
158173
#[allow(unused_variables, unused_mut, non_snake_case)]
159174
unsafe fn from_components(mut func: impl FnMut() -> *mut u8) -> Self {
160175
Self {
@@ -481,7 +496,7 @@ pub(crate) fn bevy_ecs_path() -> syn::Path {
481496
BevyManifest::default().get_path("bevy_ecs")
482497
}
483498

484-
#[proc_macro_derive(Component)]
499+
#[proc_macro_derive(Component, attributes(storage))]
485500
pub fn derive_component(input: TokenStream) -> TokenStream {
486501
component::derive_component(input)
487502
}

crates/bevy_ecs/src/bundle.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ pub use bevy_ecs_macros::Bundle;
22

33
use crate::{
44
archetype::ComponentStatus,
5-
component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo},
5+
component::{
6+
Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, TypeInfo,
7+
},
68
entity::Entity,
79
storage::{SparseSetIndex, SparseSets, Table},
810
};
@@ -60,6 +62,8 @@ pub unsafe trait Bundle: Send + Sync + 'static {
6062
/// "mem::forget" the bundle fields, so callers are responsible for dropping the fields if
6163
/// that is desirable.
6264
fn get_components(self, func: impl FnMut(*mut u8));
65+
66+
fn is_dense() -> bool;
6367
}
6468

6569
macro_rules! tuple_impl {
@@ -70,6 +74,11 @@ macro_rules! tuple_impl {
7074
vec![$(TypeInfo::of::<$name>()),*]
7175
}
7276

77+
#[inline(always)]
78+
fn is_dense() -> bool {
79+
true $(&& $name::Storage::STORAGE_TYPE == StorageType::Table)*
80+
}
81+
7382
#[allow(unused_variables, unused_mut)]
7483
unsafe fn from_components(mut func: impl FnMut() -> *mut u8) -> Self {
7584
#[allow(non_snake_case)]

crates/bevy_ecs/src/component/mod.rs

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,72 @@ use thiserror::Error;
3131
/// as one of the arguments.
3232
///
3333
/// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle).
34-
pub trait Component: Send + Sync + 'static {}
34+
pub trait Component: Send + Sync + 'static {
35+
type Storage: ComponentStorage;
36+
}
37+
38+
pub struct TableStorage;
39+
pub struct SparseStorage;
40+
41+
pub trait ComponentStorage: sealed::Sealed {
42+
// because the trait is selaed, those items are private API.
43+
const STORAGE_TYPE: StorageType;
44+
}
45+
46+
impl ComponentStorage for TableStorage {
47+
const STORAGE_TYPE: StorageType = StorageType::Table;
48+
}
49+
impl ComponentStorage for SparseStorage {
50+
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
51+
}
52+
53+
mod sealed {
54+
pub trait Sealed {}
55+
impl Sealed for super::TableStorage {}
56+
impl Sealed for super::SparseStorage {}
57+
}
3558

3659
// ECS dependencies cannot derive Component, so we must implement it manually for relevant structs.
37-
impl<T> Component for bevy_tasks::Task<T> where Self: Send + Sync + 'static {}
60+
impl<T> Component for bevy_tasks::Task<T>
61+
where
62+
Self: Send + Sync + 'static,
63+
{
64+
type Storage = TableStorage;
65+
}
3866

3967
// For our own convinience, let's implement Component for primitives in tests.
4068
// It will eventually be removed, once tests are not using them anymore.
4169
// Consider those impls deprecated.
4270
#[cfg(test)]
4371
mod private_test_component_impls {
44-
use super::Component;
45-
impl Component for &'static str {}
46-
impl Component for usize {}
47-
impl Component for i32 {}
48-
impl Component for u32 {}
49-
impl Component for u64 {}
50-
impl Component for f32 {}
51-
impl Component for f64 {}
52-
impl Component for u8 {}
53-
impl Component for bool {}
72+
use super::{Component, TableStorage};
73+
impl Component for &'static str {
74+
type Storage = TableStorage;
75+
}
76+
impl Component for usize {
77+
type Storage = TableStorage;
78+
}
79+
impl Component for i32 {
80+
type Storage = TableStorage;
81+
}
82+
impl Component for u32 {
83+
type Storage = TableStorage;
84+
}
85+
impl Component for u64 {
86+
type Storage = TableStorage;
87+
}
88+
impl Component for f32 {
89+
type Storage = TableStorage;
90+
}
91+
impl Component for f64 {
92+
type Storage = TableStorage;
93+
}
94+
impl Component for u8 {
95+
type Storage = TableStorage;
96+
}
97+
impl Component for bool {
98+
type Storage = TableStorage;
99+
}
54100
}
55101

56102
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -165,10 +211,10 @@ pub struct ComponentDescriptor {
165211
}
166212

167213
impl ComponentDescriptor {
168-
pub fn new<T: Component>(storage_type: StorageType) -> Self {
214+
pub fn new<T: Component>(_storage_type: StorageType) -> Self {
169215
Self {
170216
name: std::any::type_name::<T>().to_string(),
171-
storage_type,
217+
storage_type: T::Storage::STORAGE_TYPE,
172218
is_send_and_sync: true,
173219
type_id: Some(TypeId::of::<T>()),
174220
layout: Layout::new::<T>(),

0 commit comments

Comments
 (0)