Start implementing proc macro for updates

This commit is contained in:
hodasemi 2025-04-09 09:00:42 +02:00
parent 4370853e23
commit 7ee6ef0a08
3 changed files with 284 additions and 52 deletions

View file

@ -13,6 +13,3 @@ ron.workspace = true
utilities.workspace = true utilities.workspace = true
update_macros = { path = "../update_macros" } update_macros = { path = "../update_macros" }
[features]
timings = []

View file

@ -4,15 +4,10 @@ use std::ops::{Deref, DerefMut};
use std::{any::TypeId, marker::PhantomData}; use std::{any::TypeId, marker::PhantomData};
use std::collections::HashMap; use std::collections::HashMap;
#[cfg(feature = "timings")]
use std::time::Instant;
use anyhow::Result; use anyhow::Result;
use indexmap::IndexMap; use indexmap::IndexMap;
#[cfg(feature = "timings")]
use super::super::timings::Timings;
use crate::resources::Resource as ResourceTrait; use crate::resources::Resource as ResourceTrait;
use crate::*; use crate::*;
use update_macros::{implement_pair_update, implement_single_update}; use update_macros::{implement_pair_update, implement_single_update};
@ -1152,18 +1147,12 @@ impl From<ArchetypePair> for Update {
} }
pub struct Updates { pub struct Updates {
#[cfg(feature = "timings")] updates: Vec<(u32, Update)>,
timings: Timings,
updates: Vec<(String, u32, Update)>,
} }
impl Default for Updates { impl Default for Updates {
fn default() -> Self { fn default() -> Self {
Self { Self {
#[cfg(feature = "timings")]
timings: Timings::default,
updates: Vec::new(), updates: Vec::new(),
} }
} }
@ -1171,22 +1160,9 @@ impl Default for Updates {
impl Updates { impl Updates {
pub(crate) fn update(&mut self, world: &mut World) -> Result<()> { pub(crate) fn update(&mut self, world: &mut World) -> Result<()> {
#[cfg(feature = "timings")]
if let Some(timings) = self.timings.check_timing(world.now(), None) {
if !timings.is_empty() {
println!("timings: {:#?}", timings);
}
}
#[cfg(feature = "timings")]
let timings = &mut self.timings;
self.updates self.updates
.iter() .iter()
.try_for_each(|(_name, _, update)| -> Result<()> { .try_for_each(|(_, update)| -> Result<()> {
#[cfg(feature = "timings")]
let before = Instant::now();
match update { match update {
Update::Single(archetype) => { Update::Single(archetype) => {
archetype.execute(world)?; archetype.execute(world)?;
@ -1196,9 +1172,6 @@ impl Updates {
} }
} }
#[cfg(feature = "timings")]
timings.add(_name, Instant::now().duration_since(before));
Ok(()) Ok(())
})?; })?;
@ -1210,7 +1183,7 @@ impl Updates {
entity_object: &EntityObject, entity_object: &EntityObject,
entities: &IndexMap<Entity, EntityObject>, entities: &IndexMap<Entity, EntityObject>,
) -> Result<()> { ) -> Result<()> {
for (_, _, update) in self.updates.iter_mut() { for (_, update) in self.updates.iter_mut() {
match update { match update {
Update::Single(archetype) => { Update::Single(archetype) => {
archetype.add_entity(entity_object)?; archetype.add_entity(entity_object)?;
@ -1225,7 +1198,7 @@ impl Updates {
} }
pub(crate) fn remove_entity(&mut self, entity: Entity) { pub(crate) fn remove_entity(&mut self, entity: Entity) {
for (_, _, update) in self.updates.iter_mut() { for (_, update) in self.updates.iter_mut() {
match update { match update {
Update::Single(archetype) => { Update::Single(archetype) => {
archetype.remove_entity(entity); archetype.remove_entity(entity);
@ -1244,13 +1217,10 @@ impl Updates {
// self.timings.clear(); // self.timings.clear();
// } // }
pub(crate) fn add(&mut self, name: &str, priority: u32, update: Update) -> Result<()> { pub(crate) fn add(&mut self, priority: u32, update: Update) -> Result<()> {
#[cfg(feature = "timings")] self.updates.push((priority, update));
self.timings.add_timing_afterwards(name);
self.updates.push((name.to_string(), priority, update));
self.updates self.updates
.sort_by(|(_, lhs_prio, _), (_, rhs_prio, _)| lhs_prio.cmp(rhs_prio)); .sort_by(|(lhs_prio, _), (rhs_prio, _)| lhs_prio.cmp(rhs_prio));
Ok(()) Ok(())
} }

View file

@ -16,46 +16,113 @@ impl Query {
} }
} }
pub fn types(&self) -> proc_macro2::TokenStream { pub fn types(&self) -> TokenStream2 {
let component_list = self let component_list = self
.components .components
.iter() .iter()
.map(|c| quote! { #c, }) .map(|c| quote! { #c })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let components = if component_list.len() == 1 { let components = if component_list.len() == 1 {
let component = &component_list[0]; let component = &component_list[0];
quote! { #component } quote! { #component }
} else { } else {
quote! { ( #(#component_list)* ) } quote! { ( #( #component_list, )* ) }
}; };
components components
} }
pub fn dedup_checks(&self) -> TokenStream2 {
let components = &self.components;
let filter = &self.filter;
quote! {
#( #filter::verify_dedup::<#components>(); )*
}
}
pub fn check_entity(&self, index: usize) -> TokenStream2 {
let components = &self.components;
let filter = &self.filter;
quote! {
(
let entity = &entites[#index];
#(
if !entity.components.contains::<#components>() {
return false;
}
)*
if !#filter::check(entity) {
return false;
}
)
}
}
pub fn component_store_and_query(&self, index: usize) -> (TokenStream2, TokenStream2, Ident) {
let components = &self.components;
let entity_name = format_ident!("entity_{index}");
let component_name = self
.components
.iter()
.map(|component| format_ident!("q_{index}_{component}"))
.collect::<Vec<_>>();
let query_ident = format_ident!("query_{index}");
let entity_ident = format_ident!("entity_{index}");
(
quote! {
let #entity_name = &entities[#index];
let #entity_ident = entity_name.as_entity();
#(
let #component_name = UnsafeComponentStore::from(
entity.get_component::<#components>()?
);
)*
},
quote! {
let #query_ident = Query {
entity: #entity_ident,
c: unsafe { ( #( #component_name.as_mut() )* ) },
filter: PhantomData,
};
},
query_ident,
)
}
pub fn components(&self) -> &Vec<Ident> {
&self.components
}
pub fn filter(&self) -> &Ident { pub fn filter(&self) -> &Ident {
&self.filter &self.filter
} }
} }
impl ToTokens for Query { impl ToTokens for Query {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream2) {
let component_list = self let component_list = self
.components .components
.iter() .iter()
.map(|c| quote! { &mut #c, }) .map(|c| quote! { &mut #c })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let components = if component_list.len() == 1 { let components = if component_list.len() == 1 {
let component = &component_list[0]; let component = &component_list[0];
quote! { #component } quote! { #component }
} else { } else {
quote! { ( #(#component_list)* ) } quote! { ( #( #component_list, )* ) }
}; };
let filter = self.filter.clone(); let filter = self.filter.clone();
proc_macro2::TokenStream::from(quote! { TokenStream2::from(quote! {
Query<#components, #filter> Query<#components, #filter>
}) })
.to_tokens(tokens); .to_tokens(tokens);
@ -68,6 +135,10 @@ struct Update {
} }
impl Update { impl Update {
const MIN_RESOURCE: usize = 0;
const MIN_COMPONENTS: usize = 1;
const MIN_QUERIES: usize = 1;
pub fn new(queries: impl Iterator<Item = usize>, resources: usize) -> Self { pub fn new(queries: impl Iterator<Item = usize>, resources: usize) -> Self {
Self { Self {
queries: queries queries: queries
@ -85,14 +156,208 @@ impl Update {
} }
} }
pub fn query_types(&self) pub fn filter_types(&self) -> TokenStream2 {
let filter_types = self
.queries
.iter()
.map(|query| query.filter())
.collect::<Vec<_>>();
if filter_types.len() == 1 {
let filter = &filter_types[0];
quote! {
#filter
}
} else {
quote! {
( #( #filter_types, )* )
}
}
}
pub fn filter_requirements(&self) -> TokenStream2 {
let filter_requirements = self
.queries
.iter()
.map(|query| query.filter())
.collect::<Vec<_>>();
quote! {
#( #filter_requirements: CheckFilter + 'static, )*
}
}
pub fn query_types(&self) -> TokenStream2 {
let query_types = self
.queries
.iter()
.map(|query| query.types())
.collect::<Vec<_>>();
quote! {
( #( #query_types, )* )
}
}
pub fn component_requirements(&self) -> TokenStream2 {
let component_requirements = self
.queries
.iter()
.map(|query| {
query.components.iter().map(|component| {
quote! {
#component: EntityComponent + ComponentDebug,
}
})
})
.flatten()
.collect::<Vec<_>>();
quote! {
#( #component_requirements, )*
}
}
pub fn resource_types(&self) -> TokenStream2 {
let resource_types = &self.resources;
quote! {
( #( #resource_types, )* )
}
}
pub fn reource_requirement(&self) -> TokenStream2 {
let resource_types = &self.resources;
quote! {
#( #resource_types: ResourceTrait, )*
}
}
}
impl ToTokens for Update {
fn to_tokens(&self, tokens: &mut TokenStream2) {
// generics to specify trait implementations
let filter_types = self.filter_types();
let query_types = self.query_types();
let resource_types = self.resource_types();
// function parameter
let queries = &self.queries;
let resources = if self.resources.is_empty() {
quote! {}
} else {
let resources = &self.resources;
quote! {
#( &mut #resources, )*
}
};
// trait requirements
let filter_requirements = self.filter_requirements();
let component_requirements = self.component_requirements();
let reource_requirement = self.reource_requirement();
let verify_dedup = self
.queries
.iter()
.map(|query| query.dedup_checks())
.collect::<Vec<_>>();
let check_entities = self
.queries
.iter()
.enumerate()
.map(|(index, query)| query.check_entity(index))
.collect::<Vec<_>>();
let component_stores = self
.queries
.iter()
.enumerate()
.map(|(index, query)| query.component_store_and_query(index))
.collect::<Vec<_>>();
TokenStream2::from(quote! {
impl<Func, #filter_types, #query_types, #resource_types> CreateArchetype<#query_types, #resource_types, Func, #filter_types> for Archetype
where
Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static,
#filter_requirements
#component_requirements
#reource_requirement
{
fn create(f: Func) -> Self {
#( #verify_dedup )*
Self {
check_entity: Box::new(move |entities| {
#( #check_entities )*
true
}),
create_callback: Box::new(move |entities| {
let e = entity.as_entity();
let f = f.clone();
Ok(Box::new(move |commands, world| {
f(commands )
}))
}),
entities: IndexMap::new(),
}
}
}
impl<Func, #filter_types, #query_types, #resource_types> AddUpdates<#query_types, #resource_types, Func, #filter_types> for Updates
where
Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static,
#filter_requirements
#component_requirements
#reource_requirement
{
fn add_update(
&mut self,
priority: u32,
func: Func,
) -> Result<()> {
self.add(priority, Archetype::create(func))
}
}
impl<Func, #filter_types, #query_types, #resource_types> AddUpdates<#query_types, #resource_types, Func, #filter_types> for WorldBuilder
where
Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static,
#filter_requirements
#component_requirements
#reource_requirement
{
fn add_update(
&mut self,
priority: u32,
func: Func,
) -> Result<()> {
self.updates.add_update(priority, func)
}
}
})
.to_tokens(tokens);
}
} }
pub fn update(max_components: usize, max_queries: usize) -> TokenStream { pub fn update(max_components: usize, max_queries: usize) -> TokenStream {
let updates = (Update::MIN_QUERIES..max_queries)
.map(|query_count| {
//
})
.collect::<Vec<_>>();
TokenStream::from(quote! { TokenStream::from(quote! {
impl<Func, Filter, > #( #updates )
}) })
} }