use variadics_please::all_tuples_with_size; use wgpu::{BindGroupEntry, BindingResource}; use super::{Sampler, TextureView}; /// Helper for constructing bindgroups. /// /// Allows constructing the descriptor's entries as: /// ```ignore (render_device cannot be easily accessed) /// render_device.create_bind_group( /// "my_bind_group", /// &my_layout, /// &BindGroupEntries::with_indices(( /// (2, &my_sampler), /// (3, my_uniform), /// )), /// ); /// ``` /// /// instead of /// /// ```ignore (render_device cannot be easily accessed) /// 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 /// /// ```ignore (render_device cannot be easily accessed) /// render_device.create_bind_group( /// "my_bind_group", /// &my_layout, /// &BindGroupEntries::sequential(( /// &my_sampler, /// my_uniform, /// )), /// ); /// ``` /// /// instead of /// /// ```ignore (render_device cannot be easily accessed) /// render_device.create_bind_group( /// "my_bind_group", /// &my_layout, /// &[ /// BindGroupEntry { /// binding: 0, /// resource: BindingResource::Sampler(&my_sampler), /// }, /// BindGroupEntry { /// binding: 1, /// resource: my_uniform, /// }, /// ], /// ); /// ``` /// /// or /// /// ```ignore (render_device cannot be easily accessed) /// render_device.create_bind_group( /// "my_bind_group", /// &my_layout, /// &BindGroupEntries::single(my_uniform), /// ); /// ``` /// /// instead of /// /// ```ignore (render_device cannot be easily accessed) /// render_device.create_bind_group( /// "my_bind_group", /// &my_layout, /// &[ /// BindGroupEntry { /// binding: 0, /// resource: my_uniform, /// }, /// ], /// ); /// ``` pub struct BindGroupEntries<'b, const N: usize = 1> { entries: [BindGroupEntry<'b>; N], } impl<'b, const N: usize> BindGroupEntries<'b, N> { #[inline] pub fn sequential(resources: impl IntoBindingArray<'b, N>) -> Self { let mut i = 0; Self { entries: resources.into_array().map(|resource| { let binding = i; i += 1; BindGroupEntry { binding, resource } }), } } #[inline] pub fn with_indices(indexed_resources: impl IntoIndexedBindingArray<'b, N>) -> Self { Self { entries: indexed_resources .into_array() .map(|(binding, resource)| BindGroupEntry { binding, resource }), } } } impl<'b> BindGroupEntries<'b, 1> { pub fn single(resource: impl IntoBinding<'b>) -> [BindGroupEntry<'b>; 1] { [BindGroupEntry { binding: 0, resource: resource.into_binding(), }] } } impl<'b, const N: usize> core::ops::Deref for BindGroupEntries<'b, N> { type Target = [BindGroupEntry<'b>]; fn deref(&self) -> &[BindGroupEntry<'b>] { &self.entries } } pub trait IntoBinding<'a> { fn into_binding(self) -> BindingResource<'a>; } impl<'a> IntoBinding<'a> for &'a TextureView { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::TextureView(self) } } impl<'a> IntoBinding<'a> for &'a wgpu::TextureView { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::TextureView(self) } } impl<'a> IntoBinding<'a> for &'a [&'a wgpu::TextureView] { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::TextureViewArray(self) } } impl<'a> IntoBinding<'a> for &'a Sampler { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::Sampler(self) } } impl<'a> IntoBinding<'a> for &'a [&'a wgpu::Sampler] { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::SamplerArray(self) } } impl<'a> IntoBinding<'a> for BindingResource<'a> { #[inline] fn into_binding(self) -> BindingResource<'a> { self } } impl<'a> IntoBinding<'a> for wgpu::BufferBinding<'a> { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::Buffer(self) } } impl<'a> IntoBinding<'a> for &'a [wgpu::BufferBinding<'a>] { #[inline] fn into_binding(self) -> BindingResource<'a> { BindingResource::BufferArray(self) } } pub trait IntoBindingArray<'b, const N: usize> { fn into_array(self) -> [BindingResource<'b>; N]; } macro_rules! impl_to_binding_slice { ($N: expr, $(#[$meta:meta])* $(($T: ident, $I: ident)),*) => { $(#[$meta])* impl<'b, $($T: IntoBinding<'b>),*> IntoBindingArray<'b, $N> for ($($T,)*) { #[inline] fn into_array(self) -> [BindingResource<'b>; $N] { let ($($I,)*) = self; [$($I.into_binding(), )*] } } } } all_tuples_with_size!( #[doc(fake_variadic)] impl_to_binding_slice, 1, 32, T, s ); pub trait IntoIndexedBindingArray<'b, const N: usize> { fn into_array(self) -> [(u32, BindingResource<'b>); N]; } macro_rules! impl_to_indexed_binding_slice { ($N: expr, $(($T: ident, $S: ident, $I: ident)),*) => { impl<'b, $($T: IntoBinding<'b>),*> IntoIndexedBindingArray<'b, $N> for ($((u32, $T),)*) { #[inline] fn into_array(self) -> [(u32, BindingResource<'b>); $N] { let ($(($S, $I),)*) = self; [$(($S, $I.into_binding())), *] } } } } all_tuples_with_size!(impl_to_indexed_binding_slice, 1, 32, T, n, s); pub struct DynamicBindGroupEntries<'b> { entries: Vec>, } impl<'b> Default for DynamicBindGroupEntries<'b> { fn default() -> Self { Self::new() } } impl<'b> DynamicBindGroupEntries<'b> { pub fn sequential(entries: impl IntoBindingArray<'b, N>) -> Self { Self { entries: entries .into_array() .into_iter() .enumerate() .map(|(ix, resource)| BindGroupEntry { binding: ix as u32, resource, }) .collect(), } } pub fn extend_sequential( mut self, entries: impl IntoBindingArray<'b, N>, ) -> Self { let start = self.entries.last().unwrap().binding + 1; self.entries.extend( entries .into_array() .into_iter() .enumerate() .map(|(ix, resource)| BindGroupEntry { binding: start + ix as u32, resource, }), ); self } pub fn new_with_indices(entries: impl IntoIndexedBindingArray<'b, N>) -> Self { Self { entries: entries .into_array() .into_iter() .map(|(binding, resource)| BindGroupEntry { binding, resource }) .collect(), } } pub fn new() -> Self { Self { entries: Vec::new(), } } pub fn extend_with_indices( mut self, entries: impl IntoIndexedBindingArray<'b, N>, ) -> Self { self.entries.extend( entries .into_array() .into_iter() .map(|(binding, resource)| BindGroupEntry { binding, resource }), ); self } } impl<'b> core::ops::Deref for DynamicBindGroupEntries<'b> { type Target = [BindGroupEntry<'b>]; fn deref(&self) -> &[BindGroupEntry<'b>] { &self.entries } }