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 &self.prepared_surfaces
185 );
186 emit_scivis_draw_calls!(
187 &self.resources,
188 &mut render_pass,
189 &self.point_cloud_gpu_data,
190 &self.glyph_gpu_data,
191 &self.polyline_gpu_data,
192 &self.volume_gpu_data,
193 &self.streamtube_gpu_data,
194 camera_bg,
195 &self.tube_gpu_data,
196 &self.image_slice_gpu_data,
197 &self.tensor_glyph_gpu_data,
198 &self.ribbon_gpu_data,
199 &self.volume_surface_slice_gpu_data,
200 &self.sprite_gpu_data,
201 &self.mesh_instance_gpu_data,
202 false
203 );
204 if !self.mesh_uniforms.tvm_wireframe_draws.is_empty() {
206 if let Some(ref tvm_bg) = self.mesh_uniforms.tvm_wireframe_bg {
207 render_pass.set_bind_group(0, camera_bg, &[]);
208 for mesh_id in &self.mesh_uniforms.tvm_wireframe_draws {
209 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
210 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
211 render_pass.set_bind_group(
212 2,
213 &self.resources.deform.dummy_bind_group,
214 &[],
215 );
216 render_pass.set_bind_group(1, tvm_bg, &[]);
217 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
218 render_pass.set_index_buffer(
219 mesh.edge_index_buffer.slice(..),
220 wgpu::IndexFormat::Uint32,
221 );
222 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
223 }
224 }
225 }
226 }
227 if !self.implicit_gpu_data.is_empty() {
229 if let Some(ref dual) = self.resources.implicit.pipeline {
230 render_pass.set_pipeline(dual.for_format(false));
231 render_pass.set_bind_group(0, camera_bg, &[]);
232 for gpu in &self.implicit_gpu_data {
233 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
234 render_pass.draw(0..6, 0..1);
235 }
236 }
237 }
238 if !self.mc_gpu_data.is_empty() {
240 if let Some(ref dual) = self.resources.mc.surface_pipeline {
241 render_pass.set_pipeline(dual.for_format(false));
242 render_pass.set_bind_group(0, camera_bg, &[]);
243 for mc in &self.mc_gpu_data {
244 let vol = &self.resources.mc.volumes[mc.volume_idx];
245 render_pass.set_bind_group(1, &mc.render_bg, &[]);
246 for slab in &vol.slabs {
247 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
248 render_pass.draw_indirect(&slab.indirect_buf, 0);
249 }
250 }
251 }
252 }
253 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
255 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
257 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
258 &self.resources.sub_highlight.fill_ldr_pipeline,
259 &self.resources.sub_highlight.edge_ldr_pipeline,
260 &self.resources.sub_highlight.sprite_ldr_pipeline,
261 ) {
262 if sub_hl.fill_vertex_count > 0 {
263 render_pass.set_pipeline(fill_pl);
264 render_pass.set_bind_group(0, camera_bg, &[]);
265 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
266 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
267 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
268 }
269 if sub_hl.edge_segment_count > 0 {
270 render_pass.set_pipeline(edge_pl);
271 render_pass.set_bind_group(0, camera_bg, &[]);
272 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
273 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
274 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
275 }
276 if sub_hl.sprite_point_count > 0 {
277 render_pass.set_pipeline(sprite_pl);
278 render_pass.set_bind_group(0, camera_bg, &[]);
279 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
280 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
281 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
282 }
283 }
284 }
285 if !self.screen_image_gpu_data.is_empty() {
287 if let Some(pipeline) = &self.resources.screen_image.pipeline {
288 render_pass.set_pipeline(pipeline);
289 for gpu in &self.screen_image_gpu_data {
290 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
291 render_pass.draw(0..6, 0..1);
292 }
293 }
294 }
295 emit_overlay_2d!(self, render_pass);
296 }
297
298 true
299 }
300
301 pub(crate) fn paint_dyn_res_blit<'rp>(
307 &self,
308 render_pass: &mut wgpu::RenderPass<'rp>,
309 frame: &FrameData,
310 ) {
311 let vp_idx = frame.camera.viewport_index;
312 if let Some(dr) = self
313 .viewport_slots
314 .get(vp_idx)
315 .and_then(|s| s.dyn_res.as_ref())
316 {
317 if let Some(pipeline) = &self.resources.post.dyn_res_upscale_ds_pipeline {
318 render_pass.set_pipeline(pipeline);
319 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
320 render_pass.draw(0..3, 0..1);
321 }
322 }
323 }
324
325 pub(crate) fn prepare_hdr_callback(
342 &mut self,
343 device: &wgpu::Device,
344 queue: &wgpu::Queue,
345 frame: &FrameData,
346 ) -> wgpu::CommandBuffer {
347 self.prepare(device, queue, frame);
348
349 let vp_idx = frame.camera.viewport_index;
350 let ppp = frame.camera.pixels_per_point;
356 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
357 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
358
359 self.resources.ensure_dyn_res_pipeline(device);
361 self.resources.ensure_dyn_res_ds_pipeline(device);
362
363 self.ensure_viewport_slot(device, vp_idx);
365 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
366 None => true,
367 Some(t) => t.size != [w, h],
368 };
369 if needs_create {
370 let target = self.resources.create_hdr_callback_target(device, [w, h]);
371 self.viewport_slots[vp_idx].hdr_callback = Some(target);
372 }
373
374 let output_view = self.viewport_slots[vp_idx]
378 .hdr_callback
379 .as_ref()
380 .unwrap()
381 .texture
382 .create_view(&wgpu::TextureViewDescriptor::default());
383
384 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
385 }
386
387 pub(crate) fn prepare_hdr_callback_viewport(
403 &mut self,
404 device: &wgpu::Device,
405 queue: &wgpu::Queue,
406 id: ViewportId,
407 frame: &FrameData,
408 ) -> wgpu::CommandBuffer {
409 let vp_idx = id.0;
410 let ppp = frame.camera.pixels_per_point;
411 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
412 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
413
414 self.resources.ensure_dyn_res_pipeline(device);
415 self.resources.ensure_dyn_res_ds_pipeline(device);
416
417 self.ensure_viewport_slot(device, vp_idx);
418 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
419 None => true,
420 Some(t) => t.size != [w, h],
421 };
422 if needs_create {
423 let target = self.resources.create_hdr_callback_target(device, [w, h]);
424 self.viewport_slots[vp_idx].hdr_callback = Some(target);
425 }
426
427 let output_view = self.viewport_slots[vp_idx]
428 .hdr_callback
429 .as_ref()
430 .unwrap()
431 .texture
432 .create_view(&wgpu::TextureViewDescriptor::default());
433
434 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
435 }
436
437 pub(crate) fn paint_hdr_blit<'rp>(
443 &self,
444 render_pass: &mut wgpu::RenderPass<'rp>,
445 frame: &FrameData,
446 ) {
447 let vp_idx = frame.camera.viewport_index;
448 if let Some(hc) = self
449 .viewport_slots
450 .get(vp_idx)
451 .and_then(|s| s.hdr_callback.as_ref())
452 {
453 if let Some(pipeline) = &self.resources.post.dyn_res_upscale_ds_pipeline {
454 render_pass.set_pipeline(pipeline);
455 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
456 render_pass.draw(0..3, 0..1);
457 }
458 }
459 if frame.effects.show_shadow_atlas {
461 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
462 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
463 render_pass.draw(0..6, 0..1);
464 }
465 }
466
467 pub(crate) fn paint_hdr_blit_no_ds<'rp>(
471 &self,
472 render_pass: &mut wgpu::RenderPass<'rp>,
473 frame: &FrameData,
474 ) {
475 let vp_idx = frame.camera.viewport_index;
476 if let Some(hc) = self
477 .viewport_slots
478 .get(vp_idx)
479 .and_then(|s| s.hdr_callback.as_ref())
480 {
481 if let Some(pipeline) = &self.resources.post.dyn_res_upscale_pipeline {
482 render_pass.set_pipeline(pipeline);
483 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
484 render_pass.draw(0..3, 0..1);
485 }
486 }
487 }
488
489 pub(crate) fn prepare_callback(
503 &mut self,
504 device: &wgpu::Device,
505 queue: &wgpu::Queue,
506 frame: &FrameData,
507 ) -> Vec<wgpu::CommandBuffer> {
508 if frame.effects.post_process.enabled {
509 let cb = self.prepare_hdr_callback(device, queue, frame);
510 vec![cb]
511 } else {
512 self.prepare(device, queue, frame);
513 if self.current_render_scale < 1.0 - 0.001 {
514 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
515 label: Some("ldr_dyn_res_callback_encoder"),
516 });
517 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
518 vec![encoder.finish()]
519 } else {
520 Vec::new()
521 }
522 }
523 }
524
525 pub(crate) fn paint_callback<'rp>(
531 &self,
532 render_pass: &mut wgpu::RenderPass<'rp>,
533 frame: &FrameData,
534 ) {
535 let vp_idx = frame.camera.viewport_index;
536 if frame.effects.post_process.enabled {
537 if self
538 .viewport_slots
539 .get(vp_idx)
540 .and_then(|s| s.hdr_callback.as_ref())
541 .is_some()
542 {
543 self.paint_hdr_blit(render_pass, frame);
544 return;
545 }
546 }
547 if self.current_render_scale < 1.0 - 0.001
548 && self
549 .viewport_slots
550 .get(vp_idx)
551 .and_then(|s| s.dyn_res.as_ref())
552 .is_some()
553 {
554 self.paint_dyn_res_blit(render_pass, frame);
555 } else {
556 self.paint_to(render_pass, frame);
557 }
558 }
559
560 pub(crate) fn render_viewport(
574 &mut self,
575 device: &wgpu::Device,
576 queue: &wgpu::Queue,
577 output_view: &wgpu::TextureView,
578 id: ViewportId,
579 frame: &FrameData,
580 ) -> wgpu::CommandBuffer {
581 self.render_frame_internal(device, queue, output_view, id.0, frame)
582 }
583
584 pub(crate) fn render(
592 &mut self,
593 device: &wgpu::Device,
594 queue: &wgpu::Queue,
595 output_view: &wgpu::TextureView,
596 frame: &FrameData,
597 ) -> wgpu::CommandBuffer {
598 self.prepare(device, queue, frame);
600 self.render_frame_internal(
601 device,
602 queue,
603 output_view,
604 frame.camera.viewport_index,
605 frame,
606 )
607 }
608
609 fn render_frame_internal(
614 &mut self,
615 device: &wgpu::Device,
616 queue: &wgpu::Queue,
617 output_view: &wgpu::TextureView,
618 vp_idx: usize,
619 frame: &FrameData,
620 ) -> wgpu::CommandBuffer {
621 let paint_start = std::time::Instant::now();
622 let scene_items_owned: Vec<SceneRenderItem> = {
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 self.prepared_surfaces
635 .iter()
636 .cloned()
637 .chain(extra)
638 .collect()
639 };
640 let scene_items: &[SceneRenderItem] = &scene_items_owned;
641
642 let bg_colour = frame.viewport.background_colour.unwrap_or([
643 65.0 / 255.0,
644 65.0 / 255.0,
645 65.0 / 255.0,
646 1.0,
647 ]);
648 let ppp = frame.camera.pixels_per_point;
649 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
650 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
651
652 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
654 self.ensure_viewport_hdr(
655 device,
656 queue,
657 vp_idx,
658 w.max(1),
659 h.max(1),
660 ssaa_factor,
661 self.current_render_scale,
662 );
663
664 if self.ts_query_set.is_none()
666 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
667 {
668 let ts_count = 2 * super::GPU_TS_SLOTS;
670 let ts_bytes = ts_count as u64 * 8;
671 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
672 label: Some("ts_query_set"),
673 ty: wgpu::QueryType::Timestamp,
674 count: ts_count,
675 }));
676 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
677 label: Some("ts_resolve_buf"),
678 size: ts_bytes,
679 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
680 mapped_at_creation: false,
681 }));
682 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
683 label: Some("ts_staging_buf"),
684 size: ts_bytes,
685 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
686 mapped_at_creation: false,
687 }));
688 self.ts_period = queue.get_timestamp_period();
689 self.ts_can_fill_gaps = device
693 .features()
694 .contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS);
695 }
696
697 let cmd_buf = if !frame.effects.post_process.enabled {
698 self.render_frame_ldr(device, queue, output_view, vp_idx, frame, bg_colour, w, h)
699 } else {
700 self.render_frame_hdr(
701 device,
702 queue,
703 output_view,
704 vp_idx,
705 frame,
706 scene_items,
707 bg_colour,
708 w,
709 h,
710 ssaa_factor,
711 )
712 };
713 self.last_stats.cpu_paint_ms = paint_start.elapsed().as_secs_f32() * 1000.0;
716 cmd_buf
717 }
718
719 pub fn render_to_texture(
729 &mut self,
730 device: &wgpu::Device,
731 queue: &wgpu::Queue,
732 output_view: &wgpu::TextureView,
733 frame: &FrameData,
734 ) {
735 let cmd_buf = self.render(device, queue, output_view, frame);
736 queue.submit(std::iter::once(cmd_buf));
737 }
738
739 pub fn render_offscreen(
753 &mut self,
754 device: &wgpu::Device,
755 queue: &wgpu::Queue,
756 frame: &FrameData,
757 width: u32,
758 height: u32,
759 ) -> Vec<u8> {
760 let target_format = self.resources.target_format;
762 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
763 label: Some("offscreen_target"),
764 size: wgpu::Extent3d {
765 width: width.max(1),
766 height: height.max(1),
767 depth_or_array_layers: 1,
768 },
769 mip_level_count: 1,
770 sample_count: 1,
771 dimension: wgpu::TextureDimension::D2,
772 format: target_format,
773 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
774 view_formats: &[],
775 });
776
777 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
779
780 let cmd_buf = self.render(device, queue, &output_view, frame);
788 queue.submit(std::iter::once(cmd_buf));
789
790 let bytes_per_pixel = 4u32;
792 let unpadded_row = width * bytes_per_pixel;
793 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
794 let padded_row = (unpadded_row + align - 1) & !(align - 1);
795 let buffer_size = (padded_row * height.max(1)) as u64;
796
797 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
798 label: Some("offscreen_staging"),
799 size: buffer_size,
800 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
801 mapped_at_creation: false,
802 });
803
804 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
805 label: Some("offscreen_copy_encoder"),
806 });
807 copy_encoder.copy_texture_to_buffer(
808 wgpu::TexelCopyTextureInfo {
809 texture: &offscreen_texture,
810 mip_level: 0,
811 origin: wgpu::Origin3d::ZERO,
812 aspect: wgpu::TextureAspect::All,
813 },
814 wgpu::TexelCopyBufferInfo {
815 buffer: &staging_buf,
816 layout: wgpu::TexelCopyBufferLayout {
817 offset: 0,
818 bytes_per_row: Some(padded_row),
819 rows_per_image: Some(height.max(1)),
820 },
821 },
822 wgpu::Extent3d {
823 width: width.max(1),
824 height: height.max(1),
825 depth_or_array_layers: 1,
826 },
827 );
828 queue.submit(std::iter::once(copy_encoder.finish()));
829
830 let (tx, rx) = std::sync::mpsc::channel();
832 staging_buf
833 .slice(..)
834 .map_async(wgpu::MapMode::Read, move |result| {
835 let _ = tx.send(result);
836 });
837 device
838 .poll(wgpu::PollType::Wait {
839 submission_index: None,
840 timeout: Some(std::time::Duration::from_secs(5)),
841 })
842 .unwrap();
843 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
844
845 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
846 {
847 let mapped = staging_buf.slice(..).get_mapped_range();
848 let data: &[u8] = &mapped;
849 if padded_row == unpadded_row {
850 pixels.extend_from_slice(data);
852 } else {
853 for row in 0..height as usize {
855 let start = row * padded_row as usize;
856 let end = start + unpadded_row as usize;
857 pixels.extend_from_slice(&data[start..end]);
858 }
859 }
860 }
861 staging_buf.unmap();
862
863 let is_bgra = matches!(
865 target_format,
866 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
867 );
868 if is_bgra {
869 for pixel in pixels.chunks_exact_mut(4) {
870 pixel.swap(0, 2); }
872 }
873
874 pixels
875 }
876
877 fn ensure_backdrop_blur_state(&mut self, device: &wgpu::Device, w: u32, h: u32) {
883 let need_recreate = match &self.backdrop_blur_state {
884 Some(s) => s.size != [w, h] || s.format != self.resources.target_format,
885 None => true,
886 };
887 if !need_recreate {
888 return;
889 }
890
891 let format = self.resources.target_format;
892 let blur_w = (w / 2).max(1);
893 let blur_h = (h / 2).max(1);
894
895 let intermediate_texture = device.create_texture(&wgpu::TextureDescriptor {
896 label: Some("backdrop_intermediate"),
897 size: wgpu::Extent3d {
898 width: w,
899 height: h,
900 depth_or_array_layers: 1,
901 },
902 mip_level_count: 1,
903 sample_count: 1,
904 dimension: wgpu::TextureDimension::D2,
905 format,
906 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
907 view_formats: &[],
908 });
909 let intermediate_view = intermediate_texture.create_view(&Default::default());
910
911 let make_blur_tex = |label: &str| {
912 let t = device.create_texture(&wgpu::TextureDescriptor {
913 label: Some(label),
914 size: wgpu::Extent3d {
915 width: blur_w,
916 height: blur_h,
917 depth_or_array_layers: 1,
918 },
919 mip_level_count: 1,
920 sample_count: 1,
921 dimension: wgpu::TextureDimension::D2,
922 format,
923 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
924 | wgpu::TextureUsages::TEXTURE_BINDING,
925 view_formats: &[],
926 });
927 let v = t.create_view(&Default::default());
928 (t, v)
929 };
930 let (blur_a_texture, blur_a_view) = make_blur_tex("backdrop_blur_a");
931 let (blur_b_texture, blur_b_view) = make_blur_tex("backdrop_blur_b");
932
933 self.backdrop_blur_state = Some(crate::resources::BackdropBlurState {
934 intermediate_texture,
935 intermediate_view,
936 blur_a_texture,
937 blur_a_view,
938 blur_b_texture,
939 blur_b_view,
940 size: [w, h],
941 format,
942 });
943 }
944
945 fn run_backdrop_blur(
949 &self,
950 encoder: &mut wgpu::CommandEncoder,
951 device: &wgpu::Device,
952 _queue: &wgpu::Queue,
953 source_view: &wgpu::TextureView,
954 spread: f32,
955 ) -> wgpu::BindGroup {
956 let bs = self.backdrop_blur_state.as_ref().unwrap();
957 let blur_bgl = self.resources.backdrop_blur.bgl.as_ref().unwrap();
958 let blur_sampler = self.resources.backdrop_blur.sampler.as_ref().unwrap();
959 let blur_pipeline = self.resources.backdrop_blur.pipeline.as_ref().unwrap();
960 let blit_pipeline = self
962 .resources
963 .post
964 .dyn_res_upscale_pipeline
965 .as_ref()
966 .unwrap();
967 let blit_bgl = self.resources.post.dyn_res_upscale_bgl.as_ref().unwrap();
968 let blit_sampler = self.resources.post.dyn_res_linear_sampler.as_ref().unwrap();
969
970 let downsample_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
972 label: Some("backdrop_downsample_bg"),
973 layout: blit_bgl,
974 entries: &[
975 wgpu::BindGroupEntry {
976 binding: 0,
977 resource: wgpu::BindingResource::TextureView(source_view),
978 },
979 wgpu::BindGroupEntry {
980 binding: 1,
981 resource: wgpu::BindingResource::Sampler(blit_sampler),
982 },
983 ],
984 });
985 {
986 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
987 label: Some("backdrop_downsample"),
988 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
989 view: &bs.blur_a_view,
990 resolve_target: None,
991 ops: wgpu::Operations {
992 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
993 store: wgpu::StoreOp::Store,
994 },
995 depth_slice: None,
996 })],
997 depth_stencil_attachment: None,
998 timestamp_writes: None,
999 occlusion_query_set: None,
1000 });
1001 pass.set_pipeline(blit_pipeline);
1002 pass.set_bind_group(0, &downsample_bg, &[]);
1003 pass.draw(0..3, 0..1);
1004 }
1005
1006 let effective_spread = (spread / 2.0).max(1.0);
1008
1009 let h_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
1011 label: Some("blur_h_uniform"),
1012 contents: bytemuck::cast_slice(&[1u32, effective_spread.to_bits(), 0u32, 0u32]),
1013 usage: wgpu::BufferUsages::UNIFORM,
1014 });
1015 let h_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1016 label: Some("blur_h_bg"),
1017 layout: blur_bgl,
1018 entries: &[
1019 wgpu::BindGroupEntry {
1020 binding: 0,
1021 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
1022 },
1023 wgpu::BindGroupEntry {
1024 binding: 1,
1025 resource: wgpu::BindingResource::Sampler(blur_sampler),
1026 },
1027 wgpu::BindGroupEntry {
1028 binding: 2,
1029 resource: h_uniform.as_entire_binding(),
1030 },
1031 ],
1032 });
1033 {
1034 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1035 label: Some("backdrop_blur_h"),
1036 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1037 view: &bs.blur_b_view,
1038 resolve_target: None,
1039 ops: wgpu::Operations {
1040 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1041 store: wgpu::StoreOp::Store,
1042 },
1043 depth_slice: None,
1044 })],
1045 depth_stencil_attachment: None,
1046 timestamp_writes: None,
1047 occlusion_query_set: None,
1048 });
1049 pass.set_pipeline(blur_pipeline);
1050 pass.set_bind_group(0, &h_bg, &[]);
1051 pass.draw(0..3, 0..1);
1052 }
1053
1054 let v_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
1056 label: Some("blur_v_uniform"),
1057 contents: bytemuck::cast_slice(&[0u32, effective_spread.to_bits(), 0u32, 0u32]),
1058 usage: wgpu::BufferUsages::UNIFORM,
1059 });
1060 let v_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1061 label: Some("blur_v_bg"),
1062 layout: blur_bgl,
1063 entries: &[
1064 wgpu::BindGroupEntry {
1065 binding: 0,
1066 resource: wgpu::BindingResource::TextureView(&bs.blur_b_view),
1067 },
1068 wgpu::BindGroupEntry {
1069 binding: 1,
1070 resource: wgpu::BindingResource::Sampler(blur_sampler),
1071 },
1072 wgpu::BindGroupEntry {
1073 binding: 2,
1074 resource: v_uniform.as_entire_binding(),
1075 },
1076 ],
1077 });
1078 {
1079 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1080 label: Some("backdrop_blur_v"),
1081 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1082 view: &bs.blur_a_view,
1083 resolve_target: None,
1084 ops: wgpu::Operations {
1085 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1086 store: wgpu::StoreOp::Store,
1087 },
1088 depth_slice: None,
1089 })],
1090 depth_stencil_attachment: None,
1091 timestamp_writes: None,
1092 occlusion_query_set: None,
1093 });
1094 pass.set_pipeline(blur_pipeline);
1095 pass.set_bind_group(0, &v_bg, &[]);
1096 pass.draw(0..3, 0..1);
1097 }
1098
1099 let tex_bgl = self.resources.overlay_shape.tex_bgl.as_ref().unwrap();
1103 let tex_sampler = self.resources.overlay_shape.tex_sampler.as_ref().unwrap();
1104 device.create_bind_group(&wgpu::BindGroupDescriptor {
1105 label: Some("backdrop_blur_overlay_bg"),
1106 layout: tex_bgl,
1107 entries: &[
1108 wgpu::BindGroupEntry {
1109 binding: 0,
1110 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
1111 },
1112 wgpu::BindGroupEntry {
1113 binding: 1,
1114 resource: wgpu::BindingResource::Sampler(tex_sampler),
1115 },
1116 ],
1117 })
1118 }
1119
1120 fn has_backdrop_blur_shapes(&self) -> bool {
1122 self.overlay_shape_gpu_data
1123 .as_ref()
1124 .map_or(false, |sd| sd.blur_vertex_count > 0)
1125 }
1126
1127 fn draw_blur_shapes<'rp>(
1129 &'rp self,
1130 render_pass: &mut wgpu::RenderPass<'rp>,
1131 blur_bind_group: &'rp wgpu::BindGroup,
1132 ) {
1133 if let Some(ref sd) = self.overlay_shape_gpu_data {
1134 if sd.blur_vertex_count > 0 {
1135 if let (Some(pipeline), Some(vbuf)) = (
1136 &self.resources.overlay_shape.tex_pipeline,
1137 &sd.blur_vertex_buf,
1138 ) {
1139 render_pass.set_pipeline(pipeline);
1140 render_pass.set_bind_group(0, blur_bind_group, &[]);
1141 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1142 render_pass.draw(0..sd.blur_vertex_count, 0..1);
1143 }
1144 }
1145 }
1146 }
1147}