tessera_ui_basic_components/pipelines/blur/
pipeline.rs1use encase::{ShaderType, UniformBuffer};
2use tessera_ui::{
3 renderer::compute::ComputablePipeline,
4 wgpu::{self, util::DeviceExt},
5};
6
7use super::command::BlurCommand;
8
9#[derive(ShaderType)]
10struct BlurUniforms {
11 radius: f32,
12 direction_x: f32,
13 direction_y: f32,
14}
15
16pub struct BlurPipeline {
17 pipeline: wgpu::ComputePipeline,
18 bind_group_layout: wgpu::BindGroupLayout,
19}
20
21impl BlurPipeline {
22 pub fn new(device: &wgpu::Device) -> Self {
23 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
24 label: Some("Blur Shader"),
25 source: wgpu::ShaderSource::Wgsl(include_str!("blur.wgsl").into()),
26 });
27
28 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
29 entries: &[
30 wgpu::BindGroupLayoutEntry {
32 binding: 0,
33 visibility: wgpu::ShaderStages::COMPUTE,
34 ty: wgpu::BindingType::Buffer {
35 ty: wgpu::BufferBindingType::Uniform,
36 has_dynamic_offset: false,
37 min_binding_size: None,
38 },
39 count: None,
40 },
41 wgpu::BindGroupLayoutEntry {
43 binding: 1,
44 visibility: wgpu::ShaderStages::COMPUTE,
45 ty: wgpu::BindingType::Texture {
46 sample_type: wgpu::TextureSampleType::Float { filterable: true },
47 view_dimension: wgpu::TextureViewDimension::D2,
48 multisampled: false,
49 },
50 count: None,
51 },
52 wgpu::BindGroupLayoutEntry {
54 binding: 2,
55 visibility: wgpu::ShaderStages::COMPUTE,
56 ty: wgpu::BindingType::StorageTexture {
57 access: wgpu::StorageTextureAccess::WriteOnly,
58 format: wgpu::TextureFormat::Rgba8Unorm,
62 view_dimension: wgpu::TextureViewDimension::D2,
63 },
64 count: None,
65 },
66 ],
67 label: Some("blur_bind_group_layout"),
68 });
69
70 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
71 label: Some("Blur Pipeline Layout"),
72 bind_group_layouts: &[&bind_group_layout],
73 push_constant_ranges: &[],
74 });
75
76 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
77 label: Some("Blur Pipeline"),
78 layout: Some(&pipeline_layout),
79 module: &shader,
80 entry_point: Some("main"),
81 compilation_options: Default::default(),
82 cache: None,
83 });
84
85 Self {
86 pipeline,
87 bind_group_layout,
88 }
89 }
90}
91
92impl ComputablePipeline<BlurCommand> for BlurPipeline {
93 fn dispatch(
94 &mut self,
95 device: &wgpu::Device,
96 _queue: &wgpu::Queue,
97 config: &wgpu::SurfaceConfiguration,
98 compute_pass: &mut wgpu::ComputePass<'_>,
99 command: &BlurCommand,
100 _resource_manager: &mut tessera_ui::ComputeResourceManager,
101 input_view: &wgpu::TextureView,
102 output_view: &wgpu::TextureView,
103 ) {
104 let uniforms = BlurUniforms {
105 radius: command.radius,
106 direction_x: command.direction.0,
107 direction_y: command.direction.1,
108 };
109 let mut buffer = UniformBuffer::new(Vec::new());
110 buffer.write(&uniforms).unwrap();
111 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
112 label: Some("Blur Uniform Buffer"),
113 contents: &buffer.into_inner(),
114 usage: wgpu::BufferUsages::UNIFORM,
115 });
116
117 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
118 layout: &self.bind_group_layout,
119 entries: &[
120 wgpu::BindGroupEntry {
121 binding: 0,
122 resource: uniform_buffer.as_entire_binding(),
123 },
124 wgpu::BindGroupEntry {
125 binding: 1,
126 resource: wgpu::BindingResource::TextureView(input_view),
127 },
128 wgpu::BindGroupEntry {
129 binding: 2,
130 resource: wgpu::BindingResource::TextureView(output_view),
131 },
132 ],
133 label: Some("blur_bind_group"),
134 });
135
136 compute_pass.set_pipeline(&self.pipeline);
137 compute_pass.set_bind_group(0, &bind_group, &[]);
138 compute_pass.dispatch_workgroups(config.width.div_ceil(8), config.height.div_ceil(8), 1);
139 }
140}