1use crate::error::{RenderError, Result};
6use wgpu::util::DeviceExt;
7
8#[repr(C)]
10#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
11pub struct PixelVarsUniform {
12 pub x: f32,
14 pub y: f32,
15 pub rad: f32,
16 pub ang: f32,
17
18 pub bass: f32,
20 pub mid: f32,
21 pub treb: f32,
22 pub bass_att: f32,
23 pub mid_att: f32,
24 pub treb_att: f32,
25
26 pub time: f32,
28 pub frame: f32,
29 pub fps: f32,
30
31 pub _padding: f32,
33
34 pub q: [[f32; 4]; 16],
36}
37
38impl Default for PixelVarsUniform {
39 fn default() -> Self {
40 Self {
41 x: 0.0,
42 y: 0.0,
43 rad: 0.0,
44 ang: 0.0,
45 bass: 0.0,
46 mid: 0.0,
47 treb: 0.0,
48 bass_att: 0.0,
49 mid_att: 0.0,
50 treb_att: 0.0,
51 time: 0.0,
52 frame: 0.0,
53 fps: 60.0,
54 _padding: 0.0,
55 q: [[0.0; 4]; 16],
56 }
57 }
58}
59
60#[allow(dead_code)]
62pub struct PerPixelPipeline {
63 device: wgpu::Device,
64 queue: wgpu::Queue,
65
66 render_pipeline: Option<wgpu::RenderPipeline>,
68 bind_group_layout: wgpu::BindGroupLayout,
69 bind_group: Option<wgpu::BindGroup>,
70
71 vars_buffer: wgpu::Buffer,
73 vars: PixelVarsUniform,
74
75 input_texture: Option<wgpu::Texture>,
77 output_texture: Option<wgpu::Texture>,
78 sampler: wgpu::Sampler,
79
80 width: u32,
82 height: u32,
83}
84
85impl PerPixelPipeline {
86 pub fn new(device: wgpu::Device, queue: wgpu::Queue, width: u32, height: u32) -> Result<Self> {
87 let vars = PixelVarsUniform::default();
89 let vars_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
90 label: Some("Per-Pixel Vars Buffer"),
91 contents: bytemuck::cast_slice(&[vars]),
92 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
93 });
94
95 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
97 label: Some("Per-Pixel Bind Group Layout"),
98 entries: &[
99 wgpu::BindGroupLayoutEntry {
101 binding: 0,
102 visibility: wgpu::ShaderStages::FRAGMENT,
103 ty: wgpu::BindingType::Buffer {
104 ty: wgpu::BufferBindingType::Uniform,
105 has_dynamic_offset: false,
106 min_binding_size: None,
107 },
108 count: None,
109 },
110 wgpu::BindGroupLayoutEntry {
112 binding: 1,
113 visibility: wgpu::ShaderStages::FRAGMENT,
114 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
115 count: None,
116 },
117 wgpu::BindGroupLayoutEntry {
119 binding: 2,
120 visibility: wgpu::ShaderStages::FRAGMENT,
121 ty: wgpu::BindingType::Texture {
122 sample_type: wgpu::TextureSampleType::Float { filterable: true },
123 view_dimension: wgpu::TextureViewDimension::D2,
124 multisampled: false,
125 },
126 count: None,
127 },
128 ],
129 });
130
131 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
133 label: Some("Per-Pixel Sampler"),
134 address_mode_u: wgpu::AddressMode::ClampToEdge,
135 address_mode_v: wgpu::AddressMode::ClampToEdge,
136 address_mode_w: wgpu::AddressMode::ClampToEdge,
137 mag_filter: wgpu::FilterMode::Linear,
138 min_filter: wgpu::FilterMode::Linear,
139 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
140 ..Default::default()
141 });
142
143 Ok(Self {
144 device,
145 queue,
146 render_pipeline: None,
147 bind_group_layout,
148 bind_group: None,
149 vars_buffer,
150 vars,
151 input_texture: None,
152 output_texture: None,
153 sampler,
154 width,
155 height,
156 })
157 }
158
159 pub fn set_shader(&mut self, shader_module: &wgpu::ShaderModule) -> Result<()> {
161 let pipeline_layout = self
162 .device
163 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
164 label: Some("Per-Pixel Pipeline Layout"),
165 bind_group_layouts: &[Some(&self.bind_group_layout)],
166 immediate_size: 0,
167 });
168
169 self.render_pipeline = Some(self.device.create_render_pipeline(
170 &wgpu::RenderPipelineDescriptor {
171 label: Some("Per-Pixel Render Pipeline"),
172 layout: Some(&pipeline_layout),
173 vertex: wgpu::VertexState {
174 module: shader_module,
175 entry_point: Some("vs_main"),
176 buffers: &[],
177 compilation_options: Default::default(),
178 },
179 fragment: Some(wgpu::FragmentState {
180 module: shader_module,
181 entry_point: Some("fs_main"),
182 targets: &[Some(wgpu::ColorTargetState {
183 format: wgpu::TextureFormat::Rgba8Unorm,
184 blend: Some(wgpu::BlendState::REPLACE),
185 write_mask: wgpu::ColorWrites::ALL,
186 })],
187 compilation_options: Default::default(),
188 }),
189 primitive: wgpu::PrimitiveState {
190 topology: wgpu::PrimitiveTopology::TriangleList,
191 strip_index_format: None,
192 front_face: wgpu::FrontFace::Ccw,
193 cull_mode: None,
194 unclipped_depth: false,
195 polygon_mode: wgpu::PolygonMode::Fill,
196 conservative: false,
197 },
198 depth_stencil: None,
199 multisample: wgpu::MultisampleState {
200 count: 1,
201 mask: !0,
202 alpha_to_coverage_enabled: false,
203 },
204 multiview_mask: None,
205 cache: None,
206 },
207 ));
208
209 Ok(())
210 }
211
212 pub fn update_vars(&mut self, vars: PixelVarsUniform) {
214 self.vars = vars;
215 self.queue
216 .write_buffer(&self.vars_buffer, 0, bytemuck::cast_slice(&[self.vars]));
217 }
218
219 pub fn set_input_texture(&mut self, texture: wgpu::Texture) {
221 self.input_texture = Some(texture);
222 self.update_bind_group();
223 }
224
225 fn update_bind_group(&mut self) {
227 if let Some(ref input_texture) = self.input_texture {
228 let input_view = input_texture.create_view(&wgpu::TextureViewDescriptor::default());
229
230 self.bind_group = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
231 label: Some("Per-Pixel Bind Group"),
232 layout: &self.bind_group_layout,
233 entries: &[
234 wgpu::BindGroupEntry {
235 binding: 0,
236 resource: self.vars_buffer.as_entire_binding(),
237 },
238 wgpu::BindGroupEntry {
239 binding: 1,
240 resource: wgpu::BindingResource::Sampler(&self.sampler),
241 },
242 wgpu::BindGroupEntry {
243 binding: 2,
244 resource: wgpu::BindingResource::TextureView(&input_view),
245 },
246 ],
247 }));
248 }
249 }
250
251 pub fn render(&mut self, output_view: &wgpu::TextureView) -> Result<()> {
253 let pipeline = self
254 .render_pipeline
255 .as_ref()
256 .ok_or_else(|| RenderError::RenderFailed("No shader set".to_string()))?;
257
258 let bind_group = self
259 .bind_group
260 .as_ref()
261 .ok_or_else(|| RenderError::RenderFailed("No bind group".to_string()))?;
262
263 let mut encoder = self
264 .device
265 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
266 label: Some("Per-Pixel Render Encoder"),
267 });
268
269 {
270 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
271 label: Some("Per-Pixel Render Pass"),
272 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
273 view: output_view,
274 depth_slice: None,
275 resolve_target: None,
276 ops: wgpu::Operations {
277 load: wgpu::LoadOp::Load,
278 store: wgpu::StoreOp::Store,
279 },
280 })],
281 depth_stencil_attachment: None,
282 timestamp_writes: None,
283 occlusion_query_set: None,
284 multiview_mask: None,
285 });
286
287 render_pass.set_pipeline(pipeline);
288 render_pass.set_bind_group(0, bind_group, &[]);
289 render_pass.draw(0..6, 0..1); }
291
292 self.queue.submit(Some(encoder.finish()));
293
294 Ok(())
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[test]
303 fn test_pixel_vars_size() {
304 assert_eq!(std::mem::size_of::<PixelVarsUniform>(), 312);
306 }
307
308 #[test]
309 fn test_pixel_vars_default() {
310 let vars = PixelVarsUniform::default();
311 assert_eq!(vars.fps, 60.0);
312 assert_eq!(vars.time, 0.0);
313 }
314}