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