mew/
pipeline.rs

1use crate::typemap;
2use std::{
3    marker::PhantomData,
4    convert::AsRef,
5    ops::Deref,
6};
7
8
9/// Define a pipeline, with internal bind group types.
10///
11/// You need to number each field and include the total count of bind groups,
12/// it's just a limitation of macros.
13///
14/// ```
15/// pipeline! { ThingPipeline {
16///     bind_groups: [
17///         0 StuffBindGroup,
18///         1 CameraBindGroup,
19///         2 TextureBindGroup,
20///     ],
21/// } }
22///
23/// let thing_pipeline = StuffBindGroup::new(...);
24/// queue.write_buffer(&stuff_bind_group.something_else_buffer, 0, &[1, 2, 3, ..]);
25/// ```
26#[macro_export]
27macro_rules! pipeline {
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    { @texturefmt $var:ident , SURFACE } => {
39        $var
40    };
41    { @texturefmt $var:ident , $fragmentfmt:ident } => {
42        Some($crate::wgpu::TextureFormat::$fragmentfmt)
43    };
44
45    { $struct:ident =>
46        bind_groups : [
47            $( $bindgroupnum:tt => $bindgroup:ty ),* $(,)?
48        ],
49        vertex_types: [
50            $( $vertexnum:tt => $vertexty:ty ),* $(,)?
51        ],
52        fragment_targets: [
53            $( $fragmentnum:tt => $blend:ident $mask:ident as $fragmentfmt:ident ),* $(,)?
54        ],
55        push_constants: [
56            $( $pushfrom:literal .. $pushto:literal @ $pushshaderstage:ident ),* $(,)?
57        ],
58        depth: $depthfmt:expr,
59        cull: $cull:expr,
60    } => {
61        pub struct $struct {
62            pipeline: $crate::wgpu::RenderPipeline,
63        }
64
65        impl ::std::ops::Deref for $struct {
66            type Target = $crate::wgpu::RenderPipeline;
67            fn deref(&self) -> &Self::Target {
68                &self.pipeline
69            }
70        }
71
72        impl $struct {
73            pub fn get_layouts_from_refs<GETTER>(getter: &GETTER) -> <Self as $crate::MewPipeline>::BindGroupLayoutSet<'_>
74            where
75                GETTER: $( AsRef<$crate::MewBindGroupLayout<$bindgroup>> + )*
76            {
77                ( $( {
78                    let layout: &$crate::MewBindGroupLayout<$bindgroup> = getter.as_ref();
79                    layout
80                }, )* )
81            }
82        }
83
84        impl $crate::MewPipeline for $struct {
85            type BindGroupSet = ( $( $bindgroup , )* );
86            type BindGroupLayoutSet<'a> = ( $( &'a MewBindGroupLayout<$bindgroup> , )* );
87            type BindGroupArray<T> = [T; $crate::pipeline!{ @len $( $bindgroupnum )* }];
88            type VertexArray<T> = [T; $crate::pipeline!{ @len $( $vertexnum )* }];
89            type FragmentArray<T> = [T; $crate::pipeline!{ @len $( $fragmentnum )* }];
90
91            fn new(pipeline: $crate::wgpu::RenderPipeline) -> Self {
92                Self {
93                    pipeline,
94                }
95            }
96
97            fn bind_group_layouts<'a>(bind_group_layouts: &Self::BindGroupLayoutSet<'a>) -> Self::BindGroupArray<&'a $crate::wgpu::BindGroupLayout> {
98                [ $(
99                    bind_group_layouts.$bindgroupnum,
100                )* ]
101            }
102
103
104            /// Returns a layout descriptor struct.
105            fn layout_desc<'a>(bind_group_layouts: &'a Self::BindGroupArray<&'a $crate::wgpu::BindGroupLayout>) -> $crate::wgpu::PipelineLayoutDescriptor<'a> {
106                $crate::wgpu::PipelineLayoutDescriptor {
107                    label: Some(concat!(stringify!($struct), " Layout Descriptor")),
108                    bind_group_layouts,
109                    push_constant_ranges: &[ $(
110                        $crate::wgpu::PushConstantRange {
111                            stages: $crate::wgpu::ShaderStages::$pushshaderstage,
112                            range: $pushfrom..$pushto,
113                        },
114                    )* ],
115                }
116            }
117
118            fn fragment_targets<'a>(surface_fmt: Option<$crate::wgpu::TextureFormat>) -> Self::FragmentArray<Option<$crate::wgpu::ColorTargetState>> {
119                [ $( $crate::pipeline!{ @texturefmt surface_fmt, $fragmentfmt }.map(|format| $crate::wgpu::ColorTargetState {
120                    format,
121                    blend: Some($crate::wgpu::BlendState::$blend),
122                    write_mask: $crate::wgpu::ColorWrites::$mask,
123                }), )* ]
124            }
125
126            fn pipeline_desc<'a>(
127                layout: &'a $crate::MewPipelineLayout<Self>,
128                shader: &'a $crate::wgpu::ShaderModule,
129                vertex_entry: Option<&'a str>,
130                fragment_entry: Option<&'a str>,
131                fragment_targets: &'a Self::FragmentArray<Option<$crate::wgpu::ColorTargetState>>,
132            ) -> $crate::wgpu::RenderPipelineDescriptor<'a> {
133                use ::std::sync::LazyLock;
134                use $crate::wgpu::{
135                    Face::*,
136                    TextureFormat::*,
137                };
138
139                static VERTICES: LazyLock<<$struct as $crate::MewPipeline>::VertexArray<$crate::wgpu::VertexBufferLayout<'static>>> = LazyLock::new(||
140                    [ $( <$vertexty>::vertex_layout(), )* ]
141                );
142
143                $crate::wgpu::RenderPipelineDescriptor {
144                    label: Some(concat!(stringify!($struct), " Descriptor")),
145                    layout: Some(layout),
146                    vertex: $crate::wgpu::VertexState {
147                        module: shader,
148                        entry_point: vertex_entry,
149                        compilation_options: Default::default(),
150                        buffers: &*VERTICES,
151                    },
152                    fragment: Some($crate::wgpu::FragmentState {
153                        module: shader,
154                        entry_point: fragment_entry,
155                        compilation_options: Default::default(),
156                        targets: fragment_targets,
157                    }),
158                    primitive: $crate::wgpu::PrimitiveState {
159                        topology: $crate::wgpu::PrimitiveTopology::TriangleList,
160                        strip_index_format: None,
161                        front_face: $crate::wgpu::FrontFace::Ccw,
162                        cull_mode: $cull,
163                        polygon_mode: $crate::wgpu::PolygonMode::Fill,
164                        unclipped_depth: false,
165                        conservative: false,
166                    },
167                    depth_stencil: $depthfmt.map(|format| $crate::wgpu::DepthStencilState {
168                        format,
169                        depth_write_enabled: true,
170                        depth_compare: $crate::wgpu::CompareFunction::Less,
171                        stencil: Default::default(),
172                        bias: Default::default(),
173                    }),
174                    multisample: $crate::wgpu::MultisampleState {
175                        count: 1,
176                        mask: !0,
177                        alpha_to_coverage_enabled: true,
178                    },
179                    multiview: None,
180                    cache: None,
181                }
182            }
183        }
184    };
185}
186
187
188/// Trait for pipeline wrapper types, to get layouts and descriptors about the
189/// inner pipeline.
190///
191/// The number of internal bind groups must be defined in the generic parameter,
192/// because associated `const`s are "uNsTAbLe", apparently.
193///
194/// Note that there is _one_ function that could not be included in this trait,
195/// being `get_layouts_from_refs<GETTER>(getter: &GETTER) -> `[`MewPipeline`]`<Self>::BindGroupLayoutSet<'_>`.  
196/// This sin against all Machines takes in a "getter" that implements [`AsRef`]
197/// for all its internal bind group types, which is precisely what [`typemap!`]
198/// does semi-statically.  
199/// The render context expects this function so that the wrapper type can
200/// extract whatever layouts it requires without needing to dump the whole
201/// render context into a generic parameter. So if you're implementing your own
202/// stuff to plug into this library, don't forget about that function.  
203/// I don't use a hashmap here, because going by type is (probably?) a static
204/// function call.
205///
206/// Ideally, avoid using this - it's horribly designed, so just use the
207/// [`pipeline`] macro.
208pub trait MewPipeline {
209    /// A tuple of the pipeline's internal bind groups (max of 4).
210    type BindGroupSet;
211    /// A tuple of layouts of each of the pipeline's internal bind groups.
212    type BindGroupLayoutSet<'a>;
213    type BindGroupArray<T>;
214    type VertexArray<T>;
215    type FragmentArray<T>;
216
217    /// Wrap a pipeline.
218    fn new(pipeline: wgpu::RenderPipeline) -> Self;
219    /// Get the `BindGroupLayout` array to descrive the layout of the internal bind
220    /// groups.
221    fn bind_group_layouts<'a>(bind_groups: &Self::BindGroupLayoutSet<'a>) -> Self::BindGroupArray<&'a wgpu::BindGroupLayout>;
222    /// Get the `PipelineLayoutDescriptor` for the pipeline. Requires the output
223    /// from [`Self::bind_group_layouts`] because `const fn`s in traits are
224    /// "uNsTAbLe", apparently, and lifetimes exist.
225    fn layout_desc<'a>(bind_group_layouts: &'a Self::BindGroupArray<&'a wgpu::BindGroupLayout>) -> wgpu::PipelineLayoutDescriptor<'a>;
226
227    fn fragment_targets<'a>(surface_fmt: Option<wgpu::TextureFormat>) -> Self::FragmentArray<Option<wgpu::ColorTargetState>>;
228
229    fn pipeline_desc<'a>(
230        layout: &'a MewPipelineLayout<Self>,
231        shader: &'a wgpu::ShaderModule,
232        vertex_entry: Option<&'a str>,
233        fragment_entry: Option<&'a str>,
234        fragment_targets: &'a Self::FragmentArray<Option<wgpu::ColorTargetState>>,
235    ) -> wgpu::RenderPipelineDescriptor<'a>;
236}
237
238
239/// Wrapper for the layout of some pipeline, so that the compiler will scream
240/// at you if you mix up pipelines.
241pub struct MewPipelineLayout<PIPE: ?Sized> {
242    layout: wgpu::PipelineLayout,
243    _marker: PhantomData<PIPE>,
244}
245impl<PIPE: ?Sized> Deref for MewPipelineLayout<PIPE> {
246    type Target = wgpu::PipelineLayout;
247    fn deref(&self) -> &Self::Target {
248        &self.layout
249    }
250}
251impl<PIPE: ?Sized> MewPipelineLayout<PIPE> {
252    /// Wrap a `PipelineLayout`.
253    pub fn new(layout: wgpu::PipelineLayout) -> Self {
254        Self {
255            layout,
256            _marker: PhantomData,
257        }
258    }
259}