
# Objective Simplify bind group creation code. alternative to (and based on) #9476 ## Solution - Add a `BindGroupEntries` struct that can transparently be used where `&[BindGroupEntry<'b>]` is required in BindGroupDescriptors. Allows constructing the descriptor's entries as: ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &BindGroupEntries::with_indexes(( (2, &my_sampler), (3, my_uniform), )), ); ``` instead of ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &[ BindGroupEntry { binding: 2, resource: BindingResource::Sampler(&my_sampler), }, BindGroupEntry { binding: 3, resource: my_uniform, }, ], ); ``` or ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &BindGroupEntries::sequential((&my_sampler, my_uniform)), ); ``` instead of ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &[ BindGroupEntry { binding: 0, resource: BindingResource::Sampler(&my_sampler), }, BindGroupEntry { binding: 1, resource: my_uniform, }, ], ); ``` the structs has no user facing macros, is tuple-type-based so stack allocated, and has no noticeable impact on compile time. - Also adds a `DynamicBindGroupEntries` struct with a similar api that uses a `Vec` under the hood and allows extending the entries. - Modifies `RenderDevice::create_bind_group` to take separate arguments `label`, `layout` and `entries` instead of a `BindGroupDescriptor` struct. The struct can't be stored due to the internal references, and with only 3 members arguably does not add enough context to justify itself. - Modify the codebase to use the new api and the `BindGroupEntries` / `DynamicBindGroupEntries` structs where appropriate (whenever the entries slice contains more than 1 member). ## Migration Guide - Calls to `RenderDevice::create_bind_group({BindGroupDescriptor { label, layout, entries })` must be amended to `RenderDevice::create_bind_group(label, layout, entries)`. - If `label`s have been specified as `"bind_group_name".into()`, they need to change to just `"bind_group_name"`. `Some("bind_group_name")` and `None` will still work, but `Some("bind_group_name")` can optionally be simplified to just `"bind_group_name"`. --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
174 lines
4.5 KiB
Rust
174 lines
4.5 KiB
Rust
use proc_macro::TokenStream;
|
|
use quote::{format_ident, quote};
|
|
use syn::{
|
|
parse::{Parse, ParseStream},
|
|
parse_macro_input,
|
|
token::Comma,
|
|
Ident, LitInt, Result,
|
|
};
|
|
struct AllTuples {
|
|
macro_ident: Ident,
|
|
start: usize,
|
|
end: usize,
|
|
idents: Vec<Ident>,
|
|
}
|
|
|
|
impl Parse for AllTuples {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let macro_ident = input.parse::<Ident>()?;
|
|
input.parse::<Comma>()?;
|
|
let start = input.parse::<LitInt>()?.base10_parse()?;
|
|
input.parse::<Comma>()?;
|
|
let end = input.parse::<LitInt>()?.base10_parse()?;
|
|
input.parse::<Comma>()?;
|
|
let mut idents = vec![input.parse::<Ident>()?];
|
|
while input.parse::<Comma>().is_ok() {
|
|
idents.push(input.parse::<Ident>()?);
|
|
}
|
|
|
|
Ok(AllTuples {
|
|
macro_ident,
|
|
start,
|
|
end,
|
|
idents,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Helper macro to generate tuple pyramids. Useful to generate scaffolding to work around Rust
|
|
/// lacking variadics. Invoking `all_tuples!(impl_foo, start, end, P, Q, ..)`
|
|
/// invokes `impl_foo` providing ident tuples through arity `start..=end`.
|
|
/// # Examples
|
|
/// A single parameter.
|
|
/// ```
|
|
/// use std::marker::PhantomData;
|
|
/// use bevy_utils_proc_macros::all_tuples;
|
|
///
|
|
/// struct Foo<T> {
|
|
/// // ..
|
|
/// _phantom: PhantomData<T>
|
|
/// }
|
|
///
|
|
/// trait WrappedInFoo {
|
|
/// type Tup;
|
|
/// }
|
|
///
|
|
/// macro_rules! impl_wrapped_in_foo {
|
|
/// ($($T:ident),*) => {
|
|
/// impl<$($T),*> WrappedInFoo for ($($T,)*) {
|
|
/// type Tup = ($(Foo<$T>,)*);
|
|
/// }
|
|
/// };
|
|
/// }
|
|
///
|
|
/// all_tuples!(impl_wrapped_in_foo, 0, 15, T);
|
|
/// // impl_wrapped_in_foo!();
|
|
/// // impl_wrapped_in_foo!(P0);
|
|
/// // impl_wrapped_in_foo!(P0, P1);
|
|
/// // ..
|
|
/// // impl_wrapped_in_foo!(P0 .. P14);
|
|
/// ```
|
|
/// Multiple parameters.
|
|
/// ```
|
|
/// use bevy_utils_proc_macros::all_tuples;
|
|
///
|
|
/// trait Append {
|
|
/// type Out<Item>;
|
|
/// fn append<Item>(tup: Self, item: Item) -> Self::Out<Item>;
|
|
/// }
|
|
///
|
|
/// impl Append for () {
|
|
/// type Out<Item> = (Item,);
|
|
/// fn append<Item>(_: Self, item: Item) -> Self::Out<Item> {
|
|
/// (item,)
|
|
/// }
|
|
/// }
|
|
///
|
|
/// macro_rules! impl_append {
|
|
/// ($(($P:ident, $p:ident)),*) => {
|
|
/// impl<$($P),*> Append for ($($P,)*) {
|
|
/// type Out<Item> = ($($P),*, Item);
|
|
/// fn append<Item>(($($p,)*): Self, item: Item) -> Self::Out<Item> {
|
|
/// ($($p),*, item)
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// }
|
|
///
|
|
/// all_tuples!(impl_append, 1, 15, P, p);
|
|
/// // impl_append!((P0, p0));
|
|
/// // impl_append!((P0, p0), (P1, p1));
|
|
/// // impl_append!((P0, p0), (P1, p1), (P2, p2));
|
|
/// // ..
|
|
/// // impl_append!((P0, p0) .. (P14, p14));
|
|
/// ````
|
|
#[proc_macro]
|
|
pub fn all_tuples(input: TokenStream) -> TokenStream {
|
|
let input = parse_macro_input!(input as AllTuples);
|
|
let len = 1 + input.end - input.start;
|
|
let mut ident_tuples = Vec::with_capacity(len);
|
|
for i in 0..=len {
|
|
let idents = input
|
|
.idents
|
|
.iter()
|
|
.map(|ident| format_ident!("{}{}", ident, i));
|
|
if input.idents.len() < 2 {
|
|
ident_tuples.push(quote! {
|
|
#(#idents)*
|
|
});
|
|
} else {
|
|
ident_tuples.push(quote! {
|
|
(#(#idents),*)
|
|
});
|
|
}
|
|
}
|
|
|
|
let macro_ident = &input.macro_ident;
|
|
let invocations = (input.start..=input.end).map(|i| {
|
|
let ident_tuples = &ident_tuples[..i];
|
|
quote! {
|
|
#macro_ident!(#(#ident_tuples),*);
|
|
}
|
|
});
|
|
TokenStream::from(quote! {
|
|
#(
|
|
#invocations
|
|
)*
|
|
})
|
|
}
|
|
|
|
#[proc_macro]
|
|
pub fn all_tuples_with_size(input: TokenStream) -> TokenStream {
|
|
let input = parse_macro_input!(input as AllTuples);
|
|
let len = 1 + input.end - input.start;
|
|
let mut ident_tuples = Vec::with_capacity(len);
|
|
for i in 0..=len {
|
|
let idents = input
|
|
.idents
|
|
.iter()
|
|
.map(|ident| format_ident!("{}{}", ident, i));
|
|
if input.idents.len() < 2 {
|
|
ident_tuples.push(quote! {
|
|
#(#idents)*
|
|
});
|
|
} else {
|
|
ident_tuples.push(quote! {
|
|
(#(#idents),*)
|
|
});
|
|
}
|
|
}
|
|
|
|
let macro_ident = &input.macro_ident;
|
|
let invocations = (input.start..=input.end).map(|i| {
|
|
let ident_tuples = &ident_tuples[..i];
|
|
quote! {
|
|
#macro_ident!(#i, #(#ident_tuples),*);
|
|
}
|
|
});
|
|
TokenStream::from(quote! {
|
|
#(
|
|
#invocations
|
|
)*
|
|
})
|
|
}
|