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    { @len } => {
29        0
30    };
31    { @len $last:tt } => {
32        $last + 1
33    };
34    { @len $first:tt $( $rest:tt )+ } => {
35        $crate::bind_group! { @len $( $rest )+ }
36    };
37
38    { @bufferout $ty:ty } => {
39        $ty
40    };
41    { @bufferout $out:ty as $in:ty } => {
42        $out
43    };
44
45    { @bufferin $ty:ty } => {
46        $ty
47    };
48    { @bufferin $out:ty as $in:ty } => {
49        $in
50    };
51
52    { $struct:ident [
53        $( $num:tt $name:ident @ $( $shaderstage:ident )|+ => $buffer:ty $( as $bufferinner:ty )? ),* $(,)?
54    ] } => {
55        pub struct $struct {
56            bind_group: $crate::wgpu::BindGroup,
57            $( pub $name : $crate::bind_group! { @bufferout $buffer $( as $bufferinner )? } , )*
58        }
59
60        impl ::std::ops::Deref for $struct {
61            type Target = $crate::wgpu::BindGroup;
62            fn deref(&self) -> &Self::Target {
63                &self.bind_group
64            }
65        }
66
67        impl $crate::bindgroup::MewBindGroup for $struct {
68            type BufferSet = ( $( $crate::bind_group! { @bufferout $buffer $( as $bufferinner)? } , )* );
69            type BufferArray<T> = [T; $crate::bind_group! { @len $( $num )* }];
70
71            fn new(bind_group: $crate::wgpu::BindGroup, buffers: Self::BufferSet) -> Self {
72                Self {
73                    bind_group,
74                    $( $name : buffers.$num , )*
75                }
76            }
77
78            fn layout_entries() -> &'static Self::BufferArray<$crate::wgpu::BindGroupLayoutEntry> {
79                use ::std::sync::LazyLock;
80
81                static ARRAY: LazyLock<<$struct as $crate::bindgroup::MewBindGroup>::BufferArray<$crate::wgpu::BindGroupLayoutEntry>> = LazyLock::new(|| [ $(
82                    $crate::wgpu::BindGroupLayoutEntry {
83                        binding: $num,
84                        visibility: $( $crate::wgpu::ShaderStages::$shaderstage )|+,
85                        ty: <$crate::bind_group! { @bufferin $buffer $( as $bufferinner )? }>::binding_type(),
86                        count: None,
87                    },
88                )* ]);
89
90                &*ARRAY
91            }
92
93            fn layout_desc() -> $crate::wgpu::BindGroupLayoutDescriptor<'static> {
94                $crate::wgpu::BindGroupLayoutDescriptor {
95                    label: Some(concat!(stringify!($struct), " Layout Descriptor")),
96                    entries: Self::layout_entries(),
97                }
98            }
99
100            fn layout_desc_with(entries: &Self::BufferArray<$crate::wgpu::BindGroupLayoutEntry>) -> $crate::wgpu::BindGroupLayoutDescriptor {
101                $crate::wgpu::BindGroupLayoutDescriptor {
102                    label: Some(concat!(stringify!($struct), " Layout Descriptor")),
103                    entries,
104                }
105            }
106
107            fn bind_group_entries(buffers: &Self::BufferSet) -> Self::BufferArray<$crate::wgpu::BindGroupEntry> {
108                [ $(
109                    $crate::wgpu::BindGroupEntry {
110                        binding: $num,
111                        resource: buffers.$num.as_binding(),
112                    },
113                )* ]
114            }
115
116            fn bind_group_desc<'a>(layout: &'a $crate::bindgroup::MewBindGroupLayout<Self>, entries: &'a Self::BufferArray<$crate::wgpu::BindGroupEntry>) -> $crate::wgpu::BindGroupDescriptor<'a> {
117                $crate::wgpu::BindGroupDescriptor {
118                    label: Some(stringify!($struct)),
119                    layout,
120                    entries,
121                }
122            }
123        }
124    };
125}
126pub use bind_group;
127
128
129/// Trait for bind group wrapper types, to get layouts and descriptors about
130/// the inner bind group.
131///
132/// The number of internal buffers must be defined in the generic parameter,
133/// because associated `const`s are "uNsTAbLe", apparently.
134///
135/// Ideally, avoid using this - it's horribly designed, so just use the
136/// [`bind_group`] macro.
137pub trait MewBindGroup {
138    /// A tuple of the bind group's internal buffers.
139    type BufferSet;
140    type BufferArray<T>;
141
142    fn new(bind_group: wgpu::BindGroup, buffers: Self::BufferSet) -> Self;
143    fn layout_entries() -> &'static Self::BufferArray<wgpu::BindGroupLayoutEntry>;
144    fn layout_desc_with<'a>(entries: &'a Self::BufferArray<wgpu::BindGroupLayoutEntry>) -> wgpu::BindGroupLayoutDescriptor<'a>;
145    fn layout_desc() -> wgpu::BindGroupLayoutDescriptor<'static>;
146    fn bind_group_entries(buffers: &Self::BufferSet) -> Self::BufferArray<wgpu::BindGroupEntry>;
147    fn bind_group_desc<'a>(layout: &'a MewBindGroupLayout<Self>, entries: &'a Self::BufferArray<wgpu::BindGroupEntry>) -> wgpu::BindGroupDescriptor<'a>;
148}
149
150
151/// Wrapper for the layout of some bind group, so that the compiler will scream
152/// at you if you mix up layouts.
153pub struct MewBindGroupLayout<BIND: ?Sized> {
154    layout: wgpu::BindGroupLayout,
155    _marker: PhantomData<BIND>,
156}
157impl<BIND: ?Sized> Deref for MewBindGroupLayout<BIND> {
158    type Target = wgpu::BindGroupLayout;
159    fn deref(&self) -> &Self::Target {
160        &self.layout
161    }
162}
163impl<BIND> MewBindGroupLayout<BIND> {
164    /// Wrap a `BindGroupLayout`.
165    pub fn new(layout: wgpu::BindGroupLayout) -> Self {
166        Self {
167            layout,
168            _marker: PhantomData,
169        }
170    }
171}