comfy_wgpu/
post_processing.rs

1use crate::*;
2
3pub fn insert_post_processing_effect(
4    renderer: &WgpuRenderer,
5    index: i32,
6    name: &str,
7    shader_id: ShaderId,
8) {
9    let effect = PostProcessingEffect::new(
10        name.to_string(),
11        &renderer.context.device,
12        &[&renderer.context.texture_layout],
13        &renderer.context.config.borrow(),
14        renderer.render_texture_format,
15        shader_id,
16        &renderer.shaders.borrow_mut(),
17    );
18
19    let mut effects = renderer.post_processing_effects.borrow_mut();
20
21    if index == -1 {
22        effects.push(effect);
23    } else if index >= 0 {
24        effects.insert(index as usize, effect);
25    } else {
26        panic!("Invalid index = {}, must be -1 or non-negative.", index);
27    }
28}
29
30pub struct PostProcessingEffect {
31    pub id: ShaderId,
32    pub name: String,
33    pub enabled: bool,
34    pub render_texture: Texture,
35    pub bind_group: wgpu::BindGroup,
36    pub pipeline: wgpu::RenderPipeline,
37}
38
39impl PostProcessingEffect {
40    pub fn new_with_mip(
41        name: String,
42        device: &wgpu::Device,
43        bind_group_layouts: &[&wgpu::BindGroupLayout],
44        config: &wgpu::SurfaceConfiguration,
45        format: wgpu::TextureFormat,
46        shader_id: ShaderId,
47        shaders: &ShaderMap,
48        mip_level_count: u32,
49        blend: wgpu::BlendState,
50    ) -> Self {
51        let render_texture = Texture::create_scaled_mip_filter_surface_texture(
52            device,
53            config,
54            format,
55            1.0,
56            mip_level_count,
57            wgpu::FilterMode::Linear,
58            &name,
59        );
60
61        let bind_group = device.simple_bind_group(
62            Some(&format!("{} Post Processing Bind Group", name)),
63            &render_texture,
64            bind_group_layouts[0],
65        );
66
67        let shader = shaders.get(shader_id).unwrap();
68
69        let pipeline = create_post_processing_pipeline(
70            &name,
71            device,
72            format,
73            bind_group_layouts,
74            shader.clone(),
75            blend,
76        );
77
78        Self {
79            id: shader_id,
80            name,
81            enabled: true,
82            render_texture,
83            bind_group,
84            pipeline,
85        }
86    }
87
88    pub fn new(
89        name: String,
90        device: &wgpu::Device,
91        bind_group_layouts: &[&wgpu::BindGroupLayout],
92        config: &wgpu::SurfaceConfiguration,
93        format: wgpu::TextureFormat,
94        shader_id: ShaderId,
95        shaders: &ShaderMap,
96    ) -> Self {
97        Self::new_with_mip(
98            name,
99            device,
100            bind_group_layouts,
101            config,
102            format,
103            shader_id,
104            shaders,
105            1,
106            wgpu::BlendState::REPLACE,
107        )
108    }
109}
110
111pub const USER_SHADER_PREFIX: &str = include_str!(concat!(
112    env!("CARGO_MANIFEST_DIR"),
113    "/shaders/user_post_processing_vertex.wgsl"
114));
115
116pub fn create_user_shader_module(
117    device: &wgpu::Device,
118    shader: &Shader,
119) -> wgpu::ShaderModule {
120    let full_shader = format!(
121        "{}{}{}",
122        CAMERA_BIND_GROUP_PREFIX, USER_SHADER_PREFIX, &shader.source
123    );
124
125    let descriptor = wgpu::ShaderModuleDescriptor {
126        label: Some(&shader.name),
127        source: wgpu::ShaderSource::Wgsl(full_shader.as_str().into()),
128    };
129
130    device.create_shader_module(descriptor)
131}
132
133pub fn create_post_processing_pipeline(
134    name: &str,
135    device: &wgpu::Device,
136    format: wgpu::TextureFormat,
137    bind_group_layouts: &[&wgpu::BindGroupLayout],
138    shader: Shader,
139    blend: wgpu::BlendState,
140) -> wgpu::RenderPipeline {
141    // let shader = create_user_shader_module(device, &shader);
142    let shader = device.create_shader_module(shader_to_wgpu(&shader));
143
144    let pipeline_layout =
145        device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
146            label: Some(&format!("{} Post Processing Pipeline Layout", name)),
147            bind_group_layouts,
148            push_constant_ranges: &[],
149        });
150
151    device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
152        label: Some(&format!("{} Post Processing Pipeline", name)),
153        layout: Some(&pipeline_layout),
154        vertex: wgpu::VertexState {
155            module: &shader,
156            entry_point: "vs_main",
157            buffers: &[],
158        },
159        fragment: Some(wgpu::FragmentState {
160            module: &shader,
161            entry_point: "fs_main",
162            targets: &[Some(wgpu::ColorTargetState {
163                format,
164                blend: Some(blend),
165                write_mask: wgpu::ColorWrites::ALL,
166            })],
167        }),
168        primitive: wgpu::PrimitiveState {
169            topology: wgpu::PrimitiveTopology::TriangleList,
170            strip_index_format: None,
171            front_face: wgpu::FrontFace::Ccw,
172            cull_mode: None,
173            unclipped_depth: false,
174            polygon_mode: wgpu::PolygonMode::Fill,
175            conservative: false,
176        },
177        depth_stencil: None,
178        multisample: wgpu::MultisampleState {
179            count: 1,
180            mask: !0,
181            alpha_to_coverage_enabled: false,
182        },
183        multiview: None,
184    })
185}
186
187pub fn draw_post_processing_output(
188    name: &str,
189    encoder: &mut wgpu::CommandEncoder,
190    post_processing_pipeline: &wgpu::RenderPipeline,
191    post_processing_bind_group: &wgpu::BindGroup,
192    lighting_params_bind_group: &wgpu::BindGroup,
193    target_view: &wgpu::TextureView,
194    should_clear: bool,
195    blend_constant: Option<f64>,
196) {
197    let mut render_pass =
198        encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
199            label: Some(&format!("{} Post Processing Render Pass", name)),
200            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
201                view: target_view,
202                resolve_target: None,
203                ops: wgpu::Operations {
204                    load: if should_clear {
205                        wgpu::LoadOp::Clear(wgpu::Color {
206                            r: 0.0,
207                            g: 0.0,
208                            b: 0.0,
209                            a: 1.0,
210                        })
211                    } else {
212                        wgpu::LoadOp::Load
213                    },
214                    store: wgpu::StoreOp::Store,
215                },
216            })],
217            depth_stencil_attachment: None,
218            timestamp_writes: None,
219            occlusion_query_set: None,
220        });
221
222    render_pass.push_debug_group(name);
223
224    render_pass.set_pipeline(post_processing_pipeline);
225    render_pass.set_bind_group(0, post_processing_bind_group, &[]);
226    render_pass.set_bind_group(1, lighting_params_bind_group, &[]);
227
228    if let Some(blend) = blend_constant {
229        render_pass.set_blend_constant(wgpu::Color {
230            r: blend,
231            g: blend,
232            b: blend,
233            a: 1.0,
234        });
235    }
236
237    render_pass.draw(0..3, 0..1);
238
239    render_pass.pop_debug_group();
240}