1use crate::error::{RenderError, Result};
6use wgpu::util::DeviceExt;
7
8#[repr(C)]
10#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
11pub struct VertexVarsUniform {
12 pub time: f32,
14 pub frame: f32,
16 pub fps: f32,
18 pub bass: f32,
20
21 pub mid: f32,
23 pub treb: f32,
25 pub bass_att: f32,
27 pub mid_att: f32,
29
30 pub treb_att: f32,
32 pub _padding1: f32,
34 pub _padding2: f32,
35 pub _padding3: f32,
36
37 pub q: [[f32; 4]; 16],
39}
40
41impl Default for VertexVarsUniform {
42 fn default() -> Self {
43 Self {
44 time: 0.0,
45 frame: 0.0,
46 fps: 60.0,
47 bass: 0.0,
48 mid: 0.0,
49 treb: 0.0,
50 bass_att: 0.0,
51 mid_att: 0.0,
52 treb_att: 0.0,
53 _padding1: 0.0,
54 _padding2: 0.0,
55 _padding3: 0.0,
56 q: [[0.0; 4]; 16],
57 }
58 }
59}
60
61pub struct PerVertexPipeline {
63 device: wgpu::Device,
64 queue: wgpu::Queue,
65
66 uniform_buffer: wgpu::Buffer,
68
69 bind_group_layout: wgpu::BindGroupLayout,
71
72 bind_group: Option<wgpu::BindGroup>,
74
75 render_pipeline: Option<wgpu::RenderPipeline>,
77
78 vertex_buffer: wgpu::Buffer,
80
81 index_buffer: wgpu::Buffer,
83
84 num_indices: u32,
86}
87
88impl PerVertexPipeline {
89 pub fn new(device: wgpu::Device, queue: wgpu::Queue, vertex_count: u32) -> Result<Self> {
91 let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
93 label: Some("Vertex Vars Uniform Buffer"),
94 size: std::mem::size_of::<VertexVarsUniform>() as u64,
95 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
96 mapped_at_creation: false,
97 });
98
99 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
101 label: Some("Per-Vertex Bind Group Layout"),
102 entries: &[
103 wgpu::BindGroupLayoutEntry {
105 binding: 0,
106 visibility: wgpu::ShaderStages::VERTEX,
107 ty: wgpu::BindingType::Buffer {
108 ty: wgpu::BufferBindingType::Uniform,
109 has_dynamic_offset: false,
110 min_binding_size: None,
111 },
112 count: None,
113 },
114 ],
115 });
116
117 let vertices = Self::create_waveform_vertices(vertex_count);
119 let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
120 label: Some("Vertex Buffer"),
121 contents: bytemuck::cast_slice(&vertices),
122 usage: wgpu::BufferUsages::VERTEX,
123 });
124
125 let indices = Self::create_waveform_indices(vertex_count);
127 let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
128 label: Some("Index Buffer"),
129 contents: bytemuck::cast_slice(&indices),
130 usage: wgpu::BufferUsages::INDEX,
131 });
132
133 Ok(Self {
134 device,
135 queue,
136 uniform_buffer,
137 bind_group_layout,
138 bind_group: None,
139 render_pipeline: None,
140 vertex_buffer,
141 index_buffer,
142 num_indices: indices.len() as u32,
143 })
144 }
145
146 fn create_waveform_vertices(count: u32) -> Vec<[f32; 3]> {
148 let mut vertices = Vec::with_capacity(count as usize);
149
150 for i in 0..count {
151 let t = i as f32 / (count - 1) as f32;
152 let x = t * 2.0 - 1.0; let y = 0.0;
154 let z = 0.0;
155 vertices.push([x, y, z]);
156 }
157
158 vertices
159 }
160
161 fn create_waveform_indices(count: u32) -> Vec<u16> {
163 let mut indices = Vec::with_capacity((count * 2) as usize);
164
165 for i in 0..(count - 1) {
166 indices.push(i as u16);
167 indices.push((i + 1) as u16);
168 }
169
170 indices
171 }
172
173 pub fn set_shader(&mut self, shader_module: &wgpu::ShaderModule) -> Result<()> {
175 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
177 label: Some("Per-Vertex Bind Group"),
178 layout: &self.bind_group_layout,
179 entries: &[wgpu::BindGroupEntry {
180 binding: 0,
181 resource: self.uniform_buffer.as_entire_binding(),
182 }],
183 });
184
185 let pipeline_layout = self
187 .device
188 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
189 label: Some("Per-Vertex Pipeline Layout"),
190 bind_group_layouts: &[Some(&self.bind_group_layout)],
191 immediate_size: 0,
192 });
193
194 let render_pipeline = self
196 .device
197 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
198 label: Some("Per-Vertex Render Pipeline"),
199 layout: Some(&pipeline_layout),
200 vertex: wgpu::VertexState {
201 module: shader_module,
202 entry_point: Some("vs_main"),
203 buffers: &[wgpu::VertexBufferLayout {
204 array_stride: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
205 step_mode: wgpu::VertexStepMode::Vertex,
206 attributes: &[wgpu::VertexAttribute {
207 offset: 0,
208 shader_location: 0,
209 format: wgpu::VertexFormat::Float32x3,
210 }],
211 }],
212 compilation_options: wgpu::PipelineCompilationOptions::default(),
213 },
214 fragment: Some(wgpu::FragmentState {
215 module: shader_module,
216 entry_point: Some("fs_main"),
217 targets: &[Some(wgpu::ColorTargetState {
218 format: wgpu::TextureFormat::Rgba8Unorm,
219 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
220 write_mask: wgpu::ColorWrites::ALL,
221 })],
222 compilation_options: wgpu::PipelineCompilationOptions::default(),
223 }),
224 primitive: wgpu::PrimitiveState {
225 topology: wgpu::PrimitiveTopology::LineList,
226 strip_index_format: None,
227 front_face: wgpu::FrontFace::Ccw,
228 cull_mode: None,
229 unclipped_depth: false,
230 polygon_mode: wgpu::PolygonMode::Fill,
231 conservative: false,
232 },
233 depth_stencil: None,
234 multisample: wgpu::MultisampleState {
235 count: 1,
236 mask: !0,
237 alpha_to_coverage_enabled: false,
238 },
239 multiview_mask: None,
240 cache: None,
241 });
242
243 self.bind_group = Some(bind_group);
244 self.render_pipeline = Some(render_pipeline);
245
246 Ok(())
247 }
248
249 pub fn update_vars(&mut self, vars: VertexVarsUniform) {
251 self.queue
252 .write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[vars]));
253 }
254
255 pub fn render(&mut self, output_view: &wgpu::TextureView) -> Result<()> {
257 let pipeline = self
258 .render_pipeline
259 .as_ref()
260 .ok_or_else(|| RenderError::RenderFailed("No shader set".to_string()))?;
261
262 let bind_group = self
263 .bind_group
264 .as_ref()
265 .ok_or_else(|| RenderError::RenderFailed("No bind group".to_string()))?;
266
267 let mut encoder = self
268 .device
269 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
270 label: Some("Per-Vertex Render Encoder"),
271 });
272
273 {
274 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
275 label: Some("Per-Vertex Render Pass"),
276 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
277 view: output_view,
278 depth_slice: None,
279 resolve_target: None,
280 ops: wgpu::Operations {
281 load: wgpu::LoadOp::Load,
282 store: wgpu::StoreOp::Store,
283 },
284 })],
285 depth_stencil_attachment: None,
286 timestamp_writes: None,
287 occlusion_query_set: None,
288 multiview_mask: None,
289 });
290
291 render_pass.set_pipeline(pipeline);
292 render_pass.set_bind_group(0, bind_group, &[]);
293 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
294 render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
295 render_pass.draw_indexed(0..self.num_indices, 0, 0..1);
296 }
297
298 self.queue.submit(std::iter::once(encoder.finish()));
299
300 Ok(())
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_vertex_vars_size() {
310 assert_eq!(std::mem::size_of::<VertexVarsUniform>(), 304);
312 }
313
314 #[test]
315 fn test_vertex_vars_default() {
316 let vars = VertexVarsUniform::default();
317 assert_eq!(vars.fps, 60.0);
318 assert_eq!(vars.time, 0.0);
319 }
320
321 #[test]
322 fn test_waveform_vertices() {
323 let vertices = PerVertexPipeline::create_waveform_vertices(100);
324 assert_eq!(vertices.len(), 100);
325 assert_eq!(vertices[0][0], -1.0); assert_eq!(vertices[99][0], 1.0); }
328
329 #[test]
330 fn test_waveform_indices() {
331 let indices = PerVertexPipeline::create_waveform_indices(100);
332 assert_eq!(indices.len(), 198); }
334}