use crate::settings; #[derive(Debug)] pub struct Blit { format: wgpu::TextureFormat, pipeline: wgpu::RenderPipeline, constants: wgpu::BindGroup, texture_layout: wgpu::BindGroupLayout, sample_count: u32, targets: Option, } impl Blit { pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, antialiasing: settings::Antialiasing, ) -> Blit { let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare: wgpu::CompareFunction::Always, }); let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, bindings: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler { comparison: false }, }], }); let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &constant_layout, bindings: &[wgpu::Binding { binding: 0, resource: wgpu::BindingResource::Sampler(&sampler), }], }); let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, bindings: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { dimension: wgpu::TextureViewDimension::D2, component_type: wgpu::TextureComponentType::Float, multisampled: false, }, }], }); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&constant_layout, &texture_layout], }); let vs = include_bytes!("../shader/blit.vert.spv"); let vs_module = device.create_shader_module( &wgpu::read_spirv(std::io::Cursor::new(&vs[..])) .expect("Read blit vertex shader as SPIR-V"), ); let fs = include_bytes!("../shader/blit.frag.spv"); let fs_module = device.create_shader_module( &wgpu::read_spirv(std::io::Cursor::new(&fs[..])) .expect("Read blit fragment shader as SPIR-V"), ); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::None, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, vertex_state: wgpu::VertexStateDescriptor { index_format: wgpu::IndexFormat::Uint16, vertex_buffers: &[], }, sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Blit { format, pipeline, constants: constant_bind_group, texture_layout, sample_count: antialiasing.sample_count(), targets: None, } } pub fn targets( &mut self, device: &wgpu::Device, width: u32, height: u32, ) -> (&wgpu::TextureView, &wgpu::TextureView) { match &mut self.targets { None => { self.targets = Some(Targets::new( &device, self.format, &self.texture_layout, self.sample_count, width, height, )); } Some(targets) => { if targets.width != width || targets.height != height { self.targets = Some(Targets::new( &device, self.format, &self.texture_layout, self.sample_count, width, height, )); } } } let targets = self.targets.as_ref().unwrap(); (&targets.attachment, &targets.resolve) } pub fn draw( &self, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, ) { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[ wgpu::RenderPassColorAttachmentDescriptor { attachment: target, resolve_target: None, load_op: wgpu::LoadOp::Load, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0, }, }, ], depth_stencil_attachment: None, }); render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.constants, &[]); render_pass.set_bind_group( 1, &self.targets.as_ref().unwrap().bind_group, &[], ); render_pass.draw(0..6, 0..1); } } #[derive(Debug)] struct Targets { attachment: wgpu::TextureView, resolve: wgpu::TextureView, bind_group: wgpu::BindGroup, width: u32, height: u32, } impl Targets { pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, texture_layout: &wgpu::BindGroupLayout, sample_count: u32, width: u32, height: u32, ) -> Targets { let extent = wgpu::Extent3d { width, height, depth: 1, }; let attachment = device.create_texture(&wgpu::TextureDescriptor { label: None, size: extent, array_layer_count: 1, mip_level_count: 1, sample_count, dimension: wgpu::TextureDimension::D2, format, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, }); let resolve = device.create_texture(&wgpu::TextureDescriptor { label: None, size: extent, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, }); let attachment = attachment.create_default_view(); let resolve = resolve.create_default_view(); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: texture_layout, bindings: &[wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&resolve), }], }); Targets { attachment, resolve, bind_group, width, height, } } }