mew/
pipeline.rs

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