diff --git a/crates/bevy_legion/legion_fn_system_macro/Cargo.toml b/crates/bevy_legion/legion_fn_system_macro/Cargo.toml new file mode 100644 index 0000000000..935bbcd39e --- /dev/null +++ b/crates/bevy_legion/legion_fn_system_macro/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "legion_fn_system_macro" +version = "0.1.0" +authors = ["Carter Anderson "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +proc-macro2 = "1.0" +quote = "1.0" +darling = "0.10.2" +proc-macro-crate = "0.1.4" \ No newline at end of file diff --git a/crates/bevy_legion/legion_fn_system_macro/src/lib.rs b/crates/bevy_legion/legion_fn_system_macro/src/lib.rs new file mode 100644 index 0000000000..91946e0087 --- /dev/null +++ b/crates/bevy_legion/legion_fn_system_macro/src/lib.rs @@ -0,0 +1,198 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::Ident; + + +#[proc_macro] +pub fn impl_fn_systems(_input: TokenStream) -> TokenStream { + let max_resources = 8; + let max_views = 8; + let resources = (0..max_resources) + .map(|i| Ident::new(&format!("R{}", i), Span::call_site())) + .collect::>(); + let resource_vars = (0..max_resources) + .map(|i| Ident::new(&format!("r{}", i), Span::call_site())) + .collect::>(); + let views = (0..max_views) + .map(|i| Ident::new(&format!("V{}", i), Span::call_site())) + .collect::>(); + let view_vars = (0..max_views) + .map(|i| Ident::new(&format!("v{}", i), Span::call_site())) + .collect::>(); + let filter_idents = (0..max_views) + .map(|i| Ident::new(&format!("VF{}", i), Span::call_site())) + .collect::>(); + + let mut tokens = TokenStream::new(); + + let command_buffer = vec![Ident::new("CommandBuffer", Span::call_site())]; + let command_buffer_var = vec![Ident::new("_command_buffer", Span::call_site())]; + for resource_count in 0..max_resources { + let resource = &resources[0..resource_count]; + let resource_var = &resource_vars[0..resource_count]; + + let resource_tuple = if resource_count == 0 { + quote!{ ()} + } else if resource_count == 1 { + quote!{ #(#resource),* } + } else { + quote!{ (#(#resource),*) } + }; + + let resource_var_tuple = if resource_count == 0 { + quote!{ ()} + } else if resource_count == 1 { + quote!{ #(#resource_var),* } + } else { + quote!{ (#(#resource_var),*) } + }; + + let resource_access = if resource_count == 0 { + quote! { Access::default() } + }else { + quote! {{ + let mut resource_access: Access = Access::default(); + resource_access + .reads + .extend(<#resource_tuple as ResourceSet>::read_types().iter()); + resource_access + .writes + .extend(<#resource_tuple as ResourceSet>::write_types().iter()); + resource_access + }} + }; + + for view_count in 0..max_views { + let view = &views[0..view_count]; + let view_var = &view_vars[0..view_count]; + let filter = &filter_idents[0..view_count]; + + let view_tuple = if view_count == 0 { + quote!{ ()} + } else if view_count == 1 { + quote!{ #(#view),* } + } else { + quote!{ (#(#view),*) } + }; + + let component_access = if view_count == 0 { + quote! { Access::default() } + }else { + quote! {{ + let mut component_access: Access = Access::default(); + component_access + .reads + .extend(<#view_tuple as View>::read_types().iter()); + component_access + .writes + .extend(<#view_tuple as View>::write_types().iter()); + component_access + }} + }; + + let system_query = if view_count == 0 { + quote! { () } + } else if view_count == 1 { + quote! { SystemQuery< + #(#view),*, + #(#filter),*, + > } + } else { + quote! { SystemQuery< + (#(#view),*), + EntityFilterTuple< + And<( + #(<#filter as EntityFilter>::ArchetypeFilter),* + )>, + And<( + #(<#filter as EntityFilter>::ChunksetFilter),* + )>, + And<( + #(<#filter as EntityFilter>::ChunkFilter),* + )>, + > + > } + }; + + let query = if view_count == 0 { + quote!{()} + } else { + quote!{<#view_tuple>::query()} + }; + + for command_buffer_index in 0..2 { + let command_buffer = &command_buffer[0..command_buffer_index]; + let command_buffer_var = &command_buffer_var[0..command_buffer_index]; + + let run_fn = if view_count == 0 { + quote! { self(#(#command_buffer_var,)*#(#resource_var),*) } + } else { + quote! { + for (#(#view_var),*) in _query.iter_mut(_world) { + self(#(#command_buffer_var,)*#(#resource_var.clone(),)* #(#view_var),*); + } + } + }; + + tokens.extend(TokenStream::from(quote! { + impl<'a, + Func, + #(#resource: ResourceSet + 'static + Clone,)* + #(#view: for<'b> View<'b> + DefaultFilter + ViewElement, + #filter: EntityFilter + Sync + 'static),* + > IntoSystem<'a, (#(#command_buffer)*), (#(#resource,)*), (#(#view,)*)> for Func + where + Func: FnMut(#(&mut #command_buffer,)* #(#resource,)* #(#view),*) + Send + Sync + 'static, + #(<#view as View<'a>>::Iter: Iterator),* + { + fn system_id(mut self, id: SystemId) -> Box { + let resource_access: Access = #resource_access; + let component_access: Access = #component_access; + + let run_fn = FuncSystemFnWrapper( + move |_command_buffer, + _world, + _resources: #resource_tuple, + _query: &mut #system_query + | { + let #resource_var_tuple = _resources; + #run_fn + }, + PhantomData, + ); + + Box::new(FuncSystem { + name: id, + queries: AtomicRefCell::new(#query), + access: SystemAccess { + resources: resource_access, + components: component_access, + tags: Access::default(), + }, + archetypes: ArchetypeAccess::Some(BitSet::default()), + _resources: PhantomData::<#resource_tuple>, + command_buffer: FxHashMap::default(), + run_fn: AtomicRefCell::new(run_fn), + }) + } + + fn system_named(self, name: &'static str) -> Box { + self.system_id(name.into()) + } + + fn system(self) -> Box { + self.system_id(std::any::type_name::().to_string().into()) + } + } + })); + } + + + } + } + + tokens +} diff --git a/crates/bevy_legion/legion_systems/Cargo.toml b/crates/bevy_legion/legion_systems/Cargo.toml index fdbcfb463f..363e05c3d7 100644 --- a/crates/bevy_legion/legion_systems/Cargo.toml +++ b/crates/bevy_legion/legion_systems/Cargo.toml @@ -20,6 +20,7 @@ more-system-fns = [] [dependencies] legion-core = { path = "../legion_core", version = "0.2.1", default-features = false } +legion_fn_system_macro = { path = "../legion_fn_system_macro" } downcast-rs = "1.0" itertools = "0.9" diff --git a/crates/bevy_legion/legion_systems/src/system_fn.rs b/crates/bevy_legion/legion_systems/src/system_fn.rs index f68bff05f4..96e346e28e 100644 --- a/crates/bevy_legion/legion_systems/src/system_fn.rs +++ b/crates/bevy_legion/legion_systems/src/system_fn.rs @@ -4,6 +4,7 @@ use crate::{ system_fn_types::{FuncSystem, FuncSystemFnWrapper}, Access, SystemAccess, SystemId, SystemQuery, }; +use legion_fn_system_macro::impl_fn_systems; use bit_set::BitSet; use fxhash::FxHashMap; use legion_core::{ @@ -24,246 +25,7 @@ pub trait IntoSystem<'a, CommandBuffer, Resources, Components> { fn system(self) -> Box; } -macro_rules! impl_system { - (($($command_buffer:ident)*), ($(($resource:ident, $resource_var:ident)),*), ($(($view:ident, $filter:ident, $view_var:ident)),*)) => { - impl<'a, - Func, - $($resource: ResourceSet + 'static + Clone,)* - $($view: for<'b> View<'b> + DefaultFilter + ViewElement, - $filter: EntityFilter + Sync + 'static),* - > IntoSystem<'a, tuple!($($command_buffer)*), ($($resource,)*), ($($view,)*)> for Func - where - Func: FnMut($(&mut $command_buffer,)* $($resource,)* $($view),*) + Send + Sync + 'static, - $(<$view as View<'a>>::Iter: Iterator),* - { - fn system_id(mut self, id: SystemId) -> Box { - let resource_access: Access = resource_access!(($($resource),*)); - let component_access: Access = component_access!(($($view),*)); - - let run_fn = function_wrapper!(self, ($($command_buffer)*), ($($resource, $resource_var),*), ($($view, $filter, $view_var),*)); - Box::new(FuncSystem { - name: id, - queries: AtomicRefCell::new(query!($($view),*)), - access: SystemAccess { - resources: resource_access, - components: component_access, - tags: Access::default(), - }, - archetypes: ArchetypeAccess::Some(BitSet::default()), - _resources: PhantomData::, - command_buffer: FxHashMap::default(), - run_fn: AtomicRefCell::new(run_fn), - }) - } - - fn system_named(self, name: &'static str) -> Box { - self.system_id(name.into()) - } - - fn system(self) -> Box { - self.system_id(std::any::type_name::().to_string().into()) - } - } - } -} - -macro_rules! function_wrapper { - ($me:ident, ($($command_buffer:ident)*), ($($resource:ident, $resource_var:ident),*), ($($view:ident, $filter:ident, $view_var:ident),*)) => { - FuncSystemFnWrapper( - move |_command_buffer, - _world, - _resources: tuple!($($resource),*), - _query: &mut system_query!($($view, $filter),*) - | { - let tuple!($($resource_var),*) = _resources; - run_function!($me, ($(_command_buffer, $command_buffer)*), ($($resource, $resource_var),*), _world, _query, ($($view, $filter, $view_var),*)) - }, - PhantomData, - ) - }; -} - -macro_rules! run_function { - ($me:ident, ($($command_buffer_var:ident, $command_buffer:ident)*), ($($resource:ident, $resource_var:ident),*), $world:ident, $query:ident, ()) => { - $me($($command_buffer_var,)*$($resource_var),*); - }; - ($me:ident, ($($command_buffer_var:ident, $command_buffer:ident)*), ($($resource:ident, $resource_var:ident),*), $world:ident, $query:ident, ($($view:ident, $filter:ident, $view_var:ident),+)) => { - for tuple!($($view_var),*) in $query.iter_mut($world) { - $me($($command_buffer_var,)*$($resource_var.clone(),)* $($view_var),*); - } - } -} - -macro_rules! tuple { - () => { () }; - // single value: v1 - ($value:ident) => { $value }; - // multiple values: (v1, v2, v3) - ($($value:ident),+) => { ($($value),+) } -} - -macro_rules! component_access { - (()) => {Access::default()}; - (($($view:ident),+)) => {{ - let mut component_access: Access = Access::default(); - component_access - .reads - .extend(::read_types().iter()); - component_access - .writes - .extend(::write_types().iter()); - component_access - }} -} - -macro_rules! resource_access { - (()) => {Access::default()}; - (($($resource:ident),+)) => {{ - let mut component_access: Access = Access::default(); - component_access - .reads - .extend(::read_types().iter()); - component_access - .writes - .extend(::write_types().iter()); - component_access - }} -} - -macro_rules! system_query { - () => { - () - }; - ($view:ident, $filter:ident) => { - SystemQuery< - $view, - $filter - > - }; - ($($view:ident, $filter:ident),+) => { - SystemQuery< - ($($view),+), - EntityFilterTuple< - And<( - $(<$filter as EntityFilter>::ArchetypeFilter),+ - )>, - And<( - $(<$filter as EntityFilter>::ChunksetFilter),+ - )>, - And<( - $(<$filter as EntityFilter>::ChunkFilter),+ - )>, - > - > - } -} - -macro_rules! query { - () => { () }; - ($($query:ident),+) => { - ::query() - } -} - -macro_rules! impl_system_variants { - ($(($resource:ident, $resource_var:ident)),*) => { - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ()]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7))]; - #[rustfmt::skip] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8))]; - - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9))]; - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10))]; - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11))]; - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11), (V12, V12F, v12))]; - - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ()]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7))]; - #[rustfmt::skip] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8))]; - - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9))]; - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10))]; - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11))]; - #[rustfmt::skip] - #[cfg(feature = "more-system-fns")] - impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11), (V12, V12F, v12))]; - } -} - -#[rustfmt::skip] -impl_system_variants![]; -#[rustfmt::skip] -impl_system_variants![(R1, r1)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7)]; -#[rustfmt::skip] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8)]; - -#[rustfmt::skip] -#[cfg(feature = "more-system-fns")] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9)]; -#[rustfmt::skip] -#[cfg(feature = "more-system-fns")] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9), (R10, r10)]; -#[rustfmt::skip] -#[cfg(feature = "more-system-fns")] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9), (R10, r10), (R11, r11)]; -#[rustfmt::skip] -#[cfg(feature = "more-system-fns")] -impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9), (R10, r10), (R11, r11), (R12, r12)]; +impl_fn_systems!(); #[cfg(test)] mod tests {