1use super::RenderEngine;
2use crate::tone_mapping::ToneMapPass;
3
4impl RenderEngine {
5 pub fn create_screenshot_target(&mut self) -> wgpu::TextureView {
13 let bytes_per_row = Self::aligned_bytes_per_row(self.width);
15 let buffer_size = u64::from(bytes_per_row * self.height);
16
17 let hdr_texture = self.device.create_texture(&wgpu::TextureDescriptor {
19 label: Some("screenshot HDR texture"),
20 size: wgpu::Extent3d {
21 width: self.width,
22 height: self.height,
23 depth_or_array_layers: 1,
24 },
25 mip_level_count: 1,
26 sample_count: 1,
27 dimension: wgpu::TextureDimension::D2,
28 format: wgpu::TextureFormat::Rgba16Float, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
30 view_formats: &[],
31 });
32
33 let hdr_view = hdr_texture.create_view(&wgpu::TextureViewDescriptor::default());
34
35 let texture = self.device.create_texture(&wgpu::TextureDescriptor {
37 label: Some("screenshot texture"),
38 size: wgpu::Extent3d {
39 width: self.width,
40 height: self.height,
41 depth_or_array_layers: 1,
42 },
43 mip_level_count: 1,
44 sample_count: 1,
45 dimension: wgpu::TextureDimension::D2,
46 format: self.surface_config.format,
47 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
48 view_formats: &[],
49 });
50
51 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
53 label: Some("screenshot buffer"),
54 size: buffer_size,
55 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
56 mapped_at_creation: false,
57 });
58
59 self.screenshot_hdr_texture = Some(hdr_texture);
60 self.screenshot_hdr_view = Some(hdr_view);
61 self.screenshot_texture = Some(texture);
62 self.screenshot_buffer = Some(buffer);
63
64 self.screenshot_hdr_view.as_ref().unwrap().clone()
66 }
67
68 pub fn screenshot_texture_view(&self) -> Option<wgpu::TextureView> {
70 self.screenshot_texture
71 .as_ref()
72 .map(|t| t.create_view(&wgpu::TextureViewDescriptor::default()))
73 }
74
75 pub fn apply_screenshot_tone_mapping(&mut self, encoder: &mut wgpu::CommandEncoder) {
77 let Some(hdr_view) = &self.screenshot_hdr_view else {
78 log::error!("Screenshot HDR view not initialized");
79 return;
80 };
81
82 let Some(screenshot_texture) = &self.screenshot_texture else {
83 log::error!("Screenshot texture not initialized");
84 return;
85 };
86
87 let screenshot_view =
88 screenshot_texture.create_view(&wgpu::TextureViewDescriptor::default());
89
90 if let Some(tone_map_pass) = &self.tone_map_pass {
94 let ssao_view = self.ssao_output_view.as_ref().unwrap_or(hdr_view);
96 tone_map_pass.render_to_target(
97 &self.device,
98 encoder,
99 hdr_view,
100 ssao_view,
101 &screenshot_view,
102 );
103 }
104 }
105
106 pub fn screenshot_depth_view(&self) -> &wgpu::TextureView {
108 &self.depth_view
109 }
110
111 fn aligned_bytes_per_row(width: u32) -> u32 {
113 let bytes_per_pixel = 4u32; let unaligned = width * bytes_per_pixel;
115 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
116 unaligned.div_ceil(align) * align
117 }
118
119 pub fn capture_screenshot(&mut self) -> Result<Vec<u8>, crate::screenshot::ScreenshotError> {
126 let texture = self
127 .screenshot_texture
128 .as_ref()
129 .ok_or(crate::screenshot::ScreenshotError::InvalidImageData)?;
130 let buffer = self
131 .screenshot_buffer
132 .as_ref()
133 .ok_or(crate::screenshot::ScreenshotError::InvalidImageData)?;
134
135 let bytes_per_row = Self::aligned_bytes_per_row(self.width);
136
137 let mut encoder = self
139 .device
140 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
141 label: Some("screenshot copy encoder"),
142 });
143
144 encoder.copy_texture_to_buffer(
145 wgpu::TexelCopyTextureInfo {
146 texture,
147 mip_level: 0,
148 origin: wgpu::Origin3d::ZERO,
149 aspect: wgpu::TextureAspect::All,
150 },
151 wgpu::TexelCopyBufferInfo {
152 buffer,
153 layout: wgpu::TexelCopyBufferLayout {
154 offset: 0,
155 bytes_per_row: Some(bytes_per_row),
156 rows_per_image: Some(self.height),
157 },
158 },
159 wgpu::Extent3d {
160 width: self.width,
161 height: self.height,
162 depth_or_array_layers: 1,
163 },
164 );
165
166 self.queue.submit(std::iter::once(encoder.finish()));
167
168 let buffer_slice = buffer.slice(..);
170 let (tx, rx) = std::sync::mpsc::channel();
171 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
172 tx.send(result).unwrap();
173 });
174 let _ = self.device.poll(wgpu::PollType::wait_indefinitely());
175 rx.recv()
176 .map_err(|_| crate::screenshot::ScreenshotError::BufferMapFailed)?
177 .map_err(|_| crate::screenshot::ScreenshotError::BufferMapFailed)?;
178
179 let data = buffer_slice.get_mapped_range();
181 let mut result = Vec::with_capacity((self.width * self.height * 4) as usize);
182 let row_bytes = (self.width * 4) as usize;
183
184 for row in 0..self.height {
185 let start = (row * bytes_per_row) as usize;
186 let end = start + row_bytes;
187 result.extend_from_slice(&data[start..end]);
188 }
189
190 drop(data);
191 buffer.unmap();
192
193 self.screenshot_texture = None;
195 self.screenshot_buffer = None;
196 self.screenshot_hdr_texture = None;
197 self.screenshot_hdr_view = None;
198
199 Ok(result)
200 }
201
202 pub(crate) fn init_tone_mapping(&mut self) {
204 self.tone_map_pass = Some(ToneMapPass::new(&self.device, self.surface_config.format));
205 self.create_hdr_texture();
206 self.create_normal_texture();
207 self.create_ssao_noise_texture();
208 self.init_ssao_pass();
209 }
210
211 pub(crate) fn init_ssao_pass(&mut self) {
213 let ssao_pass = crate::ssao_pass::SsaoPass::new(&self.device, self.width, self.height);
214 self.ssao_pass = Some(ssao_pass);
215 self.create_ssao_output_texture();
216 }
217
218 pub(crate) fn init_ssaa_pass(&mut self) {
222 self.ssaa_pass = Some(crate::ssaa_pass::SsaaPass::new(
223 &self.device,
224 wgpu::TextureFormat::Rgba16Float,
225 ));
226 }
227
228 #[must_use]
230 pub fn ssaa_factor(&self) -> u32 {
231 self.ssaa_factor
232 }
233
234 pub fn set_ssaa_factor(&mut self, factor: u32) {
237 let factor = factor.clamp(1, 4);
238 if factor == self.ssaa_factor {
239 return;
240 }
241
242 let _ = self.device.poll(wgpu::PollType::wait_indefinitely());
244
245 self.ssaa_factor = factor;
246
247 if let Some(ref mut ssaa_pass) = self.ssaa_pass {
249 ssaa_pass.set_ssaa_factor(&self.queue, factor);
250 }
251
252 self.recreate_ssaa_textures();
254 }
255
256 pub(crate) fn recreate_ssaa_textures(&mut self) {
258 let ssaa_width = self.width * self.ssaa_factor;
259 let ssaa_height = self.height * self.ssaa_factor;
260
261 let (depth_texture, depth_view, depth_only_view) =
263 Self::create_depth_texture(&self.device, ssaa_width, ssaa_height);
264 self.depth_texture = depth_texture;
265 self.depth_view = depth_view;
266 self.depth_only_view = depth_only_view;
267
268 self.create_hdr_texture_with_size(ssaa_width, ssaa_height);
270
271 self.create_normal_texture_with_size(ssaa_width, ssaa_height);
273
274 self.create_ssao_output_texture_with_size(ssaa_width, ssaa_height);
276
277 if let Some(ref mut ssao_pass) = self.ssao_pass {
279 ssao_pass.resize(&self.device, &self.queue, ssaa_width, ssaa_height);
280 }
281
282 if self.ssaa_factor > 1 {
284 self.create_ssaa_intermediate_texture();
285 } else {
286 self.ssaa_intermediate_texture = None;
287 self.ssaa_intermediate_view = None;
288 }
289 }
290
291 pub(crate) fn create_ssaa_intermediate_texture(&mut self) {
293 let texture = self.device.create_texture(&wgpu::TextureDescriptor {
294 label: Some("SSAA Intermediate Texture"),
295 size: wgpu::Extent3d {
296 width: self.width,
297 height: self.height,
298 depth_or_array_layers: 1,
299 },
300 mip_level_count: 1,
301 sample_count: 1,
302 dimension: wgpu::TextureDimension::D2,
303 format: wgpu::TextureFormat::Rgba16Float,
304 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
305 view_formats: &[],
306 });
307
308 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
309 self.ssaa_intermediate_texture = Some(texture);
310 self.ssaa_intermediate_view = Some(view);
311 }
312
313 pub fn ensure_depth_peel_pass(&mut self) {
315 let (render_w, render_h) = self.render_dimensions();
316
317 if self.mesh_bind_group_layout.is_none() {
318 self.create_mesh_pipeline();
319 }
320
321 if let Some(ref mut pass) = self.depth_peel_pass {
322 pass.resize(&self.device, render_w, render_h);
323 } else {
324 self.depth_peel_pass = Some(crate::depth_peel_pass::DepthPeelPass::new(
325 &self.device,
326 render_w,
327 render_h,
328 self.mesh_bind_group_layout.as_ref().unwrap(),
329 &self.slice_plane_bind_group_layout,
330 &self.matcap_bind_group_layout,
331 ));
332 }
333 }
334
335 pub fn depth_peel_pass(&self) -> Option<&crate::depth_peel_pass::DepthPeelPass> {
337 self.depth_peel_pass.as_ref()
338 }
339
340 pub fn depth_peel_pass_mut(&mut self) -> Option<&mut crate::depth_peel_pass::DepthPeelPass> {
342 self.depth_peel_pass.as_mut()
343 }
344
345 pub fn hdr_view(&self) -> Option<&wgpu::TextureView> {
347 self.hdr_view.as_ref()
348 }
349
350 pub fn normal_view(&self) -> Option<&wgpu::TextureView> {
352 self.normal_view.as_ref()
353 }
354
355 pub fn ssao_noise_view(&self) -> Option<&wgpu::TextureView> {
357 self.ssao_noise_view.as_ref()
358 }
359
360 pub fn ssao_output_view(&self) -> Option<&wgpu::TextureView> {
362 self.ssao_output_view.as_ref()
363 }
364
365 pub fn ssao_pass(&self) -> Option<&crate::ssao_pass::SsaoPass> {
367 self.ssao_pass.as_ref()
368 }
369
370 pub fn render_ssao(
373 &self,
374 encoder: &mut wgpu::CommandEncoder,
375 config: &polyscope_core::SsaoConfig,
376 ) -> bool {
377 let (ssao_pass, depth_view, normal_view, noise_view, output_view) = match (
380 &self.ssao_pass,
381 Some(&self.depth_only_view),
382 self.normal_view.as_ref(),
383 self.ssao_noise_view.as_ref(),
384 self.ssao_output_view.as_ref(),
385 ) {
386 (Some(pass), Some(depth), Some(normal), Some(noise), Some(output)) => {
387 (pass, depth, normal, noise, output)
388 }
389 _ => return false,
390 };
391
392 if !config.enabled {
393 return false;
394 }
395
396 let (render_w, render_h) = self.render_dimensions();
399 let proj = self.camera.projection_matrix();
400 let inv_proj = proj.inverse();
401 ssao_pass.update_uniforms(
402 &self.queue,
403 proj,
404 inv_proj,
405 config.radius,
406 config.bias,
407 config.intensity,
408 config.sample_count,
409 render_w as f32,
410 render_h as f32,
411 );
412
413 let ssao_bind_group =
415 ssao_pass.create_ssao_bind_group(&self.device, depth_view, normal_view, noise_view);
416 let blur_bind_group = ssao_pass.create_blur_bind_group(&self.device, depth_view);
418
419 ssao_pass.render_ssao(encoder, &ssao_bind_group);
421
422 ssao_pass.render_blur(encoder, output_view, &blur_bind_group);
424
425 true
426 }
427
428 pub fn tone_map_pass(&self) -> Option<&ToneMapPass> {
430 self.tone_map_pass.as_ref()
431 }
432
433 pub fn update_tone_mapping(
435 &self,
436 exposure: f32,
437 white_level: f32,
438 gamma: f32,
439 ssao_enabled: bool,
440 ) {
441 if let Some(tone_map) = &self.tone_map_pass {
442 tone_map.update_uniforms(&self.queue, exposure, white_level, gamma, ssao_enabled);
443 }
444 }
445
446 pub fn render_tone_mapping(
453 &self,
454 encoder: &mut wgpu::CommandEncoder,
455 output_view: &wgpu::TextureView,
456 ) {
457 if let (Some(tone_map), Some(hdr_view)) = (&self.tone_map_pass, &self.hdr_view) {
458 if self.ssaa_factor > 1 {
460 if let (Some(intermediate_view), Some(ssaa_pass)) =
461 (&self.ssaa_intermediate_view, &self.ssaa_pass)
462 {
463 ssaa_pass.render_to_target(&self.device, encoder, hdr_view, intermediate_view);
465
466 let bind_group = tone_map.create_bind_group(
471 &self.device,
472 intermediate_view,
473 intermediate_view,
474 );
475 tone_map.render(encoder, output_view, &bind_group);
476 return;
477 }
478 }
479
480 let ssao_view = self.ssao_output_view.as_ref().unwrap_or(hdr_view);
482 let bind_group = tone_map.create_bind_group(&self.device, hdr_view, ssao_view);
483 tone_map.render(encoder, output_view, &bind_group);
484 }
485 }
486}