#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct BackdropUniforms {
pub radius: [f32; 4],
pub rect_pos: [f32; 2],
pub rect_size: [f32; 2],
pub screen_size: [f32; 2],
pub time: f32,
pub brightness: f32,
pub saturation: f32,
pub contrast: f32,
pub blur_type: f32,
pub opacity: f32,
pub padding: [f32; 2],
pub _end_padding: [f32; 2],
}
pub struct BlitPipeline {
pub pipeline: wgpu::RenderPipeline,
pub backdrop_pipeline: wgpu::RenderPipeline,
pub bgl: wgpu::BindGroupLayout,
pub backdrop_bgl: wgpu::BindGroupLayout,
pub sampler: wgpu::Sampler,
}
impl BlitPipeline {
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
let shader = device.create_shader_module(wgpu::include_wgsl!("shaders/blit.wgsl"));
let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Blit BGL"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
let backdrop_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Blit Backdrop BGL"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Blit Pipeline Layout"),
bind_group_layouts: &[Some(&bgl)],
immediate_size: 0,
});
let backdrop_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Blit Backdrop Pipeline Layout"),
bind_group_layouts: &[Some(&bgl), Some(&backdrop_bgl)],
immediate_size: 0,
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Blit Pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: None, write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let backdrop_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Blit Backdrop Pipeline"),
layout: Some(&backdrop_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_backdrop"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_backdrop"),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Blit Sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
..Default::default()
});
Self { pipeline, backdrop_pipeline, bgl, backdrop_bgl, sampler }
}
pub fn blit<'a>(
&'a self,
device: &wgpu::Device,
pass: &mut wgpu::RenderPass<'a>,
src_view: &wgpu::TextureView,
) {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Blit BG"),
layout: &self.bgl,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(src_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&self.sampler),
},
],
});
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &bg, &[]);
pass.draw(0..3, 0..1);
}
pub fn blit_to(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
src_view: &wgpu::TextureView,
dst_view: &wgpu::TextureView,
load: wgpu::LoadOp<wgpu::Color>,
) {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Blit BG"),
layout: &self.bgl,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(src_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&self.sampler),
},
],
});
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Blit Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: dst_view,
resolve_target: None,
ops: wgpu::Operations { load, store: wgpu::StoreOp::Store },
depth_slice: None,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &bg, &[]);
pass.draw(0..3, 0..1);
}
pub fn blit_to_clipped(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
src_view: &wgpu::TextureView,
dst_view: &wgpu::TextureView,
rect_pos: [f32; 2],
rect_size: [f32; 2],
radius: [f32; 4],
surface_w: u32,
surface_h: u32,
brightness: f32,
saturation: f32,
contrast: f32,
blur_type: f32,
opacity: f32,
) {
use wgpu::util::DeviceExt;
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Blit Clipped BG"),
layout: &self.bgl,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(src_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&self.sampler),
},
],
});
let uniforms = BackdropUniforms {
radius,
rect_pos,
rect_size,
screen_size: [surface_w as f32, surface_h as f32],
time: 0.0,
brightness,
saturation,
contrast,
blur_type,
opacity,
padding: [0.0; 2],
_end_padding: [0.0; 2],
};
let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Blit Backdrop Uniform Buffer"),
contents: bytemuck::bytes_of(&uniforms),
usage: wgpu::BufferUsages::UNIFORM,
});
let uniform_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Blit Backdrop Uniform BG"),
layout: &self.backdrop_bgl,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buf.as_entire_binding(),
},
],
});
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Blit Clipped Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: dst_view,
resolve_target: None,
ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store },
depth_slice: None,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&self.backdrop_pipeline);
pass.set_bind_group(0, &bg, &[]);
pass.set_bind_group(1, &uniform_bg, &[]);
pass.draw(0..6, 0..1);
}
}