1use std::ops::Range;
2
3use consts::VERTEX_BUFFER_LAYOUT;
4use encase::ShaderType;
5use nalgebra::{Vector2, Vector4};
6use wgpu::{
7 BindGroup, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BlendComponent, BlendState,
8 ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
9 FragmentState, IndexFormat, MultisampleState, PipelineCompilationOptions,
10 PipelineLayoutDescriptor, PrimitiveState, PrimitiveTopology, RenderPass, ShaderModule,
11 ShaderModuleDescriptor, ShaderStages, StencilState, VertexBufferLayout, VertexState,
12};
13
14use crate::{
15 bindings::{Bindable, BindableResourceId, IndexBuffer, VertexBuffer},
16 gpu::Gpu,
17 misc::ids::PipelineId,
18 DEPTH_TEXTURE_FORMAT, TEXTURE_FORMAT,
19};
20
21use super::PipelineStatus;
22pub mod consts;
23pub mod pass;
24
25#[derive(ShaderType)]
26pub struct Vertex {
27 pub position: Vector4<f32>,
28 pub uv: Vector2<f32>,
29}
30
31impl Vertex {
32 pub const fn new(position: Vector4<f32>, uv: Vector2<f32>) -> Self {
33 Self { position, uv }
34 }
35}
36
37pub struct RenderPipeline {
38 gpu: Gpu,
39
40 id: PipelineId,
41 pipeline: wgpu::RenderPipeline,
42 entries: Vec<BindableResourceId>,
43 bind_group: BindGroup,
44}
45
46#[derive(Clone)]
47pub struct RenderPipelineBuilder {
48 gpu: Gpu,
49
50 module: ShaderModule,
51 vertex_layout: VertexBufferLayout<'static>,
52 instance_layout: Option<VertexBufferLayout<'static>>,
53 bind_group_layout: Vec<BindGroupLayoutEntry>,
54 bind_group: Vec<BindableResourceId>,
55
56 topology: PrimitiveTopology,
57 depth_compare: CompareFunction,
58}
59
60impl RenderPipeline {
61 fn recreate_bind_group(&mut self) {
62 if self.gpu.binding_manager.get_pipeline(self.id).dirty {
63 self.bind_group = self.gpu.binding_manager.create_bind_group(
64 &self.gpu.device,
65 &self.pipeline.get_bind_group_layout(0),
66 &self.entries,
67 );
68 }
69 }
70
71 pub fn draw<T>(
72 &mut self,
73 render_pass: &mut RenderPass,
74 index: &IndexBuffer,
75 vertex: &VertexBuffer<T>,
76 indices: Range<u32>,
77 ) {
78 self.recreate_bind_group();
79
80 render_pass.set_pipeline(&self.pipeline);
81 render_pass.set_bind_group(0, Some(&self.bind_group), &[]);
82 render_pass.set_index_buffer(index.get().slice(..), IndexFormat::Uint32);
83 render_pass.set_vertex_buffer(0, vertex.get().slice(..));
84 render_pass.draw_indexed(indices, 0, 0..1);
85 }
86
87 pub fn draw_quad(&mut self, render_pass: &mut RenderPass, instances: Range<u32>) {
88 self.recreate_bind_group();
89 let (vertex, index) = self.gpu.default_buffers();
90
91 render_pass.set_pipeline(&self.pipeline);
92 render_pass.set_bind_group(0, Some(&self.bind_group), &[]);
93 render_pass.set_index_buffer(index.get().slice(..), IndexFormat::Uint32);
94 render_pass.set_vertex_buffer(0, vertex.get().slice(..));
95 render_pass.draw_indexed(0..6, 0, instances);
96 }
97
98 pub fn instance_quad<T>(
99 &mut self,
100 render_pass: &mut RenderPass,
101 instances: &VertexBuffer<T>,
102 range: Range<u32>,
103 ) {
104 let (vertex, index) = self.gpu.default_buffers();
105
106 render_pass.set_pipeline(&self.pipeline);
107 render_pass.set_bind_group(0, Some(&self.bind_group), &[]);
108 render_pass.set_index_buffer(index.get().slice(..), IndexFormat::Uint32);
109 render_pass.set_vertex_buffer(0, vertex.get().slice(..));
110 render_pass.set_vertex_buffer(1, instances.get().slice(..));
111 render_pass.draw_indexed(0..6, 0, range);
112 }
113}
114
115impl RenderPipelineBuilder {
116 pub fn bind(mut self, entry: &impl Bindable, visibility: ShaderStages) -> Self {
117 let binding = self.bind_group.len() as u32;
118
119 self.bind_group.push(entry.resource_id());
120 self.bind_group_layout.push(BindGroupLayoutEntry {
121 binding,
122 visibility,
123 ty: entry.binding_type(),
124 count: entry.count(),
125 });
126
127 self
128 }
129
130 pub fn vertex_layout(mut self, layout: VertexBufferLayout<'static>) -> Self {
131 self.vertex_layout = layout;
132 self
133 }
134
135 pub fn instance_layout(mut self, layout: VertexBufferLayout<'static>) -> Self {
136 self.instance_layout = Some(layout);
137 self
138 }
139
140 pub fn depth_compare(mut self, compare: CompareFunction) -> Self {
141 self.depth_compare = compare;
142 self
143 }
144
145 pub fn topology(mut self, topology: PrimitiveTopology) -> Self {
146 self.topology = topology;
147 self
148 }
149
150 pub fn finish(self) -> RenderPipeline {
151 let device = &self.gpu.device;
152
153 let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
154 label: None,
155 entries: &self.bind_group_layout,
156 });
157
158 let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
159 label: None,
160 bind_group_layouts: &[&bind_group_layout],
161 push_constant_ranges: &[],
162 });
163
164 let mut vertex_buffers = vec![self.vertex_layout];
165 if let Some(layout) = self.instance_layout {
166 vertex_buffers.push(layout);
167 }
168
169 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
170 label: None,
171 layout: Some(&layout),
172 vertex: VertexState {
173 module: &self.module,
174 entry_point: Some("vert"),
175 buffers: &vertex_buffers,
176 compilation_options: PipelineCompilationOptions::default(),
177 },
178 fragment: Some(FragmentState {
179 module: &self.module,
180 entry_point: Some("frag"),
181 targets: &[Some(ColorTargetState {
182 format: TEXTURE_FORMAT,
183 blend: Some(BlendState {
184 color: BlendComponent::OVER,
185 alpha: BlendComponent::OVER,
186 }),
187 write_mask: ColorWrites::all(),
188 })],
189 compilation_options: PipelineCompilationOptions::default(),
190 }),
191 primitive: PrimitiveState {
192 topology: self.topology,
193 ..PrimitiveState::default()
194 },
195 depth_stencil: Some(DepthStencilState {
196 format: DEPTH_TEXTURE_FORMAT,
197 depth_write_enabled: true,
198 depth_compare: self.depth_compare,
199 stencil: StencilState::default(),
200 bias: DepthBiasState::default(),
201 }),
202 multisample: MultisampleState::default(),
203 multiview: None,
204 cache: None,
205 });
206
207 let bind_group = self.gpu.binding_manager.create_bind_group(
208 &self.gpu.device,
209 &pipeline.get_bind_group_layout(0),
210 &self.bind_group,
211 );
212
213 let id = PipelineId::new();
214 self.gpu.binding_manager.add_pipeline(
215 id,
216 PipelineStatus {
217 resources: self.bind_group.clone(),
218 dirty: false,
219 },
220 );
221
222 RenderPipeline {
223 gpu: self.gpu,
224 id,
225 pipeline,
226 bind_group,
227 entries: self.bind_group,
228 }
229 }
230}
231
232impl Gpu {
233 pub fn render_pipeline(&self, source: ShaderModuleDescriptor) -> RenderPipelineBuilder {
234 let module = self.device.create_shader_module(source);
235
236 RenderPipelineBuilder {
237 gpu: self.clone(),
238 module,
239 vertex_layout: VERTEX_BUFFER_LAYOUT,
240 instance_layout: None,
241 bind_group_layout: Vec::new(),
242 bind_group: Vec::new(),
243
244 topology: PrimitiveTopology::TriangleList,
245 depth_compare: CompareFunction::LessEqual,
246 }
247 }
248}
249
250impl Drop for RenderPipeline {
251 fn drop(&mut self) {
252 self.gpu.binding_manager.remove_pipeline(self.id);
253 }
254}