mew/
bindgroup.rs

1use std::{
2    marker::PhantomData,
3    ops::Deref,
4};
5
6
7/// Define a bind group, with internal buffer types (and their field names), and
8/// their shader visibility (see
9/// [`wgpu::ShaderStages`](https://docs.rs/wgpu/latest/wgpu/struct.ShaderStages.html)).
10///
11/// You need to number each field, tuples are used and counting is hard in
12/// macros.
13///
14/// ```
15/// bind_group! { StuffBindGroup [
16///     0 matrix_buffer @ VERTEX | COMPUTE => MatrixBuffer,
17///     1 something_else_buffer @ FRAGMENT => Arc<SomethingElseBuffer> as SomethingElseBuffer,
18///     2 another_matrix_buffer @ VERTEX_FRAGMENT => MatrixBuffer,
19/// ] }
20///
21/// // TODO: Create buffers (see buffer docs)
22/// let bind_group: StuffBindGroup = render_context.new_bind_group((matrix_buffer, ..));
23///
24/// queue.write_buffer(&stuff_bind_group.something_else_buffer, 0, &[1, 2, 3, ..]);
25/// ```
26#[macro_export]
27macro_rules! bind_group {
28    { @bufferout $ty:ty } => {
29        $ty
30    };
31    { @bufferout $out:ty as $in:ty } => {
32        $out
33    };
34
35    { @bufferin $ty:ty } => {
36        $ty
37    };
38    { @bufferin $out:ty as $in:ty } => {
39        $in
40    };
41
42    { $struct:ident [
43        $( $num:tt $name:ident @ $( $shaderstage:ident )|+ => $buffer:ty $( as $bufferinner:ty )? ),* $(,)?
44    ] } => {
45        pub struct $struct {
46            bind_group: $crate::wgpu::BindGroup,
47            $( pub $name : $crate::bind_group! { @bufferout $buffer $( as $bufferinner )? } , )*
48        }
49
50        impl ::std::ops::Deref for $struct {
51            type Target = $crate::wgpu::BindGroup;
52            fn deref(&self) -> &Self::Target {
53                &self.bind_group
54            }
55        }
56
57        impl $crate::bindgroup::MewBindGroup for $struct {
58            type BufferSet = ( $( $crate::bind_group! { @bufferout $buffer $( as $bufferinner)? } , )* );
59            type BufferArray<T> = [T; $crate::len! { $( $num )* }];
60
61            fn new(bind_group: $crate::wgpu::BindGroup, buffers: Self::BufferSet) -> Self {
62                Self {
63                    bind_group,
64                    $( $name : buffers.$num , )*
65                }
66            }
67
68            fn layout_entries() -> &'static Self::BufferArray<$crate::wgpu::BindGroupLayoutEntry> {
69                use ::std::sync::LazyLock;
70                #[allow(unused_imports)]
71                use $crate::{
72                    buffer::MewBuffer,
73                    sampler::MewSampler,
74                    texture::MewTexture,
75                };
76
77                static ARRAY: LazyLock<<$struct as $crate::bindgroup::MewBindGroup>::BufferArray<$crate::wgpu::BindGroupLayoutEntry>> = LazyLock::new(|| [ $(
78                    $crate::wgpu::BindGroupLayoutEntry {
79                        binding: $num,
80                        visibility: $( $crate::wgpu::ShaderStages::$shaderstage )|+,
81                        ty: <$crate::bind_group! { @bufferin $buffer $( as $bufferinner )? }>::binding_type(),
82                        count: None,
83                    },
84                )* ]);
85
86                &*ARRAY
87            }
88
89            fn layout_desc() -> $crate::wgpu::BindGroupLayoutDescriptor<'static> {
90                $crate::wgpu::BindGroupLayoutDescriptor {
91                    label: Some(concat!(stringify!($struct), " Layout Descriptor")),
92                    entries: Self::layout_entries(),
93                }
94            }
95
96            fn layout_desc_with(entries: &Self::BufferArray<$crate::wgpu::BindGroupLayoutEntry>) -> $crate::wgpu::BindGroupLayoutDescriptor {
97                $crate::wgpu::BindGroupLayoutDescriptor {
98                    label: Some(concat!(stringify!($struct), " Layout Descriptor")),
99                    entries,
100                }
101            }
102
103            fn bind_group_entries(buffers: &Self::BufferSet) -> Self::BufferArray<$crate::wgpu::BindGroupEntry> {
104                #[allow(unused_imports)]
105                use $crate::{
106                    buffer::MewBuffer,
107                    sampler::MewSampler,
108                    texture::MewTexture,
109                };
110
111                [ $(
112                    $crate::wgpu::BindGroupEntry {
113                        binding: $num,
114                        resource: buffers.$num.as_binding(),
115                    },
116                )* ]
117            }
118
119            fn bind_group_desc<'a>(layout: &'a $crate::bindgroup::MewBindGroupLayout<Self>, entries: &'a Self::BufferArray<$crate::wgpu::BindGroupEntry>) -> $crate::wgpu::BindGroupDescriptor<'a> {
120                $crate::wgpu::BindGroupDescriptor {
121                    label: Some(stringify!($struct)),
122                    layout,
123                    entries,
124                }
125            }
126        }
127    };
128}
129pub use bind_group;
130
131
132/// Trait for bind group wrapper types, to get layouts and descriptors about
133/// the inner bind group.
134///
135/// The number of internal buffers must be defined in the generic parameter,
136/// because associated `const`s are "uNsTAbLe", apparently.
137///
138/// Ideally, avoid using this - it's horribly designed, so just use the
139/// [`bind_group`] macro.
140pub trait MewBindGroup {
141    /// A tuple of the bind group's internal buffers.
142    type BufferSet;
143    /// A generic array the length of the number of buffers (literally `[T; N]`).
144    type BufferArray<T>;
145
146    /// Construct by pairing the bind group with its buffers.
147    fn new(bind_group: wgpu::BindGroup, buffers: Self::BufferSet) -> Self;
148    /// Get an array of layout entries for all child buffers.
149
150    fn layout_entries() -> &'static Self::BufferArray<wgpu::BindGroupLayoutEntry>;
151    /// Get a static layout descriptor.
152
153    fn layout_desc() -> wgpu::BindGroupLayoutDescriptor<'static>;
154    /// Get a layout descriptor using dynamic layout entries (not recommended, I
155    /// forgor why I put this in).
156
157    fn layout_desc_with<'a>(entries: &'a Self::BufferArray<wgpu::BindGroupLayoutEntry>) -> wgpu::BindGroupLayoutDescriptor<'a>;
158    /// Get an array of bind group entries, for use with [`Self::bind_group_desc`].
159    /// Necessary to keep borrow checker happy.
160    fn bind_group_entries(buffers: &Self::BufferSet) -> Self::BufferArray<wgpu::BindGroupEntry>;
161    /// Get a bind group descriptor using its layout and an array of its entries
162    /// from [`Self::bind_group_entries`].
163    fn bind_group_desc<'a>(layout: &'a MewBindGroupLayout<Self>, entries: &'a Self::BufferArray<wgpu::BindGroupEntry>) -> wgpu::BindGroupDescriptor<'a>;
164
165    //fn dyn_descriptor() -> Box<dyn MewBindGroupDescriptor>;
166}
167
168/*
169pub trait MewBindGroupDescriptor {
170    fn type_id(&self) -> TypeId;
171    fn layout_desc(&self) -> wgpu::BindGroupLayoutDescriptor<'static>;
172}
173*/
174
175
176/// Wrapper for the layout of some bind group, so that the compiler will scream
177/// at you if you mix up layouts.
178pub struct MewBindGroupLayout<BIND: ?Sized> {
179    layout: wgpu::BindGroupLayout,
180    _marker: PhantomData<BIND>,
181}
182impl<BIND: ?Sized> Deref for MewBindGroupLayout<BIND> {
183    type Target = wgpu::BindGroupLayout;
184    fn deref(&self) -> &Self::Target {
185        &self.layout
186    }
187}
188impl<BIND> MewBindGroupLayout<BIND> {
189    /// Wrap a `BindGroupLayout`.
190    pub fn new(layout: wgpu::BindGroupLayout) -> Self {
191        Self {
192            layout,
193            _marker: PhantomData,
194        }
195    }
196}