1use super::*;
2use wgpu::util::DeviceExt;
3
4macro_rules! emit_overlay_2d {
11 ($this:ident, $render_pass:ident) => {{
12 if let Some(ref sd) = $this.overlay_shape_gpu_data {
14 if sd.vertex_count > 0 {
15 if let Some(pipeline) = &$this.resources.overlay_shape_pipeline {
16 if let Some(vbuf) = &sd.vertex_buf {
17 $render_pass.set_pipeline(pipeline);
18 $render_pass.set_vertex_buffer(0, vbuf.slice(..));
19 $render_pass.draw(0..sd.vertex_count, 0..1);
20 }
21 }
22 }
23 if !sd.tex_batches.is_empty() {
24 if let Some(pipeline) = &$this.resources.overlay_shape_tex_pipeline {
25 $render_pass.set_pipeline(pipeline);
26 for batch in &sd.tex_batches {
27 $render_pass.set_bind_group(0, &batch.bind_group, &[]);
28 $render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
29 $render_pass.draw(0..batch.vertex_count, 0..1);
30 }
31 }
32 }
33 }
34 if let Some(ref rr) = $this.overlay_rect_gpu_data {
36 if let Some(pipeline) = &$this.resources.overlay_text_pipeline {
37 $render_pass.set_pipeline(pipeline);
38 $render_pass.set_bind_group(0, &rr.bind_group, &[]);
39 $render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
40 $render_pass.draw(0..rr.vertex_count, 0..1);
41 }
42 }
43 if let Some(ref ld) = $this.label_gpu_data {
45 if let Some(pipeline) = &$this.resources.overlay_text_pipeline {
46 $render_pass.set_pipeline(pipeline);
47 $render_pass.set_bind_group(0, &ld.bind_group, &[]);
48 $render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
49 $render_pass.draw(0..ld.vertex_count, 0..1);
50 }
51 }
52 if let Some(ref sb) = $this.scalar_bar_gpu_data {
54 if let Some(pipeline) = &$this.resources.overlay_text_pipeline {
55 $render_pass.set_pipeline(pipeline);
56 $render_pass.set_bind_group(0, &sb.bind_group, &[]);
57 $render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
58 $render_pass.draw(0..sb.vertex_count, 0..1);
59 }
60 }
61 if let Some(ref rd) = $this.ruler_gpu_data {
63 if let Some(pipeline) = &$this.resources.overlay_text_pipeline {
64 $render_pass.set_pipeline(pipeline);
65 $render_pass.set_bind_group(0, &rd.bind_group, &[]);
66 $render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
67 $render_pass.draw(0..rd.vertex_count, 0..1);
68 }
69 }
70 if let Some(ref lb) = $this.loading_bar_gpu_data {
72 if let Some(pipeline) = &$this.resources.overlay_text_pipeline {
73 $render_pass.set_pipeline(pipeline);
74 $render_pass.set_bind_group(0, &lb.bind_group, &[]);
75 $render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
76 $render_pass.draw(0..lb.vertex_count, 0..1);
77 }
78 }
79 if !$this.overlay_image_gpu_data.is_empty() {
81 if let Some(pipeline) = &$this.resources.screen_image_pipeline {
82 $render_pass.set_pipeline(pipeline);
83 for gpu in &$this.overlay_image_gpu_data {
84 $render_pass.set_bind_group(0, &gpu.bind_group, &[]);
85 $render_pass.draw(0..6, 0..1);
86 }
87 }
88 }
89 }};
90}
91
92mod hdr_path;
93mod ldr_path;
94mod paint_direct;
95
96impl ViewportRenderer {
97 pub(crate) fn prepare_ldr_dyn_res(
112 &mut self,
113 encoder: &mut wgpu::CommandEncoder,
114 device: &wgpu::Device,
115 frame: &FrameData,
116 ) -> bool {
117 if self.current_render_scale >= 1.0 - 0.001 {
118 return false;
119 }
120
121 let vp_idx = frame.camera.viewport_index;
122 let w = (frame.camera.viewport_size[0] as u32).max(1);
123 let h = (frame.camera.viewport_size[1] as u32).max(1);
124 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
125 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
126
127 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
128 self.resources.ensure_dyn_res_ds_pipeline(device);
129
130 let bg_colour = frame.viewport.background_colour.unwrap_or([
131 65.0 / 255.0,
132 65.0 / 255.0,
133 65.0 / 255.0,
134 1.0,
135 ]);
136
137 {
138 let slot = &self.viewport_slots[vp_idx];
139 let dr = slot.dyn_res.as_ref().unwrap();
140 let colour_view = &dr.colour_view;
141 let depth_view = &dr.depth_view;
142 let camera_bg = &slot.camera_bind_group;
143 let grid_bg = &slot.grid_bind_group;
144
145 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
146 label: Some("ldr_dyn_res_render_pass"),
147 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
148 view: colour_view,
149 resolve_target: None,
150 ops: wgpu::Operations {
151 load: wgpu::LoadOp::Clear(wgpu::Color {
152 r: bg_colour[0] as f64,
153 g: bg_colour[1] as f64,
154 b: bg_colour[2] as f64,
155 a: bg_colour[3] as f64,
156 }),
157 store: wgpu::StoreOp::Store,
158 },
159 depth_slice: None,
160 })],
161 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
162 view: depth_view,
163 depth_ops: Some(wgpu::Operations {
164 load: wgpu::LoadOp::Clear(1.0),
165 store: wgpu::StoreOp::Discard,
166 }),
167 stencil_ops: None,
168 }),
169 timestamp_writes: None,
170 occlusion_query_set: None,
171 });
172 emit_draw_calls!(
173 &self.resources,
174 &mut render_pass,
175 frame,
176 self.instancing.use_instancing,
177 &self.instancing.batches,
178 camera_bg,
179 grid_bg,
180 &self.compute_filter_results,
181 Some(slot),
182 &self.mesh_uniforms.wireframe_bind_groups,
183 &self.mesh_uniforms.bind_groups
184 );
185 emit_scivis_draw_calls!(
186 &self.resources,
187 &mut render_pass,
188 &self.point_cloud_gpu_data,
189 &self.glyph_gpu_data,
190 &self.polyline_gpu_data,
191 &self.volume_gpu_data,
192 &self.streamtube_gpu_data,
193 camera_bg,
194 &self.tube_gpu_data,
195 &self.image_slice_gpu_data,
196 &self.tensor_glyph_gpu_data,
197 &self.ribbon_gpu_data,
198 &self.volume_surface_slice_gpu_data,
199 &self.sprite_gpu_data,
200 &self.mesh_instance_gpu_data,
201 false
202 );
203 if !self.mesh_uniforms.tvm_wireframe_draws.is_empty() {
205 if let Some(ref tvm_bg) = self.mesh_uniforms.tvm_wireframe_bg {
206 render_pass.set_bind_group(0, camera_bg, &[]);
207 for mesh_id in &self.mesh_uniforms.tvm_wireframe_draws {
208 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
209 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
210 render_pass.set_bind_group(
211 2,
212 &self.resources.deform.dummy_bind_group,
213 &[],
214 );
215 render_pass.set_bind_group(1, tvm_bg, &[]);
216 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
217 render_pass.set_index_buffer(
218 mesh.edge_index_buffer.slice(..),
219 wgpu::IndexFormat::Uint32,
220 );
221 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
222 }
223 }
224 }
225 }
226 if !self.implicit_gpu_data.is_empty() {
228 if let Some(ref dual) = self.resources.implicit_pipeline {
229 render_pass.set_pipeline(dual.for_format(false));
230 render_pass.set_bind_group(0, camera_bg, &[]);
231 for gpu in &self.implicit_gpu_data {
232 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
233 render_pass.draw(0..6, 0..1);
234 }
235 }
236 }
237 if !self.mc_gpu_data.is_empty() {
239 if let Some(ref dual) = self.resources.mc_surface_pipeline {
240 render_pass.set_pipeline(dual.for_format(false));
241 render_pass.set_bind_group(0, camera_bg, &[]);
242 for mc in &self.mc_gpu_data {
243 let vol = &self.resources.mc_volumes[mc.volume_idx];
244 render_pass.set_bind_group(1, &mc.render_bg, &[]);
245 for slab in &vol.slabs {
246 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
247 render_pass.draw_indirect(&slab.indirect_buf, 0);
248 }
249 }
250 }
251 }
252 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
254 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
256 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
257 &self.resources.sub_highlight_fill_ldr_pipeline,
258 &self.resources.sub_highlight_edge_ldr_pipeline,
259 &self.resources.sub_highlight_sprite_ldr_pipeline,
260 ) {
261 if sub_hl.fill_vertex_count > 0 {
262 render_pass.set_pipeline(fill_pl);
263 render_pass.set_bind_group(0, camera_bg, &[]);
264 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
265 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
266 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
267 }
268 if sub_hl.edge_segment_count > 0 {
269 render_pass.set_pipeline(edge_pl);
270 render_pass.set_bind_group(0, camera_bg, &[]);
271 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
272 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
273 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
274 }
275 if sub_hl.sprite_point_count > 0 {
276 render_pass.set_pipeline(sprite_pl);
277 render_pass.set_bind_group(0, camera_bg, &[]);
278 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
279 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
280 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
281 }
282 }
283 }
284 if !self.screen_image_gpu_data.is_empty() {
286 if let Some(pipeline) = &self.resources.screen_image_pipeline {
287 render_pass.set_pipeline(pipeline);
288 for gpu in &self.screen_image_gpu_data {
289 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
290 render_pass.draw(0..6, 0..1);
291 }
292 }
293 }
294 emit_overlay_2d!(self, render_pass);
295 }
296
297 true
298 }
299
300 pub(crate) fn paint_dyn_res_blit<'rp>(
306 &self,
307 render_pass: &mut wgpu::RenderPass<'rp>,
308 frame: &FrameData,
309 ) {
310 let vp_idx = frame.camera.viewport_index;
311 if let Some(dr) = self
312 .viewport_slots
313 .get(vp_idx)
314 .and_then(|s| s.dyn_res.as_ref())
315 {
316 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
317 render_pass.set_pipeline(pipeline);
318 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
319 render_pass.draw(0..3, 0..1);
320 }
321 }
322 }
323
324 pub(crate) fn prepare_hdr_callback(
341 &mut self,
342 device: &wgpu::Device,
343 queue: &wgpu::Queue,
344 frame: &FrameData,
345 ) -> wgpu::CommandBuffer {
346 self.prepare(device, queue, frame);
347
348 let vp_idx = frame.camera.viewport_index;
349 let ppp = frame.camera.pixels_per_point;
355 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
356 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
357
358 self.resources.ensure_dyn_res_pipeline(device);
360 self.resources.ensure_dyn_res_ds_pipeline(device);
361
362 self.ensure_viewport_slot(device, vp_idx);
364 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
365 None => true,
366 Some(t) => t.size != [w, h],
367 };
368 if needs_create {
369 let target = self.resources.create_hdr_callback_target(device, [w, h]);
370 self.viewport_slots[vp_idx].hdr_callback = Some(target);
371 }
372
373 let output_view = self.viewport_slots[vp_idx]
377 .hdr_callback
378 .as_ref()
379 .unwrap()
380 .texture
381 .create_view(&wgpu::TextureViewDescriptor::default());
382
383 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
384 }
385
386 pub(crate) fn prepare_hdr_callback_viewport(
402 &mut self,
403 device: &wgpu::Device,
404 queue: &wgpu::Queue,
405 id: ViewportId,
406 frame: &FrameData,
407 ) -> wgpu::CommandBuffer {
408 let vp_idx = id.0;
409 let ppp = frame.camera.pixels_per_point;
410 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
411 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
412
413 self.resources.ensure_dyn_res_pipeline(device);
414 self.resources.ensure_dyn_res_ds_pipeline(device);
415
416 self.ensure_viewport_slot(device, vp_idx);
417 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
418 None => true,
419 Some(t) => t.size != [w, h],
420 };
421 if needs_create {
422 let target = self.resources.create_hdr_callback_target(device, [w, h]);
423 self.viewport_slots[vp_idx].hdr_callback = Some(target);
424 }
425
426 let output_view = self.viewport_slots[vp_idx]
427 .hdr_callback
428 .as_ref()
429 .unwrap()
430 .texture
431 .create_view(&wgpu::TextureViewDescriptor::default());
432
433 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
434 }
435
436 pub(crate) fn paint_hdr_blit<'rp>(
442 &self,
443 render_pass: &mut wgpu::RenderPass<'rp>,
444 frame: &FrameData,
445 ) {
446 let vp_idx = frame.camera.viewport_index;
447 if let Some(hc) = self
448 .viewport_slots
449 .get(vp_idx)
450 .and_then(|s| s.hdr_callback.as_ref())
451 {
452 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
453 render_pass.set_pipeline(pipeline);
454 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
455 render_pass.draw(0..3, 0..1);
456 }
457 }
458 if frame.effects.show_shadow_atlas {
460 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
461 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
462 render_pass.draw(0..6, 0..1);
463 }
464 }
465
466 pub(crate) fn paint_hdr_blit_no_ds<'rp>(
470 &self,
471 render_pass: &mut wgpu::RenderPass<'rp>,
472 frame: &FrameData,
473 ) {
474 let vp_idx = frame.camera.viewport_index;
475 if let Some(hc) = self
476 .viewport_slots
477 .get(vp_idx)
478 .and_then(|s| s.hdr_callback.as_ref())
479 {
480 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
481 render_pass.set_pipeline(pipeline);
482 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
483 render_pass.draw(0..3, 0..1);
484 }
485 }
486 }
487
488 pub(crate) fn prepare_callback(
502 &mut self,
503 device: &wgpu::Device,
504 queue: &wgpu::Queue,
505 frame: &FrameData,
506 ) -> Vec<wgpu::CommandBuffer> {
507 if frame.effects.post_process.enabled {
508 let cb = self.prepare_hdr_callback(device, queue, frame);
509 vec![cb]
510 } else {
511 self.prepare(device, queue, frame);
512 if self.current_render_scale < 1.0 - 0.001 {
513 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
514 label: Some("ldr_dyn_res_callback_encoder"),
515 });
516 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
517 vec![encoder.finish()]
518 } else {
519 Vec::new()
520 }
521 }
522 }
523
524 pub(crate) fn paint_callback<'rp>(
530 &self,
531 render_pass: &mut wgpu::RenderPass<'rp>,
532 frame: &FrameData,
533 ) {
534 let vp_idx = frame.camera.viewport_index;
535 if frame.effects.post_process.enabled {
536 if self
537 .viewport_slots
538 .get(vp_idx)
539 .and_then(|s| s.hdr_callback.as_ref())
540 .is_some()
541 {
542 self.paint_hdr_blit(render_pass, frame);
543 return;
544 }
545 }
546 if self.current_render_scale < 1.0 - 0.001
547 && self
548 .viewport_slots
549 .get(vp_idx)
550 .and_then(|s| s.dyn_res.as_ref())
551 .is_some()
552 {
553 self.paint_dyn_res_blit(render_pass, frame);
554 } else {
555 self.paint_to(render_pass, frame);
556 }
557 }
558
559 pub(crate) fn render_viewport(
573 &mut self,
574 device: &wgpu::Device,
575 queue: &wgpu::Queue,
576 output_view: &wgpu::TextureView,
577 id: ViewportId,
578 frame: &FrameData,
579 ) -> wgpu::CommandBuffer {
580 self.render_frame_internal(device, queue, output_view, id.0, frame)
581 }
582
583 pub(crate) fn render(
591 &mut self,
592 device: &wgpu::Device,
593 queue: &wgpu::Queue,
594 output_view: &wgpu::TextureView,
595 frame: &FrameData,
596 ) -> wgpu::CommandBuffer {
597 self.prepare(device, queue, frame);
599 self.render_frame_internal(
600 device,
601 queue,
602 output_view,
603 frame.camera.viewport_index,
604 frame,
605 )
606 }
607
608 fn render_frame_internal(
613 &mut self,
614 device: &wgpu::Device,
615 queue: &wgpu::Queue,
616 output_view: &wgpu::TextureView,
617 vp_idx: usize,
618 frame: &FrameData,
619 ) -> wgpu::CommandBuffer {
620 let paint_start = std::time::Instant::now();
621 let scene_items_owned: Vec<SceneRenderItem> = {
625 let surfaces = match &frame.scene.surfaces {
626 SurfaceSubmission::Flat(items) => items.as_ref(),
627 };
628 let extra = frame
629 .scene
630 .volume_meshes
631 .iter()
632 .filter(|item| item.transparency.is_none())
633 .map(|item| item.to_render_item());
634 surfaces.iter().cloned().chain(extra).collect()
635 };
636 let scene_items: &[SceneRenderItem] = &scene_items_owned;
637
638 let bg_colour = frame.viewport.background_colour.unwrap_or([
639 65.0 / 255.0,
640 65.0 / 255.0,
641 65.0 / 255.0,
642 1.0,
643 ]);
644 let ppp = frame.camera.pixels_per_point;
645 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
646 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
647
648 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
650 self.ensure_viewport_hdr(
651 device,
652 queue,
653 vp_idx,
654 w.max(1),
655 h.max(1),
656 ssaa_factor,
657 self.current_render_scale,
658 );
659
660 if self.ts_query_set.is_none()
662 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
663 {
664 let ts_count = 2 * super::GPU_TS_SLOTS;
666 let ts_bytes = ts_count as u64 * 8;
667 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
668 label: Some("ts_query_set"),
669 ty: wgpu::QueryType::Timestamp,
670 count: ts_count,
671 }));
672 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
673 label: Some("ts_resolve_buf"),
674 size: ts_bytes,
675 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
676 mapped_at_creation: false,
677 }));
678 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
679 label: Some("ts_staging_buf"),
680 size: ts_bytes,
681 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
682 mapped_at_creation: false,
683 }));
684 self.ts_period = queue.get_timestamp_period();
685 }
686
687 let cmd_buf = if !frame.effects.post_process.enabled {
688 self.render_frame_ldr(device, queue, output_view, vp_idx, frame, bg_colour, w, h)
689 } else {
690 self.render_frame_hdr(
691 device,
692 queue,
693 output_view,
694 vp_idx,
695 frame,
696 scene_items,
697 bg_colour,
698 w,
699 h,
700 ssaa_factor,
701 )
702 };
703 self.last_stats.cpu_paint_ms = paint_start.elapsed().as_secs_f32() * 1000.0;
706 cmd_buf
707 }
708
709 pub fn render_offscreen(
723 &mut self,
724 device: &wgpu::Device,
725 queue: &wgpu::Queue,
726 frame: &FrameData,
727 width: u32,
728 height: u32,
729 ) -> Vec<u8> {
730 let target_format = self.resources.target_format;
732 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
733 label: Some("offscreen_target"),
734 size: wgpu::Extent3d {
735 width: width.max(1),
736 height: height.max(1),
737 depth_or_array_layers: 1,
738 },
739 mip_level_count: 1,
740 sample_count: 1,
741 dimension: wgpu::TextureDimension::D2,
742 format: target_format,
743 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
744 view_formats: &[],
745 });
746
747 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
749
750 let cmd_buf = self.render(device, queue, &output_view, frame);
758 queue.submit(std::iter::once(cmd_buf));
759
760 let bytes_per_pixel = 4u32;
762 let unpadded_row = width * bytes_per_pixel;
763 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
764 let padded_row = (unpadded_row + align - 1) & !(align - 1);
765 let buffer_size = (padded_row * height.max(1)) as u64;
766
767 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
768 label: Some("offscreen_staging"),
769 size: buffer_size,
770 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
771 mapped_at_creation: false,
772 });
773
774 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
775 label: Some("offscreen_copy_encoder"),
776 });
777 copy_encoder.copy_texture_to_buffer(
778 wgpu::TexelCopyTextureInfo {
779 texture: &offscreen_texture,
780 mip_level: 0,
781 origin: wgpu::Origin3d::ZERO,
782 aspect: wgpu::TextureAspect::All,
783 },
784 wgpu::TexelCopyBufferInfo {
785 buffer: &staging_buf,
786 layout: wgpu::TexelCopyBufferLayout {
787 offset: 0,
788 bytes_per_row: Some(padded_row),
789 rows_per_image: Some(height.max(1)),
790 },
791 },
792 wgpu::Extent3d {
793 width: width.max(1),
794 height: height.max(1),
795 depth_or_array_layers: 1,
796 },
797 );
798 queue.submit(std::iter::once(copy_encoder.finish()));
799
800 let (tx, rx) = std::sync::mpsc::channel();
802 staging_buf
803 .slice(..)
804 .map_async(wgpu::MapMode::Read, move |result| {
805 let _ = tx.send(result);
806 });
807 device
808 .poll(wgpu::PollType::Wait {
809 submission_index: None,
810 timeout: Some(std::time::Duration::from_secs(5)),
811 })
812 .unwrap();
813 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
814
815 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
816 {
817 let mapped = staging_buf.slice(..).get_mapped_range();
818 let data: &[u8] = &mapped;
819 if padded_row == unpadded_row {
820 pixels.extend_from_slice(data);
822 } else {
823 for row in 0..height as usize {
825 let start = row * padded_row as usize;
826 let end = start + unpadded_row as usize;
827 pixels.extend_from_slice(&data[start..end]);
828 }
829 }
830 }
831 staging_buf.unmap();
832
833 let is_bgra = matches!(
835 target_format,
836 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
837 );
838 if is_bgra {
839 for pixel in pixels.chunks_exact_mut(4) {
840 pixel.swap(0, 2); }
842 }
843
844 pixels
845 }
846
847 fn ensure_backdrop_blur_state(&mut self, device: &wgpu::Device, w: u32, h: u32) {
853 let need_recreate = match &self.backdrop_blur_state {
854 Some(s) => s.size != [w, h] || s.format != self.resources.target_format,
855 None => true,
856 };
857 if !need_recreate {
858 return;
859 }
860
861 let format = self.resources.target_format;
862 let blur_w = (w / 2).max(1);
863 let blur_h = (h / 2).max(1);
864
865 let intermediate_texture = device.create_texture(&wgpu::TextureDescriptor {
866 label: Some("backdrop_intermediate"),
867 size: wgpu::Extent3d {
868 width: w,
869 height: h,
870 depth_or_array_layers: 1,
871 },
872 mip_level_count: 1,
873 sample_count: 1,
874 dimension: wgpu::TextureDimension::D2,
875 format,
876 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
877 view_formats: &[],
878 });
879 let intermediate_view = intermediate_texture.create_view(&Default::default());
880
881 let make_blur_tex = |label: &str| {
882 let t = device.create_texture(&wgpu::TextureDescriptor {
883 label: Some(label),
884 size: wgpu::Extent3d {
885 width: blur_w,
886 height: blur_h,
887 depth_or_array_layers: 1,
888 },
889 mip_level_count: 1,
890 sample_count: 1,
891 dimension: wgpu::TextureDimension::D2,
892 format,
893 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
894 | wgpu::TextureUsages::TEXTURE_BINDING,
895 view_formats: &[],
896 });
897 let v = t.create_view(&Default::default());
898 (t, v)
899 };
900 let (blur_a_texture, blur_a_view) = make_blur_tex("backdrop_blur_a");
901 let (blur_b_texture, blur_b_view) = make_blur_tex("backdrop_blur_b");
902
903 self.backdrop_blur_state = Some(crate::resources::BackdropBlurState {
904 intermediate_texture,
905 intermediate_view,
906 blur_a_texture,
907 blur_a_view,
908 blur_b_texture,
909 blur_b_view,
910 size: [w, h],
911 format,
912 });
913 }
914
915 fn run_backdrop_blur(
919 &self,
920 encoder: &mut wgpu::CommandEncoder,
921 device: &wgpu::Device,
922 _queue: &wgpu::Queue,
923 source_view: &wgpu::TextureView,
924 spread: f32,
925 ) -> wgpu::BindGroup {
926 let bs = self.backdrop_blur_state.as_ref().unwrap();
927 let blur_bgl = self.resources.backdrop_blur_bgl.as_ref().unwrap();
928 let blur_sampler = self.resources.backdrop_blur_sampler.as_ref().unwrap();
929 let blur_pipeline = self.resources.backdrop_blur_pipeline.as_ref().unwrap();
930 let blit_pipeline = self.resources.dyn_res_upscale_pipeline.as_ref().unwrap();
932 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
933 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
934
935 let downsample_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
937 label: Some("backdrop_downsample_bg"),
938 layout: blit_bgl,
939 entries: &[
940 wgpu::BindGroupEntry {
941 binding: 0,
942 resource: wgpu::BindingResource::TextureView(source_view),
943 },
944 wgpu::BindGroupEntry {
945 binding: 1,
946 resource: wgpu::BindingResource::Sampler(blit_sampler),
947 },
948 ],
949 });
950 {
951 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
952 label: Some("backdrop_downsample"),
953 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
954 view: &bs.blur_a_view,
955 resolve_target: None,
956 ops: wgpu::Operations {
957 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
958 store: wgpu::StoreOp::Store,
959 },
960 depth_slice: None,
961 })],
962 depth_stencil_attachment: None,
963 timestamp_writes: None,
964 occlusion_query_set: None,
965 });
966 pass.set_pipeline(blit_pipeline);
967 pass.set_bind_group(0, &downsample_bg, &[]);
968 pass.draw(0..3, 0..1);
969 }
970
971 let effective_spread = (spread / 2.0).max(1.0);
973
974 let h_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
976 label: Some("blur_h_uniform"),
977 contents: bytemuck::cast_slice(&[1u32, effective_spread.to_bits(), 0u32, 0u32]),
978 usage: wgpu::BufferUsages::UNIFORM,
979 });
980 let h_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
981 label: Some("blur_h_bg"),
982 layout: blur_bgl,
983 entries: &[
984 wgpu::BindGroupEntry {
985 binding: 0,
986 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
987 },
988 wgpu::BindGroupEntry {
989 binding: 1,
990 resource: wgpu::BindingResource::Sampler(blur_sampler),
991 },
992 wgpu::BindGroupEntry {
993 binding: 2,
994 resource: h_uniform.as_entire_binding(),
995 },
996 ],
997 });
998 {
999 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1000 label: Some("backdrop_blur_h"),
1001 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1002 view: &bs.blur_b_view,
1003 resolve_target: None,
1004 ops: wgpu::Operations {
1005 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1006 store: wgpu::StoreOp::Store,
1007 },
1008 depth_slice: None,
1009 })],
1010 depth_stencil_attachment: None,
1011 timestamp_writes: None,
1012 occlusion_query_set: None,
1013 });
1014 pass.set_pipeline(blur_pipeline);
1015 pass.set_bind_group(0, &h_bg, &[]);
1016 pass.draw(0..3, 0..1);
1017 }
1018
1019 let v_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
1021 label: Some("blur_v_uniform"),
1022 contents: bytemuck::cast_slice(&[0u32, effective_spread.to_bits(), 0u32, 0u32]),
1023 usage: wgpu::BufferUsages::UNIFORM,
1024 });
1025 let v_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1026 label: Some("blur_v_bg"),
1027 layout: blur_bgl,
1028 entries: &[
1029 wgpu::BindGroupEntry {
1030 binding: 0,
1031 resource: wgpu::BindingResource::TextureView(&bs.blur_b_view),
1032 },
1033 wgpu::BindGroupEntry {
1034 binding: 1,
1035 resource: wgpu::BindingResource::Sampler(blur_sampler),
1036 },
1037 wgpu::BindGroupEntry {
1038 binding: 2,
1039 resource: v_uniform.as_entire_binding(),
1040 },
1041 ],
1042 });
1043 {
1044 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1045 label: Some("backdrop_blur_v"),
1046 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1047 view: &bs.blur_a_view,
1048 resolve_target: None,
1049 ops: wgpu::Operations {
1050 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1051 store: wgpu::StoreOp::Store,
1052 },
1053 depth_slice: None,
1054 })],
1055 depth_stencil_attachment: None,
1056 timestamp_writes: None,
1057 occlusion_query_set: None,
1058 });
1059 pass.set_pipeline(blur_pipeline);
1060 pass.set_bind_group(0, &v_bg, &[]);
1061 pass.draw(0..3, 0..1);
1062 }
1063
1064 let tex_bgl = self.resources.overlay_shape_tex_bgl.as_ref().unwrap();
1068 let tex_sampler = self.resources.overlay_shape_tex_sampler.as_ref().unwrap();
1069 device.create_bind_group(&wgpu::BindGroupDescriptor {
1070 label: Some("backdrop_blur_overlay_bg"),
1071 layout: tex_bgl,
1072 entries: &[
1073 wgpu::BindGroupEntry {
1074 binding: 0,
1075 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
1076 },
1077 wgpu::BindGroupEntry {
1078 binding: 1,
1079 resource: wgpu::BindingResource::Sampler(tex_sampler),
1080 },
1081 ],
1082 })
1083 }
1084
1085 fn has_backdrop_blur_shapes(&self) -> bool {
1087 self.overlay_shape_gpu_data
1088 .as_ref()
1089 .map_or(false, |sd| sd.blur_vertex_count > 0)
1090 }
1091
1092 fn draw_blur_shapes<'rp>(
1094 &'rp self,
1095 render_pass: &mut wgpu::RenderPass<'rp>,
1096 blur_bind_group: &'rp wgpu::BindGroup,
1097 ) {
1098 if let Some(ref sd) = self.overlay_shape_gpu_data {
1099 if sd.blur_vertex_count > 0 {
1100 if let (Some(pipeline), Some(vbuf)) = (
1101 &self.resources.overlay_shape_tex_pipeline,
1102 &sd.blur_vertex_buf,
1103 ) {
1104 render_pass.set_pipeline(pipeline);
1105 render_pass.set_bind_group(0, blur_bind_group, &[]);
1106 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1107 render_pass.draw(0..sd.blur_vertex_count, 0..1);
1108 }
1109 }
1110 }
1111 }
1112}