mew/
pipeline.rs

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