use wgpu::util::DeviceExt; use winit::dpi::PhysicalSize; use super::Filter; pub struct SquareFilter { uniform_buffer: wgpu::Buffer, bind_group: wgpu::BindGroup, pipeline: wgpu::RenderPipeline, } impl SquareFilter { pub fn new( device: &wgpu::Device, screen: &wgpu::TextureView, resolution: PhysicalSize, surface_format: wgpu::TextureFormat, ) -> SquareFilter { let uniforms = Uniforms { texture_scale: texture_scale_from_resolution(resolution), }; let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&[uniforms]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, }); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { multisampled: false, view_dimension: wgpu::TextureViewDimension::D2, sample_type: wgpu::TextureSampleType::Float { filterable: true }, }, count: None, }, wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, wgpu::BindGroupLayoutEntry { binding: 2, visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, count: None, }, ], label: None, }); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { mag_filter: wgpu::FilterMode::Linear, ..Default::default() }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&screen), }, wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, wgpu::BindGroupEntry { binding: 2, resource: uniform_buffer.as_entire_binding(), }, ], label: None, }); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(include_str!("square.wgsl").into()), }); let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }); let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", buffers: &[], }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", targets: &[Some(wgpu::ColorTargetState { format: surface_format, blend: None, write_mask: wgpu::ColorWrites::ALL, })], }), primitive: Default::default(), depth_stencil: None, multisample: Default::default(), multiview: None, }); SquareFilter { uniform_buffer, bind_group, pipeline: render_pipeline, } } } impl Filter for SquareFilter { fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize) { let uniforms = Uniforms { texture_scale: texture_scale_from_resolution(new_size), }; queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms])); } fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.bind_group, &[]); render_pass.draw(0..6, 0..1); } } fn texture_scale_from_resolution(res: PhysicalSize) -> [f32; 4] { let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0); [ scale / res.width as f32, scale / res.height as f32, 2.0 / scale, 0.0, ] } #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct Uniforms { texture_scale: [f32; 4], }