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 = 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}