1use super::*;
2use wgpu::util::DeviceExt;
3
4impl ViewportRenderer {
5 pub(crate) fn paint_to<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
6 let vp_idx = frame.camera.viewport_index;
7 let camera_bg = self.viewport_camera_bind_group(vp_idx);
8 let grid_bg = self.viewport_grid_bind_group(vp_idx);
9 let vp_slot = self.viewport_slots.get(vp_idx);
10 emit_draw_calls!(
11 &self.resources,
12 &mut *render_pass,
13 frame,
14 self.use_instancing,
15 &self.instanced_batches,
16 camera_bg,
17 grid_bg,
18 &self.compute_filter_results,
19 vp_slot,
20 &self.wireframe_bind_groups
21 );
22 emit_scivis_draw_calls!(
23 &self.resources,
24 &mut *render_pass,
25 &self.point_cloud_gpu_data,
26 &self.glyph_gpu_data,
27 &self.polyline_gpu_data,
28 &self.volume_gpu_data,
29 &self.streamtube_gpu_data,
30 camera_bg,
31 &self.tube_gpu_data,
32 &self.image_slice_gpu_data,
33 &self.tensor_glyph_gpu_data,
34 &self.ribbon_gpu_data,
35 &self.volume_surface_slice_gpu_data,
36 &self.sprite_gpu_data,
37 false
38 );
39 if !self.gaussian_splat_draw_data.is_empty() {
41 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
42 render_pass.set_pipeline(dual.for_format(false));
43 render_pass.set_bind_group(0, camera_bg, &[]);
44 for dd in &self.gaussian_splat_draw_data {
45 if dd.wireframe {
46 continue;
47 }
48 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
49 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
50 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
51 render_pass.draw(0..6, 0..dd.count);
52 }
53 }
54 }
55 }
56 }
57 if !self.tvm_wireframe_draws.is_empty() {
59 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
60 render_pass.set_bind_group(0, camera_bg, &[]);
61 for mesh_id in &self.tvm_wireframe_draws {
62 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
63 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
64 render_pass.set_bind_group(1, tvm_bg, &[]);
65 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
66 render_pass.set_index_buffer(
67 mesh.edge_index_buffer.slice(..),
68 wgpu::IndexFormat::Uint32,
69 );
70 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
71 }
72 }
73 }
74 }
75 if !self.implicit_gpu_data.is_empty() {
77 if let Some(ref dual) = self.resources.implicit_pipeline {
78 render_pass.set_pipeline(dual.for_format(false));
79 render_pass.set_bind_group(0, camera_bg, &[]);
80 for gpu in &self.implicit_gpu_data {
81 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
82 render_pass.draw(0..6, 0..1);
83 }
84 }
85 }
86 if !self.mc_gpu_data.is_empty() {
88 render_pass.set_bind_group(0, camera_bg, &[]);
89 for mc in &self.mc_gpu_data {
90 let vol = &self.resources.mc_volumes[mc.volume_idx];
91 if mc.wireframe || frame.viewport.wireframe_mode {
92 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
93 render_pass.set_pipeline(dual.for_format(false));
94 for (slab, wire_bg) in
95 vol.slabs.iter().zip(mc.wire_slab_bgs.iter())
96 {
97 render_pass.set_bind_group(1, wire_bg, &[]);
98 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
99 }
100 }
101 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
102 render_pass.set_pipeline(dual.for_format(false));
103 render_pass.set_bind_group(1, &mc.render_bg, &[]);
104 for slab in &vol.slabs {
105 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
106 render_pass.draw_indirect(&slab.indirect_buf, 0);
107 }
108 }
109 }
110 }
111 emit_outline_composite!(&self.resources, &mut *render_pass, vp_slot);
113 if let Some(sub_hl) = self
115 .viewport_slots
116 .get(vp_idx)
117 .and_then(|s| s.sub_highlight.as_ref())
118 {
119 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
120 &self.resources.sub_highlight_fill_ldr_pipeline,
121 &self.resources.sub_highlight_edge_ldr_pipeline,
122 &self.resources.sub_highlight_sprite_ldr_pipeline,
123 ) {
124 if sub_hl.fill_vertex_count > 0 {
125 render_pass.set_pipeline(fill_pl);
126 render_pass.set_bind_group(0, camera_bg, &[]);
127 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
128 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
129 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
130 }
131 if sub_hl.edge_segment_count > 0 {
132 render_pass.set_pipeline(edge_pl);
133 render_pass.set_bind_group(0, camera_bg, &[]);
134 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
135 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
136 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
137 }
138 if sub_hl.sprite_point_count > 0 {
139 render_pass.set_pipeline(sprite_pl);
140 render_pass.set_bind_group(0, camera_bg, &[]);
141 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
142 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
143 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
144 }
145 }
146 }
147 if !self.screen_image_gpu_data.is_empty() {
149 if let Some(pipeline) = &self.resources.screen_image_pipeline {
150 render_pass.set_pipeline(pipeline);
151 for gpu in &self.screen_image_gpu_data {
152 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
153 render_pass.draw(0..6, 0..1);
154 }
155 }
156 }
157 if let Some(ref sd) = self.overlay_shape_gpu_data {
159 if sd.vertex_count > 0 {
160 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
161 if let Some(vbuf) = &sd.vertex_buf {
162 render_pass.set_pipeline(pipeline);
163 render_pass.set_vertex_buffer(0, vbuf.slice(..));
164 render_pass.draw(0..sd.vertex_count, 0..1);
165 }
166 }
167 }
168 if !sd.tex_batches.is_empty() {
169 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
170 render_pass.set_pipeline(pipeline);
171 for batch in &sd.tex_batches {
172 render_pass.set_bind_group(0, &batch.bind_group, &[]);
173 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
174 render_pass.draw(0..batch.vertex_count, 0..1);
175 }
176 }
177 }
178 }
179 if let Some(ref rr) = self.overlay_rect_gpu_data {
181 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
182 render_pass.set_pipeline(pipeline);
183 render_pass.set_bind_group(0, &rr.bind_group, &[]);
184 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
185 render_pass.draw(0..rr.vertex_count, 0..1);
186 }
187 }
188 if let Some(ref ld) = self.label_gpu_data {
190 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
191 render_pass.set_pipeline(pipeline);
192 render_pass.set_bind_group(0, &ld.bind_group, &[]);
193 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
194 render_pass.draw(0..ld.vertex_count, 0..1);
195 }
196 }
197 if let Some(ref sb) = self.scalar_bar_gpu_data {
199 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
200 render_pass.set_pipeline(pipeline);
201 render_pass.set_bind_group(0, &sb.bind_group, &[]);
202 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
203 render_pass.draw(0..sb.vertex_count, 0..1);
204 }
205 }
206 if let Some(ref rd) = self.ruler_gpu_data {
208 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
209 render_pass.set_pipeline(pipeline);
210 render_pass.set_bind_group(0, &rd.bind_group, &[]);
211 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
212 render_pass.draw(0..rd.vertex_count, 0..1);
213 }
214 }
215 if let Some(ref lb) = self.loading_bar_gpu_data {
217 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
218 render_pass.set_pipeline(pipeline);
219 render_pass.set_bind_group(0, &lb.bind_group, &[]);
220 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
221 render_pass.draw(0..lb.vertex_count, 0..1);
222 }
223 }
224 if !self.overlay_image_gpu_data.is_empty() {
226 if let Some(pipeline) = &self.resources.screen_image_pipeline {
227 render_pass.set_pipeline(pipeline);
228 for gpu in &self.overlay_image_gpu_data {
229 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
230 render_pass.draw(0..6, 0..1);
231 }
232 }
233 }
234 if frame.effects.show_shadow_atlas {
236 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
237 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
238 render_pass.draw(0..6, 0..1);
239 }
240 }
241
242 pub(crate) fn prepare_ldr_dyn_res(
257 &mut self,
258 encoder: &mut wgpu::CommandEncoder,
259 device: &wgpu::Device,
260 frame: &FrameData,
261 ) -> bool {
262 if self.current_render_scale >= 1.0 - 0.001 {
263 return false;
264 }
265
266 let vp_idx = frame.camera.viewport_index;
267 let w = (frame.camera.viewport_size[0] as u32).max(1);
268 let h = (frame.camera.viewport_size[1] as u32).max(1);
269 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
270 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
271
272 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
273 self.resources.ensure_dyn_res_ds_pipeline(device);
274
275 let bg_colour = frame.viewport.background_colour.unwrap_or([
276 65.0 / 255.0,
277 65.0 / 255.0,
278 65.0 / 255.0,
279 1.0,
280 ]);
281
282 {
283 let slot = &self.viewport_slots[vp_idx];
284 let dr = slot.dyn_res.as_ref().unwrap();
285 let colour_view = &dr.colour_view;
286 let depth_view = &dr.depth_view;
287 let camera_bg = &slot.camera_bind_group;
288 let grid_bg = &slot.grid_bind_group;
289
290 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
291 label: Some("ldr_dyn_res_render_pass"),
292 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
293 view: colour_view,
294 resolve_target: None,
295 ops: wgpu::Operations {
296 load: wgpu::LoadOp::Clear(wgpu::Color {
297 r: bg_colour[0] as f64,
298 g: bg_colour[1] as f64,
299 b: bg_colour[2] as f64,
300 a: bg_colour[3] as f64,
301 }),
302 store: wgpu::StoreOp::Store,
303 },
304 depth_slice: None,
305 })],
306 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
307 view: depth_view,
308 depth_ops: Some(wgpu::Operations {
309 load: wgpu::LoadOp::Clear(1.0),
310 store: wgpu::StoreOp::Discard,
311 }),
312 stencil_ops: None,
313 }),
314 timestamp_writes: None,
315 occlusion_query_set: None,
316 });
317 emit_draw_calls!(
318 &self.resources,
319 &mut render_pass,
320 frame,
321 self.use_instancing,
322 &self.instanced_batches,
323 camera_bg,
324 grid_bg,
325 &self.compute_filter_results,
326 Some(slot),
327 &self.wireframe_bind_groups
328 );
329 emit_scivis_draw_calls!(
330 &self.resources,
331 &mut render_pass,
332 &self.point_cloud_gpu_data,
333 &self.glyph_gpu_data,
334 &self.polyline_gpu_data,
335 &self.volume_gpu_data,
336 &self.streamtube_gpu_data,
337 camera_bg,
338 &self.tube_gpu_data,
339 &self.image_slice_gpu_data,
340 &self.tensor_glyph_gpu_data,
341 &self.ribbon_gpu_data,
342 &self.volume_surface_slice_gpu_data,
343 &self.sprite_gpu_data,
344 false
345 );
346 if !self.tvm_wireframe_draws.is_empty() {
348 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
349 render_pass.set_bind_group(0, camera_bg, &[]);
350 for mesh_id in &self.tvm_wireframe_draws {
351 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
352 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
353 render_pass.set_bind_group(1, tvm_bg, &[]);
354 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
355 render_pass.set_index_buffer(
356 mesh.edge_index_buffer.slice(..),
357 wgpu::IndexFormat::Uint32,
358 );
359 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
360 }
361 }
362 }
363 }
364 if !self.implicit_gpu_data.is_empty() {
366 if let Some(ref dual) = self.resources.implicit_pipeline {
367 render_pass.set_pipeline(dual.for_format(false));
368 render_pass.set_bind_group(0, camera_bg, &[]);
369 for gpu in &self.implicit_gpu_data {
370 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
371 render_pass.draw(0..6, 0..1);
372 }
373 }
374 }
375 if !self.mc_gpu_data.is_empty() {
377 if let Some(ref dual) = self.resources.mc_surface_pipeline {
378 render_pass.set_pipeline(dual.for_format(false));
379 render_pass.set_bind_group(0, camera_bg, &[]);
380 for mc in &self.mc_gpu_data {
381 let vol = &self.resources.mc_volumes[mc.volume_idx];
382 render_pass.set_bind_group(1, &mc.render_bg, &[]);
383 for slab in &vol.slabs {
384 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
385 render_pass.draw_indirect(&slab.indirect_buf, 0);
386 }
387 }
388 }
389 }
390 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
392 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
394 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
395 &self.resources.sub_highlight_fill_ldr_pipeline,
396 &self.resources.sub_highlight_edge_ldr_pipeline,
397 &self.resources.sub_highlight_sprite_ldr_pipeline,
398 ) {
399 if sub_hl.fill_vertex_count > 0 {
400 render_pass.set_pipeline(fill_pl);
401 render_pass.set_bind_group(0, camera_bg, &[]);
402 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
403 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
404 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
405 }
406 if sub_hl.edge_segment_count > 0 {
407 render_pass.set_pipeline(edge_pl);
408 render_pass.set_bind_group(0, camera_bg, &[]);
409 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
410 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
411 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
412 }
413 if sub_hl.sprite_point_count > 0 {
414 render_pass.set_pipeline(sprite_pl);
415 render_pass.set_bind_group(0, camera_bg, &[]);
416 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
417 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
418 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
419 }
420 }
421 }
422 if !self.screen_image_gpu_data.is_empty() {
424 if let Some(pipeline) = &self.resources.screen_image_pipeline {
425 render_pass.set_pipeline(pipeline);
426 for gpu in &self.screen_image_gpu_data {
427 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
428 render_pass.draw(0..6, 0..1);
429 }
430 }
431 }
432 if let Some(ref sd) = self.overlay_shape_gpu_data {
434 if sd.vertex_count > 0 {
435 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
436 if let Some(vbuf) = &sd.vertex_buf {
437 render_pass.set_pipeline(pipeline);
438 render_pass.set_vertex_buffer(0, vbuf.slice(..));
439 render_pass.draw(0..sd.vertex_count, 0..1);
440 }
441 }
442 }
443 if !sd.tex_batches.is_empty() {
444 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
445 render_pass.set_pipeline(pipeline);
446 for batch in &sd.tex_batches {
447 render_pass.set_bind_group(0, &batch.bind_group, &[]);
448 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
449 render_pass.draw(0..batch.vertex_count, 0..1);
450 }
451 }
452 }
453 }
454 if let Some(ref rr) = self.overlay_rect_gpu_data {
456 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
457 render_pass.set_pipeline(pipeline);
458 render_pass.set_bind_group(0, &rr.bind_group, &[]);
459 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
460 render_pass.draw(0..rr.vertex_count, 0..1);
461 }
462 }
463 if let Some(ref ld) = self.label_gpu_data {
465 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
466 render_pass.set_pipeline(pipeline);
467 render_pass.set_bind_group(0, &ld.bind_group, &[]);
468 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
469 render_pass.draw(0..ld.vertex_count, 0..1);
470 }
471 }
472 if let Some(ref sb) = self.scalar_bar_gpu_data {
474 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
475 render_pass.set_pipeline(pipeline);
476 render_pass.set_bind_group(0, &sb.bind_group, &[]);
477 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
478 render_pass.draw(0..sb.vertex_count, 0..1);
479 }
480 }
481 if let Some(ref rd) = self.ruler_gpu_data {
483 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
484 render_pass.set_pipeline(pipeline);
485 render_pass.set_bind_group(0, &rd.bind_group, &[]);
486 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
487 render_pass.draw(0..rd.vertex_count, 0..1);
488 }
489 }
490 if let Some(ref lb) = self.loading_bar_gpu_data {
492 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
493 render_pass.set_pipeline(pipeline);
494 render_pass.set_bind_group(0, &lb.bind_group, &[]);
495 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
496 render_pass.draw(0..lb.vertex_count, 0..1);
497 }
498 }
499 if !self.overlay_image_gpu_data.is_empty() {
501 if let Some(pipeline) = &self.resources.screen_image_pipeline {
502 render_pass.set_pipeline(pipeline);
503 for gpu in &self.overlay_image_gpu_data {
504 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
505 render_pass.draw(0..6, 0..1);
506 }
507 }
508 }
509 }
510
511 true
512 }
513
514 pub(crate) fn paint_dyn_res_blit<'rp>(
520 &self,
521 render_pass: &mut wgpu::RenderPass<'rp>,
522 frame: &FrameData,
523 ) {
524 let vp_idx = frame.camera.viewport_index;
525 if let Some(dr) = self
526 .viewport_slots
527 .get(vp_idx)
528 .and_then(|s| s.dyn_res.as_ref())
529 {
530 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
531 render_pass.set_pipeline(pipeline);
532 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
533 render_pass.draw(0..3, 0..1);
534 }
535 }
536 }
537
538 pub(crate) fn prepare_hdr_callback(
555 &mut self,
556 device: &wgpu::Device,
557 queue: &wgpu::Queue,
558 frame: &FrameData,
559 ) -> wgpu::CommandBuffer {
560 self.prepare(device, queue, frame);
561
562 let vp_idx = frame.camera.viewport_index;
563 let ppp = frame.camera.pixels_per_point;
569 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
570 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
571
572 self.resources.ensure_dyn_res_pipeline(device);
574 self.resources.ensure_dyn_res_ds_pipeline(device);
575
576 self.ensure_viewport_slot(device, vp_idx);
578 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
579 None => true,
580 Some(t) => t.size != [w, h],
581 };
582 if needs_create {
583 let target = self.resources.create_hdr_callback_target(device, [w, h]);
584 self.viewport_slots[vp_idx].hdr_callback = Some(target);
585 }
586
587 let output_view = self.viewport_slots[vp_idx]
591 .hdr_callback
592 .as_ref()
593 .unwrap()
594 .texture
595 .create_view(&wgpu::TextureViewDescriptor::default());
596
597 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
598 }
599
600 pub(crate) fn prepare_hdr_callback_viewport(
616 &mut self,
617 device: &wgpu::Device,
618 queue: &wgpu::Queue,
619 id: ViewportId,
620 frame: &FrameData,
621 ) -> wgpu::CommandBuffer {
622 let vp_idx = id.0;
623 let ppp = frame.camera.pixels_per_point;
624 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
625 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
626
627 self.resources.ensure_dyn_res_pipeline(device);
628 self.resources.ensure_dyn_res_ds_pipeline(device);
629
630 self.ensure_viewport_slot(device, vp_idx);
631 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
632 None => true,
633 Some(t) => t.size != [w, h],
634 };
635 if needs_create {
636 let target = self.resources.create_hdr_callback_target(device, [w, h]);
637 self.viewport_slots[vp_idx].hdr_callback = Some(target);
638 }
639
640 let output_view = self.viewport_slots[vp_idx]
641 .hdr_callback
642 .as_ref()
643 .unwrap()
644 .texture
645 .create_view(&wgpu::TextureViewDescriptor::default());
646
647 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
648 }
649
650 pub(crate) fn paint_hdr_blit<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
656 let vp_idx = frame.camera.viewport_index;
657 if let Some(hc) = self
658 .viewport_slots
659 .get(vp_idx)
660 .and_then(|s| s.hdr_callback.as_ref())
661 {
662 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
663 render_pass.set_pipeline(pipeline);
664 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
665 render_pass.draw(0..3, 0..1);
666 }
667 }
668 if frame.effects.show_shadow_atlas {
670 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
671 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
672 render_pass.draw(0..6, 0..1);
673 }
674 }
675
676 pub(crate) fn paint_hdr_blit_no_ds<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
680 let vp_idx = frame.camera.viewport_index;
681 if let Some(hc) = self
682 .viewport_slots
683 .get(vp_idx)
684 .and_then(|s| s.hdr_callback.as_ref())
685 {
686 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
687 render_pass.set_pipeline(pipeline);
688 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
689 render_pass.draw(0..3, 0..1);
690 }
691 }
692 }
693
694 pub(crate) fn prepare_callback(
708 &mut self,
709 device: &wgpu::Device,
710 queue: &wgpu::Queue,
711 frame: &FrameData,
712 ) -> Vec<wgpu::CommandBuffer> {
713 if frame.effects.post_process.enabled {
714 let cb = self.prepare_hdr_callback(device, queue, frame);
715 vec![cb]
716 } else {
717 self.prepare(device, queue, frame);
718 if self.current_render_scale < 1.0 - 0.001 {
719 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
720 label: Some("ldr_dyn_res_callback_encoder"),
721 });
722 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
723 vec![encoder.finish()]
724 } else {
725 Vec::new()
726 }
727 }
728 }
729
730 pub(crate) fn paint_callback<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
736 let vp_idx = frame.camera.viewport_index;
737 if frame.effects.post_process.enabled {
738 if self
739 .viewport_slots
740 .get(vp_idx)
741 .and_then(|s| s.hdr_callback.as_ref())
742 .is_some()
743 {
744 self.paint_hdr_blit(render_pass, frame);
745 return;
746 }
747 }
748 if self.current_render_scale < 1.0 - 0.001
749 && self
750 .viewport_slots
751 .get(vp_idx)
752 .and_then(|s| s.dyn_res.as_ref())
753 .is_some()
754 {
755 self.paint_dyn_res_blit(render_pass, frame);
756 } else {
757 self.paint_to(render_pass, frame);
758 }
759 }
760
761 pub(crate) fn render_viewport(
775 &mut self,
776 device: &wgpu::Device,
777 queue: &wgpu::Queue,
778 output_view: &wgpu::TextureView,
779 id: ViewportId,
780 frame: &FrameData,
781 ) -> wgpu::CommandBuffer {
782 self.render_frame_internal(device, queue, output_view, id.0, frame)
783 }
784
785 pub(crate) fn render(
793 &mut self,
794 device: &wgpu::Device,
795 queue: &wgpu::Queue,
796 output_view: &wgpu::TextureView,
797 frame: &FrameData,
798 ) -> wgpu::CommandBuffer {
799 self.prepare(device, queue, frame);
801 self.render_frame_internal(
802 device,
803 queue,
804 output_view,
805 frame.camera.viewport_index,
806 frame,
807 )
808 }
809
810 fn render_frame_internal(
815 &mut self,
816 device: &wgpu::Device,
817 queue: &wgpu::Queue,
818 output_view: &wgpu::TextureView,
819 vp_idx: usize,
820 frame: &FrameData,
821 ) -> wgpu::CommandBuffer {
822 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
824 SurfaceSubmission::Flat(items) => items.as_ref(),
825 };
826
827 let bg_colour = frame.viewport.background_colour.unwrap_or([
828 65.0 / 255.0,
829 65.0 / 255.0,
830 65.0 / 255.0,
831 1.0,
832 ]);
833 let ppp = frame.camera.pixels_per_point;
834 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
835 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
836
837 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
839 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor, self.current_render_scale);
840
841 if self.ts_query_set.is_none()
843 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
844 {
845 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
846 label: Some("ts_query_set"),
847 ty: wgpu::QueryType::Timestamp,
848 count: 2,
849 }));
850 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
851 label: Some("ts_resolve_buf"),
852 size: 16,
853 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
854 mapped_at_creation: false,
855 }));
856 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
857 label: Some("ts_staging_buf"),
858 size: 16,
859 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
860 mapped_at_creation: false,
861 }));
862 self.ts_period = queue.get_timestamp_period();
863 }
864
865 if !frame.effects.post_process.enabled {
866 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
870 label: Some("ldr_encoder"),
871 });
872
873 let use_dyn_res = self.current_render_scale < 1.0 - 0.001;
874 let needs_blur = self.has_backdrop_blur_shapes();
875
876 if use_dyn_res {
877 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
878 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
879 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
880 }
881
882 if needs_blur && !use_dyn_res {
886 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
887 }
888
889 {
890 let slot = &self.viewport_slots[vp_idx];
891 let slot_hdr = slot.hdr.as_ref().expect(
892 "HDR state missing in LDR path; ensure_viewport_hdr must have been called",
893 );
894 let camera_bg = &slot.camera_bind_group;
895 let grid_bg = &slot.grid_bind_group;
896 let (scene_colour_view, scene_depth_view): (&wgpu::TextureView, &wgpu::TextureView) =
898 if use_dyn_res {
899 let dr = slot.dyn_res.as_ref().unwrap();
900 (&dr.colour_view, &dr.depth_view)
901 } else if needs_blur {
902 let bs = self.backdrop_blur_state.as_ref().unwrap();
903 (&bs.intermediate_view, &slot_hdr.outline_depth_view)
904 } else {
905 (output_view, &slot_hdr.outline_depth_view)
906 };
907 let ts_writes =
908 self.ts_query_set
909 .as_ref()
910 .map(|qs| wgpu::RenderPassTimestampWrites {
911 query_set: qs,
912 beginning_of_pass_write_index: Some(0),
913 end_of_pass_write_index: Some(1),
914 });
915 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
916 label: Some("ldr_render_pass"),
917 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
918 view: scene_colour_view,
919 resolve_target: None,
920 ops: wgpu::Operations {
921 load: wgpu::LoadOp::Clear(wgpu::Color {
922 r: bg_colour[0] as f64,
923 g: bg_colour[1] as f64,
924 b: bg_colour[2] as f64,
925 a: bg_colour[3] as f64,
926 }),
927 store: wgpu::StoreOp::Store,
928 },
929 depth_slice: None,
930 })],
931 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
932 view: scene_depth_view,
933 depth_ops: Some(wgpu::Operations {
934 load: wgpu::LoadOp::Clear(1.0),
935 store: wgpu::StoreOp::Discard,
936 }),
937 stencil_ops: None,
938 }),
939 timestamp_writes: ts_writes,
940 occlusion_query_set: None,
941 });
942 emit_draw_calls!(
943 &self.resources,
944 &mut render_pass,
945 frame,
946 self.use_instancing,
947 &self.instanced_batches,
948 camera_bg,
949 grid_bg,
950 &self.compute_filter_results,
951 Some(slot),
952 &self.wireframe_bind_groups
953 );
954 emit_scivis_draw_calls!(
955 &self.resources,
956 &mut render_pass,
957 &self.point_cloud_gpu_data,
958 &self.glyph_gpu_data,
959 &self.polyline_gpu_data,
960 &self.volume_gpu_data,
961 &self.streamtube_gpu_data,
962 camera_bg,
963 &self.tube_gpu_data,
964 &self.image_slice_gpu_data,
965 &self.tensor_glyph_gpu_data,
966 &self.ribbon_gpu_data,
967 &self.volume_surface_slice_gpu_data,
968 &self.sprite_gpu_data,
969 false
970 );
971 if !self.tvm_wireframe_draws.is_empty() {
973 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
974 render_pass.set_bind_group(0, camera_bg, &[]);
975 for mesh_id in &self.tvm_wireframe_draws {
976 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
977 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
978 render_pass.set_bind_group(1, tvm_bg, &[]);
979 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
980 render_pass.set_index_buffer(
981 mesh.edge_index_buffer.slice(..),
982 wgpu::IndexFormat::Uint32,
983 );
984 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
985 }
986 }
987 }
988 }
989 if !self.implicit_gpu_data.is_empty() {
991 if let Some(ref dual) = self.resources.implicit_pipeline {
992 render_pass.set_pipeline(dual.for_format(false));
993 render_pass.set_bind_group(0, camera_bg, &[]);
994 for gpu in &self.implicit_gpu_data {
995 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
996 render_pass.draw(0..6, 0..1);
997 }
998 }
999 }
1000 if !self.mc_gpu_data.is_empty() {
1002 if let Some(ref dual) = self.resources.mc_surface_pipeline {
1003 render_pass.set_pipeline(dual.for_format(false));
1004 render_pass.set_bind_group(0, camera_bg, &[]);
1005 for mc in &self.mc_gpu_data {
1006 let vol = &self.resources.mc_volumes[mc.volume_idx];
1007 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1008 for slab in &vol.slabs {
1009 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1010 render_pass.draw_indirect(&slab.indirect_buf, 0);
1011 }
1012 }
1013 }
1014 }
1015 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
1017 if !self.screen_image_gpu_data.is_empty() {
1022 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
1023 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
1024 for gpu in &self.screen_image_gpu_data {
1025 if let (Some(dc_bg), Some(dc_pipe)) =
1026 (&gpu.depth_bind_group, dc_pipeline)
1027 {
1028 render_pass.set_pipeline(dc_pipe);
1029 render_pass.set_bind_group(0, dc_bg, &[]);
1030 } else {
1031 render_pass.set_pipeline(overlay_pipeline);
1032 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1033 }
1034 render_pass.draw(0..6, 0..1);
1035 }
1036 }
1037 }
1038 if !needs_blur {
1041 if let Some(ref sd) = self.overlay_shape_gpu_data {
1043 if sd.vertex_count > 0 {
1044 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1045 if let Some(vbuf) = &sd.vertex_buf {
1046 render_pass.set_pipeline(pipeline);
1047 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1048 render_pass.draw(0..sd.vertex_count, 0..1);
1049 }
1050 }
1051 }
1052 if !sd.tex_batches.is_empty() {
1053 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1054 render_pass.set_pipeline(pipeline);
1055 for batch in &sd.tex_batches {
1056 render_pass.set_bind_group(0, &batch.bind_group, &[]);
1057 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1058 render_pass.draw(0..batch.vertex_count, 0..1);
1059 }
1060 }
1061 }
1062 }
1063 if let Some(ref rr) = self.overlay_rect_gpu_data {
1065 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1066 render_pass.set_pipeline(pipeline);
1067 render_pass.set_bind_group(0, &rr.bind_group, &[]);
1068 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1069 render_pass.draw(0..rr.vertex_count, 0..1);
1070 }
1071 }
1072 if let Some(ref ld) = self.label_gpu_data {
1074 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1075 render_pass.set_pipeline(pipeline);
1076 render_pass.set_bind_group(0, &ld.bind_group, &[]);
1077 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1078 render_pass.draw(0..ld.vertex_count, 0..1);
1079 }
1080 }
1081 if let Some(ref sb) = self.scalar_bar_gpu_data {
1083 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1084 render_pass.set_pipeline(pipeline);
1085 render_pass.set_bind_group(0, &sb.bind_group, &[]);
1086 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1087 render_pass.draw(0..sb.vertex_count, 0..1);
1088 }
1089 }
1090 if let Some(ref rd) = self.ruler_gpu_data {
1092 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1093 render_pass.set_pipeline(pipeline);
1094 render_pass.set_bind_group(0, &rd.bind_group, &[]);
1095 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1096 render_pass.draw(0..rd.vertex_count, 0..1);
1097 }
1098 }
1099 if !self.overlay_image_gpu_data.is_empty() {
1101 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1102 render_pass.set_pipeline(pipeline);
1103 for gpu in &self.overlay_image_gpu_data {
1104 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1105 render_pass.draw(0..6, 0..1);
1106 }
1107 }
1108 }
1109 }
1110 }
1111 if needs_blur {
1116 let spread = self.overlay_shape_gpu_data.as_ref()
1117 .map(|d| d.max_blur_radius)
1118 .unwrap_or(1.0);
1119 let blur_bg = {
1120 let source = if use_dyn_res {
1121 &self.viewport_slots[vp_idx].dyn_res.as_ref().unwrap().colour_view
1122 } else {
1123 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1124 };
1125 self.run_backdrop_blur(&mut encoder, device, queue, source, spread)
1126 };
1127
1128 let slot = &self.viewport_slots[vp_idx];
1130 let slot_hdr = slot.hdr.as_ref().unwrap();
1131 let overlay_colour_view: &wgpu::TextureView = if use_dyn_res {
1132 &slot.dyn_res.as_ref().unwrap().colour_view
1133 } else {
1134 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1135 };
1136 let overlay_depth_view: &wgpu::TextureView = if use_dyn_res {
1137 &slot.dyn_res.as_ref().unwrap().depth_view
1138 } else {
1139 &slot_hdr.outline_depth_view
1140 };
1141 {
1142 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1143 label: Some("ldr_overlay_blur_pass"),
1144 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1145 view: overlay_colour_view,
1146 resolve_target: None,
1147 ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store },
1148 depth_slice: None,
1149 })],
1150 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1151 view: overlay_depth_view,
1152 depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Discard }),
1153 stencil_ops: None,
1154 }),
1155 timestamp_writes: None,
1156 occlusion_query_set: None,
1157 });
1158 self.draw_blur_shapes(&mut overlay_pass, &blur_bg);
1160 if let Some(ref sd) = self.overlay_shape_gpu_data {
1162 if sd.vertex_count > 0 {
1163 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1164 if let Some(vbuf) = &sd.vertex_buf {
1165 overlay_pass.set_pipeline(pipeline);
1166 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1167 overlay_pass.draw(0..sd.vertex_count, 0..1);
1168 }
1169 }
1170 }
1171 if !sd.tex_batches.is_empty() {
1172 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1173 overlay_pass.set_pipeline(pipeline);
1174 for batch in &sd.tex_batches {
1175 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
1176 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1177 overlay_pass.draw(0..batch.vertex_count, 0..1);
1178 }
1179 }
1180 }
1181 }
1182 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1183 if let Some(ref rr) = self.overlay_rect_gpu_data {
1184 overlay_pass.set_pipeline(pipeline);
1185 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
1186 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1187 overlay_pass.draw(0..rr.vertex_count, 0..1);
1188 }
1189 if let Some(ref ld) = self.label_gpu_data {
1190 overlay_pass.set_pipeline(pipeline);
1191 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
1192 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1193 overlay_pass.draw(0..ld.vertex_count, 0..1);
1194 }
1195 if let Some(ref sb) = self.scalar_bar_gpu_data {
1196 overlay_pass.set_pipeline(pipeline);
1197 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
1198 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1199 overlay_pass.draw(0..sb.vertex_count, 0..1);
1200 }
1201 if let Some(ref rd) = self.ruler_gpu_data {
1202 overlay_pass.set_pipeline(pipeline);
1203 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
1204 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1205 overlay_pass.draw(0..rd.vertex_count, 0..1);
1206 }
1207 }
1208 if !self.overlay_image_gpu_data.is_empty() {
1209 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1210 overlay_pass.set_pipeline(pipeline);
1211 for gpu in &self.overlay_image_gpu_data {
1212 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
1213 overlay_pass.draw(0..6, 0..1);
1214 }
1215 }
1216 }
1217 }
1218 }
1219
1220 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
1222 self.ts_query_set.as_ref(),
1223 self.ts_resolve_buf.as_ref(),
1224 self.ts_staging_buf.as_ref(),
1225 ) {
1226 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
1227 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
1228 self.ts_needs_readback = true;
1229 }
1230
1231 if use_dyn_res {
1233 let upscale_bg = &self.viewport_slots[vp_idx]
1234 .dyn_res
1235 .as_ref()
1236 .unwrap()
1237 .upscale_bind_group;
1238 let mut upscale_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1239 label: Some("dyn_res_upscale_pass"),
1240 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1241 view: output_view,
1242 resolve_target: None,
1243 ops: wgpu::Operations {
1244 load: wgpu::LoadOp::Load,
1245 store: wgpu::StoreOp::Store,
1246 },
1247 depth_slice: None,
1248 })],
1249 depth_stencil_attachment: None,
1250 timestamp_writes: None,
1251 occlusion_query_set: None,
1252 });
1253 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1254 upscale_pass.set_pipeline(pipeline);
1255 upscale_pass.set_bind_group(0, upscale_bg, &[]);
1256 upscale_pass.draw(0..3, 0..1);
1257 }
1258 } else if needs_blur {
1259 let bs = self.backdrop_blur_state.as_ref().unwrap();
1261 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
1262 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
1263 let blit_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1264 label: Some("backdrop_blit_bg"),
1265 layout: blit_bgl,
1266 entries: &[
1267 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.intermediate_view) },
1268 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blit_sampler) },
1269 ],
1270 });
1271 let mut blit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1272 label: Some("backdrop_blit_pass"),
1273 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1274 view: output_view,
1275 resolve_target: None,
1276 ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store },
1277 depth_slice: None,
1278 })],
1279 depth_stencil_attachment: None,
1280 timestamp_writes: None,
1281 occlusion_query_set: None,
1282 });
1283 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1284 blit_pass.set_pipeline(pipeline);
1285 blit_pass.set_bind_group(0, &blit_bg, &[]);
1286 blit_pass.draw(0..3, 0..1);
1287 }
1288 }
1289
1290 return encoder.finish();
1291 }
1292
1293 let pp = &frame.effects.post_process;
1295
1296 let hdr_clear_rgb = [
1297 bg_colour[0].powf(2.2),
1298 bg_colour[1].powf(2.2),
1299 bg_colour[2].powf(2.2),
1300 ];
1301
1302 let mode = match pp.tone_mapping {
1304 crate::renderer::ToneMapping::Reinhard => 0u32,
1305 crate::renderer::ToneMapping::Aces => 1u32,
1306 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
1307 };
1308 let tm_uniform = crate::resources::ToneMapUniform {
1309 exposure: pp.exposure,
1310 mode,
1311 bloom_enabled: if pp.bloom { 1 } else { 0 },
1312 ssao_enabled: if pp.ssao { 1 } else { 0 },
1313 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
1314 edl_enabled: if pp.edl_enabled { 1 } else { 0 },
1315 edl_radius: pp.edl_radius,
1316 edl_strength: pp.edl_strength,
1317 background_colour: bg_colour,
1318 near_plane: frame.camera.render_camera.near,
1319 far_plane: frame.camera.render_camera.far,
1320 lic_enabled: if scene_items.iter().any(|i| i.lic.is_some() && !i.settings.hidden) {
1321 1
1322 } else {
1323 0
1324 },
1325 lic_strength: scene_items
1326 .iter()
1327 .filter(|i| !i.settings.hidden)
1328 .find_map(|i| i.lic.as_ref().map(|l| l.config.strength))
1329 .unwrap_or(0.5),
1330 };
1331 {
1332 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1333 queue.write_buffer(
1334 &hdr.tone_map_uniform_buf,
1335 0,
1336 bytemuck::cast_slice(&[tm_uniform]),
1337 );
1338
1339 if pp.ssao {
1341 let proj = frame.camera.render_camera.projection;
1342 let inv_proj = proj.inverse();
1343 let ssao_uniform = crate::resources::SsaoUniform {
1344 inv_proj: inv_proj.to_cols_array_2d(),
1345 proj: proj.to_cols_array_2d(),
1346 radius: 0.5,
1347 bias: 0.025,
1348 _pad: [0.0; 2],
1349 };
1350 queue.write_buffer(
1351 &hdr.ssao_uniform_buf,
1352 0,
1353 bytemuck::cast_slice(&[ssao_uniform]),
1354 );
1355 }
1356
1357 if pp.contact_shadows {
1359 let proj = frame.camera.render_camera.projection;
1360 let inv_proj = proj.inverse();
1361 let light_dir_world: glam::Vec3 =
1362 if let Some(l) = frame.effects.lighting.lights.first() {
1363 match l.kind {
1364 LightKind::Directional { direction } => {
1365 glam::Vec3::from(direction).normalize()
1366 }
1367 LightKind::Spot { direction, .. } => {
1368 glam::Vec3::from(direction).normalize()
1369 }
1370 _ => glam::Vec3::new(0.0, -1.0, 0.0),
1371 }
1372 } else {
1373 glam::Vec3::new(0.0, -1.0, 0.0)
1374 };
1375 let view = frame.camera.render_camera.view;
1376 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
1377 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
1378 let cs_uniform = crate::resources::ContactShadowUniform {
1379 inv_proj: inv_proj.to_cols_array_2d(),
1380 proj: proj.to_cols_array_2d(),
1381 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
1382 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
1383 params: [
1384 pp.contact_shadow_max_distance,
1385 pp.contact_shadow_steps as f32,
1386 pp.contact_shadow_thickness,
1387 0.0,
1388 ],
1389 };
1390 queue.write_buffer(
1391 &hdr.contact_shadow_uniform_buf,
1392 0,
1393 bytemuck::cast_slice(&[cs_uniform]),
1394 );
1395 }
1396
1397 if pp.bloom {
1399 let bloom_u = crate::resources::BloomUniform {
1400 threshold: pp.bloom_threshold,
1401 intensity: pp.bloom_intensity,
1402 horizontal: 0,
1403 _pad: 0,
1404 };
1405 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
1406 }
1407 }
1408
1409 if pp.dof_enabled {
1411 let (w, h) = {
1412 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1413 (hdr.scene_size[0] as f32, hdr.scene_size[1] as f32)
1414 };
1415 let dof_uniform = crate::resources::DofUniform {
1416 focal_distance: pp.dof_focal_distance,
1417 focal_range: pp.dof_focal_range,
1418 max_blur_radius: pp.dof_max_blur_radius,
1419 near_plane: frame.camera.render_camera.near,
1420 far_plane: frame.camera.render_camera.far,
1421 viewport_width: w,
1422 viewport_height: h,
1423 _pad: 0.0,
1424 };
1425 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1426 queue.write_buffer(
1427 &hdr.dof_uniform_buf,
1428 0,
1429 bytemuck::cast_slice(&[dof_uniform]),
1430 );
1431 }
1432
1433 {
1435 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1436 self.resources.rebuild_tone_map_bind_group(
1437 device,
1438 hdr,
1439 pp.bloom,
1440 pp.ssao,
1441 pp.contact_shadows,
1442 scene_items.iter().any(|i| i.lic.is_some() && !i.settings.hidden),
1443 pp.dof_enabled,
1444 );
1445 }
1446
1447 {
1452 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
1453 self.instanced_batches.iter().any(|b| b.is_transparent)
1454 } else {
1455 scene_items
1456 .iter()
1457 .any(|i| !i.settings.hidden && i.settings.opacity < 1.0)
1458 } || frame
1459 .scene
1460 .transparent_volume_meshes
1461 .iter()
1462 .any(|i| !i.settings.hidden);
1463 if needs_oit {
1464 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1465 let [sw, sh] = hdr.scene_size;
1466 self.resources
1467 .ensure_viewport_oit(device, hdr, sw, sh);
1468 }
1469 }
1470
1471 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1475 label: Some("hdr_encoder"),
1476 });
1477
1478 let slot = &self.viewport_slots[vp_idx];
1480 let camera_bg = &slot.camera_bind_group;
1481 let slot_hdr = slot.hdr.as_ref().expect(
1482 "HDR state missing; ensure_viewport_hdr must be called before render_frame_internal",
1483 );
1484
1485 {
1489 let use_ssaa = ssaa_factor > 1
1491 && slot_hdr.ssaa_colour_view.is_some()
1492 && slot_hdr.ssaa_depth_view.is_some();
1493 let scene_colour_view = if use_ssaa {
1494 slot_hdr.ssaa_colour_view.as_ref().unwrap()
1495 } else {
1496 &slot_hdr.hdr_view
1497 };
1498 let scene_depth_view = if use_ssaa {
1499 slot_hdr.ssaa_depth_view.as_ref().unwrap()
1500 } else {
1501 &slot_hdr.hdr_depth_view
1502 };
1503
1504 let clear_wgpu = wgpu::Color {
1505 r: hdr_clear_rgb[0] as f64,
1506 g: hdr_clear_rgb[1] as f64,
1507 b: hdr_clear_rgb[2] as f64,
1508 a: 0.0,
1511 };
1512
1513 let hdr_ts_writes =
1514 self.ts_query_set
1515 .as_ref()
1516 .map(|qs| wgpu::RenderPassTimestampWrites {
1517 query_set: qs,
1518 beginning_of_pass_write_index: Some(0),
1519 end_of_pass_write_index: Some(1),
1520 });
1521 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1522 label: Some("hdr_scene_pass"),
1523 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1524 view: scene_colour_view,
1525 resolve_target: None,
1526 ops: wgpu::Operations {
1527 load: wgpu::LoadOp::Clear(clear_wgpu),
1528 store: wgpu::StoreOp::Store,
1529 },
1530 depth_slice: None,
1531 })],
1532 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1533 view: scene_depth_view,
1534 depth_ops: Some(wgpu::Operations {
1535 load: wgpu::LoadOp::Clear(1.0),
1536 store: wgpu::StoreOp::Store,
1537 }),
1538 stencil_ops: Some(wgpu::Operations {
1539 load: wgpu::LoadOp::Clear(1),
1540 store: wgpu::StoreOp::Store,
1541 }),
1542 }),
1543 timestamp_writes: hdr_ts_writes,
1544 occlusion_query_set: None,
1545 });
1546
1547 let resources = &self.resources;
1548 render_pass.set_bind_group(0, camera_bg, &[]);
1549
1550 let show_skybox = frame
1552 .effects
1553 .environment
1554 .as_ref()
1555 .is_some_and(|e| e.show_skybox)
1556 && resources.ibl_skybox_view.is_some();
1557
1558 let use_instancing = self.use_instancing;
1559 let batches = &self.instanced_batches;
1560 let compute_filter_results = &self.compute_filter_results;
1561
1562 if !scene_items.is_empty() {
1563 if use_instancing && !batches.is_empty() {
1564 let excluded_items: Vec<&SceneRenderItem> = scene_items
1565 .iter()
1566 .filter(|item| {
1567 !item.settings.hidden
1568 && (item.active_attribute.is_some()
1569 || item.material.is_two_sided()
1570 || item.material.matcap_id().is_some())
1571 && resources.mesh_store.get(item.mesh_id).is_some()
1572 })
1573 .collect();
1574
1575 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1579 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1580 for (batch_global_idx, batch) in batches.iter().enumerate() {
1581 if batch.is_transparent {
1582 transparent_batches.push((batch_global_idx, batch));
1583 } else {
1584 opaque_batches.push((batch_global_idx, batch));
1585 }
1586 }
1587
1588 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
1589 let use_indirect = self.gpu_culling_enabled
1590 && resources.hdr_solid_instanced_cull_pipeline.is_some()
1591 && resources.indirect_args_buf.is_some();
1592
1593 if use_indirect {
1594 if let (Some(pipeline), Some(indirect_buf)) = (
1595 &resources.hdr_solid_instanced_cull_pipeline,
1596 &resources.indirect_args_buf,
1597 ) {
1598 render_pass.set_pipeline(pipeline);
1599 for (batch_global_idx, batch) in &opaque_batches {
1600 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1601 continue;
1602 };
1603 let mat_key = (
1604 batch.texture_id.unwrap_or(u64::MAX),
1605 batch.normal_map_id.unwrap_or(u64::MAX),
1606 batch.ao_map_id.unwrap_or(u64::MAX),
1607 );
1608 let Some(inst_tex_bg) =
1609 resources.instance_cull_bind_groups.get(&mat_key)
1610 else {
1611 continue;
1612 };
1613 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1614 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1615 render_pass.set_index_buffer(
1616 mesh.index_buffer.slice(..),
1617 wgpu::IndexFormat::Uint32,
1618 );
1619 render_pass.draw_indexed_indirect(
1622 indirect_buf,
1623 *batch_global_idx as u64 * 20,
1624 );
1625 }
1626 }
1627 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
1628 render_pass.set_pipeline(pipeline);
1629 for (_, batch) in &opaque_batches {
1630 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1631 continue;
1632 };
1633 let mat_key = (
1634 batch.texture_id.unwrap_or(u64::MAX),
1635 batch.normal_map_id.unwrap_or(u64::MAX),
1636 batch.ao_map_id.unwrap_or(u64::MAX),
1637 );
1638 let Some(inst_tex_bg) =
1639 resources.instance_bind_groups.get(&mat_key)
1640 else {
1641 continue;
1642 };
1643 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1644 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1645 render_pass.set_index_buffer(
1646 mesh.index_buffer.slice(..),
1647 wgpu::IndexFormat::Uint32,
1648 );
1649 render_pass.draw_indexed(
1650 0..mesh.index_count,
1651 0,
1652 batch.instance_offset
1653 ..batch.instance_offset + batch.instance_count,
1654 );
1655 }
1656 }
1657 }
1658
1659 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
1664 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
1665 let mut wf_idx = 0usize;
1666 for item in scene_items {
1667 if item.settings.hidden {
1668 continue;
1669 }
1670 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1671 continue;
1672 };
1673 let skin_bg = item.skin_instance.and_then(|inst| {
1674 resources.skin_instance_bind_group(item.mesh_id, inst)
1675 });
1676 if let (Some(bg), Some(hdr_skinned_wf)) =
1677 (skin_bg, resources.hdr_skinned_wireframe_pipeline.as_ref())
1678 {
1679 render_pass.set_pipeline(hdr_skinned_wf);
1680 render_pass.set_bind_group(2, bg, &[]);
1681 } else {
1682 render_pass.set_pipeline(hdr_wf);
1683 }
1684 let bg = self
1685 .wireframe_bind_groups
1686 .get(wf_idx)
1687 .unwrap_or(&mesh.object_bind_group);
1688 render_pass.set_bind_group(1, bg, &[]);
1689 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1690 render_pass.set_index_buffer(
1691 mesh.edge_index_buffer.slice(..),
1692 wgpu::IndexFormat::Uint32,
1693 );
1694 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1695 wf_idx += 1;
1696 }
1697 }
1698 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
1699 &resources.hdr_solid_pipeline,
1700 &resources.hdr_solid_two_sided_pipeline,
1701 ) {
1702 for item in excluded_items
1708 .into_iter()
1709 .filter(|item| item.settings.opacity >= 1.0 && !item.material.is_blend())
1710 {
1711 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1712 continue;
1713 };
1714 let skin_bg = item.skin_instance.and_then(|inst| {
1715 resources.skin_instance_bind_group(item.mesh_id, inst)
1716 });
1717 if let (Some(bg), Some(hdr_skinned_pl)) = (
1718 skin_bg,
1719 if item.material.is_two_sided() {
1720 resources.hdr_skinned_solid_two_sided_pipeline.as_ref()
1721 } else {
1722 resources.hdr_skinned_solid_pipeline.as_ref()
1723 },
1724 ) {
1725 render_pass.set_pipeline(hdr_skinned_pl);
1726 render_pass.set_bind_group(2, bg, &[]);
1727 } else {
1728 let pipeline = if item.material.is_two_sided() {
1729 hdr_solid_two_sided
1730 } else {
1731 hdr_solid
1732 };
1733 render_pass.set_pipeline(pipeline);
1734 }
1735 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1736 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1737 let filter = compute_filter_results
1738 .iter()
1739 .find(|r| r.mesh_id == item.mesh_id);
1740 if let Some(fr) = filter {
1741 render_pass.set_index_buffer(
1742 fr.index_buffer.slice(..),
1743 wgpu::IndexFormat::Uint32,
1744 );
1745 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1746 } else {
1747 render_pass.set_index_buffer(
1748 mesh.index_buffer.slice(..),
1749 wgpu::IndexFormat::Uint32,
1750 );
1751 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1752 }
1753 }
1754 }
1755
1756 if let Some(hdr_wf) = &resources.hdr_wireframe_pipeline {
1760 for item in scene_items.iter().filter(|i| i.show_normals && !i.settings.hidden) {
1761 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1762 continue;
1763 };
1764 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1765 if mesh.normal_line_count > 0 {
1766 render_pass.set_pipeline(hdr_wf);
1767 render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
1768 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1769 render_pass.draw(0..mesh.normal_line_count, 0..1);
1770 }
1771 }
1772 }
1773 }
1774 } else {
1775 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
1777 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
1778 let pos =
1779 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
1780 (pos - eye).length()
1781 };
1782
1783 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
1784 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
1785 for item in scene_items {
1786 if item.settings.hidden || resources.mesh_store.get(item.mesh_id).is_none() {
1787 continue;
1788 }
1789 if item.settings.opacity < 1.0 || item.material.is_blend() {
1790 transparent.push(item);
1791 } else {
1792 opaque.push(item);
1793 }
1794 }
1795 opaque.sort_by(|a, b| {
1796 dist_from_eye(a)
1797 .partial_cmp(&dist_from_eye(b))
1798 .unwrap_or(std::cmp::Ordering::Equal)
1799 });
1800 transparent.sort_by(|a, b| {
1801 dist_from_eye(b)
1802 .partial_cmp(&dist_from_eye(a))
1803 .unwrap_or(std::cmp::Ordering::Equal)
1804 });
1805
1806 let draw_item_hdr =
1807 |render_pass: &mut wgpu::RenderPass<'_>,
1808 item: &SceneRenderItem,
1809 solid_pl: &wgpu::RenderPipeline,
1810 trans_pl: &wgpu::RenderPipeline,
1811 wf_pl: &wgpu::RenderPipeline| {
1812 let mesh = resources.mesh_store.get(item.mesh_id).unwrap();
1813 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1816
1817 let skin_bg = item.skin_instance.and_then(|inst| {
1822 resources.skin_instance_bind_group(item.mesh_id, inst)
1823 });
1824 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1825 matches!(
1826 a.kind,
1827 crate::resources::AttributeKind::Face
1828 | crate::resources::AttributeKind::FaceColour
1829 | crate::resources::AttributeKind::Halfedge
1830 | crate::resources::AttributeKind::Corner
1831 )
1832 });
1833 if frame.viewport.wireframe_mode {
1834 if let (Some(bg), Some(hdr_skinned_wf)) =
1835 (skin_bg, resources.hdr_skinned_wireframe_pipeline.as_ref())
1836 {
1837 render_pass.set_pipeline(hdr_skinned_wf);
1838 render_pass.set_bind_group(2, bg, &[]);
1839 } else {
1840 render_pass.set_pipeline(wf_pl);
1841 }
1842 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1843 render_pass.set_index_buffer(
1844 mesh.edge_index_buffer.slice(..),
1845 wgpu::IndexFormat::Uint32,
1846 );
1847 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1848 } else if is_face_attr {
1849 if let Some(ref fvb) = mesh.face_vertex_buffer {
1850 let pl = if item.settings.opacity < 1.0 {
1851 trans_pl
1852 } else {
1853 solid_pl
1854 };
1855 render_pass.set_pipeline(pl);
1856 render_pass.set_vertex_buffer(0, fvb.slice(..));
1857 render_pass.draw(0..mesh.index_count, 0..1);
1858 }
1859 } else {
1860 let filter = compute_filter_results
1861 .iter()
1862 .find(|r| r.mesh_id == item.mesh_id);
1863 let pl = if item.settings.opacity < 1.0 {
1864 trans_pl
1865 } else {
1866 solid_pl
1867 };
1868 let is_blended = item.settings.opacity < 1.0
1869 || item.material.is_blend();
1870 let hdr_skinned_pl = if is_blended {
1871 resources.hdr_skinned_transparent_pipeline.as_ref()
1872 } else if item.material.is_two_sided() {
1873 resources.hdr_skinned_solid_two_sided_pipeline.as_ref()
1874 } else {
1875 resources.hdr_skinned_solid_pipeline.as_ref()
1876 };
1877 if let (Some(bg), Some(hdr_skinned)) = (skin_bg, hdr_skinned_pl)
1878 {
1879 render_pass.set_pipeline(hdr_skinned);
1880 render_pass.set_bind_group(2, bg, &[]);
1881 } else {
1882 render_pass.set_pipeline(pl);
1883 }
1884 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1885 if let Some(fr) = filter {
1886 render_pass.set_index_buffer(
1887 fr.index_buffer.slice(..),
1888 wgpu::IndexFormat::Uint32,
1889 );
1890 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1891 } else {
1892 render_pass.set_index_buffer(
1893 mesh.index_buffer.slice(..),
1894 wgpu::IndexFormat::Uint32,
1895 );
1896 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1897 }
1898 }
1899 if item.show_normals {
1900 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1901 if mesh.normal_line_count > 0 {
1902 render_pass.set_pipeline(wf_pl);
1903 render_pass.set_bind_group(
1904 1,
1905 &mesh.normal_bind_group,
1906 &[],
1907 );
1908 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1909 render_pass.draw(0..mesh.normal_line_count, 0..1);
1910 }
1911 }
1912 }
1913 };
1914
1915 let _ = &transparent; if let (
1919 Some(hdr_solid),
1920 Some(hdr_solid_two_sided),
1921 Some(hdr_trans),
1922 Some(hdr_wf),
1923 ) = (
1924 &resources.hdr_solid_pipeline,
1925 &resources.hdr_solid_two_sided_pipeline,
1926 &resources.hdr_transparent_pipeline,
1927 &resources.hdr_wireframe_pipeline,
1928 ) {
1929 for item in &opaque {
1930 let solid_pl = if item.material.is_two_sided() {
1931 hdr_solid_two_sided
1932 } else {
1933 hdr_solid
1934 };
1935 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
1936 }
1937 }
1938 }
1939 }
1940
1941 if !slot.cap_buffers.is_empty() {
1943 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
1944 render_pass.set_pipeline(hdr_overlay);
1945 render_pass.set_bind_group(0, camera_bg, &[]);
1946 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
1947 render_pass.set_bind_group(1, bg, &[]);
1948 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1949 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1950 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
1951 }
1952 }
1953 }
1954
1955 emit_scivis_draw_calls!(
1957 &self.resources,
1958 &mut render_pass,
1959 &self.point_cloud_gpu_data,
1960 &self.glyph_gpu_data,
1961 &self.polyline_gpu_data,
1962 &self.volume_gpu_data,
1963 &self.streamtube_gpu_data,
1964 camera_bg,
1965 &self.tube_gpu_data,
1966 &self.image_slice_gpu_data,
1967 &self.tensor_glyph_gpu_data,
1968 &self.ribbon_gpu_data,
1969 &self.volume_surface_slice_gpu_data,
1970 &self.sprite_gpu_data,
1971 true
1972 );
1973
1974 if !self.implicit_gpu_data.is_empty() {
1976 if let Some(ref dual) = self.resources.implicit_pipeline {
1977 render_pass.set_pipeline(dual.for_format(true));
1978 render_pass.set_bind_group(0, camera_bg, &[]);
1979 for gpu in &self.implicit_gpu_data {
1980 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1981 render_pass.draw(0..6, 0..1);
1982 }
1983 }
1984 }
1985 if !self.mc_gpu_data.is_empty() {
1987 render_pass.set_bind_group(0, camera_bg, &[]);
1988 for mc in &self.mc_gpu_data {
1989 let vol = &self.resources.mc_volumes[mc.volume_idx];
1990 if mc.wireframe || frame.viewport.wireframe_mode {
1991 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
1992 render_pass.set_pipeline(dual.for_format(true));
1993 for (slab, wire_bg) in
1994 vol.slabs.iter().zip(mc.wire_slab_bgs.iter())
1995 {
1996 render_pass.set_bind_group(1, wire_bg, &[]);
1997 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
1998 }
1999 }
2000 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
2001 render_pass.set_pipeline(dual.for_format(true));
2002 render_pass.set_bind_group(1, &mc.render_bg, &[]);
2003 for slab in &vol.slabs {
2004 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
2005 render_pass.draw_indirect(&slab.indirect_buf, 0);
2006 }
2007 }
2008 }
2009 }
2010
2011 if !self.gaussian_splat_draw_data.is_empty() {
2013 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
2014 render_pass.set_pipeline(dual.for_format(true));
2015 render_pass.set_bind_group(0, camera_bg, &[]);
2016 for dd in &self.gaussian_splat_draw_data {
2017 if dd.wireframe {
2018 continue;
2019 }
2020 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
2021 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
2022 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
2023 render_pass.draw(0..6, 0..dd.count);
2024 }
2025 }
2026 }
2027 }
2028 }
2029 if !self.tvm_wireframe_draws.is_empty() {
2031 if let (Some(tvm_bg), Some(hdr_wf)) = (&self.tvm_wireframe_bg, &resources.hdr_wireframe_pipeline) {
2032 for mesh_id in &self.tvm_wireframe_draws {
2033 if let Some(mesh) = resources.mesh_store.get(*mesh_id) {
2034 render_pass.set_pipeline(hdr_wf);
2035 render_pass.set_bind_group(1, tvm_bg, &[]);
2036 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2037 render_pass.set_index_buffer(
2038 mesh.edge_index_buffer.slice(..),
2039 wgpu::IndexFormat::Uint32,
2040 );
2041 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
2042 }
2043 }
2044 }
2045 }
2046
2047 if show_skybox {
2049 render_pass.set_bind_group(0, camera_bg, &[]);
2050 render_pass.set_pipeline(&resources.skybox_pipeline);
2051 render_pass.draw(0..3, 0..1);
2052 }
2053 }
2054
2055 if ssaa_factor > 1 {
2060 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2061 if let (Some(pipeline), Some(bg)) = (
2062 &self.resources.ssaa_resolve_pipeline,
2063 &slot_hdr.ssaa_resolve_bind_group,
2064 ) {
2065 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2066 label: Some("ssaa_resolve_pass"),
2067 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2068 view: &slot_hdr.hdr_view,
2069 resolve_target: None,
2070 ops: wgpu::Operations {
2071 load: wgpu::LoadOp::Load,
2072 store: wgpu::StoreOp::Store,
2073 },
2074 depth_slice: None,
2075 })],
2076 depth_stencil_attachment: None,
2077 timestamp_writes: None,
2078 occlusion_query_set: None,
2079 });
2080 resolve_pass.set_pipeline(pipeline);
2081 resolve_pass.set_bind_group(0, bg, &[]);
2082 resolve_pass.draw(0..3, 0..1);
2083 }
2084 }
2085
2086 if !self.decal_exclude_items.is_empty() {
2091 if let Some(exclude_pl) = self.resources.decal_exclude_pipeline.as_ref() {
2092 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2093 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2094 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2095 label: Some("decal_exclude_pass"),
2096 color_attachments: &[],
2097 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2098 view: &slot_hdr.hdr_depth_view,
2099 depth_ops: Some(wgpu::Operations {
2100 load: wgpu::LoadOp::Load,
2101 store: wgpu::StoreOp::Store,
2102 }),
2103 stencil_ops: Some(wgpu::Operations {
2104 load: wgpu::LoadOp::Load,
2105 store: wgpu::StoreOp::Store,
2106 }),
2107 }),
2108 timestamp_writes: None,
2109 occlusion_query_set: None,
2110 });
2111 pass.set_pipeline(exclude_pl);
2112 pass.set_stencil_reference(0);
2113 pass.set_bind_group(0, camera_bg, &[]);
2114 for item in &self.decal_exclude_items {
2115 if let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) {
2116 pass.set_bind_group(1, &item.bind_group, &[]);
2117 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2118 if mesh.index_count > 0 {
2119 pass.set_index_buffer(
2120 mesh.index_buffer.slice(..),
2121 wgpu::IndexFormat::Uint32,
2122 );
2123 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2124 }
2125 }
2126 }
2127 }
2128 }
2129
2130 if !self.decal_gpu_data.is_empty() {
2136 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2137 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2138 let depth_bg = &slot_hdr.decal_depth_bg;
2139 let replace_pipeline = self.resources.decal_replace_pipeline.as_ref();
2140 let multiply_pipeline = self.resources.decal_multiply_pipeline.as_ref();
2141 let additive_pipeline = self.resources.decal_additive_pipeline.as_ref();
2142 if replace_pipeline.is_some() || multiply_pipeline.is_some() || additive_pipeline.is_some() {
2143 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2144 label: Some("decal_pass"),
2145 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2146 view: &slot_hdr.hdr_view,
2147 resolve_target: None,
2148 ops: wgpu::Operations {
2149 load: wgpu::LoadOp::Load,
2150 store: wgpu::StoreOp::Store,
2151 },
2152 depth_slice: None,
2153 })],
2154 depth_stencil_attachment: None,
2155 timestamp_writes: None,
2156 occlusion_query_set: None,
2157 });
2158 pass.set_bind_group(0, camera_bg, &[]);
2159 pass.set_bind_group(1, depth_bg, &[]);
2160 for gpu in &self.decal_gpu_data {
2161 let pipeline = match gpu.blend_mode {
2162 crate::renderer::DecalBlendMode::Replace => replace_pipeline,
2163 crate::renderer::DecalBlendMode::Multiply => multiply_pipeline,
2164 crate::renderer::DecalBlendMode::Additive => additive_pipeline,
2165 };
2166 if let Some(pl) = pipeline {
2167 pass.set_pipeline(&pl.hdr);
2168 pass.set_bind_group(2, &gpu.bind_group, &[]);
2169 pass.draw(0..6, 0..1);
2170 }
2171 }
2172 }
2173 }
2174
2175 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
2181 let resources = &self.resources;
2182 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
2183 &resources.sub_highlight_fill_pipeline,
2184 &resources.sub_highlight_edge_pipeline,
2185 &resources.sub_highlight_sprite_pipeline,
2186 ) {
2187 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2188 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2189 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2190 label: Some("sub_highlight_pass"),
2191 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2192 view: &slot_hdr.hdr_view,
2193 resolve_target: None,
2194 ops: wgpu::Operations {
2195 load: wgpu::LoadOp::Load,
2196 store: wgpu::StoreOp::Store,
2197 },
2198 depth_slice: None,
2199 })],
2200 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2201 view: &slot_hdr.hdr_depth_view,
2202 depth_ops: Some(wgpu::Operations {
2203 load: wgpu::LoadOp::Load,
2204 store: wgpu::StoreOp::Store,
2210 }),
2211 stencil_ops: None,
2212 }),
2213 timestamp_writes: None,
2214 occlusion_query_set: None,
2215 });
2216
2217 if sub_hl.fill_vertex_count > 0 {
2218 pass.set_pipeline(fill_pl);
2219 pass.set_bind_group(0, camera_bg, &[]);
2220 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
2221 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
2222 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
2223 }
2224 if sub_hl.edge_segment_count > 0 {
2225 pass.set_pipeline(edge_pl);
2226 pass.set_bind_group(0, camera_bg, &[]);
2227 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
2228 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
2229 pass.draw(0..6, 0..sub_hl.edge_segment_count);
2230 }
2231 if sub_hl.sprite_point_count > 0 {
2232 pass.set_pipeline(sprite_pl);
2233 pass.set_bind_group(0, camera_bg, &[]);
2234 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
2235 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
2236 pass.draw(0..6, 0..sub_hl.sprite_point_count);
2237 }
2238 }
2239 }
2240
2241 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
2246 self.instanced_batches.iter().any(|b| b.is_transparent)
2251 || scene_items.iter().any(|i| {
2252 !i.settings.hidden
2253 && (i.settings.opacity < 1.0 || i.material.is_blend())
2254 && (i.active_attribute.is_some()
2255 || i.material.is_two_sided()
2256 || i.material.matcap_id().is_some())
2257 })
2258 } else {
2259 scene_items
2260 .iter()
2261 .any(|i| !i.settings.hidden && i.settings.opacity < 1.0)
2262 } || frame
2263 .scene
2264 .transparent_volume_meshes
2265 .iter()
2266 .any(|i| !i.settings.hidden);
2267
2268 if has_transparent {
2269 if let (Some(accum_view), Some(reveal_view)) = (
2271 slot_hdr.oit_accum_view.as_ref(),
2272 slot_hdr.oit_reveal_view.as_ref(),
2273 ) {
2274 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2275 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2277 label: Some("oit_pass"),
2278 color_attachments: &[
2279 Some(wgpu::RenderPassColorAttachment {
2280 view: accum_view,
2281 resolve_target: None,
2282 ops: wgpu::Operations {
2283 load: wgpu::LoadOp::Clear(wgpu::Color {
2284 r: 0.0,
2285 g: 0.0,
2286 b: 0.0,
2287 a: 0.0,
2288 }),
2289 store: wgpu::StoreOp::Store,
2290 },
2291 depth_slice: None,
2292 }),
2293 Some(wgpu::RenderPassColorAttachment {
2294 view: reveal_view,
2295 resolve_target: None,
2296 ops: wgpu::Operations {
2297 load: wgpu::LoadOp::Clear(wgpu::Color {
2298 r: 1.0,
2299 g: 1.0,
2300 b: 1.0,
2301 a: 1.0,
2302 }),
2303 store: wgpu::StoreOp::Store,
2304 },
2305 depth_slice: None,
2306 }),
2307 ],
2308 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2309 view: hdr_depth_view,
2310 depth_ops: Some(wgpu::Operations {
2311 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
2313 }),
2314 stencil_ops: None,
2315 }),
2316 timestamp_writes: None,
2317 occlusion_query_set: None,
2318 });
2319
2320 oit_pass.set_bind_group(0, camera_bg, &[]);
2321
2322 if self.use_instancing && !self.instanced_batches.is_empty() {
2323 let use_indirect_oit = self.gpu_culling_enabled
2324 && self.resources.oit_instanced_cull_pipeline.is_some()
2325 && self.resources.indirect_args_buf.is_some();
2326
2327 if use_indirect_oit {
2328 if let (Some(pipeline), Some(indirect_buf)) = (
2329 &self.resources.oit_instanced_cull_pipeline,
2330 &self.resources.indirect_args_buf,
2331 ) {
2332 oit_pass.set_pipeline(pipeline);
2333 for (batch_global_idx, batch) in
2334 self.instanced_batches.iter().enumerate()
2335 {
2336 if !batch.is_transparent {
2337 continue;
2338 }
2339 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id)
2340 else {
2341 continue;
2342 };
2343 let mat_key = (
2344 batch.texture_id.unwrap_or(u64::MAX),
2345 batch.normal_map_id.unwrap_or(u64::MAX),
2346 batch.ao_map_id.unwrap_or(u64::MAX),
2347 );
2348 let Some(inst_tex_bg) =
2349 self.resources.instance_cull_bind_groups.get(&mat_key)
2350 else {
2351 continue;
2352 };
2353 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
2354 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2355 oit_pass.set_index_buffer(
2356 mesh.index_buffer.slice(..),
2357 wgpu::IndexFormat::Uint32,
2358 );
2359 oit_pass.draw_indexed_indirect(
2360 indirect_buf,
2361 batch_global_idx as u64 * 20,
2362 );
2363 }
2364 }
2365 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
2366 oit_pass.set_pipeline(pipeline);
2367 for batch in &self.instanced_batches {
2368 if !batch.is_transparent {
2369 continue;
2370 }
2371 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id) else {
2372 continue;
2373 };
2374 let mat_key = (
2375 batch.texture_id.unwrap_or(u64::MAX),
2376 batch.normal_map_id.unwrap_or(u64::MAX),
2377 batch.ao_map_id.unwrap_or(u64::MAX),
2378 );
2379 let Some(inst_tex_bg) =
2380 self.resources.instance_bind_groups.get(&mat_key)
2381 else {
2382 continue;
2383 };
2384 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
2385 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2386 oit_pass.set_index_buffer(
2387 mesh.index_buffer.slice(..),
2388 wgpu::IndexFormat::Uint32,
2389 );
2390 oit_pass.draw_indexed(
2391 0..mesh.index_count,
2392 0,
2393 batch.instance_offset..batch.instance_offset + batch.instance_count,
2394 );
2395 }
2396 }
2397
2398 if let Some(ref pipeline) = self.resources.oit_pipeline {
2402 oit_pass.set_pipeline(pipeline);
2403 let mut last_skinned = false;
2404 for item in scene_items {
2405 if item.settings.hidden || (item.settings.opacity >= 1.0 && !item.material.is_blend()) {
2406 continue;
2407 }
2408 let skin_bg = item.skin_instance.and_then(|inst| {
2409 self.resources.skin_instance_bind_group(item.mesh_id, inst)
2410 });
2411 if skin_bg.is_none()
2412 && item.active_attribute.is_none()
2413 && !item.material.is_two_sided()
2414 && item.material.matcap_id().is_none()
2415 {
2416 continue;
2417 }
2418 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
2419 continue;
2420 };
2421 let want_skinned = skin_bg.is_some()
2422 && self.resources.skinned_oit_pipeline.is_some();
2423 if want_skinned != last_skinned {
2424 if want_skinned {
2425 oit_pass.set_pipeline(
2426 self.resources.skinned_oit_pipeline.as_ref().unwrap(),
2427 );
2428 } else {
2429 oit_pass.set_pipeline(pipeline);
2430 }
2431 last_skinned = want_skinned;
2432 }
2433 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
2434 if let Some(bg) = skin_bg {
2435 oit_pass.set_bind_group(2, bg, &[]);
2436 }
2437 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2438 oit_pass.set_index_buffer(
2439 mesh.index_buffer.slice(..),
2440 wgpu::IndexFormat::Uint32,
2441 );
2442 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2443 }
2444 }
2445 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
2446 oit_pass.set_pipeline(pipeline);
2447 let mut last_skinned = false;
2448 for item in scene_items {
2449 if item.settings.hidden || item.settings.opacity >= 1.0 {
2450 continue;
2451 }
2452 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
2453 continue;
2454 };
2455 let skin_bg = item.skin_instance.and_then(|inst| {
2456 self.resources.skin_instance_bind_group(item.mesh_id, inst)
2457 });
2458 let want_skinned = skin_bg.is_some()
2459 && self.resources.skinned_oit_pipeline.is_some();
2460 if want_skinned != last_skinned {
2461 if want_skinned {
2462 oit_pass.set_pipeline(
2463 self.resources.skinned_oit_pipeline.as_ref().unwrap(),
2464 );
2465 } else {
2466 oit_pass.set_pipeline(pipeline);
2467 }
2468 last_skinned = want_skinned;
2469 }
2470 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
2471 if let Some(bg) = skin_bg {
2472 oit_pass.set_bind_group(2, bg, &[]);
2473 }
2474 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2475 oit_pass.set_index_buffer(
2476 mesh.index_buffer.slice(..),
2477 wgpu::IndexFormat::Uint32,
2478 );
2479 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2480 }
2481 }
2482
2483 if !frame.scene.transparent_volume_meshes.is_empty() {
2487 self.resources.ensure_pt_pipeline(device);
2488 if let Some(pipeline) = self.resources.pt_pipeline.as_ref() {
2489 oit_pass.set_pipeline(pipeline);
2490 oit_pass.set_bind_group(0, camera_bg, &[]);
2491 for item in &frame.scene.transparent_volume_meshes {
2492 if item.settings.hidden {
2493 continue;
2494 }
2495 if item.settings.wireframe || frame.viewport.wireframe_mode {
2496 continue;
2497 }
2498 let Some(gpu) = self.resources.projected_tet_store.get(item.id.0)
2499 else {
2500 continue;
2501 };
2502 let (scalar_min, scalar_max) =
2503 item.scalar_range.unwrap_or(gpu.scalar_range);
2504 let uniform = crate::resources::ProjectedTetUniform {
2505 density: item.density,
2506 scalar_min,
2507 scalar_max,
2508 threshold_min: item.threshold_min,
2509 threshold_max: item.threshold_max,
2510 unlit: if item.settings.unlit { 1 } else { 0 },
2511 };
2512 queue.write_buffer(
2513 &gpu.uniform_buffer,
2514 0,
2515 bytemuck::bytes_of(&uniform),
2516 );
2517 for chunk in &gpu.chunks {
2518 oit_pass.set_bind_group(1, &chunk.bind_group, &[]);
2519 oit_pass.draw(0..6, 0..chunk.tet_count);
2520 }
2521 }
2522 }
2523 }
2524 }
2525 }
2526
2527 if has_transparent {
2532 if let (Some(pipeline), Some(bg)) = (
2533 self.resources.oit_composite_pipeline.as_ref(),
2534 slot_hdr.oit_composite_bind_group.as_ref(),
2535 ) {
2536 let hdr_view = &slot_hdr.hdr_view;
2537 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2538 label: Some("oit_composite_pass"),
2539 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2540 view: hdr_view,
2541 resolve_target: None,
2542 ops: wgpu::Operations {
2543 load: wgpu::LoadOp::Load,
2544 store: wgpu::StoreOp::Store,
2545 },
2546 depth_slice: None,
2547 })],
2548 depth_stencil_attachment: None,
2549 timestamp_writes: None,
2550 occlusion_query_set: None,
2551 });
2552 composite_pass.set_pipeline(pipeline);
2553 composite_pass.set_bind_group(0, bg, &[]);
2554 composite_pass.draw(0..3, 0..1);
2555 }
2556 }
2557
2558 if !self.prepared_scatter_volumes.is_empty() {
2566 let scatter = &frame.effects.scatter;
2567 let [sw, sh] = slot_hdr.scene_size;
2568 let want_downsample = scatter.downsample;
2569 let (tw, th) = if want_downsample {
2570 ((sw / 2).max(1), (sh / 2).max(1))
2571 } else {
2572 (sw.max(1), sh.max(1))
2573 };
2574
2575 while self.scatter_viewport_states.len() <= vp_idx {
2577 self.scatter_viewport_states.push(None);
2578 }
2579
2580 self.resources
2582 .ensure_scatter_pipeline(device, wgpu::TextureFormat::Rgba16Float);
2583 self.resources
2584 .ensure_scatter_composite_pipeline(device, wgpu::TextureFormat::Rgba16Float);
2585 self.resources
2586 .ensure_scatter_temporal_resolve_pipeline(device);
2587
2588 let needs_alloc = match self.scatter_viewport_states[vp_idx].as_ref() {
2590 None => true,
2591 Some(s) => s.size != [tw, th] || s.downsampled != want_downsample,
2592 };
2593 if needs_alloc {
2594 let make_tex = |label: &str| {
2595 device.create_texture(&wgpu::TextureDescriptor {
2596 label: Some(label),
2597 size: wgpu::Extent3d {
2598 width: tw,
2599 height: th,
2600 depth_or_array_layers: 1,
2601 },
2602 mip_level_count: 1,
2603 sample_count: 1,
2604 dimension: wgpu::TextureDimension::D2,
2605 format: wgpu::TextureFormat::Rgba16Float,
2606 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
2607 | wgpu::TextureUsages::TEXTURE_BINDING,
2608 view_formats: &[],
2609 })
2610 };
2611 let raw_tex = make_tex("scatter_raw_current");
2612 let hist_a_tex = make_tex("scatter_history_a");
2613 let hist_b_tex = make_tex("scatter_history_b");
2614 let raw_view = raw_tex.create_view(&wgpu::TextureViewDescriptor::default());
2615 let hist_a_view = hist_a_tex.create_view(&wgpu::TextureViewDescriptor::default());
2616 let hist_b_view = hist_b_tex.create_view(&wgpu::TextureViewDescriptor::default());
2617 let composite_bg_raw = self
2618 .resources
2619 .make_scatter_composite_bg(device, &raw_view);
2620 let composite_bg_history_a = self
2621 .resources
2622 .make_scatter_composite_bg(device, &hist_a_view);
2623 let composite_bg_history_b = self
2624 .resources
2625 .make_scatter_composite_bg(device, &hist_b_view);
2626 let temporal_resolve_bg_read_a =
2627 self.resources.make_scatter_temporal_resolve_bg(
2628 device,
2629 queue,
2630 &raw_view,
2631 &hist_a_view,
2632 &slot_hdr.hdr_depth_only_view,
2633 );
2634 let temporal_resolve_bg_read_b =
2635 self.resources.make_scatter_temporal_resolve_bg(
2636 device,
2637 queue,
2638 &raw_view,
2639 &hist_b_view,
2640 &slot_hdr.hdr_depth_only_view,
2641 );
2642 self.scatter_viewport_states[vp_idx] =
2643 Some(crate::resources::ScatterViewportState {
2644 raw_current_texture: raw_tex,
2645 raw_current_view: raw_view,
2646 history_a_texture: hist_a_tex,
2647 history_a_view: hist_a_view,
2648 history_b_texture: hist_b_tex,
2649 history_b_view: hist_b_view,
2650 composite_bg_raw,
2651 composite_bg_history_a,
2652 composite_bg_history_b,
2653 temporal_resolve_bg_read_a,
2654 temporal_resolve_bg_read_b,
2655 size: [tw, th],
2656 downsampled: want_downsample,
2657 parity: 0,
2658 history_valid: false,
2659 prev_view_proj: [[0.0; 4]; 4],
2660 });
2661 }
2662
2663 let (parity, history_valid, prev_view_proj) = {
2664 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
2665 (s.parity, s.history_valid, s.prev_view_proj)
2666 };
2667
2668 self.resources.clear_scatter_per_volume_tex_cache();
2670 let n = self
2671 .resources
2672 .write_scatter_per_volume_buffer(device, queue, &self.prepared_scatter_volumes);
2673 let stride = self.resources.scatter_per_volume_stride();
2674
2675 let time_seconds = self.start_instant.elapsed().as_secs_f32();
2677 let global_steps = scatter.quality.default_steps();
2678 let depth_token = (vp_idx as u64).wrapping_mul(1_000_003)
2679 ^ (tw as u64).wrapping_mul(7919)
2680 ^ (th as u64).wrapping_mul(31);
2681 self.resources.write_scatter_frame_uniform(
2682 device,
2683 queue,
2684 &slot_hdr.hdr_depth_only_view,
2685 depth_token,
2686 time_seconds,
2687 global_steps,
2688 scatter.blue_noise_jitter,
2689 self.frame_counter,
2690 );
2691
2692 if scatter.temporal {
2694 self.resources.write_scatter_temporal_uniform(
2695 device,
2696 queue,
2697 prev_view_proj,
2698 scatter.temporal_blend,
2699 history_valid,
2700 );
2701 }
2702
2703 let mut per_vol_tex_bgs: Vec<wgpu::BindGroup> =
2705 Vec::with_capacity(n as usize);
2706 for (volume, _, _) in self.prepared_scatter_volumes.iter().take(n as usize) {
2707 let (lut_id, density_id) =
2708 crate::resources::ViewportGpuResources::scatter_volume_tex_ids(volume);
2709 let bg = self.resources.ensure_scatter_per_volume_tex_bg(
2710 device, queue, lut_id, density_id,
2711 );
2712 per_vol_tex_bgs.push(bg);
2713 }
2714
2715 if n > 0 {
2716 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
2717 let raw_view = &s.raw_current_view;
2718 if let (Some(pipeline), Some(per_vol_bg), Some(frame_bg)) = (
2719 self.resources.scatter_pipeline.as_ref(),
2720 self.resources.scatter_per_volume_bg.as_ref(),
2721 self.resources.scatter_frame_bg.as_ref(),
2722 ) {
2723 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2724 label: Some("scatter_volume_pass"),
2725 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2726 view: raw_view,
2727 resolve_target: None,
2728 ops: wgpu::Operations {
2729 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2730 store: wgpu::StoreOp::Store,
2731 },
2732 depth_slice: None,
2733 })],
2734 depth_stencil_attachment: None,
2735 timestamp_writes: None,
2736 occlusion_query_set: None,
2737 });
2738 pass.set_pipeline(pipeline);
2739 pass.set_bind_group(0, camera_bg, &[]);
2740 pass.set_bind_group(3, frame_bg, &[]);
2741 for i in 0..n {
2742 let dyn_offset = i * stride;
2743 pass.set_bind_group(1, per_vol_bg, &[dyn_offset]);
2744 pass.set_bind_group(2, &per_vol_tex_bgs[i as usize], &[]);
2745 pass.draw(0..6, 0..1);
2746 }
2747 }
2748
2749 let source_for_composite: &wgpu::BindGroup = if scatter.temporal {
2751 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
2752 let (resolve_bg, target_view, source_composite) = if parity == 0 {
2754 (
2756 &s.temporal_resolve_bg_read_b,
2757 &s.history_a_view,
2758 &s.composite_bg_history_a,
2759 )
2760 } else {
2761 (
2762 &s.temporal_resolve_bg_read_a,
2763 &s.history_b_view,
2764 &s.composite_bg_history_b,
2765 )
2766 };
2767 if let Some(resolve_pipeline) =
2768 self.resources.scatter_temporal_resolve_pipeline.as_ref()
2769 {
2770 let mut pass =
2771 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2772 label: Some("scatter_temporal_resolve_pass"),
2773 color_attachments: &[Some(
2774 wgpu::RenderPassColorAttachment {
2775 view: target_view,
2776 resolve_target: None,
2777 ops: wgpu::Operations {
2778 load: wgpu::LoadOp::Clear(
2779 wgpu::Color::TRANSPARENT,
2780 ),
2781 store: wgpu::StoreOp::Store,
2782 },
2783 depth_slice: None,
2784 },
2785 )],
2786 depth_stencil_attachment: None,
2787 timestamp_writes: None,
2788 occlusion_query_set: None,
2789 });
2790 pass.set_pipeline(resolve_pipeline);
2791 pass.set_bind_group(0, resolve_bg, &[]);
2792 pass.draw(0..3, 0..1);
2793 }
2794 source_composite
2795 } else {
2796 &s.composite_bg_raw
2797 };
2798
2799 if let Some(composite_pipeline) =
2801 self.resources.scatter_composite_pipeline.as_ref()
2802 {
2803 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2804 label: Some("scatter_composite_pass"),
2805 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2806 view: &slot_hdr.hdr_view,
2807 resolve_target: None,
2808 ops: wgpu::Operations {
2809 load: wgpu::LoadOp::Load,
2810 store: wgpu::StoreOp::Store,
2811 },
2812 depth_slice: None,
2813 })],
2814 depth_stencil_attachment: None,
2815 timestamp_writes: None,
2816 occlusion_query_set: None,
2817 });
2818 pass.set_pipeline(composite_pipeline);
2819 pass.set_bind_group(0, source_for_composite, &[]);
2820 pass.draw(0..3, 0..1);
2821 }
2822
2823 let s = self.scatter_viewport_states[vp_idx].as_mut().unwrap();
2825 s.prev_view_proj =
2826 frame.camera.render_camera.view_proj().to_cols_array_2d();
2827 s.parity = 1 - s.parity;
2828 s.history_valid = scatter.temporal;
2829 } else if let Some(s) = self.scatter_viewport_states[vp_idx].as_mut() {
2830 s.history_valid = false;
2831 }
2832 }
2833
2834 if !self.lic_gpu_data.is_empty() {
2840 if let (Some(surface_pipeline), Some(advect_pipeline)) = (
2841 self.resources.lic_surface_pipeline.as_ref(),
2842 self.resources.lic_advect_pipeline.as_ref(),
2843 ) {
2844 let camera_bg = &slot.camera_bind_group;
2845 {
2847 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2848 label: Some("lic_surface_pass"),
2849 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2850 view: &slot_hdr.lic_vector_view,
2851 resolve_target: None,
2852 ops: wgpu::Operations {
2853 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2854 store: wgpu::StoreOp::Store,
2855 },
2856 depth_slice: None,
2857 })],
2858 depth_stencil_attachment: None,
2859 timestamp_writes: None,
2860 occlusion_query_set: None,
2861 });
2862 pass.set_pipeline(surface_pipeline);
2863 pass.set_bind_group(0, camera_bg, &[]);
2864 for gpu in &self.lic_gpu_data {
2865 let Some(mesh) = self.resources.mesh_store.get(gpu.mesh_id) else {
2866 continue;
2867 };
2868 let Some(vec_buf) =
2869 mesh.vector_attribute_buffers.get(&gpu.vector_attribute)
2870 else {
2871 continue;
2872 };
2873 pass.set_bind_group(1, &gpu.bind_group, &[]);
2874 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2875 pass.set_vertex_buffer(1, vec_buf.slice(..));
2876 pass.set_index_buffer(
2877 mesh.index_buffer.slice(..),
2878 wgpu::IndexFormat::Uint32,
2879 );
2880 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2881 }
2882 }
2883 {
2885 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2886 label: Some("lic_advect_pass"),
2887 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2888 view: &slot_hdr.lic_output_view,
2889 resolve_target: None,
2890 ops: wgpu::Operations {
2891 load: wgpu::LoadOp::Clear(wgpu::Color {
2892 r: 0.5,
2893 g: 0.0,
2894 b: 0.0,
2895 a: 1.0,
2896 }),
2897 store: wgpu::StoreOp::Store,
2898 },
2899 depth_slice: None,
2900 })],
2901 depth_stencil_attachment: None,
2902 timestamp_writes: None,
2903 occlusion_query_set: None,
2904 });
2905 pass.set_pipeline(advect_pipeline);
2906 pass.set_bind_group(0, &slot_hdr.lic_advect_bind_group, &[]);
2907 pass.draw(0..3, 0..1);
2908 }
2909 }
2910 }
2911
2912 if !slot.outline_object_buffers.is_empty()
2918 || !slot.splat_outline_buffers.is_empty()
2919 || !slot.streamtube_outline_items.is_empty()
2920 || !slot.tube_outline_items.is_empty()
2921 || !slot.ribbon_outline_items.is_empty()
2922 || !slot.polyline_outline_indices.is_empty()
2923 || !slot.volume_outline_indices.is_empty()
2924 || !slot.glyph_outline_indices.is_empty()
2925 || !slot.tensor_glyph_outline_indices.is_empty()
2926 || !slot.sprite_outline_indices.is_empty()
2927 || !slot.raw_geom_outline_buffers.is_empty()
2928 || !slot.screen_rect_outline_buffers.is_empty()
2929 || !slot.implicit_outline_indices.is_empty()
2930 || !slot.mc_outline_data.is_empty()
2931 {
2932 let hdr_pipeline = self
2934 .resources
2935 .outline_composite_pipeline_hdr
2936 .as_ref()
2937 .or(self.resources.outline_composite_pipeline_single.as_ref());
2938 if let Some(pipeline) = hdr_pipeline {
2939 let bg = &slot_hdr.outline_composite_bind_group;
2940 let hdr_view = &slot_hdr.hdr_view;
2941 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2942 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2943 label: Some("hdr_outline_composite_pass"),
2944 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2945 view: hdr_view,
2946 resolve_target: None,
2947 ops: wgpu::Operations {
2948 load: wgpu::LoadOp::Load,
2949 store: wgpu::StoreOp::Store,
2950 },
2951 depth_slice: None,
2952 })],
2953 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2954 view: hdr_depth_view,
2955 depth_ops: Some(wgpu::Operations {
2956 load: wgpu::LoadOp::Load,
2957 store: wgpu::StoreOp::Store,
2958 }),
2959 stencil_ops: None,
2960 }),
2961 timestamp_writes: None,
2962 occlusion_query_set: None,
2963 });
2964 outline_pass.set_pipeline(pipeline);
2965 outline_pass.set_bind_group(0, bg, &[]);
2966 outline_pass.draw(0..3, 0..1);
2967 }
2968 }
2969
2970 let throttle_effects = self.degradation_effects_throttled;
2973
2974 if pp.ssao && !throttle_effects {
2978 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
2979 {
2980 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2981 label: Some("ssao_pass"),
2982 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2983 view: &slot_hdr.ssao_view,
2984 resolve_target: None,
2985 ops: wgpu::Operations {
2986 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2987 store: wgpu::StoreOp::Store,
2988 },
2989 depth_slice: None,
2990 })],
2991 depth_stencil_attachment: None,
2992 timestamp_writes: None,
2993 occlusion_query_set: None,
2994 });
2995 ssao_pass.set_pipeline(ssao_pipeline);
2996 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
2997 ssao_pass.draw(0..3, 0..1);
2998 }
2999
3000 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
3002 let mut ssao_blur_pass =
3003 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3004 label: Some("ssao_blur_pass"),
3005 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3006 view: &slot_hdr.ssao_blur_view,
3007 resolve_target: None,
3008 ops: wgpu::Operations {
3009 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3010 store: wgpu::StoreOp::Store,
3011 },
3012 depth_slice: None,
3013 })],
3014 depth_stencil_attachment: None,
3015 timestamp_writes: None,
3016 occlusion_query_set: None,
3017 });
3018 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
3019 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
3020 ssao_blur_pass.draw(0..3, 0..1);
3021 }
3022 }
3023 }
3024
3025 if pp.contact_shadows && !throttle_effects {
3029 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
3030 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3031 label: Some("contact_shadow_pass"),
3032 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3033 view: &slot_hdr.contact_shadow_view,
3034 resolve_target: None,
3035 depth_slice: None,
3036 ops: wgpu::Operations {
3037 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3038 store: wgpu::StoreOp::Store,
3039 },
3040 })],
3041 depth_stencil_attachment: None,
3042 timestamp_writes: None,
3043 occlusion_query_set: None,
3044 });
3045 cs_pass.set_pipeline(cs_pipeline);
3046 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
3047 cs_pass.draw(0..3, 0..1);
3048 }
3049 }
3050
3051 if pp.bloom && !throttle_effects {
3055 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
3057 {
3058 let mut threshold_pass =
3059 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3060 label: Some("bloom_threshold_pass"),
3061 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3062 view: &slot_hdr.bloom_threshold_view,
3063 resolve_target: None,
3064 ops: wgpu::Operations {
3065 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3066 store: wgpu::StoreOp::Store,
3067 },
3068 depth_slice: None,
3069 })],
3070 depth_stencil_attachment: None,
3071 timestamp_writes: None,
3072 occlusion_query_set: None,
3073 });
3074 threshold_pass.set_pipeline(bloom_threshold_pipeline);
3075 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
3076 threshold_pass.draw(0..3, 0..1);
3077 }
3078
3079 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
3082 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
3083 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
3084 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
3085 let bloom_ping_view = &slot_hdr.bloom_ping_view;
3086 let bloom_pong_view = &slot_hdr.bloom_pong_view;
3087 const BLUR_ITERATIONS: usize = 4;
3088 for i in 0..BLUR_ITERATIONS {
3089 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
3091 {
3092 let mut h_pass =
3093 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3094 label: Some("bloom_blur_h_pass"),
3095 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3096 view: bloom_ping_view,
3097 resolve_target: None,
3098 ops: wgpu::Operations {
3099 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3100 store: wgpu::StoreOp::Store,
3101 },
3102 depth_slice: None,
3103 })],
3104 depth_stencil_attachment: None,
3105 timestamp_writes: None,
3106 occlusion_query_set: None,
3107 });
3108 h_pass.set_pipeline(blur_pipeline);
3109 h_pass.set_bind_group(0, h_bg, &[]);
3110 h_pass.draw(0..3, 0..1);
3111 }
3112 {
3114 let mut v_pass =
3115 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3116 label: Some("bloom_blur_v_pass"),
3117 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3118 view: bloom_pong_view,
3119 resolve_target: None,
3120 ops: wgpu::Operations {
3121 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3122 store: wgpu::StoreOp::Store,
3123 },
3124 depth_slice: None,
3125 })],
3126 depth_stencil_attachment: None,
3127 timestamp_writes: None,
3128 occlusion_query_set: None,
3129 });
3130 v_pass.set_pipeline(blur_pipeline);
3131 v_pass.set_bind_group(0, blur_v_bg, &[]);
3132 v_pass.draw(0..3, 0..1);
3133 }
3134 }
3135 }
3136 }
3137 }
3138
3139 if pp.dof_enabled && !throttle_effects {
3143 if let Some(dof_pipeline) = &self.resources.dof_pipeline {
3144 let mut dof_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3145 label: Some("dof_pass"),
3146 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3147 view: &slot_hdr.dof_view,
3148 resolve_target: None,
3149 depth_slice: None,
3150 ops: wgpu::Operations {
3151 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3152 store: wgpu::StoreOp::Store,
3153 },
3154 })],
3155 depth_stencil_attachment: None,
3156 timestamp_writes: None,
3157 occlusion_query_set: None,
3158 });
3159 dof_pass.set_pipeline(dof_pipeline);
3160 dof_pass.set_bind_group(0, &slot_hdr.dof_bg, &[]);
3161 dof_pass.draw(0..3, 0..1);
3162 }
3163 }
3164
3165 let use_fxaa = pp.fxaa;
3173 let use_hdr_upscale = slot_hdr.upscale_bind_group.is_some();
3174 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
3175 let tone_target: &wgpu::TextureView = if use_fxaa {
3176 &slot_hdr.fxaa_view
3177 } else if use_hdr_upscale {
3178 slot_hdr.upscale_view.as_ref().unwrap()
3179 } else {
3180 output_view
3181 };
3182 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3183 label: Some("tone_map_pass"),
3184 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3185 view: tone_target,
3186 resolve_target: None,
3187 ops: wgpu::Operations {
3188 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3189 store: wgpu::StoreOp::Store,
3190 },
3191 depth_slice: None,
3192 })],
3193 depth_stencil_attachment: None,
3194 timestamp_writes: None,
3195 occlusion_query_set: None,
3196 });
3197 tone_pass.set_pipeline(tone_map_pipeline);
3198 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
3199 tone_pass.draw(0..3, 0..1);
3200 }
3201
3202 if use_fxaa {
3206 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
3207 let fxaa_target: &wgpu::TextureView = if use_hdr_upscale {
3208 slot_hdr.upscale_view.as_ref().unwrap()
3209 } else {
3210 output_view
3211 };
3212 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3213 label: Some("fxaa_pass"),
3214 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3215 view: fxaa_target,
3216 resolve_target: None,
3217 ops: wgpu::Operations {
3218 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3219 store: wgpu::StoreOp::Store,
3220 },
3221 depth_slice: None,
3222 })],
3223 depth_stencil_attachment: None,
3224 timestamp_writes: None,
3225 occlusion_query_set: None,
3226 });
3227 fxaa_pass.set_pipeline(fxaa_pipeline);
3228 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
3229 fxaa_pass.draw(0..3, 0..1);
3230 }
3231 }
3232
3233 if use_hdr_upscale {
3238 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3239 if let Some(upscale_bg) = &slot_hdr.upscale_bind_group {
3240 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
3241 let mut upscale_pass =
3242 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3243 label: Some("hdr_upscale_pass"),
3244 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3245 view: output_view,
3246 resolve_target: None,
3247 ops: wgpu::Operations {
3248 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3249 store: wgpu::StoreOp::Store,
3250 },
3251 depth_slice: None,
3252 })],
3253 depth_stencil_attachment: None,
3254 timestamp_writes: None,
3255 occlusion_query_set: None,
3256 });
3257 upscale_pass.set_pipeline(pipeline);
3258 upscale_pass.set_bind_group(0, upscale_bg, &[]);
3259 upscale_pass.draw(0..3, 0..1);
3260 }
3261 }
3262 }
3263
3264 {
3270 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3271 if let Some(blit_bg) = &slot_hdr.depth_blit_bind_group {
3272 if let Some(blit_pipeline) = &self.resources.depth_blit_pipeline {
3273 let mut blit_pass =
3274 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3275 label: Some("depth_blit_pass"),
3276 color_attachments: &[],
3277 depth_stencil_attachment: Some(
3278 wgpu::RenderPassDepthStencilAttachment {
3279 view: &slot_hdr.output_depth_view,
3280 depth_ops: Some(wgpu::Operations {
3281 load: wgpu::LoadOp::Clear(1.0),
3282 store: wgpu::StoreOp::Store,
3283 }),
3284 stencil_ops: None,
3285 },
3286 ),
3287 timestamp_writes: None,
3288 occlusion_query_set: None,
3289 });
3290 blit_pass.set_pipeline(blit_pipeline);
3291 blit_pass.set_bind_group(0, blit_bg, &[]);
3292 blit_pass.draw(0..3, 0..1);
3293 }
3294 }
3295 }
3296
3297 if frame.viewport.show_grid {
3301 let slot = &self.viewport_slots[vp_idx];
3302 let slot_hdr = slot.hdr.as_ref().unwrap();
3303 let grid_bg = &slot.grid_bind_group;
3304 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3305 label: Some("hdr_grid_pass"),
3306 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3307 view: output_view,
3308 resolve_target: None,
3309 ops: wgpu::Operations {
3310 load: wgpu::LoadOp::Load,
3311 store: wgpu::StoreOp::Store,
3312 },
3313 depth_slice: None,
3314 })],
3315 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3316 view: &slot_hdr.output_depth_view,
3317 depth_ops: Some(wgpu::Operations {
3318 load: wgpu::LoadOp::Load,
3319 store: wgpu::StoreOp::Store,
3320 }),
3321 stencil_ops: None,
3322 }),
3323 timestamp_writes: None,
3324 occlusion_query_set: None,
3325 });
3326 grid_pass.set_pipeline(&self.resources.grid_pipeline);
3327 grid_pass.set_bind_group(0, grid_bg, &[]);
3328 grid_pass.draw(0..3, 0..1);
3329 }
3330
3331 if !matches!(
3334 frame.effects.ground_plane.mode,
3335 crate::renderer::types::GroundPlaneMode::None
3336 ) {
3337 let slot = &self.viewport_slots[vp_idx];
3338 let slot_hdr = slot.hdr.as_ref().unwrap();
3339 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3340 label: Some("hdr_ground_plane_pass"),
3341 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3342 view: output_view,
3343 resolve_target: None,
3344 ops: wgpu::Operations {
3345 load: wgpu::LoadOp::Load,
3346 store: wgpu::StoreOp::Store,
3347 },
3348 depth_slice: None,
3349 })],
3350 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3351 view: &slot_hdr.output_depth_view,
3352 depth_ops: Some(wgpu::Operations {
3353 load: wgpu::LoadOp::Load,
3354 store: wgpu::StoreOp::Store,
3355 }),
3356 stencil_ops: None,
3357 }),
3358 timestamp_writes: None,
3359 occlusion_query_set: None,
3360 });
3361 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
3362 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
3363 gp_pass.draw(0..3, 0..1);
3364 }
3365
3366 if !self.screen_image_gpu_data.is_empty() {
3374 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
3375 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3376 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
3377 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3378 label: Some("screen_image_pass"),
3379 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3380 view: output_view,
3381 resolve_target: None,
3382 ops: wgpu::Operations {
3383 load: wgpu::LoadOp::Load,
3384 store: wgpu::StoreOp::Store,
3385 },
3386 depth_slice: None,
3387 })],
3388 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3389 view: &slot_hdr.output_depth_view,
3390 depth_ops: Some(wgpu::Operations {
3391 load: wgpu::LoadOp::Load,
3392 store: wgpu::StoreOp::Discard,
3393 }),
3394 stencil_ops: None,
3395 }),
3396 timestamp_writes: None,
3397 occlusion_query_set: None,
3398 });
3399 for gpu in &self.screen_image_gpu_data {
3400 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
3401 img_pass.set_pipeline(dc_pipe);
3402 img_pass.set_bind_group(0, dc_bg, &[]);
3403 } else {
3404 img_pass.set_pipeline(overlay_pipeline);
3405 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
3406 }
3407 img_pass.draw(0..6, 0..1);
3408 }
3409 }
3410 }
3411
3412 {
3416 let slot = &self.viewport_slots[vp_idx];
3417 let slot_hdr = slot.hdr.as_ref().unwrap();
3418 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
3419 && slot.gizmo_index_count > 0)
3420 || !slot.constraint_line_buffers.is_empty()
3421 || !slot.clip_plane_fill_buffers.is_empty()
3422 || !slot.clip_plane_line_buffers.is_empty()
3423 || !slot.xray_object_buffers.is_empty();
3424 if has_editor_overlays {
3425 let camera_bg = &slot.camera_bind_group;
3426 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3427 label: Some("hdr_editor_overlay_pass"),
3428 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3429 view: output_view,
3430 resolve_target: None,
3431 ops: wgpu::Operations {
3432 load: wgpu::LoadOp::Load,
3433 store: wgpu::StoreOp::Store,
3434 },
3435 depth_slice: None,
3436 })],
3437 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3438 view: &slot_hdr.output_depth_view,
3439 depth_ops: Some(wgpu::Operations {
3440 load: wgpu::LoadOp::Load,
3441 store: wgpu::StoreOp::Discard,
3442 }),
3443 stencil_ops: None,
3444 }),
3445 timestamp_writes: None,
3446 occlusion_query_set: None,
3447 });
3448
3449 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
3450 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
3451 overlay_pass.set_bind_group(0, camera_bg, &[]);
3452 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
3453 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
3454 overlay_pass.set_index_buffer(
3455 slot.gizmo_index_buffer.slice(..),
3456 wgpu::IndexFormat::Uint32,
3457 );
3458 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
3459 }
3460
3461 if !slot.constraint_line_buffers.is_empty() {
3462 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
3463 overlay_pass.set_bind_group(0, camera_bg, &[]);
3464 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
3465 overlay_pass.set_bind_group(1, bg, &[]);
3466 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3467 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3468 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
3469 }
3470 }
3471
3472 if !slot.clip_plane_fill_buffers.is_empty() {
3473 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
3474 overlay_pass.set_bind_group(0, camera_bg, &[]);
3475 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
3476 overlay_pass.set_bind_group(1, bg, &[]);
3477 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3478 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3479 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
3480 }
3481 }
3482
3483 if !slot.clip_plane_line_buffers.is_empty() {
3484 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
3485 overlay_pass.set_bind_group(0, camera_bg, &[]);
3486 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
3487 overlay_pass.set_bind_group(1, bg, &[]);
3488 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3489 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3490 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
3491 }
3492 }
3493
3494 if !slot.xray_object_buffers.is_empty() {
3495 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
3496 overlay_pass.set_bind_group(0, camera_bg, &[]);
3497 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
3498 let Some(mesh) = self.resources.mesh_store.get(*mesh_id) else {
3499 continue;
3500 };
3501 overlay_pass.set_bind_group(1, bg, &[]);
3502 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3503 overlay_pass.set_index_buffer(
3504 mesh.index_buffer.slice(..),
3505 wgpu::IndexFormat::Uint32,
3506 );
3507 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3508 }
3509 }
3510 }
3511 }
3512
3513 if frame.viewport.show_axes_indicator {
3516 let slot = &self.viewport_slots[vp_idx];
3517 if slot.axes_vertex_count > 0 {
3518 let slot_hdr = slot.hdr.as_ref().unwrap();
3519 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3520 label: Some("hdr_axes_pass"),
3521 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3522 view: output_view,
3523 resolve_target: None,
3524 ops: wgpu::Operations {
3525 load: wgpu::LoadOp::Load,
3526 store: wgpu::StoreOp::Store,
3527 },
3528 depth_slice: None,
3529 })],
3530 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3531 view: &slot_hdr.output_depth_view,
3532 depth_ops: Some(wgpu::Operations {
3533 load: wgpu::LoadOp::Load,
3534 store: wgpu::StoreOp::Discard,
3535 }),
3536 stencil_ops: None,
3537 }),
3538 timestamp_writes: None,
3539 occlusion_query_set: None,
3540 });
3541 axes_pass.set_pipeline(&self.resources.axes_pipeline);
3542 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
3543 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
3544 }
3545 }
3546
3547 let has_overlay = self.overlay_shape_gpu_data.is_some()
3549 || self.overlay_rect_gpu_data.is_some()
3550 || self.label_gpu_data.is_some()
3551 || self.scalar_bar_gpu_data.is_some()
3552 || self.ruler_gpu_data.is_some()
3553 || self.loading_bar_gpu_data.is_some()
3554 || !self.overlay_image_gpu_data.is_empty();
3555
3556 let needs_hdr_blur = self.has_backdrop_blur_shapes();
3561 let hdr_blur_bg: Option<wgpu::BindGroup> = if needs_hdr_blur && has_overlay {
3562 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
3563 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3575 let source = &slot_hdr.hdr_view;
3576 let spread = self.overlay_shape_gpu_data.as_ref()
3577 .map(|d| d.max_blur_radius)
3578 .unwrap_or(8.0);
3579 Some(self.run_backdrop_blur(&mut encoder, device, queue, source, spread))
3580 } else {
3581 None
3582 };
3583
3584 if has_overlay {
3585 let hdr_depth_view = &self.viewport_slots[vp_idx]
3586 .hdr
3587 .as_ref()
3588 .unwrap()
3589 .output_depth_view;
3590 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3591 label: Some("overlay_pass"),
3592 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3593 view: output_view,
3594 resolve_target: None,
3595 ops: wgpu::Operations {
3596 load: wgpu::LoadOp::Load,
3597 store: wgpu::StoreOp::Store,
3598 },
3599 depth_slice: None,
3600 })],
3601 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3602 view: hdr_depth_view,
3603 depth_ops: Some(wgpu::Operations {
3604 load: wgpu::LoadOp::Load,
3605 store: wgpu::StoreOp::Discard,
3606 }),
3607 stencil_ops: None,
3608 }),
3609 timestamp_writes: None,
3610 occlusion_query_set: None,
3611 });
3612 if let Some(ref bg) = hdr_blur_bg {
3614 self.draw_blur_shapes(&mut overlay_pass, bg);
3615 }
3616 if let Some(ref sd) = self.overlay_shape_gpu_data {
3618 if sd.vertex_count > 0 {
3619 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
3620 if let Some(vbuf) = &sd.vertex_buf {
3621 overlay_pass.set_pipeline(pipeline);
3622 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3623 overlay_pass.draw(0..sd.vertex_count, 0..1);
3624 }
3625 }
3626 }
3627 if !sd.tex_batches.is_empty() {
3628 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
3629 overlay_pass.set_pipeline(pipeline);
3630 for batch in &sd.tex_batches {
3631 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
3632 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
3633 overlay_pass.draw(0..batch.vertex_count, 0..1);
3634 }
3635 }
3636 }
3637 }
3638 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
3639 overlay_pass.set_pipeline(pipeline);
3640 if let Some(ref rr) = self.overlay_rect_gpu_data {
3641 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
3642 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
3643 overlay_pass.draw(0..rr.vertex_count, 0..1);
3644 }
3645 if let Some(ref ld) = self.label_gpu_data {
3646 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
3647 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
3648 overlay_pass.draw(0..ld.vertex_count, 0..1);
3649 }
3650 if let Some(ref sb) = self.scalar_bar_gpu_data {
3651 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
3652 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
3653 overlay_pass.draw(0..sb.vertex_count, 0..1);
3654 }
3655 if let Some(ref rd) = self.ruler_gpu_data {
3656 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
3657 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
3658 overlay_pass.draw(0..rd.vertex_count, 0..1);
3659 }
3660 if let Some(ref lb) = self.loading_bar_gpu_data {
3661 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
3662 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
3663 overlay_pass.draw(0..lb.vertex_count, 0..1);
3664 }
3665 }
3666 if !self.overlay_image_gpu_data.is_empty() {
3668 if let Some(pipeline) = &self.resources.screen_image_pipeline {
3669 overlay_pass.set_pipeline(pipeline);
3670 for gpu in &self.overlay_image_gpu_data {
3671 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
3672 overlay_pass.draw(0..6, 0..1);
3673 }
3674 }
3675 }
3676 }
3677
3678 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
3680 self.ts_query_set.as_ref(),
3681 self.ts_resolve_buf.as_ref(),
3682 self.ts_staging_buf.as_ref(),
3683 ) {
3684 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
3685 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
3686 self.ts_needs_readback = true;
3687 }
3688
3689 encoder.finish()
3690 }
3691
3692 pub fn render_offscreen(
3706 &mut self,
3707 device: &wgpu::Device,
3708 queue: &wgpu::Queue,
3709 frame: &FrameData,
3710 width: u32,
3711 height: u32,
3712 ) -> Vec<u8> {
3713 let target_format = self.resources.target_format;
3715 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
3716 label: Some("offscreen_target"),
3717 size: wgpu::Extent3d {
3718 width: width.max(1),
3719 height: height.max(1),
3720 depth_or_array_layers: 1,
3721 },
3722 mip_level_count: 1,
3723 sample_count: 1,
3724 dimension: wgpu::TextureDimension::D2,
3725 format: target_format,
3726 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
3727 view_formats: &[],
3728 });
3729
3730 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
3732
3733 let cmd_buf = self.render(device, queue, &output_view, frame);
3741 queue.submit(std::iter::once(cmd_buf));
3742
3743 let bytes_per_pixel = 4u32;
3745 let unpadded_row = width * bytes_per_pixel;
3746 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
3747 let padded_row = (unpadded_row + align - 1) & !(align - 1);
3748 let buffer_size = (padded_row * height.max(1)) as u64;
3749
3750 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
3751 label: Some("offscreen_staging"),
3752 size: buffer_size,
3753 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
3754 mapped_at_creation: false,
3755 });
3756
3757 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
3758 label: Some("offscreen_copy_encoder"),
3759 });
3760 copy_encoder.copy_texture_to_buffer(
3761 wgpu::TexelCopyTextureInfo {
3762 texture: &offscreen_texture,
3763 mip_level: 0,
3764 origin: wgpu::Origin3d::ZERO,
3765 aspect: wgpu::TextureAspect::All,
3766 },
3767 wgpu::TexelCopyBufferInfo {
3768 buffer: &staging_buf,
3769 layout: wgpu::TexelCopyBufferLayout {
3770 offset: 0,
3771 bytes_per_row: Some(padded_row),
3772 rows_per_image: Some(height.max(1)),
3773 },
3774 },
3775 wgpu::Extent3d {
3776 width: width.max(1),
3777 height: height.max(1),
3778 depth_or_array_layers: 1,
3779 },
3780 );
3781 queue.submit(std::iter::once(copy_encoder.finish()));
3782
3783 let (tx, rx) = std::sync::mpsc::channel();
3785 staging_buf
3786 .slice(..)
3787 .map_async(wgpu::MapMode::Read, move |result| {
3788 let _ = tx.send(result);
3789 });
3790 device
3791 .poll(wgpu::PollType::Wait {
3792 submission_index: None,
3793 timeout: Some(std::time::Duration::from_secs(5)),
3794 })
3795 .unwrap();
3796 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
3797
3798 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
3799 {
3800 let mapped = staging_buf.slice(..).get_mapped_range();
3801 let data: &[u8] = &mapped;
3802 if padded_row == unpadded_row {
3803 pixels.extend_from_slice(data);
3805 } else {
3806 for row in 0..height as usize {
3808 let start = row * padded_row as usize;
3809 let end = start + unpadded_row as usize;
3810 pixels.extend_from_slice(&data[start..end]);
3811 }
3812 }
3813 }
3814 staging_buf.unmap();
3815
3816 let is_bgra = matches!(
3818 target_format,
3819 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
3820 );
3821 if is_bgra {
3822 for pixel in pixels.chunks_exact_mut(4) {
3823 pixel.swap(0, 2); }
3825 }
3826
3827 pixels
3828 }
3829
3830 fn ensure_backdrop_blur_state(
3836 &mut self,
3837 device: &wgpu::Device,
3838 w: u32,
3839 h: u32,
3840 ) {
3841 let need_recreate = match &self.backdrop_blur_state {
3842 Some(s) => s.size != [w, h] || s.format != self.resources.target_format,
3843 None => true,
3844 };
3845 if !need_recreate {
3846 return;
3847 }
3848
3849 let format = self.resources.target_format;
3850 let blur_w = (w / 2).max(1);
3851 let blur_h = (h / 2).max(1);
3852
3853 let intermediate_texture = device.create_texture(&wgpu::TextureDescriptor {
3854 label: Some("backdrop_intermediate"),
3855 size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
3856 mip_level_count: 1,
3857 sample_count: 1,
3858 dimension: wgpu::TextureDimension::D2,
3859 format,
3860 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
3861 view_formats: &[],
3862 });
3863 let intermediate_view = intermediate_texture.create_view(&Default::default());
3864
3865 let make_blur_tex = |label: &str| {
3866 let t = device.create_texture(&wgpu::TextureDescriptor {
3867 label: Some(label),
3868 size: wgpu::Extent3d { width: blur_w, height: blur_h, depth_or_array_layers: 1 },
3869 mip_level_count: 1,
3870 sample_count: 1,
3871 dimension: wgpu::TextureDimension::D2,
3872 format,
3873 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
3874 view_formats: &[],
3875 });
3876 let v = t.create_view(&Default::default());
3877 (t, v)
3878 };
3879 let (blur_a_texture, blur_a_view) = make_blur_tex("backdrop_blur_a");
3880 let (blur_b_texture, blur_b_view) = make_blur_tex("backdrop_blur_b");
3881
3882 self.backdrop_blur_state = Some(crate::resources::BackdropBlurState {
3883 intermediate_texture,
3884 intermediate_view,
3885 blur_a_texture,
3886 blur_a_view,
3887 blur_b_texture,
3888 blur_b_view,
3889 size: [w, h],
3890 format,
3891 });
3892 }
3893
3894 fn run_backdrop_blur(
3898 &self,
3899 encoder: &mut wgpu::CommandEncoder,
3900 device: &wgpu::Device,
3901 _queue: &wgpu::Queue,
3902 source_view: &wgpu::TextureView,
3903 spread: f32,
3904 ) -> wgpu::BindGroup {
3905 let bs = self.backdrop_blur_state.as_ref().unwrap();
3906 let blur_bgl = self.resources.backdrop_blur_bgl.as_ref().unwrap();
3907 let blur_sampler = self.resources.backdrop_blur_sampler.as_ref().unwrap();
3908 let blur_pipeline = self.resources.backdrop_blur_pipeline.as_ref().unwrap();
3909 let blit_pipeline = self.resources.dyn_res_upscale_pipeline.as_ref().unwrap();
3911 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
3912 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
3913
3914 let downsample_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
3916 label: Some("backdrop_downsample_bg"),
3917 layout: blit_bgl,
3918 entries: &[
3919 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(source_view) },
3920 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blit_sampler) },
3921 ],
3922 });
3923 {
3924 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3925 label: Some("backdrop_downsample"),
3926 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3927 view: &bs.blur_a_view,
3928 resolve_target: None,
3929 ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store },
3930 depth_slice: None,
3931 })],
3932 depth_stencil_attachment: None,
3933 timestamp_writes: None,
3934 occlusion_query_set: None,
3935 });
3936 pass.set_pipeline(blit_pipeline);
3937 pass.set_bind_group(0, &downsample_bg, &[]);
3938 pass.draw(0..3, 0..1);
3939 }
3940
3941 let effective_spread = (spread / 2.0).max(1.0);
3943
3944 let h_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
3946 label: Some("blur_h_uniform"),
3947 contents: bytemuck::cast_slice(&[1u32, effective_spread.to_bits(), 0u32, 0u32]),
3948 usage: wgpu::BufferUsages::UNIFORM,
3949 });
3950 let h_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
3951 label: Some("blur_h_bg"),
3952 layout: blur_bgl,
3953 entries: &[
3954 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.blur_a_view) },
3955 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blur_sampler) },
3956 wgpu::BindGroupEntry { binding: 2, resource: h_uniform.as_entire_binding() },
3957 ],
3958 });
3959 {
3960 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3961 label: Some("backdrop_blur_h"),
3962 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3963 view: &bs.blur_b_view,
3964 resolve_target: None,
3965 ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store },
3966 depth_slice: None,
3967 })],
3968 depth_stencil_attachment: None,
3969 timestamp_writes: None,
3970 occlusion_query_set: None,
3971 });
3972 pass.set_pipeline(blur_pipeline);
3973 pass.set_bind_group(0, &h_bg, &[]);
3974 pass.draw(0..3, 0..1);
3975 }
3976
3977 let v_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
3979 label: Some("blur_v_uniform"),
3980 contents: bytemuck::cast_slice(&[0u32, effective_spread.to_bits(), 0u32, 0u32]),
3981 usage: wgpu::BufferUsages::UNIFORM,
3982 });
3983 let v_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
3984 label: Some("blur_v_bg"),
3985 layout: blur_bgl,
3986 entries: &[
3987 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.blur_b_view) },
3988 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blur_sampler) },
3989 wgpu::BindGroupEntry { binding: 2, resource: v_uniform.as_entire_binding() },
3990 ],
3991 });
3992 {
3993 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3994 label: Some("backdrop_blur_v"),
3995 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3996 view: &bs.blur_a_view,
3997 resolve_target: None,
3998 ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store },
3999 depth_slice: None,
4000 })],
4001 depth_stencil_attachment: None,
4002 timestamp_writes: None,
4003 occlusion_query_set: None,
4004 });
4005 pass.set_pipeline(blur_pipeline);
4006 pass.set_bind_group(0, &v_bg, &[]);
4007 pass.draw(0..3, 0..1);
4008 }
4009
4010 let tex_bgl = self.resources.overlay_shape_tex_bgl.as_ref().unwrap();
4014 let tex_sampler = self.resources.overlay_shape_tex_sampler.as_ref().unwrap();
4015 device.create_bind_group(&wgpu::BindGroupDescriptor {
4016 label: Some("backdrop_blur_overlay_bg"),
4017 layout: tex_bgl,
4018 entries: &[
4019 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.blur_a_view) },
4020 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(tex_sampler) },
4021 ],
4022 })
4023 }
4024
4025 fn has_backdrop_blur_shapes(&self) -> bool {
4027 self.overlay_shape_gpu_data
4028 .as_ref()
4029 .map_or(false, |sd| sd.blur_vertex_count > 0)
4030 }
4031
4032 fn draw_blur_shapes<'rp>(
4034 &'rp self,
4035 render_pass: &mut wgpu::RenderPass<'rp>,
4036 blur_bind_group: &'rp wgpu::BindGroup,
4037 ) {
4038 if let Some(ref sd) = self.overlay_shape_gpu_data {
4039 if sd.blur_vertex_count > 0 {
4040 if let (Some(pipeline), Some(vbuf)) = (
4041 &self.resources.overlay_shape_tex_pipeline,
4042 &sd.blur_vertex_buf,
4043 ) {
4044 render_pass.set_pipeline(pipeline);
4045 render_pass.set_bind_group(0, blur_bind_group, &[]);
4046 render_pass.set_vertex_buffer(0, vbuf.slice(..));
4047 render_pass.draw(0..sd.blur_vertex_count, 0..1);
4048 }
4049 }
4050 }
4051 }
4052}