1use wgpu::{
4 BlendState, ColorTargetState, ColorWrites, Device, Face, FragmentState, FrontFace,
5 MultisampleState, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode,
6 PrimitiveState, PrimitiveTopology, RenderPipelineDescriptor, TextureFormat, VertexState,
7};
8
9use crate::shader::Shader;
10use crate::vertex::Vertex;
11
12#[derive(Debug)]
14pub struct RenderPipeline {
15 inner: wgpu::RenderPipeline,
16}
17
18impl RenderPipeline {
19 #[must_use]
21 pub fn inner(&self) -> &wgpu::RenderPipeline {
22 &self.inner
23 }
24}
25
26pub struct PipelineBuilder<'a> {
28 device: &'a Device,
29 shader: Option<&'a Shader>,
30 vertex_entry: &'a str,
31 fragment_entry: &'a str,
32 vertex_layouts: Vec<wgpu::VertexBufferLayout<'static>>,
33 target_format: Option<TextureFormat>,
34 blend_state: Option<BlendState>,
35 cull_mode: Option<Face>,
36 front_face: FrontFace,
37 polygon_mode: PolygonMode,
38 topology: PrimitiveTopology,
39 depth_format: Option<TextureFormat>,
40 label: Option<&'a str>,
41}
42
43impl<'a> PipelineBuilder<'a> {
44 #[must_use]
46 pub fn new(device: &'a Device) -> Self {
47 Self {
48 device,
49 shader: None,
50 vertex_entry: "vs_main",
51 fragment_entry: "fs_main",
52 vertex_layouts: Vec::new(),
53 target_format: None,
54 blend_state: None,
55 cull_mode: None,
56 front_face: FrontFace::Ccw,
57 polygon_mode: PolygonMode::Fill,
58 topology: PrimitiveTopology::TriangleList,
59 depth_format: None,
60 label: None,
61 }
62 }
63
64 #[must_use]
66 pub fn shader(mut self, shader: &'a Shader) -> Self {
67 self.shader = Some(shader);
68 self
69 }
70
71 #[must_use]
73 pub fn vertex_entry(mut self, entry: &'a str) -> Self {
74 self.vertex_entry = entry;
75 self
76 }
77
78 #[must_use]
80 pub fn fragment_entry(mut self, entry: &'a str) -> Self {
81 self.fragment_entry = entry;
82 self
83 }
84
85 #[must_use]
87 pub fn vertex<V: Vertex>(mut self) -> Self {
88 self.vertex_layouts.push(V::layout());
89 self
90 }
91
92 #[must_use]
94 pub fn target_format(mut self, format: TextureFormat) -> Self {
95 self.target_format = Some(format);
96 self
97 }
98
99 #[must_use]
101 pub fn blend(mut self, blend: BlendState) -> Self {
102 self.blend_state = Some(blend);
103 self
104 }
105
106 #[must_use]
108 pub fn alpha_blend(mut self) -> Self {
109 self.blend_state = Some(BlendState::ALPHA_BLENDING);
110 self
111 }
112
113 #[must_use]
115 pub fn cull_mode(mut self, mode: Option<Face>) -> Self {
116 self.cull_mode = mode;
117 self
118 }
119
120 #[must_use]
122 pub fn front_face(mut self, front_face: FrontFace) -> Self {
123 self.front_face = front_face;
124 self
125 }
126
127 #[must_use]
129 pub fn polygon_mode(mut self, mode: PolygonMode) -> Self {
130 self.polygon_mode = mode;
131 self
132 }
133
134 #[must_use]
136 pub fn topology(mut self, topology: PrimitiveTopology) -> Self {
137 self.topology = topology;
138 self
139 }
140
141 #[must_use]
143 pub fn depth_format(mut self, format: TextureFormat) -> Self {
144 self.depth_format = Some(format);
145 self
146 }
147
148 #[must_use]
150 pub fn label(mut self, label: &'a str) -> Self {
151 self.label = Some(label);
152 self
153 }
154
155 #[must_use]
161 pub fn build(self) -> RenderPipeline {
162 let shader = self.shader.expect("shader is required");
163 let target_format = self.target_format.expect("target format is required");
164
165 let pipeline_layout = self
166 .device
167 .create_pipeline_layout(&PipelineLayoutDescriptor {
168 label: self.label.map(|l| format!("{l} Layout")).as_deref(),
169 bind_group_layouts: &[],
170 push_constant_ranges: &[],
171 });
172
173 let depth_stencil = self.depth_format.map(|format| wgpu::DepthStencilState {
174 format,
175 depth_write_enabled: true,
176 depth_compare: wgpu::CompareFunction::Less,
177 stencil: wgpu::StencilState::default(),
178 bias: wgpu::DepthBiasState::default(),
179 });
180
181 let inner = self
182 .device
183 .create_render_pipeline(&RenderPipelineDescriptor {
184 label: self.label,
185 layout: Some(&pipeline_layout),
186 vertex: VertexState {
187 module: shader.module(),
188 entry_point: Some(self.vertex_entry),
189 compilation_options: PipelineCompilationOptions::default(),
190 buffers: &self.vertex_layouts,
191 },
192 fragment: Some(FragmentState {
193 module: shader.module(),
194 entry_point: Some(self.fragment_entry),
195 compilation_options: PipelineCompilationOptions::default(),
196 targets: &[Some(ColorTargetState {
197 format: target_format,
198 blend: self.blend_state,
199 write_mask: ColorWrites::ALL,
200 })],
201 }),
202 primitive: PrimitiveState {
203 topology: self.topology,
204 strip_index_format: None,
205 front_face: self.front_face,
206 cull_mode: self.cull_mode,
207 polygon_mode: self.polygon_mode,
208 unclipped_depth: false,
209 conservative: false,
210 },
211 depth_stencil,
212 multisample: MultisampleState {
213 count: 1,
214 mask: !0,
215 alpha_to_coverage_enabled: false,
216 },
217 multiview: None,
218 cache: None,
219 });
220
221 RenderPipeline { inner }
222 }
223}