Skip to main content

pebble/wgpu/
material.rs

1use crate::wgpu::BindingEntry;
2
3pub struct MaterialSpec {
4    pub shader_source: &'static str,
5    pub entries: Vec<BindingEntry>,
6    pub cull_mode: Option<wgpu::Face>,
7    pub depth: Option<wgpu::DepthStencilState>,
8    pub color_format: Option<wgpu::TextureFormat>,
9}
10
11impl MaterialSpec {
12    pub fn binding_index(&self, name: &str) -> Option<u32> {
13        self.entries
14            .iter()
15            .position(|e| e.name == name)
16            .map(|i| i as u32)
17    }
18}
19
20impl MaterialSpec {
21    pub fn new(shader_source: &'static str, entries: Vec<BindingEntry>) -> Self {
22        Self {
23            shader_source,
24            entries,
25            cull_mode: Some(wgpu::Face::Back),
26            depth: None,
27            color_format: None,
28        }
29    }
30    pub fn with_cull_mode(mut self, mode: Option<wgpu::Face>) -> Self {
31        self.cull_mode = mode;
32        self
33    }
34    pub fn with_depth(mut self, depth: wgpu::DepthStencilState) -> Self {
35        self.depth = Some(depth);
36        self
37    }
38    pub fn with_color_format(mut self, format: wgpu::TextureFormat) -> Self {
39        self.color_format = Some(format);
40        self
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use std::borrow::Cow;
47
48    use crate::{assets::upload::Asset, wgpu::material::MaterialSpec};
49
50    pub struct GPUMaterial {
51        pub pipeline: wgpu::RenderPipeline,
52        pub layout: wgpu::BindGroupLayout,
53    }
54
55    pub struct Material {
56        pub descriptor: MaterialSpec,
57    }
58
59    impl Asset<wgpu::Device> for GPUMaterial {
60        type Source = Material;
61        type Deps<'a> = ();
62
63        fn upload<'a>(
64            source: &Self::Source,
65            backend: &wgpu::Device,
66            deps: &Self::Deps<'a>,
67        ) -> Option<Self> {
68            let desc = &source.descriptor;
69
70            let entries: Vec<_> = source
71                .descriptor
72                .entries
73                .iter()
74                .enumerate()
75                .map(|(i, e)| e.layout_entry(i as u32))
76                .collect();
77
78            let layout = backend.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
79                label: Some("Bind Group Layout"),
80                entries: &entries,
81            });
82
83            let pipeline_layout = backend.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
84                label: Some("Pipeline Layout"),
85                bind_group_layouts: &[Some(&layout)],
86                immediate_size: 0,
87            });
88
89            let module = backend.create_shader_module(wgpu::ShaderModuleDescriptor {
90                label: Some("Shader Module"),
91                source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(source.descriptor.shader_source)),
92            });
93
94            let pipeline = backend.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
95                label: Some("Render Pipeline"),
96                layout: Some(&pipeline_layout),
97                vertex: wgpu::VertexState {
98                    module: &module,
99                    entry_point: Some("vs_main"),
100                    buffers: &[],
101                    compilation_options: Default::default(),
102                },
103                primitive: wgpu::PrimitiveState {
104                    topology: wgpu::PrimitiveTopology::TriangleList,
105                    strip_index_format: None,
106                    front_face: wgpu::FrontFace::Ccw,
107                    cull_mode: source.descriptor.cull_mode,
108                    unclipped_depth: false,
109                    polygon_mode: wgpu::PolygonMode::Fill,
110                    conservative: false,
111                },
112                depth_stencil: source.descriptor.depth.clone(),
113                multisample: wgpu::MultisampleState::default(),
114                fragment: Some(wgpu::FragmentState {
115                    module: &module,
116                    entry_point: Some("fs_main"),
117                    targets: &[],
118                    compilation_options: Default::default(),
119                }),
120                multiview_mask: None,
121                cache: None,
122            });
123
124            Some(Self { pipeline, layout })
125        }
126    }
127}