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 &self.per_item_object_bind_groups
22 );
23 emit_scivis_draw_calls!(
24 &self.resources,
25 &mut *render_pass,
26 &self.point_cloud_gpu_data,
27 &self.glyph_gpu_data,
28 &self.polyline_gpu_data,
29 &self.volume_gpu_data,
30 &self.streamtube_gpu_data,
31 camera_bg,
32 &self.tube_gpu_data,
33 &self.image_slice_gpu_data,
34 &self.tensor_glyph_gpu_data,
35 &self.ribbon_gpu_data,
36 &self.volume_surface_slice_gpu_data,
37 &self.sprite_gpu_data,
38 &self.mesh_instance_gpu_data,
39 false
40 );
41 if !self.gaussian_splat_draw_data.is_empty() {
43 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
44 render_pass.set_pipeline(dual.for_format(false));
45 render_pass.set_bind_group(0, camera_bg, &[]);
46 for dd in &self.gaussian_splat_draw_data {
47 if dd.wireframe {
48 continue;
49 }
50 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
51 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
52 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
53 render_pass.draw(0..6, 0..dd.count);
54 }
55 }
56 }
57 }
58 }
59 if !self.tvm_wireframe_draws.is_empty() {
61 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
62 render_pass.set_bind_group(0, camera_bg, &[]);
63 for mesh_id in &self.tvm_wireframe_draws {
64 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
65 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
66 render_pass.set_bind_group(
67 2,
68 &self.resources.deform.dummy_bind_group,
69 &[],
70 );
71 render_pass.set_bind_group(1, tvm_bg, &[]);
72 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
73 render_pass.set_index_buffer(
74 mesh.edge_index_buffer.slice(..),
75 wgpu::IndexFormat::Uint32,
76 );
77 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
78 }
79 }
80 }
81 }
82 if !self.implicit_gpu_data.is_empty() {
84 if let Some(ref dual) = self.resources.implicit_pipeline {
85 render_pass.set_pipeline(dual.for_format(false));
86 render_pass.set_bind_group(0, camera_bg, &[]);
87 for gpu in &self.implicit_gpu_data {
88 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
89 render_pass.draw(0..6, 0..1);
90 }
91 }
92 }
93 if !self.mc_gpu_data.is_empty() {
95 render_pass.set_bind_group(0, camera_bg, &[]);
96 for mc in &self.mc_gpu_data {
97 let vol = &self.resources.mc_volumes[mc.volume_idx];
98 if mc.wireframe || frame.viewport.wireframe_mode {
99 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
100 render_pass.set_pipeline(dual.for_format(false));
101 for (slab, wire_bg) in vol.slabs.iter().zip(mc.wire_slab_bgs.iter()) {
102 render_pass.set_bind_group(1, wire_bg, &[]);
103 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
104 }
105 }
106 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
107 render_pass.set_pipeline(dual.for_format(false));
108 render_pass.set_bind_group(1, &mc.render_bg, &[]);
109 for slab in &vol.slabs {
110 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
111 render_pass.draw_indirect(&slab.indirect_buf, 0);
112 }
113 }
114 }
115 }
116 emit_outline_composite!(&self.resources, &mut *render_pass, vp_slot);
118 if let Some(sub_hl) = self
120 .viewport_slots
121 .get(vp_idx)
122 .and_then(|s| s.sub_highlight.as_ref())
123 {
124 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
125 &self.resources.sub_highlight_fill_ldr_pipeline,
126 &self.resources.sub_highlight_edge_ldr_pipeline,
127 &self.resources.sub_highlight_sprite_ldr_pipeline,
128 ) {
129 if sub_hl.fill_vertex_count > 0 {
130 render_pass.set_pipeline(fill_pl);
131 render_pass.set_bind_group(0, camera_bg, &[]);
132 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
133 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
134 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
135 }
136 if sub_hl.edge_segment_count > 0 {
137 render_pass.set_pipeline(edge_pl);
138 render_pass.set_bind_group(0, camera_bg, &[]);
139 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
140 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
141 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
142 }
143 if sub_hl.sprite_point_count > 0 {
144 render_pass.set_pipeline(sprite_pl);
145 render_pass.set_bind_group(0, camera_bg, &[]);
146 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
147 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
148 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
149 }
150 }
151 }
152 if !self.screen_image_gpu_data.is_empty() {
154 if let Some(pipeline) = &self.resources.screen_image_pipeline {
155 render_pass.set_pipeline(pipeline);
156 for gpu in &self.screen_image_gpu_data {
157 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
158 render_pass.draw(0..6, 0..1);
159 }
160 }
161 }
162 if let Some(ref sd) = self.overlay_shape_gpu_data {
164 if sd.vertex_count > 0 {
165 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
166 if let Some(vbuf) = &sd.vertex_buf {
167 render_pass.set_pipeline(pipeline);
168 render_pass.set_vertex_buffer(0, vbuf.slice(..));
169 render_pass.draw(0..sd.vertex_count, 0..1);
170 }
171 }
172 }
173 if !sd.tex_batches.is_empty() {
174 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
175 render_pass.set_pipeline(pipeline);
176 for batch in &sd.tex_batches {
177 render_pass.set_bind_group(0, &batch.bind_group, &[]);
178 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
179 render_pass.draw(0..batch.vertex_count, 0..1);
180 }
181 }
182 }
183 }
184 if let Some(ref rr) = self.overlay_rect_gpu_data {
186 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
187 render_pass.set_pipeline(pipeline);
188 render_pass.set_bind_group(0, &rr.bind_group, &[]);
189 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
190 render_pass.draw(0..rr.vertex_count, 0..1);
191 }
192 }
193 if let Some(ref ld) = self.label_gpu_data {
195 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
196 render_pass.set_pipeline(pipeline);
197 render_pass.set_bind_group(0, &ld.bind_group, &[]);
198 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
199 render_pass.draw(0..ld.vertex_count, 0..1);
200 }
201 }
202 if let Some(ref sb) = self.scalar_bar_gpu_data {
204 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
205 render_pass.set_pipeline(pipeline);
206 render_pass.set_bind_group(0, &sb.bind_group, &[]);
207 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
208 render_pass.draw(0..sb.vertex_count, 0..1);
209 }
210 }
211 if let Some(ref rd) = self.ruler_gpu_data {
213 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
214 render_pass.set_pipeline(pipeline);
215 render_pass.set_bind_group(0, &rd.bind_group, &[]);
216 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
217 render_pass.draw(0..rd.vertex_count, 0..1);
218 }
219 }
220 if let Some(ref lb) = self.loading_bar_gpu_data {
222 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
223 render_pass.set_pipeline(pipeline);
224 render_pass.set_bind_group(0, &lb.bind_group, &[]);
225 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
226 render_pass.draw(0..lb.vertex_count, 0..1);
227 }
228 }
229 if !self.overlay_image_gpu_data.is_empty() {
231 if let Some(pipeline) = &self.resources.screen_image_pipeline {
232 render_pass.set_pipeline(pipeline);
233 for gpu in &self.overlay_image_gpu_data {
234 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
235 render_pass.draw(0..6, 0..1);
236 }
237 }
238 }
239 if frame.effects.show_shadow_atlas {
241 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
242 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
243 render_pass.draw(0..6, 0..1);
244 }
245 }
246
247 pub(crate) fn prepare_ldr_dyn_res(
262 &mut self,
263 encoder: &mut wgpu::CommandEncoder,
264 device: &wgpu::Device,
265 frame: &FrameData,
266 ) -> bool {
267 if self.current_render_scale >= 1.0 - 0.001 {
268 return false;
269 }
270
271 let vp_idx = frame.camera.viewport_index;
272 let w = (frame.camera.viewport_size[0] as u32).max(1);
273 let h = (frame.camera.viewport_size[1] as u32).max(1);
274 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
275 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
276
277 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
278 self.resources.ensure_dyn_res_ds_pipeline(device);
279
280 let bg_colour = frame.viewport.background_colour.unwrap_or([
281 65.0 / 255.0,
282 65.0 / 255.0,
283 65.0 / 255.0,
284 1.0,
285 ]);
286
287 {
288 let slot = &self.viewport_slots[vp_idx];
289 let dr = slot.dyn_res.as_ref().unwrap();
290 let colour_view = &dr.colour_view;
291 let depth_view = &dr.depth_view;
292 let camera_bg = &slot.camera_bind_group;
293 let grid_bg = &slot.grid_bind_group;
294
295 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
296 label: Some("ldr_dyn_res_render_pass"),
297 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
298 view: colour_view,
299 resolve_target: None,
300 ops: wgpu::Operations {
301 load: wgpu::LoadOp::Clear(wgpu::Color {
302 r: bg_colour[0] as f64,
303 g: bg_colour[1] as f64,
304 b: bg_colour[2] as f64,
305 a: bg_colour[3] as f64,
306 }),
307 store: wgpu::StoreOp::Store,
308 },
309 depth_slice: None,
310 })],
311 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
312 view: depth_view,
313 depth_ops: Some(wgpu::Operations {
314 load: wgpu::LoadOp::Clear(1.0),
315 store: wgpu::StoreOp::Discard,
316 }),
317 stencil_ops: None,
318 }),
319 timestamp_writes: None,
320 occlusion_query_set: None,
321 });
322 emit_draw_calls!(
323 &self.resources,
324 &mut render_pass,
325 frame,
326 self.use_instancing,
327 &self.instanced_batches,
328 camera_bg,
329 grid_bg,
330 &self.compute_filter_results,
331 Some(slot),
332 &self.wireframe_bind_groups,
333 &self.per_item_object_bind_groups
334 );
335 emit_scivis_draw_calls!(
336 &self.resources,
337 &mut render_pass,
338 &self.point_cloud_gpu_data,
339 &self.glyph_gpu_data,
340 &self.polyline_gpu_data,
341 &self.volume_gpu_data,
342 &self.streamtube_gpu_data,
343 camera_bg,
344 &self.tube_gpu_data,
345 &self.image_slice_gpu_data,
346 &self.tensor_glyph_gpu_data,
347 &self.ribbon_gpu_data,
348 &self.volume_surface_slice_gpu_data,
349 &self.sprite_gpu_data,
350 &self.mesh_instance_gpu_data,
351 false
352 );
353 if !self.tvm_wireframe_draws.is_empty() {
355 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
356 render_pass.set_bind_group(0, camera_bg, &[]);
357 for mesh_id in &self.tvm_wireframe_draws {
358 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
359 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
360 render_pass.set_bind_group(
361 2,
362 &self.resources.deform.dummy_bind_group,
363 &[],
364 );
365 render_pass.set_bind_group(1, tvm_bg, &[]);
366 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
367 render_pass.set_index_buffer(
368 mesh.edge_index_buffer.slice(..),
369 wgpu::IndexFormat::Uint32,
370 );
371 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
372 }
373 }
374 }
375 }
376 if !self.implicit_gpu_data.is_empty() {
378 if let Some(ref dual) = self.resources.implicit_pipeline {
379 render_pass.set_pipeline(dual.for_format(false));
380 render_pass.set_bind_group(0, camera_bg, &[]);
381 for gpu in &self.implicit_gpu_data {
382 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
383 render_pass.draw(0..6, 0..1);
384 }
385 }
386 }
387 if !self.mc_gpu_data.is_empty() {
389 if let Some(ref dual) = self.resources.mc_surface_pipeline {
390 render_pass.set_pipeline(dual.for_format(false));
391 render_pass.set_bind_group(0, camera_bg, &[]);
392 for mc in &self.mc_gpu_data {
393 let vol = &self.resources.mc_volumes[mc.volume_idx];
394 render_pass.set_bind_group(1, &mc.render_bg, &[]);
395 for slab in &vol.slabs {
396 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
397 render_pass.draw_indirect(&slab.indirect_buf, 0);
398 }
399 }
400 }
401 }
402 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
404 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
406 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
407 &self.resources.sub_highlight_fill_ldr_pipeline,
408 &self.resources.sub_highlight_edge_ldr_pipeline,
409 &self.resources.sub_highlight_sprite_ldr_pipeline,
410 ) {
411 if sub_hl.fill_vertex_count > 0 {
412 render_pass.set_pipeline(fill_pl);
413 render_pass.set_bind_group(0, camera_bg, &[]);
414 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
415 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
416 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
417 }
418 if sub_hl.edge_segment_count > 0 {
419 render_pass.set_pipeline(edge_pl);
420 render_pass.set_bind_group(0, camera_bg, &[]);
421 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
422 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
423 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
424 }
425 if sub_hl.sprite_point_count > 0 {
426 render_pass.set_pipeline(sprite_pl);
427 render_pass.set_bind_group(0, camera_bg, &[]);
428 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
429 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
430 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
431 }
432 }
433 }
434 if !self.screen_image_gpu_data.is_empty() {
436 if let Some(pipeline) = &self.resources.screen_image_pipeline {
437 render_pass.set_pipeline(pipeline);
438 for gpu in &self.screen_image_gpu_data {
439 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
440 render_pass.draw(0..6, 0..1);
441 }
442 }
443 }
444 if let Some(ref sd) = self.overlay_shape_gpu_data {
446 if sd.vertex_count > 0 {
447 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
448 if let Some(vbuf) = &sd.vertex_buf {
449 render_pass.set_pipeline(pipeline);
450 render_pass.set_vertex_buffer(0, vbuf.slice(..));
451 render_pass.draw(0..sd.vertex_count, 0..1);
452 }
453 }
454 }
455 if !sd.tex_batches.is_empty() {
456 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
457 render_pass.set_pipeline(pipeline);
458 for batch in &sd.tex_batches {
459 render_pass.set_bind_group(0, &batch.bind_group, &[]);
460 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
461 render_pass.draw(0..batch.vertex_count, 0..1);
462 }
463 }
464 }
465 }
466 if let Some(ref rr) = self.overlay_rect_gpu_data {
468 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
469 render_pass.set_pipeline(pipeline);
470 render_pass.set_bind_group(0, &rr.bind_group, &[]);
471 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
472 render_pass.draw(0..rr.vertex_count, 0..1);
473 }
474 }
475 if let Some(ref ld) = self.label_gpu_data {
477 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
478 render_pass.set_pipeline(pipeline);
479 render_pass.set_bind_group(0, &ld.bind_group, &[]);
480 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
481 render_pass.draw(0..ld.vertex_count, 0..1);
482 }
483 }
484 if let Some(ref sb) = self.scalar_bar_gpu_data {
486 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
487 render_pass.set_pipeline(pipeline);
488 render_pass.set_bind_group(0, &sb.bind_group, &[]);
489 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
490 render_pass.draw(0..sb.vertex_count, 0..1);
491 }
492 }
493 if let Some(ref rd) = self.ruler_gpu_data {
495 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
496 render_pass.set_pipeline(pipeline);
497 render_pass.set_bind_group(0, &rd.bind_group, &[]);
498 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
499 render_pass.draw(0..rd.vertex_count, 0..1);
500 }
501 }
502 if let Some(ref lb) = self.loading_bar_gpu_data {
504 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
505 render_pass.set_pipeline(pipeline);
506 render_pass.set_bind_group(0, &lb.bind_group, &[]);
507 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
508 render_pass.draw(0..lb.vertex_count, 0..1);
509 }
510 }
511 if !self.overlay_image_gpu_data.is_empty() {
513 if let Some(pipeline) = &self.resources.screen_image_pipeline {
514 render_pass.set_pipeline(pipeline);
515 for gpu in &self.overlay_image_gpu_data {
516 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
517 render_pass.draw(0..6, 0..1);
518 }
519 }
520 }
521 }
522
523 true
524 }
525
526 pub(crate) fn paint_dyn_res_blit<'rp>(
532 &self,
533 render_pass: &mut wgpu::RenderPass<'rp>,
534 frame: &FrameData,
535 ) {
536 let vp_idx = frame.camera.viewport_index;
537 if let Some(dr) = self
538 .viewport_slots
539 .get(vp_idx)
540 .and_then(|s| s.dyn_res.as_ref())
541 {
542 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
543 render_pass.set_pipeline(pipeline);
544 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
545 render_pass.draw(0..3, 0..1);
546 }
547 }
548 }
549
550 pub(crate) fn prepare_hdr_callback(
567 &mut self,
568 device: &wgpu::Device,
569 queue: &wgpu::Queue,
570 frame: &FrameData,
571 ) -> wgpu::CommandBuffer {
572 self.prepare(device, queue, frame);
573
574 let vp_idx = frame.camera.viewport_index;
575 let ppp = frame.camera.pixels_per_point;
581 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
582 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
583
584 self.resources.ensure_dyn_res_pipeline(device);
586 self.resources.ensure_dyn_res_ds_pipeline(device);
587
588 self.ensure_viewport_slot(device, vp_idx);
590 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
591 None => true,
592 Some(t) => t.size != [w, h],
593 };
594 if needs_create {
595 let target = self.resources.create_hdr_callback_target(device, [w, h]);
596 self.viewport_slots[vp_idx].hdr_callback = Some(target);
597 }
598
599 let output_view = self.viewport_slots[vp_idx]
603 .hdr_callback
604 .as_ref()
605 .unwrap()
606 .texture
607 .create_view(&wgpu::TextureViewDescriptor::default());
608
609 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
610 }
611
612 pub(crate) fn prepare_hdr_callback_viewport(
628 &mut self,
629 device: &wgpu::Device,
630 queue: &wgpu::Queue,
631 id: ViewportId,
632 frame: &FrameData,
633 ) -> wgpu::CommandBuffer {
634 let vp_idx = id.0;
635 let ppp = frame.camera.pixels_per_point;
636 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
637 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
638
639 self.resources.ensure_dyn_res_pipeline(device);
640 self.resources.ensure_dyn_res_ds_pipeline(device);
641
642 self.ensure_viewport_slot(device, vp_idx);
643 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
644 None => true,
645 Some(t) => t.size != [w, h],
646 };
647 if needs_create {
648 let target = self.resources.create_hdr_callback_target(device, [w, h]);
649 self.viewport_slots[vp_idx].hdr_callback = Some(target);
650 }
651
652 let output_view = self.viewport_slots[vp_idx]
653 .hdr_callback
654 .as_ref()
655 .unwrap()
656 .texture
657 .create_view(&wgpu::TextureViewDescriptor::default());
658
659 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
660 }
661
662 pub(crate) fn paint_hdr_blit<'rp>(
668 &self,
669 render_pass: &mut wgpu::RenderPass<'rp>,
670 frame: &FrameData,
671 ) {
672 let vp_idx = frame.camera.viewport_index;
673 if let Some(hc) = self
674 .viewport_slots
675 .get(vp_idx)
676 .and_then(|s| s.hdr_callback.as_ref())
677 {
678 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
679 render_pass.set_pipeline(pipeline);
680 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
681 render_pass.draw(0..3, 0..1);
682 }
683 }
684 if frame.effects.show_shadow_atlas {
686 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
687 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
688 render_pass.draw(0..6, 0..1);
689 }
690 }
691
692 pub(crate) fn paint_hdr_blit_no_ds<'rp>(
696 &self,
697 render_pass: &mut wgpu::RenderPass<'rp>,
698 frame: &FrameData,
699 ) {
700 let vp_idx = frame.camera.viewport_index;
701 if let Some(hc) = self
702 .viewport_slots
703 .get(vp_idx)
704 .and_then(|s| s.hdr_callback.as_ref())
705 {
706 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
707 render_pass.set_pipeline(pipeline);
708 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
709 render_pass.draw(0..3, 0..1);
710 }
711 }
712 }
713
714 pub(crate) fn prepare_callback(
728 &mut self,
729 device: &wgpu::Device,
730 queue: &wgpu::Queue,
731 frame: &FrameData,
732 ) -> Vec<wgpu::CommandBuffer> {
733 if frame.effects.post_process.enabled {
734 let cb = self.prepare_hdr_callback(device, queue, frame);
735 vec![cb]
736 } else {
737 self.prepare(device, queue, frame);
738 if self.current_render_scale < 1.0 - 0.001 {
739 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
740 label: Some("ldr_dyn_res_callback_encoder"),
741 });
742 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
743 vec![encoder.finish()]
744 } else {
745 Vec::new()
746 }
747 }
748 }
749
750 pub(crate) fn paint_callback<'rp>(
756 &self,
757 render_pass: &mut wgpu::RenderPass<'rp>,
758 frame: &FrameData,
759 ) {
760 let vp_idx = frame.camera.viewport_index;
761 if frame.effects.post_process.enabled {
762 if self
763 .viewport_slots
764 .get(vp_idx)
765 .and_then(|s| s.hdr_callback.as_ref())
766 .is_some()
767 {
768 self.paint_hdr_blit(render_pass, frame);
769 return;
770 }
771 }
772 if self.current_render_scale < 1.0 - 0.001
773 && self
774 .viewport_slots
775 .get(vp_idx)
776 .and_then(|s| s.dyn_res.as_ref())
777 .is_some()
778 {
779 self.paint_dyn_res_blit(render_pass, frame);
780 } else {
781 self.paint_to(render_pass, frame);
782 }
783 }
784
785 pub(crate) fn render_viewport(
799 &mut self,
800 device: &wgpu::Device,
801 queue: &wgpu::Queue,
802 output_view: &wgpu::TextureView,
803 id: ViewportId,
804 frame: &FrameData,
805 ) -> wgpu::CommandBuffer {
806 self.render_frame_internal(device, queue, output_view, id.0, frame)
807 }
808
809 pub(crate) fn render(
817 &mut self,
818 device: &wgpu::Device,
819 queue: &wgpu::Queue,
820 output_view: &wgpu::TextureView,
821 frame: &FrameData,
822 ) -> wgpu::CommandBuffer {
823 self.prepare(device, queue, frame);
825 self.render_frame_internal(
826 device,
827 queue,
828 output_view,
829 frame.camera.viewport_index,
830 frame,
831 )
832 }
833
834 fn render_frame_internal(
839 &mut self,
840 device: &wgpu::Device,
841 queue: &wgpu::Queue,
842 output_view: &wgpu::TextureView,
843 vp_idx: usize,
844 frame: &FrameData,
845 ) -> wgpu::CommandBuffer {
846 let scene_items_owned: Vec<SceneRenderItem> = {
850 let surfaces = match &frame.scene.surfaces {
851 SurfaceSubmission::Flat(items) => items.as_ref(),
852 };
853 let extra = frame
854 .scene
855 .volume_meshes
856 .iter()
857 .filter(|item| item.transparency.is_none())
858 .map(|item| item.to_render_item());
859 surfaces.iter().cloned().chain(extra).collect()
860 };
861 let scene_items: &[SceneRenderItem] = &scene_items_owned;
862
863 let bg_colour = frame.viewport.background_colour.unwrap_or([
864 65.0 / 255.0,
865 65.0 / 255.0,
866 65.0 / 255.0,
867 1.0,
868 ]);
869 let ppp = frame.camera.pixels_per_point;
870 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
871 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
872
873 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
875 self.ensure_viewport_hdr(
876 device,
877 queue,
878 vp_idx,
879 w.max(1),
880 h.max(1),
881 ssaa_factor,
882 self.current_render_scale,
883 );
884
885 if self.ts_query_set.is_none()
887 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
888 {
889 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
890 label: Some("ts_query_set"),
891 ty: wgpu::QueryType::Timestamp,
892 count: 2,
893 }));
894 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
895 label: Some("ts_resolve_buf"),
896 size: 16,
897 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
898 mapped_at_creation: false,
899 }));
900 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
901 label: Some("ts_staging_buf"),
902 size: 16,
903 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
904 mapped_at_creation: false,
905 }));
906 self.ts_period = queue.get_timestamp_period();
907 }
908
909 if !frame.effects.post_process.enabled {
910 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
914 label: Some("ldr_encoder"),
915 });
916
917 let use_dyn_res = self.current_render_scale < 1.0 - 0.001;
918 let needs_blur = self.has_backdrop_blur_shapes();
919
920 if use_dyn_res {
921 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
922 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
923 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
924 }
925
926 if needs_blur && !use_dyn_res {
930 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
931 }
932
933 {
934 let slot = &self.viewport_slots[vp_idx];
935 let slot_hdr = slot.hdr.as_ref().expect(
936 "HDR state missing in LDR path; ensure_viewport_hdr must have been called",
937 );
938 let camera_bg = &slot.camera_bind_group;
939 let grid_bg = &slot.grid_bind_group;
940 let (scene_colour_view, scene_depth_view): (
942 &wgpu::TextureView,
943 &wgpu::TextureView,
944 ) = if use_dyn_res {
945 let dr = slot.dyn_res.as_ref().unwrap();
946 (&dr.colour_view, &dr.depth_view)
947 } else if needs_blur {
948 let bs = self.backdrop_blur_state.as_ref().unwrap();
949 (&bs.intermediate_view, &slot_hdr.outline_depth_view)
950 } else {
951 (output_view, &slot_hdr.outline_depth_view)
952 };
953 let ts_writes =
954 self.ts_query_set
955 .as_ref()
956 .map(|qs| wgpu::RenderPassTimestampWrites {
957 query_set: qs,
958 beginning_of_pass_write_index: Some(0),
959 end_of_pass_write_index: Some(1),
960 });
961 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
962 label: Some("ldr_render_pass"),
963 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
964 view: scene_colour_view,
965 resolve_target: None,
966 ops: wgpu::Operations {
967 load: wgpu::LoadOp::Clear(wgpu::Color {
968 r: bg_colour[0] as f64,
969 g: bg_colour[1] as f64,
970 b: bg_colour[2] as f64,
971 a: bg_colour[3] as f64,
972 }),
973 store: wgpu::StoreOp::Store,
974 },
975 depth_slice: None,
976 })],
977 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
978 view: scene_depth_view,
979 depth_ops: Some(wgpu::Operations {
980 load: wgpu::LoadOp::Clear(1.0),
981 store: wgpu::StoreOp::Discard,
982 }),
983 stencil_ops: None,
984 }),
985 timestamp_writes: ts_writes,
986 occlusion_query_set: None,
987 });
988 emit_draw_calls!(
989 &self.resources,
990 &mut render_pass,
991 frame,
992 self.use_instancing,
993 &self.instanced_batches,
994 camera_bg,
995 grid_bg,
996 &self.compute_filter_results,
997 Some(slot),
998 &self.wireframe_bind_groups,
999 &self.per_item_object_bind_groups
1000 );
1001 emit_scivis_draw_calls!(
1002 &self.resources,
1003 &mut render_pass,
1004 &self.point_cloud_gpu_data,
1005 &self.glyph_gpu_data,
1006 &self.polyline_gpu_data,
1007 &self.volume_gpu_data,
1008 &self.streamtube_gpu_data,
1009 camera_bg,
1010 &self.tube_gpu_data,
1011 &self.image_slice_gpu_data,
1012 &self.tensor_glyph_gpu_data,
1013 &self.ribbon_gpu_data,
1014 &self.volume_surface_slice_gpu_data,
1015 &self.sprite_gpu_data,
1016 &self.mesh_instance_gpu_data,
1017 false
1018 );
1019 if !self.tvm_wireframe_draws.is_empty() {
1021 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
1022 render_pass.set_bind_group(0, camera_bg, &[]);
1023 for mesh_id in &self.tvm_wireframe_draws {
1024 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
1025 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
1026 render_pass.set_bind_group(
1027 2,
1028 &self.resources.deform.dummy_bind_group,
1029 &[],
1030 );
1031 render_pass.set_bind_group(1, tvm_bg, &[]);
1032 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1033 render_pass.set_index_buffer(
1034 mesh.edge_index_buffer.slice(..),
1035 wgpu::IndexFormat::Uint32,
1036 );
1037 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1038 }
1039 }
1040 }
1041 }
1042 if !self.implicit_gpu_data.is_empty() {
1044 if let Some(ref dual) = self.resources.implicit_pipeline {
1045 render_pass.set_pipeline(dual.for_format(false));
1046 render_pass.set_bind_group(0, camera_bg, &[]);
1047 for gpu in &self.implicit_gpu_data {
1048 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1049 render_pass.draw(0..6, 0..1);
1050 }
1051 }
1052 }
1053 if !self.mc_gpu_data.is_empty() {
1055 if let Some(ref dual) = self.resources.mc_surface_pipeline {
1056 render_pass.set_pipeline(dual.for_format(false));
1057 render_pass.set_bind_group(0, camera_bg, &[]);
1058 for mc in &self.mc_gpu_data {
1059 let vol = &self.resources.mc_volumes[mc.volume_idx];
1060 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1061 for slab in &vol.slabs {
1062 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1063 render_pass.draw_indirect(&slab.indirect_buf, 0);
1064 }
1065 }
1066 }
1067 }
1068 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
1070 if !self.screen_image_gpu_data.is_empty() {
1075 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
1076 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
1077 for gpu in &self.screen_image_gpu_data {
1078 if let (Some(dc_bg), Some(dc_pipe)) =
1079 (&gpu.depth_bind_group, dc_pipeline)
1080 {
1081 render_pass.set_pipeline(dc_pipe);
1082 render_pass.set_bind_group(0, dc_bg, &[]);
1083 } else {
1084 render_pass.set_pipeline(overlay_pipeline);
1085 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1086 }
1087 render_pass.draw(0..6, 0..1);
1088 }
1089 }
1090 }
1091 if !needs_blur {
1094 if let Some(ref sd) = self.overlay_shape_gpu_data {
1096 if sd.vertex_count > 0 {
1097 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1098 if let Some(vbuf) = &sd.vertex_buf {
1099 render_pass.set_pipeline(pipeline);
1100 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1101 render_pass.draw(0..sd.vertex_count, 0..1);
1102 }
1103 }
1104 }
1105 if !sd.tex_batches.is_empty() {
1106 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1107 render_pass.set_pipeline(pipeline);
1108 for batch in &sd.tex_batches {
1109 render_pass.set_bind_group(0, &batch.bind_group, &[]);
1110 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1111 render_pass.draw(0..batch.vertex_count, 0..1);
1112 }
1113 }
1114 }
1115 }
1116 if let Some(ref rr) = self.overlay_rect_gpu_data {
1118 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1119 render_pass.set_pipeline(pipeline);
1120 render_pass.set_bind_group(0, &rr.bind_group, &[]);
1121 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1122 render_pass.draw(0..rr.vertex_count, 0..1);
1123 }
1124 }
1125 if let Some(ref ld) = self.label_gpu_data {
1127 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1128 render_pass.set_pipeline(pipeline);
1129 render_pass.set_bind_group(0, &ld.bind_group, &[]);
1130 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1131 render_pass.draw(0..ld.vertex_count, 0..1);
1132 }
1133 }
1134 if let Some(ref sb) = self.scalar_bar_gpu_data {
1136 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1137 render_pass.set_pipeline(pipeline);
1138 render_pass.set_bind_group(0, &sb.bind_group, &[]);
1139 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1140 render_pass.draw(0..sb.vertex_count, 0..1);
1141 }
1142 }
1143 if let Some(ref rd) = self.ruler_gpu_data {
1145 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1146 render_pass.set_pipeline(pipeline);
1147 render_pass.set_bind_group(0, &rd.bind_group, &[]);
1148 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1149 render_pass.draw(0..rd.vertex_count, 0..1);
1150 }
1151 }
1152 if !self.overlay_image_gpu_data.is_empty() {
1154 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1155 render_pass.set_pipeline(pipeline);
1156 for gpu in &self.overlay_image_gpu_data {
1157 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1158 render_pass.draw(0..6, 0..1);
1159 }
1160 }
1161 }
1162 }
1163 }
1164 if needs_blur {
1169 let spread = self
1170 .overlay_shape_gpu_data
1171 .as_ref()
1172 .map(|d| d.max_blur_radius)
1173 .unwrap_or(1.0);
1174 let blur_bg = {
1175 let source = if use_dyn_res {
1176 &self.viewport_slots[vp_idx]
1177 .dyn_res
1178 .as_ref()
1179 .unwrap()
1180 .colour_view
1181 } else {
1182 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1183 };
1184 self.run_backdrop_blur(&mut encoder, device, queue, source, spread)
1185 };
1186
1187 let slot = &self.viewport_slots[vp_idx];
1189 let slot_hdr = slot.hdr.as_ref().unwrap();
1190 let overlay_colour_view: &wgpu::TextureView = if use_dyn_res {
1191 &slot.dyn_res.as_ref().unwrap().colour_view
1192 } else {
1193 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1194 };
1195 let overlay_depth_view: &wgpu::TextureView = if use_dyn_res {
1196 &slot.dyn_res.as_ref().unwrap().depth_view
1197 } else {
1198 &slot_hdr.outline_depth_view
1199 };
1200 {
1201 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1202 label: Some("ldr_overlay_blur_pass"),
1203 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1204 view: overlay_colour_view,
1205 resolve_target: None,
1206 ops: wgpu::Operations {
1207 load: wgpu::LoadOp::Load,
1208 store: wgpu::StoreOp::Store,
1209 },
1210 depth_slice: None,
1211 })],
1212 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1213 view: overlay_depth_view,
1214 depth_ops: Some(wgpu::Operations {
1215 load: wgpu::LoadOp::Load,
1216 store: wgpu::StoreOp::Discard,
1217 }),
1218 stencil_ops: None,
1219 }),
1220 timestamp_writes: None,
1221 occlusion_query_set: None,
1222 });
1223 self.draw_blur_shapes(&mut overlay_pass, &blur_bg);
1225 if let Some(ref sd) = self.overlay_shape_gpu_data {
1227 if sd.vertex_count > 0 {
1228 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1229 if let Some(vbuf) = &sd.vertex_buf {
1230 overlay_pass.set_pipeline(pipeline);
1231 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1232 overlay_pass.draw(0..sd.vertex_count, 0..1);
1233 }
1234 }
1235 }
1236 if !sd.tex_batches.is_empty() {
1237 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1238 overlay_pass.set_pipeline(pipeline);
1239 for batch in &sd.tex_batches {
1240 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
1241 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1242 overlay_pass.draw(0..batch.vertex_count, 0..1);
1243 }
1244 }
1245 }
1246 }
1247 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1248 if let Some(ref rr) = self.overlay_rect_gpu_data {
1249 overlay_pass.set_pipeline(pipeline);
1250 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
1251 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1252 overlay_pass.draw(0..rr.vertex_count, 0..1);
1253 }
1254 if let Some(ref ld) = self.label_gpu_data {
1255 overlay_pass.set_pipeline(pipeline);
1256 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
1257 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1258 overlay_pass.draw(0..ld.vertex_count, 0..1);
1259 }
1260 if let Some(ref sb) = self.scalar_bar_gpu_data {
1261 overlay_pass.set_pipeline(pipeline);
1262 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
1263 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1264 overlay_pass.draw(0..sb.vertex_count, 0..1);
1265 }
1266 if let Some(ref rd) = self.ruler_gpu_data {
1267 overlay_pass.set_pipeline(pipeline);
1268 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
1269 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1270 overlay_pass.draw(0..rd.vertex_count, 0..1);
1271 }
1272 }
1273 if !self.overlay_image_gpu_data.is_empty() {
1274 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1275 overlay_pass.set_pipeline(pipeline);
1276 for gpu in &self.overlay_image_gpu_data {
1277 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
1278 overlay_pass.draw(0..6, 0..1);
1279 }
1280 }
1281 }
1282 }
1283 }
1284
1285 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
1287 self.ts_query_set.as_ref(),
1288 self.ts_resolve_buf.as_ref(),
1289 self.ts_staging_buf.as_ref(),
1290 ) {
1291 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
1292 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
1293 self.ts_needs_readback = true;
1294 }
1295
1296 if use_dyn_res {
1298 let upscale_bg = &self.viewport_slots[vp_idx]
1299 .dyn_res
1300 .as_ref()
1301 .unwrap()
1302 .upscale_bind_group;
1303 let mut upscale_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1304 label: Some("dyn_res_upscale_pass"),
1305 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1306 view: output_view,
1307 resolve_target: None,
1308 ops: wgpu::Operations {
1309 load: wgpu::LoadOp::Load,
1310 store: wgpu::StoreOp::Store,
1311 },
1312 depth_slice: None,
1313 })],
1314 depth_stencil_attachment: None,
1315 timestamp_writes: None,
1316 occlusion_query_set: None,
1317 });
1318 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1319 upscale_pass.set_pipeline(pipeline);
1320 upscale_pass.set_bind_group(0, upscale_bg, &[]);
1321 upscale_pass.draw(0..3, 0..1);
1322 }
1323 } else if needs_blur {
1324 let bs = self.backdrop_blur_state.as_ref().unwrap();
1326 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
1327 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
1328 let blit_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1329 label: Some("backdrop_blit_bg"),
1330 layout: blit_bgl,
1331 entries: &[
1332 wgpu::BindGroupEntry {
1333 binding: 0,
1334 resource: wgpu::BindingResource::TextureView(&bs.intermediate_view),
1335 },
1336 wgpu::BindGroupEntry {
1337 binding: 1,
1338 resource: wgpu::BindingResource::Sampler(blit_sampler),
1339 },
1340 ],
1341 });
1342 let mut blit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1343 label: Some("backdrop_blit_pass"),
1344 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1345 view: output_view,
1346 resolve_target: None,
1347 ops: wgpu::Operations {
1348 load: wgpu::LoadOp::Load,
1349 store: wgpu::StoreOp::Store,
1350 },
1351 depth_slice: None,
1352 })],
1353 depth_stencil_attachment: None,
1354 timestamp_writes: None,
1355 occlusion_query_set: None,
1356 });
1357 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1358 blit_pass.set_pipeline(pipeline);
1359 blit_pass.set_bind_group(0, &blit_bg, &[]);
1360 blit_pass.draw(0..3, 0..1);
1361 }
1362 }
1363
1364 return encoder.finish();
1365 }
1366
1367 let pp = &frame.effects.post_process;
1369
1370 let hdr_clear_rgb = [
1371 bg_colour[0].powf(2.2),
1372 bg_colour[1].powf(2.2),
1373 bg_colour[2].powf(2.2),
1374 ];
1375
1376 let mode = match pp.tone_mapping {
1378 crate::renderer::ToneMapping::Reinhard => 0u32,
1379 crate::renderer::ToneMapping::Aces => 1u32,
1380 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
1381 };
1382 let tm_uniform = crate::resources::ToneMapUniform {
1383 exposure: pp.exposure,
1384 mode,
1385 bloom_enabled: if pp.bloom { 1 } else { 0 },
1386 ssao_enabled: if pp.ssao { 1 } else { 0 },
1387 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
1388 edl_enabled: if pp.edl_enabled { 1 } else { 0 },
1389 edl_radius: pp.edl_radius,
1390 edl_strength: pp.edl_strength,
1391 background_colour: bg_colour,
1392 near_plane: frame.camera.render_camera.near,
1393 far_plane: frame.camera.render_camera.far,
1394 lic_enabled: if scene_items
1395 .iter()
1396 .any(|i| i.lic.is_some() && !i.settings.hidden)
1397 {
1398 1
1399 } else {
1400 0
1401 },
1402 lic_strength: scene_items
1403 .iter()
1404 .filter(|i| !i.settings.hidden)
1405 .find_map(|i| i.lic.as_ref().map(|l| l.config.strength))
1406 .unwrap_or(0.5),
1407 };
1408 {
1409 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1410 queue.write_buffer(
1411 &hdr.tone_map_uniform_buf,
1412 0,
1413 bytemuck::cast_slice(&[tm_uniform]),
1414 );
1415
1416 if pp.ssao {
1418 let proj = frame.camera.render_camera.projection;
1419 let inv_proj = proj.inverse();
1420 let ssao_uniform = crate::resources::SsaoUniform {
1421 inv_proj: inv_proj.to_cols_array_2d(),
1422 proj: proj.to_cols_array_2d(),
1423 radius: 0.5,
1424 bias: 0.025,
1425 _pad: [0.0; 2],
1426 };
1427 queue.write_buffer(
1428 &hdr.ssao_uniform_buf,
1429 0,
1430 bytemuck::cast_slice(&[ssao_uniform]),
1431 );
1432 }
1433
1434 if pp.contact_shadows {
1436 let proj = frame.camera.render_camera.projection;
1437 let inv_proj = proj.inverse();
1438 let light_dir_world: glam::Vec3 =
1439 if let Some(l) = frame.effects.lighting.lights.first() {
1440 match l.kind {
1441 LightKind::Directional { direction } => {
1442 glam::Vec3::from(direction).normalize()
1443 }
1444 LightKind::Spot { direction, .. } => {
1445 -glam::Vec3::from(direction).normalize()
1449 }
1450 _ => glam::Vec3::new(0.0, -1.0, 0.0),
1451 }
1452 } else {
1453 glam::Vec3::new(0.0, -1.0, 0.0)
1454 };
1455 let view = frame.camera.render_camera.view;
1456 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
1457 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
1458 let cs_uniform = crate::resources::ContactShadowUniform {
1459 inv_proj: inv_proj.to_cols_array_2d(),
1460 proj: proj.to_cols_array_2d(),
1461 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
1462 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
1463 params: [
1464 pp.contact_shadow_max_distance,
1465 pp.contact_shadow_steps as f32,
1466 pp.contact_shadow_thickness,
1467 0.0,
1468 ],
1469 };
1470 queue.write_buffer(
1471 &hdr.contact_shadow_uniform_buf,
1472 0,
1473 bytemuck::cast_slice(&[cs_uniform]),
1474 );
1475 }
1476
1477 if pp.bloom {
1479 let bloom_u = crate::resources::BloomUniform {
1480 threshold: pp.bloom_threshold,
1481 intensity: pp.bloom_intensity,
1482 horizontal: 0,
1483 _pad: 0,
1484 };
1485 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
1486 }
1487 }
1488
1489 if pp.dof_enabled {
1491 let (w, h) = {
1492 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1493 (hdr.scene_size[0] as f32, hdr.scene_size[1] as f32)
1494 };
1495 let dof_uniform = crate::resources::DofUniform {
1496 focal_distance: pp.dof_focal_distance,
1497 focal_range: pp.dof_focal_range,
1498 max_blur_radius: pp.dof_max_blur_radius,
1499 near_plane: frame.camera.render_camera.near,
1500 far_plane: frame.camera.render_camera.far,
1501 viewport_width: w,
1502 viewport_height: h,
1503 _pad: 0.0,
1504 };
1505 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1506 queue.write_buffer(
1507 &hdr.dof_uniform_buf,
1508 0,
1509 bytemuck::cast_slice(&[dof_uniform]),
1510 );
1511 }
1512
1513 {
1515 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1516 self.resources.rebuild_tone_map_bind_group(
1517 device,
1518 hdr,
1519 pp.bloom,
1520 pp.ssao,
1521 pp.contact_shadows,
1522 scene_items
1523 .iter()
1524 .any(|i| i.lic.is_some() && !i.settings.hidden),
1525 pp.dof_enabled,
1526 );
1527 }
1528
1529 {
1534 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
1535 self.instanced_batches.iter().any(|b| b.is_transparent)
1536 } else {
1537 scene_items
1538 .iter()
1539 .any(|i| !i.settings.hidden && i.settings.opacity < 1.0)
1540 } || frame
1541 .scene
1542 .volume_meshes
1543 .iter()
1544 .any(|i| !i.settings.hidden && i.transparency.is_some());
1545 if needs_oit {
1546 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1547 let [sw, sh] = hdr.scene_size;
1548 self.resources.ensure_viewport_oit(device, hdr, sw, sh);
1549 }
1550 }
1551
1552 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1556 label: Some("hdr_encoder"),
1557 });
1558
1559 let slot = &self.viewport_slots[vp_idx];
1561 let camera_bg = &slot.camera_bind_group;
1562 let slot_hdr = slot.hdr.as_ref().expect(
1563 "HDR state missing; ensure_viewport_hdr must be called before render_frame_internal",
1564 );
1565
1566 {
1570 let use_ssaa = ssaa_factor > 1
1572 && slot_hdr.ssaa_colour_view.is_some()
1573 && slot_hdr.ssaa_depth_view.is_some();
1574 let scene_colour_view = if use_ssaa {
1575 slot_hdr.ssaa_colour_view.as_ref().unwrap()
1576 } else {
1577 &slot_hdr.hdr_view
1578 };
1579 let scene_depth_view = if use_ssaa {
1580 slot_hdr.ssaa_depth_view.as_ref().unwrap()
1581 } else {
1582 &slot_hdr.hdr_depth_view
1583 };
1584
1585 let clear_wgpu = wgpu::Color {
1586 r: hdr_clear_rgb[0] as f64,
1587 g: hdr_clear_rgb[1] as f64,
1588 b: hdr_clear_rgb[2] as f64,
1589 a: 0.0,
1592 };
1593
1594 let hdr_ts_writes =
1595 self.ts_query_set
1596 .as_ref()
1597 .map(|qs| wgpu::RenderPassTimestampWrites {
1598 query_set: qs,
1599 beginning_of_pass_write_index: Some(0),
1600 end_of_pass_write_index: Some(1),
1601 });
1602 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1603 label: Some("hdr_scene_pass"),
1604 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1605 view: scene_colour_view,
1606 resolve_target: None,
1607 ops: wgpu::Operations {
1608 load: wgpu::LoadOp::Clear(clear_wgpu),
1609 store: wgpu::StoreOp::Store,
1610 },
1611 depth_slice: None,
1612 })],
1613 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1614 view: scene_depth_view,
1615 depth_ops: Some(wgpu::Operations {
1616 load: wgpu::LoadOp::Clear(1.0),
1617 store: wgpu::StoreOp::Store,
1618 }),
1619 stencil_ops: Some(wgpu::Operations {
1620 load: wgpu::LoadOp::Clear(1),
1621 store: wgpu::StoreOp::Store,
1622 }),
1623 }),
1624 timestamp_writes: hdr_ts_writes,
1625 occlusion_query_set: None,
1626 });
1627
1628 let resources = &self.resources;
1629 render_pass.set_bind_group(0, camera_bg, &[]);
1630
1631 let show_skybox = frame
1633 .effects
1634 .environment
1635 .as_ref()
1636 .is_some_and(|e| e.show_skybox)
1637 && resources.ibl_skybox_view.is_some();
1638
1639 let use_instancing = self.use_instancing;
1640 let batches = &self.instanced_batches;
1641 let compute_filter_results = &self.compute_filter_results;
1642
1643 if !scene_items.is_empty() {
1644 if use_instancing && !batches.is_empty() {
1645 let excluded_items: Vec<(usize, &SceneRenderItem)> = scene_items
1646 .iter()
1647 .enumerate()
1648 .filter(|(_, item)| {
1649 !item.settings.hidden
1650 && (item.active_attribute.is_some()
1651 || item.material.is_two_sided()
1652 || item.material.matcap_id().is_some()
1653 || item.material.param_vis.is_some()
1654 || resources
1655 .deform
1656 .has_per_instance_deform_data(item.mesh_id, item.deform_instance))
1657 && resources.mesh_store.get(item.mesh_id).is_some()
1658 })
1659 .collect();
1660
1661 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1665 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1666 for (batch_global_idx, batch) in batches.iter().enumerate() {
1667 if batch.is_transparent {
1668 transparent_batches.push((batch_global_idx, batch));
1669 } else {
1670 opaque_batches.push((batch_global_idx, batch));
1671 }
1672 }
1673
1674 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
1675 let use_indirect = self.gpu_culling_enabled
1676 && resources.hdr_solid_instanced_cull_pipeline.is_some()
1677 && resources.indirect_args_buf.is_some();
1678
1679 if use_indirect {
1680 if let (Some(pipeline), Some(indirect_buf)) = (
1681 &resources.hdr_solid_instanced_cull_pipeline,
1682 &resources.indirect_args_buf,
1683 ) {
1684 render_pass.set_pipeline(pipeline);
1685 render_pass.set_bind_group(
1686 2,
1687 &resources.deform.dummy_bind_group,
1688 &[],
1689 );
1690 for (batch_global_idx, batch) in &opaque_batches {
1691 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1692 continue;
1693 };
1694 let mat_key = (
1695 batch.texture_id.unwrap_or(u64::MAX),
1696 batch.normal_map_id.unwrap_or(u64::MAX),
1697 batch.ao_map_id.unwrap_or(u64::MAX),
1698 );
1699 let Some(inst_tex_bg) =
1700 resources.instance_cull_bind_groups.get(&mat_key)
1701 else {
1702 continue;
1703 };
1704 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1705 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1706 render_pass.set_index_buffer(
1707 mesh.index_buffer.slice(..),
1708 wgpu::IndexFormat::Uint32,
1709 );
1710 render_pass.draw_indexed_indirect(
1713 indirect_buf,
1714 *batch_global_idx as u64 * 20,
1715 );
1716 }
1717 }
1718 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
1719 render_pass.set_pipeline(pipeline);
1720 render_pass.set_bind_group(
1721 2,
1722 &resources.deform.dummy_bind_group,
1723 &[],
1724 );
1725 for (_, batch) in &opaque_batches {
1726 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1727 continue;
1728 };
1729 let mat_key = (
1730 batch.texture_id.unwrap_or(u64::MAX),
1731 batch.normal_map_id.unwrap_or(u64::MAX),
1732 batch.ao_map_id.unwrap_or(u64::MAX),
1733 );
1734 let Some(inst_tex_bg) =
1735 resources.instance_bind_groups.get(&mat_key)
1736 else {
1737 continue;
1738 };
1739 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1740 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1741 render_pass.set_index_buffer(
1742 mesh.index_buffer.slice(..),
1743 wgpu::IndexFormat::Uint32,
1744 );
1745 render_pass.draw_indexed(
1746 0..mesh.index_count,
1747 0,
1748 batch.instance_offset
1749 ..batch.instance_offset + batch.instance_count,
1750 );
1751 }
1752 }
1753 }
1754
1755 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
1760 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
1761 let mut wf_idx = 0usize;
1762 for item in scene_items {
1763 if item.settings.hidden {
1764 continue;
1765 }
1766 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1767 continue;
1768 };
1769 render_pass.set_pipeline(hdr_wf);
1770 render_pass.set_bind_group(
1771 2,
1772 resources
1773 .deform
1774 .instance_bind_group_for(item.mesh_id, item.deform_instance),
1775 &[],
1776 );
1777 let bg = self
1778 .wireframe_bind_groups
1779 .get(wf_idx)
1780 .unwrap_or(&mesh.object_bind_group);
1781 render_pass.set_bind_group(1, bg, &[]);
1782 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1783 render_pass.set_index_buffer(
1784 mesh.edge_index_buffer.slice(..),
1785 wgpu::IndexFormat::Uint32,
1786 );
1787 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1788 wf_idx += 1;
1789 }
1790 }
1791 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
1792 &resources.hdr_solid_pipeline,
1793 &resources.hdr_solid_two_sided_pipeline,
1794 ) {
1795 for (item_idx, item) in
1801 excluded_items.iter().copied().filter(|(_, item)| {
1802 item.settings.opacity >= 1.0 && !item.material.is_blend()
1803 })
1804 {
1805 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1806 continue;
1807 };
1808 let pipeline = if item.material.is_two_sided() {
1809 hdr_solid_two_sided
1810 } else {
1811 hdr_solid
1812 };
1813 render_pass.set_pipeline(pipeline);
1814 render_pass.set_bind_group(
1815 2,
1816 resources
1817 .deform
1818 .instance_bind_group_for(item.mesh_id, item.deform_instance),
1819 &[],
1820 );
1821 let obj_bg = self
1822 .per_item_object_bind_groups
1823 .get(item_idx)
1824 .and_then(|opt| opt.as_ref())
1825 .unwrap_or(&mesh.object_bind_group);
1826 render_pass.set_bind_group(1, obj_bg, &[]);
1827 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1828 let filter = compute_filter_results
1829 .iter()
1830 .find(|r| r.mesh_id == item.mesh_id);
1831 if let Some(fr) = filter {
1832 render_pass.set_index_buffer(
1833 fr.index_buffer.slice(..),
1834 wgpu::IndexFormat::Uint32,
1835 );
1836 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1837 } else {
1838 render_pass.set_index_buffer(
1839 mesh.index_buffer.slice(..),
1840 wgpu::IndexFormat::Uint32,
1841 );
1842 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1843 }
1844 }
1845 }
1846
1847 if let Some(hdr_wf) = &resources.hdr_wireframe_pipeline {
1851 for item in scene_items
1852 .iter()
1853 .filter(|i| i.show_normals && !i.settings.hidden)
1854 {
1855 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1856 continue;
1857 };
1858 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1859 if mesh.normal_line_count > 0 {
1860 render_pass.set_pipeline(hdr_wf);
1861 render_pass.set_bind_group(
1862 2,
1863 &resources.deform.dummy_bind_group,
1864 &[],
1865 );
1866 render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
1867 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1868 render_pass.draw(0..mesh.normal_line_count, 0..1);
1869 }
1870 }
1871 }
1872 }
1873 } else {
1874 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
1876 let dist_from_eye = |entry: &(usize, &SceneRenderItem)| -> f32 {
1877 let item = entry.1;
1878 let pos =
1879 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
1880 (pos - eye).length()
1881 };
1882
1883 let mut opaque: Vec<(usize, &SceneRenderItem)> = Vec::new();
1884 let mut transparent: Vec<(usize, &SceneRenderItem)> = Vec::new();
1885 for (idx, item) in scene_items.iter().enumerate() {
1886 if item.settings.hidden || resources.mesh_store.get(item.mesh_id).is_none()
1887 {
1888 continue;
1889 }
1890 if item.settings.opacity < 1.0 || item.material.is_blend() {
1891 transparent.push((idx, item));
1892 } else {
1893 opaque.push((idx, item));
1894 }
1895 }
1896 opaque.sort_by(|a, b| {
1897 dist_from_eye(a)
1898 .partial_cmp(&dist_from_eye(b))
1899 .unwrap_or(std::cmp::Ordering::Equal)
1900 });
1901 transparent.sort_by(|a, b| {
1902 dist_from_eye(b)
1903 .partial_cmp(&dist_from_eye(a))
1904 .unwrap_or(std::cmp::Ordering::Equal)
1905 });
1906
1907 let per_item_bgs = &self.per_item_object_bind_groups;
1908 let draw_item_hdr =
1909 |render_pass: &mut wgpu::RenderPass<'_>,
1910 item_idx: usize,
1911 item: &SceneRenderItem,
1912 solid_pl: &wgpu::RenderPipeline,
1913 trans_pl: &wgpu::RenderPipeline,
1914 wf_pl: &wgpu::RenderPipeline| {
1915 let mesh = resources.mesh_store.get(item.mesh_id).unwrap();
1916 let obj_bg = per_item_bgs
1917 .get(item_idx)
1918 .and_then(|opt| opt.as_ref())
1919 .unwrap_or(&mesh.object_bind_group);
1920 render_pass.set_bind_group(1, obj_bg, &[]);
1921
1922 let deform_bg = resources
1923 .deform
1924 .instance_bind_group_for(item.mesh_id, item.deform_instance);
1925 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1926 matches!(
1927 a.kind,
1928 crate::resources::AttributeKind::Face
1929 | crate::resources::AttributeKind::FaceColour
1930 | crate::resources::AttributeKind::Halfedge
1931 | crate::resources::AttributeKind::Corner
1932 )
1933 });
1934 if frame.viewport.wireframe_mode {
1935 render_pass.set_pipeline(wf_pl);
1936 render_pass.set_bind_group(2, deform_bg, &[]);
1937 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1938 render_pass.set_index_buffer(
1939 mesh.edge_index_buffer.slice(..),
1940 wgpu::IndexFormat::Uint32,
1941 );
1942 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1943 } else if is_face_attr {
1944 if let Some(ref fvb) = mesh.face_vertex_buffer {
1945 let pl = if item.settings.opacity < 1.0 {
1946 trans_pl
1947 } else {
1948 solid_pl
1949 };
1950 render_pass.set_pipeline(pl);
1951 render_pass.set_bind_group(2, deform_bg, &[]);
1952 render_pass.set_vertex_buffer(0, fvb.slice(..));
1953 render_pass.draw(0..mesh.index_count, 0..1);
1954 }
1955 } else {
1956 let filter = compute_filter_results
1957 .iter()
1958 .find(|r| r.mesh_id == item.mesh_id);
1959 let pl = if item.settings.opacity < 1.0 {
1960 trans_pl
1961 } else {
1962 solid_pl
1963 };
1964 render_pass.set_pipeline(pl);
1965 render_pass.set_bind_group(2, deform_bg, &[]);
1966 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1967 if let Some(fr) = filter {
1968 render_pass.set_index_buffer(
1969 fr.index_buffer.slice(..),
1970 wgpu::IndexFormat::Uint32,
1971 );
1972 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1973 } else {
1974 render_pass.set_index_buffer(
1975 mesh.index_buffer.slice(..),
1976 wgpu::IndexFormat::Uint32,
1977 );
1978 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1979 }
1980 }
1981 if item.show_normals {
1982 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1983 if mesh.normal_line_count > 0 {
1984 render_pass.set_pipeline(wf_pl);
1985 render_pass.set_bind_group(
1986 2,
1987 &resources.deform.dummy_bind_group,
1988 &[],
1989 );
1990 render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
1991 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1992 render_pass.draw(0..mesh.normal_line_count, 0..1);
1993 }
1994 }
1995 }
1996 };
1997
1998 let _ = &transparent; if let (
2002 Some(hdr_solid),
2003 Some(hdr_solid_two_sided),
2004 Some(hdr_trans),
2005 Some(hdr_wf),
2006 ) = (
2007 &resources.hdr_solid_pipeline,
2008 &resources.hdr_solid_two_sided_pipeline,
2009 &resources.hdr_transparent_pipeline,
2010 &resources.hdr_wireframe_pipeline,
2011 ) {
2012 for (item_idx, item) in &opaque {
2013 let solid_pl = if item.material.is_two_sided() {
2014 hdr_solid_two_sided
2015 } else {
2016 hdr_solid
2017 };
2018 draw_item_hdr(
2019 &mut render_pass,
2020 *item_idx,
2021 item,
2022 solid_pl,
2023 hdr_trans,
2024 hdr_wf,
2025 );
2026 }
2027 }
2028 }
2029 }
2030
2031 if !slot.cap_buffers.is_empty() {
2033 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
2034 render_pass.set_pipeline(hdr_overlay);
2035 render_pass.set_bind_group(0, camera_bg, &[]);
2036 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
2037 render_pass.set_bind_group(1, bg, &[]);
2038 render_pass.set_vertex_buffer(0, vbuf.slice(..));
2039 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2040 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
2041 }
2042 }
2043 }
2044
2045 let sprite_slice_for_inline: &[crate::resources::SpriteGpuData] = &[];
2053 emit_scivis_draw_calls!(
2054 &self.resources,
2055 &mut render_pass,
2056 &self.point_cloud_gpu_data,
2057 &self.glyph_gpu_data,
2058 &self.polyline_gpu_data,
2059 &self.volume_gpu_data,
2060 &self.streamtube_gpu_data,
2061 camera_bg,
2062 &self.tube_gpu_data,
2063 &self.image_slice_gpu_data,
2064 &self.tensor_glyph_gpu_data,
2065 &self.ribbon_gpu_data,
2066 &self.volume_surface_slice_gpu_data,
2067 sprite_slice_for_inline,
2068 &self.mesh_instance_gpu_data,
2069 true
2070 );
2071
2072 if !self.implicit_gpu_data.is_empty() {
2074 if let Some(ref dual) = self.resources.implicit_pipeline {
2075 render_pass.set_pipeline(dual.for_format(true));
2076 render_pass.set_bind_group(0, camera_bg, &[]);
2077 for gpu in &self.implicit_gpu_data {
2078 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
2079 render_pass.draw(0..6, 0..1);
2080 }
2081 }
2082 }
2083 if !self.mc_gpu_data.is_empty() {
2085 render_pass.set_bind_group(0, camera_bg, &[]);
2086 for mc in &self.mc_gpu_data {
2087 let vol = &self.resources.mc_volumes[mc.volume_idx];
2088 if mc.wireframe || frame.viewport.wireframe_mode {
2089 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
2090 render_pass.set_pipeline(dual.for_format(true));
2091 for (slab, wire_bg) in vol.slabs.iter().zip(mc.wire_slab_bgs.iter()) {
2092 render_pass.set_bind_group(1, wire_bg, &[]);
2093 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
2094 }
2095 }
2096 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
2097 render_pass.set_pipeline(dual.for_format(true));
2098 render_pass.set_bind_group(1, &mc.render_bg, &[]);
2099 for slab in &vol.slabs {
2100 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
2101 render_pass.draw_indirect(&slab.indirect_buf, 0);
2102 }
2103 }
2104 }
2105 }
2106
2107 if !self.gaussian_splat_draw_data.is_empty() {
2109 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
2110 render_pass.set_pipeline(dual.for_format(true));
2111 render_pass.set_bind_group(0, camera_bg, &[]);
2112 for dd in &self.gaussian_splat_draw_data {
2113 if dd.wireframe {
2114 continue;
2115 }
2116 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
2117 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
2118 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
2119 render_pass.draw(0..6, 0..dd.count);
2120 }
2121 }
2122 }
2123 }
2124 }
2125 if !self.tvm_wireframe_draws.is_empty() {
2127 if let (Some(tvm_bg), Some(hdr_wf)) =
2128 (&self.tvm_wireframe_bg, &resources.hdr_wireframe_pipeline)
2129 {
2130 for mesh_id in &self.tvm_wireframe_draws {
2131 if let Some(mesh) = resources.mesh_store.get(*mesh_id) {
2132 render_pass.set_pipeline(hdr_wf);
2133 render_pass.set_bind_group(1, tvm_bg, &[]);
2134 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2135 render_pass.set_index_buffer(
2136 mesh.edge_index_buffer.slice(..),
2137 wgpu::IndexFormat::Uint32,
2138 );
2139 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
2140 }
2141 }
2142 }
2143 }
2144
2145 self.dispatch_plugin_paint(&mut render_pass, frame);
2148
2149 if show_skybox {
2151 render_pass.set_bind_group(0, camera_bg, &[]);
2152 render_pass.set_pipeline(&resources.skybox_pipeline);
2153 render_pass.draw(0..3, 0..1);
2154 }
2155 }
2156
2157 if !self.sprite_gpu_data.is_empty() {
2174 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2175 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2176 let resources = &self.resources;
2177
2178 let use_ssaa = ssaa_factor > 1
2179 && slot_hdr.ssaa_colour_view.is_some()
2180 && slot_hdr.ssaa_depth_view.is_some()
2181 && slot_hdr.ssaa_depth_only_view.is_some();
2182 let colour_view = if use_ssaa {
2183 slot_hdr.ssaa_colour_view.as_ref().unwrap()
2184 } else {
2185 &slot_hdr.hdr_view
2186 };
2187 let depth_view = if use_ssaa {
2188 slot_hdr.ssaa_depth_view.as_ref().unwrap()
2189 } else {
2190 &slot_hdr.hdr_depth_view
2191 };
2192 let depth_only_view = if use_ssaa {
2193 slot_hdr.ssaa_depth_only_view.as_ref().unwrap()
2194 } else {
2195 &slot_hdr.hdr_depth_only_view
2196 };
2197
2198 let any_depth_write = self.sprite_gpu_data.iter().any(|s| s.depth_write);
2199 let any_transparent = self.sprite_gpu_data.iter().any(|s| !s.depth_write);
2200
2201 let buckets = [
2202 (
2204 true,
2205 crate::renderer::SpriteBlend::AlphaBlend,
2206 false,
2207 resources.sprite_pipeline_depth_write.as_ref(),
2208 ),
2209 (
2210 true,
2211 crate::renderer::SpriteBlend::Additive,
2212 false,
2213 resources.sprite_pipeline_additive_depth_write.as_ref(),
2214 ),
2215 (
2216 true,
2217 crate::renderer::SpriteBlend::Premultiplied,
2218 false,
2219 resources.sprite_pipeline_premultiplied_depth_write.as_ref(),
2220 ),
2221 (
2222 false,
2223 crate::renderer::SpriteBlend::AlphaBlend,
2224 false,
2225 resources.sprite_pipeline.as_ref(),
2226 ),
2227 (
2228 false,
2229 crate::renderer::SpriteBlend::Additive,
2230 false,
2231 resources.sprite_pipeline_additive.as_ref(),
2232 ),
2233 (
2234 false,
2235 crate::renderer::SpriteBlend::Premultiplied,
2236 false,
2237 resources.sprite_pipeline_premultiplied.as_ref(),
2238 ),
2239 (
2240 true,
2241 crate::renderer::SpriteBlend::AlphaBlend,
2242 true,
2243 resources.sprite_lit_pipeline_depth_write.as_ref(),
2244 ),
2245 (
2246 true,
2247 crate::renderer::SpriteBlend::Additive,
2248 true,
2249 resources.sprite_lit_pipeline_additive_depth_write.as_ref(),
2250 ),
2251 (
2252 true,
2253 crate::renderer::SpriteBlend::Premultiplied,
2254 true,
2255 resources
2256 .sprite_lit_pipeline_premultiplied_depth_write
2257 .as_ref(),
2258 ),
2259 (
2260 false,
2261 crate::renderer::SpriteBlend::AlphaBlend,
2262 true,
2263 resources.sprite_lit_pipeline.as_ref(),
2264 ),
2265 (
2266 false,
2267 crate::renderer::SpriteBlend::Additive,
2268 true,
2269 resources.sprite_lit_pipeline_additive.as_ref(),
2270 ),
2271 (
2272 false,
2273 crate::renderer::SpriteBlend::Premultiplied,
2274 true,
2275 resources.sprite_lit_pipeline_premultiplied.as_ref(),
2276 ),
2277 ];
2278 let lit_fallback_bg = resources.sprite_lit_fallback_bg.as_ref();
2279
2280 let fallback_soft_bg = resources.sprite_soft_fallback_bg.as_ref();
2281
2282 if any_depth_write {
2286 if let Some(fallback_soft_bg) = fallback_soft_bg {
2287 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2288 label: Some("sprite_depth_write_pass"),
2289 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2290 view: colour_view,
2291 resolve_target: None,
2292 ops: wgpu::Operations {
2293 load: wgpu::LoadOp::Load,
2294 store: wgpu::StoreOp::Store,
2295 },
2296 depth_slice: None,
2297 })],
2298 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2299 view: depth_view,
2300 depth_ops: Some(wgpu::Operations {
2301 load: wgpu::LoadOp::Load,
2302 store: wgpu::StoreOp::Store,
2303 }),
2304 stencil_ops: Some(wgpu::Operations {
2305 load: wgpu::LoadOp::Load,
2306 store: wgpu::StoreOp::Store,
2307 }),
2308 }),
2309 timestamp_writes: None,
2310 occlusion_query_set: None,
2311 });
2312 for (depth_write, blend, lit, pipeline) in &buckets {
2313 if !*depth_write {
2314 continue;
2315 }
2316 let Some(dual) = pipeline else { continue };
2317 let mut bound = false;
2318 for sprite in self.sprite_gpu_data.iter() {
2319 if sprite.wireframe
2320 || !sprite.depth_write
2321 || sprite.blend != *blend
2322 || sprite.lit != *lit
2323 || sprite.refraction_strength > 0.0
2324 {
2325 continue;
2326 }
2327 if !bound {
2328 pass.set_pipeline(dual.for_format(true));
2329 pass.set_bind_group(0, camera_bg, &[]);
2330 pass.set_bind_group(2, fallback_soft_bg, &[]);
2331 bound = true;
2332 }
2333 pass.set_bind_group(1, &sprite.bind_group, &[]);
2334 if *lit {
2335 let normal_bg = sprite.lit_normal_bg.as_ref().or(lit_fallback_bg);
2336 if let Some(bg) = normal_bg {
2337 pass.set_bind_group(3, bg, &[]);
2338 }
2339 }
2340 pass.set_vertex_buffer(0, sprite.vertex_buffer.slice(..));
2341 pass.draw(0..6, 0..sprite.sprite_count);
2342 }
2343 }
2344 }
2345 }
2346
2347 if any_transparent {
2350 let real_soft_bg = if let (Some(bgl), Some(sampler)) = (
2351 resources.sprite_soft_bgl.as_ref(),
2352 resources.sprite_soft_sampler.as_ref(),
2353 ) {
2354 Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
2355 label: Some("sprite_soft_bg"),
2356 layout: bgl,
2357 entries: &[
2358 wgpu::BindGroupEntry {
2359 binding: 0,
2360 resource: wgpu::BindingResource::TextureView(depth_only_view),
2361 },
2362 wgpu::BindGroupEntry {
2363 binding: 1,
2364 resource: wgpu::BindingResource::Sampler(sampler),
2365 },
2366 ],
2367 }))
2368 } else {
2369 None
2370 };
2371
2372 if let Some(real_soft_bg) = real_soft_bg.as_ref() {
2373 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2374 label: Some("sprite_transparent_pass"),
2375 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2376 view: colour_view,
2377 resolve_target: None,
2378 ops: wgpu::Operations {
2379 load: wgpu::LoadOp::Load,
2380 store: wgpu::StoreOp::Store,
2381 },
2382 depth_slice: None,
2383 })],
2384 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2385 view: depth_view,
2386 depth_ops: None,
2387 stencil_ops: None,
2388 }),
2389 timestamp_writes: None,
2390 occlusion_query_set: None,
2391 });
2392 for (depth_write, blend, lit, pipeline) in &buckets {
2393 if *depth_write {
2394 continue;
2395 }
2396 let Some(dual) = pipeline else { continue };
2397 let mut bound = false;
2398 for sprite in self.sprite_gpu_data.iter() {
2399 if sprite.wireframe
2400 || sprite.depth_write
2401 || sprite.blend != *blend
2402 || sprite.lit != *lit
2403 || sprite.refraction_strength > 0.0
2404 {
2405 continue;
2406 }
2407 if !bound {
2408 pass.set_pipeline(dual.for_format(true));
2409 pass.set_bind_group(0, camera_bg, &[]);
2410 pass.set_bind_group(2, real_soft_bg, &[]);
2411 bound = true;
2412 }
2413 pass.set_bind_group(1, &sprite.bind_group, &[]);
2414 if *lit {
2415 let normal_bg = sprite.lit_normal_bg.as_ref().or(lit_fallback_bg);
2416 if let Some(bg) = normal_bg {
2417 pass.set_bind_group(3, bg, &[]);
2418 }
2419 }
2420 pass.set_vertex_buffer(0, sprite.vertex_buffer.slice(..));
2421 pass.draw(0..6, 0..sprite.sprite_count);
2422 }
2423 }
2424 }
2425 }
2426 }
2427
2428 if !self.particle_gpu_data.is_empty() {
2435 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2436 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2437 let resources = &self.resources;
2438
2439 let use_ssaa = ssaa_factor > 1
2440 && slot_hdr.ssaa_colour_view.is_some()
2441 && slot_hdr.ssaa_depth_view.is_some();
2442 let colour_view = if use_ssaa {
2443 slot_hdr.ssaa_colour_view.as_ref().unwrap()
2444 } else {
2445 &slot_hdr.hdr_view
2446 };
2447 let depth_view = if use_ssaa {
2448 slot_hdr.ssaa_depth_view.as_ref().unwrap()
2449 } else {
2450 &slot_hdr.hdr_depth_view
2451 };
2452
2453 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2454 label: Some("gpu_particle_sprite_pass"),
2455 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2456 view: colour_view,
2457 resolve_target: None,
2458 ops: wgpu::Operations {
2459 load: wgpu::LoadOp::Load,
2460 store: wgpu::StoreOp::Store,
2461 },
2462 depth_slice: None,
2463 })],
2464 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2465 view: depth_view,
2466 depth_ops: Some(wgpu::Operations {
2467 load: wgpu::LoadOp::Load,
2468 store: wgpu::StoreOp::Store,
2469 }),
2470 stencil_ops: None,
2471 }),
2472 timestamp_writes: None,
2473 occlusion_query_set: None,
2474 });
2475 pass.set_bind_group(0, camera_bg, &[]);
2476 let particle_lit_fallback = resources.particle_sprite_lit_fallback_bg.as_ref();
2477 for pd in &self.particle_gpu_data {
2478 if pd.hidden {
2479 continue;
2480 }
2481 let Some(system) = resources
2482 .particle_systems
2483 .get(pd.system_idx)
2484 .and_then(|s| s.as_ref())
2485 .filter(|s| s.alive)
2486 else {
2487 continue;
2488 };
2489 match pd.route {
2490 crate::resources::gpu_particles::ParticleDrawRoute::Sprite { lit } => {
2491 let dual = match (pd.blend, lit) {
2492 (crate::renderer::SpriteBlend::Additive, false) => {
2493 resources.particle_sprite_pipeline_additive.as_ref()
2494 }
2495 (crate::renderer::SpriteBlend::Premultiplied, false) => {
2496 resources.particle_sprite_pipeline_premultiplied.as_ref()
2497 }
2498 (crate::renderer::SpriteBlend::AlphaBlend, false) => {
2499 resources.particle_sprite_pipeline_alpha.as_ref()
2500 }
2501 (crate::renderer::SpriteBlend::Additive, true) => {
2502 resources.particle_sprite_lit_pipeline_additive.as_ref()
2503 }
2504 (crate::renderer::SpriteBlend::Premultiplied, true) => resources
2505 .particle_sprite_lit_pipeline_premultiplied
2506 .as_ref(),
2507 (crate::renderer::SpriteBlend::AlphaBlend, true) => {
2508 resources.particle_sprite_lit_pipeline_alpha.as_ref()
2509 }
2510 };
2511 let Some(dual) = dual else { continue };
2512 let Some(draw_bg) = system.draw_bg.as_ref() else {
2513 continue;
2514 };
2515 pass.set_pipeline(dual.for_format(true));
2516 pass.set_bind_group(1, draw_bg, &[]);
2517 if lit {
2518 let normal_bg =
2519 system.draw_lit_normal_bg.as_ref().or(particle_lit_fallback);
2520 if let Some(bg) = normal_bg {
2521 pass.set_bind_group(2, bg, &[]);
2522 }
2523 }
2524 pass.draw(0..6, 0..system.capacity);
2525 }
2526 crate::resources::gpu_particles::ParticleDrawRoute::Mesh { mesh_id } => {
2527 let dual = match pd.blend {
2528 crate::renderer::SpriteBlend::Additive => {
2529 resources.particle_mesh_pipeline_additive.as_ref()
2530 }
2531 crate::renderer::SpriteBlend::Premultiplied => {
2532 resources.particle_mesh_pipeline_premultiplied.as_ref()
2533 }
2534 crate::renderer::SpriteBlend::AlphaBlend => {
2535 resources.particle_mesh_pipeline_alpha.as_ref()
2536 }
2537 };
2538 let Some(dual) = dual else { continue };
2539 let Some(draw_bg) = system.draw_bg_mesh.as_ref() else {
2540 continue;
2541 };
2542 let Some(mesh) = resources
2543 .mesh_store
2544 .get(crate::resources::mesh_store::MeshId::from_index(mesh_id as usize))
2545 else {
2546 continue;
2547 };
2548 pass.set_pipeline(dual.for_format(true));
2549 pass.set_bind_group(1, draw_bg, &[]);
2550 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2551 pass.set_index_buffer(
2552 mesh.index_buffer.slice(..),
2553 wgpu::IndexFormat::Uint32,
2554 );
2555 pass.draw_indexed(0..mesh.index_count, 0, 0..system.capacity);
2556 }
2557 }
2558 }
2559 }
2560
2561 let has_refractive = self
2575 .sprite_gpu_data
2576 .iter()
2577 .any(|s| s.refraction_strength > 0.0 && !s.wireframe);
2578 if has_refractive && ssaa_factor == 1 {
2579 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2580 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2581 let resources = &self.resources;
2582 let physical_w = slot_hdr.hdr_texture.size().width;
2583 let physical_h = slot_hdr.hdr_texture.size().height;
2584
2585 while self.sprite_refraction_resolves.len() <= vp_idx {
2591 self.sprite_refraction_resolves.push(None);
2592 }
2593 let need_realloc = match self.sprite_refraction_resolves[vp_idx].as_ref() {
2594 Some(r) => r.size != [physical_w, physical_h],
2595 None => true,
2596 };
2597 if need_realloc {
2598 let tex = device.create_texture(&wgpu::TextureDescriptor {
2599 label: Some("sprite_refraction_resolve"),
2600 size: wgpu::Extent3d {
2601 width: physical_w.max(1),
2602 height: physical_h.max(1),
2603 depth_or_array_layers: 1,
2604 },
2605 mip_level_count: 1,
2606 sample_count: 1,
2607 dimension: wgpu::TextureDimension::D2,
2608 format: wgpu::TextureFormat::Rgba16Float,
2609 usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,
2610 view_formats: &[],
2611 });
2612 let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
2613 self.sprite_refraction_resolves[vp_idx] = Some(SpriteRefractionResolve {
2614 texture: tex,
2615 view,
2616 size: [physical_w, physical_h],
2617 });
2618 }
2619 let resolve = self.sprite_refraction_resolves[vp_idx].as_ref().unwrap();
2620 let resolve_tex = &resolve.texture;
2621 let resolve_view = &resolve.view;
2622
2623 encoder.copy_texture_to_texture(
2627 wgpu::TexelCopyTextureInfo {
2628 texture: &slot_hdr.hdr_texture,
2629 mip_level: 0,
2630 origin: wgpu::Origin3d::ZERO,
2631 aspect: wgpu::TextureAspect::All,
2632 },
2633 wgpu::TexelCopyTextureInfo {
2634 texture: resolve_tex,
2635 mip_level: 0,
2636 origin: wgpu::Origin3d::ZERO,
2637 aspect: wgpu::TextureAspect::All,
2638 },
2639 wgpu::Extent3d {
2640 width: physical_w.max(1),
2641 height: physical_h.max(1),
2642 depth_or_array_layers: 1,
2643 },
2644 );
2645
2646 let refraction_bgl = resources.sprite_refraction_bgl.as_ref().unwrap();
2648 let refraction_sampler = resources.sprite_refraction_sampler.as_ref().unwrap();
2649 let refraction_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
2650 label: Some("sprite_refraction_bg"),
2651 layout: refraction_bgl,
2652 entries: &[
2653 wgpu::BindGroupEntry {
2654 binding: 0,
2655 resource: wgpu::BindingResource::TextureView(resolve_view),
2656 },
2657 wgpu::BindGroupEntry {
2658 binding: 1,
2659 resource: wgpu::BindingResource::Sampler(refraction_sampler),
2660 },
2661 ],
2662 });
2663
2664 if let Some(pipeline) = resources.sprite_refraction_pipeline.as_ref() {
2665 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2666 label: Some("sprite_refraction_pass"),
2667 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2668 view: &slot_hdr.hdr_view,
2669 resolve_target: None,
2670 ops: wgpu::Operations {
2671 load: wgpu::LoadOp::Load,
2672 store: wgpu::StoreOp::Store,
2673 },
2674 depth_slice: None,
2675 })],
2676 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2677 view: &slot_hdr.hdr_depth_view,
2678 depth_ops: Some(wgpu::Operations {
2679 load: wgpu::LoadOp::Load,
2680 store: wgpu::StoreOp::Store,
2681 }),
2682 stencil_ops: None,
2683 }),
2684 timestamp_writes: None,
2685 occlusion_query_set: None,
2686 });
2687 pass.set_pipeline(pipeline);
2688 pass.set_bind_group(0, camera_bg, &[]);
2689 pass.set_bind_group(2, &refraction_bg, &[]);
2690 for sprite in self.sprite_gpu_data.iter() {
2691 if sprite.refraction_strength <= 0.0 || sprite.wireframe {
2692 continue;
2693 }
2694 pass.set_bind_group(1, &sprite.bind_group, &[]);
2695 pass.set_vertex_buffer(0, sprite.vertex_buffer.slice(..));
2696 pass.draw(0..6, 0..sprite.sprite_count);
2697 }
2698 }
2699 }
2700
2701 if ssaa_factor > 1 {
2706 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2707 if let (Some(pipeline), Some(bg)) = (
2708 &self.resources.ssaa_resolve_pipeline,
2709 &slot_hdr.ssaa_resolve_bind_group,
2710 ) {
2711 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2712 label: Some("ssaa_resolve_pass"),
2713 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2714 view: &slot_hdr.hdr_view,
2715 resolve_target: None,
2716 ops: wgpu::Operations {
2717 load: wgpu::LoadOp::Load,
2718 store: wgpu::StoreOp::Store,
2719 },
2720 depth_slice: None,
2721 })],
2722 depth_stencil_attachment: None,
2723 timestamp_writes: None,
2724 occlusion_query_set: None,
2725 });
2726 resolve_pass.set_pipeline(pipeline);
2727 resolve_pass.set_bind_group(0, bg, &[]);
2728 resolve_pass.draw(0..3, 0..1);
2729 }
2730 }
2731
2732 if !self.decal_exclude_items.is_empty() {
2737 if let Some(exclude_pl) = self.resources.decal_exclude_pipeline.as_ref() {
2738 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2739 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2740 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2741 label: Some("decal_exclude_pass"),
2742 color_attachments: &[],
2743 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2744 view: &slot_hdr.hdr_depth_view,
2745 depth_ops: Some(wgpu::Operations {
2746 load: wgpu::LoadOp::Load,
2747 store: wgpu::StoreOp::Store,
2748 }),
2749 stencil_ops: Some(wgpu::Operations {
2750 load: wgpu::LoadOp::Load,
2751 store: wgpu::StoreOp::Store,
2752 }),
2753 }),
2754 timestamp_writes: None,
2755 occlusion_query_set: None,
2756 });
2757 pass.set_pipeline(exclude_pl);
2758 pass.set_stencil_reference(0);
2759 pass.set_bind_group(0, camera_bg, &[]);
2760 for item in &self.decal_exclude_items {
2761 if let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) {
2762 pass.set_bind_group(1, &item.bind_group, &[]);
2763 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2764 if mesh.index_count > 0 {
2765 pass.set_index_buffer(
2766 mesh.index_buffer.slice(..),
2767 wgpu::IndexFormat::Uint32,
2768 );
2769 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2770 }
2771 }
2772 }
2773 }
2774 }
2775
2776 if !self.decal_gpu_data.is_empty() {
2782 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2783 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2784 let depth_bg = &slot_hdr.decal_depth_bg;
2785 let replace_pipeline = self.resources.decal_replace_pipeline.as_ref();
2786 let multiply_pipeline = self.resources.decal_multiply_pipeline.as_ref();
2787 let additive_pipeline = self.resources.decal_additive_pipeline.as_ref();
2788 if replace_pipeline.is_some()
2789 || multiply_pipeline.is_some()
2790 || additive_pipeline.is_some()
2791 {
2792 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2793 label: Some("decal_pass"),
2794 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2795 view: &slot_hdr.hdr_view,
2796 resolve_target: None,
2797 ops: wgpu::Operations {
2798 load: wgpu::LoadOp::Load,
2799 store: wgpu::StoreOp::Store,
2800 },
2801 depth_slice: None,
2802 })],
2803 depth_stencil_attachment: None,
2804 timestamp_writes: None,
2805 occlusion_query_set: None,
2806 });
2807 pass.set_bind_group(0, camera_bg, &[]);
2808 pass.set_bind_group(1, depth_bg, &[]);
2809 for gpu in &self.decal_gpu_data {
2810 let pipeline = match gpu.blend_mode {
2811 crate::renderer::DecalBlendMode::Replace => replace_pipeline,
2812 crate::renderer::DecalBlendMode::Multiply => multiply_pipeline,
2813 crate::renderer::DecalBlendMode::Additive => additive_pipeline,
2814 };
2815 if let Some(pl) = pipeline {
2816 pass.set_pipeline(&pl.hdr);
2817 pass.set_bind_group(2, &gpu.bind_group, &[]);
2818 pass.draw(0..6, 0..1);
2819 }
2820 }
2821 }
2822 }
2823
2824 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
2830 let resources = &self.resources;
2831 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
2832 &resources.sub_highlight_fill_pipeline,
2833 &resources.sub_highlight_edge_pipeline,
2834 &resources.sub_highlight_sprite_pipeline,
2835 ) {
2836 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2837 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2838 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2839 label: Some("sub_highlight_pass"),
2840 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2841 view: &slot_hdr.hdr_view,
2842 resolve_target: None,
2843 ops: wgpu::Operations {
2844 load: wgpu::LoadOp::Load,
2845 store: wgpu::StoreOp::Store,
2846 },
2847 depth_slice: None,
2848 })],
2849 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2850 view: &slot_hdr.hdr_depth_view,
2851 depth_ops: Some(wgpu::Operations {
2852 load: wgpu::LoadOp::Load,
2853 store: wgpu::StoreOp::Store,
2859 }),
2860 stencil_ops: None,
2861 }),
2862 timestamp_writes: None,
2863 occlusion_query_set: None,
2864 });
2865
2866 if sub_hl.fill_vertex_count > 0 {
2867 pass.set_pipeline(fill_pl);
2868 pass.set_bind_group(0, camera_bg, &[]);
2869 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
2870 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
2871 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
2872 }
2873 if sub_hl.edge_segment_count > 0 {
2874 pass.set_pipeline(edge_pl);
2875 pass.set_bind_group(0, camera_bg, &[]);
2876 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
2877 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
2878 pass.draw(0..6, 0..sub_hl.edge_segment_count);
2879 }
2880 if sub_hl.sprite_point_count > 0 {
2881 pass.set_pipeline(sprite_pl);
2882 pass.set_bind_group(0, camera_bg, &[]);
2883 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
2884 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
2885 pass.draw(0..6, 0..sub_hl.sprite_point_count);
2886 }
2887 }
2888 }
2889
2890 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
2895 self.instanced_batches.iter().any(|b| b.is_transparent)
2900 || scene_items.iter().any(|i| {
2901 !i.settings.hidden
2902 && (i.settings.opacity < 1.0 || i.material.is_blend())
2903 && (i.active_attribute.is_some()
2904 || i.material.is_two_sided()
2905 || i.material.matcap_id().is_some()
2906 || i.material.param_vis.is_some()
2907 || self
2908 .resources
2909 .deform
2910 .has_per_instance_deform_data(i.mesh_id, i.deform_instance))
2911 })
2912 } else {
2913 scene_items
2914 .iter()
2915 .any(|i| !i.settings.hidden && (i.settings.opacity < 1.0 || i.material.is_blend()))
2916 } || frame
2917 .scene
2918 .volume_meshes
2919 .iter()
2920 .any(|i| !i.settings.hidden && i.transparency.is_some());
2921
2922 if has_transparent {
2923 if let (Some(accum_view), Some(reveal_view)) = (
2925 slot_hdr.oit_accum_view.as_ref(),
2926 slot_hdr.oit_reveal_view.as_ref(),
2927 ) {
2928 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2929 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2931 label: Some("oit_pass"),
2932 color_attachments: &[
2933 Some(wgpu::RenderPassColorAttachment {
2934 view: accum_view,
2935 resolve_target: None,
2936 ops: wgpu::Operations {
2937 load: wgpu::LoadOp::Clear(wgpu::Color {
2938 r: 0.0,
2939 g: 0.0,
2940 b: 0.0,
2941 a: 0.0,
2942 }),
2943 store: wgpu::StoreOp::Store,
2944 },
2945 depth_slice: None,
2946 }),
2947 Some(wgpu::RenderPassColorAttachment {
2948 view: reveal_view,
2949 resolve_target: None,
2950 ops: wgpu::Operations {
2951 load: wgpu::LoadOp::Clear(wgpu::Color {
2952 r: 1.0,
2953 g: 1.0,
2954 b: 1.0,
2955 a: 1.0,
2956 }),
2957 store: wgpu::StoreOp::Store,
2958 },
2959 depth_slice: None,
2960 }),
2961 ],
2962 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2963 view: hdr_depth_view,
2964 depth_ops: Some(wgpu::Operations {
2965 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
2967 }),
2968 stencil_ops: None,
2969 }),
2970 timestamp_writes: None,
2971 occlusion_query_set: None,
2972 });
2973
2974 oit_pass.set_bind_group(0, camera_bg, &[]);
2975
2976 if self.use_instancing && !self.instanced_batches.is_empty() {
2977 let use_indirect_oit = self.gpu_culling_enabled
2978 && self.resources.oit_instanced_cull_pipeline.is_some()
2979 && self.resources.indirect_args_buf.is_some();
2980
2981 if use_indirect_oit {
2982 if let (Some(pipeline), Some(indirect_buf)) = (
2983 &self.resources.oit_instanced_cull_pipeline,
2984 &self.resources.indirect_args_buf,
2985 ) {
2986 oit_pass.set_pipeline(pipeline);
2987 oit_pass.set_bind_group(
2988 2,
2989 &self.resources.deform.dummy_bind_group,
2990 &[],
2991 );
2992 for (batch_global_idx, batch) in
2993 self.instanced_batches.iter().enumerate()
2994 {
2995 if !batch.is_transparent {
2996 continue;
2997 }
2998 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id)
2999 else {
3000 continue;
3001 };
3002 let mat_key = (
3003 batch.texture_id.unwrap_or(u64::MAX),
3004 batch.normal_map_id.unwrap_or(u64::MAX),
3005 batch.ao_map_id.unwrap_or(u64::MAX),
3006 );
3007 let Some(inst_tex_bg) =
3008 self.resources.instance_cull_bind_groups.get(&mat_key)
3009 else {
3010 continue;
3011 };
3012 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
3013 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3014 oit_pass.set_index_buffer(
3015 mesh.index_buffer.slice(..),
3016 wgpu::IndexFormat::Uint32,
3017 );
3018 oit_pass.draw_indexed_indirect(
3019 indirect_buf,
3020 batch_global_idx as u64 * 20,
3021 );
3022 }
3023 }
3024 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
3025 oit_pass.set_pipeline(pipeline);
3026 oit_pass.set_bind_group(
3027 2,
3028 &self.resources.deform.dummy_bind_group,
3029 &[],
3030 );
3031 for batch in &self.instanced_batches {
3032 if !batch.is_transparent {
3033 continue;
3034 }
3035 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id) else {
3036 continue;
3037 };
3038 let mat_key = (
3039 batch.texture_id.unwrap_or(u64::MAX),
3040 batch.normal_map_id.unwrap_or(u64::MAX),
3041 batch.ao_map_id.unwrap_or(u64::MAX),
3042 );
3043 let Some(inst_tex_bg) =
3044 self.resources.instance_bind_groups.get(&mat_key)
3045 else {
3046 continue;
3047 };
3048 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
3049 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3050 oit_pass.set_index_buffer(
3051 mesh.index_buffer.slice(..),
3052 wgpu::IndexFormat::Uint32,
3053 );
3054 oit_pass.draw_indexed(
3055 0..mesh.index_count,
3056 0,
3057 batch.instance_offset..batch.instance_offset + batch.instance_count,
3058 );
3059 }
3060 }
3061
3062 if let Some(ref pipeline) = self.resources.oit_pipeline {
3066 oit_pass.set_pipeline(pipeline);
3067 for (item_idx, item) in scene_items.iter().enumerate() {
3068 if item.settings.hidden
3069 || (item.settings.opacity >= 1.0 && !item.material.is_blend())
3070 {
3071 continue;
3072 }
3073 let is_skinned = self
3074 .resources
3075 .deform
3076 .has_per_instance_deform_data(item.mesh_id, item.deform_instance);
3077 if !is_skinned
3078 && item.active_attribute.is_none()
3079 && !item.material.is_two_sided()
3080 && item.material.matcap_id().is_none()
3081 {
3082 continue;
3083 }
3084 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
3085 continue;
3086 };
3087 let deform_bg = self
3088 .resources
3089 .deform
3090 .instance_bind_group_for(item.mesh_id, item.deform_instance);
3091 let obj_bg = self
3092 .per_item_object_bind_groups
3093 .get(item_idx)
3094 .and_then(|opt| opt.as_ref())
3095 .unwrap_or(&mesh.object_bind_group);
3096 oit_pass.set_bind_group(1, obj_bg, &[]);
3097 oit_pass.set_bind_group(2, deform_bg, &[]);
3098 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3099 oit_pass.set_index_buffer(
3100 mesh.index_buffer.slice(..),
3101 wgpu::IndexFormat::Uint32,
3102 );
3103 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3104 }
3105 }
3106 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
3107 oit_pass.set_pipeline(pipeline);
3108 for (item_idx, item) in scene_items.iter().enumerate() {
3109 if item.settings.hidden
3110 || (item.settings.opacity >= 1.0 && !item.material.is_blend())
3111 {
3112 continue;
3113 }
3114 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
3115 continue;
3116 };
3117 let deform_bg = self
3118 .resources
3119 .deform
3120 .instance_bind_group_for(item.mesh_id, item.deform_instance);
3121 let obj_bg = self
3122 .per_item_object_bind_groups
3123 .get(item_idx)
3124 .and_then(|opt| opt.as_ref())
3125 .unwrap_or(&mesh.object_bind_group);
3126 oit_pass.set_bind_group(1, obj_bg, &[]);
3127 oit_pass.set_bind_group(2, deform_bg, &[]);
3128 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3129 oit_pass.set_index_buffer(
3130 mesh.index_buffer.slice(..),
3131 wgpu::IndexFormat::Uint32,
3132 );
3133 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3134 }
3135 }
3136
3137 let any_transparent = frame
3143 .scene
3144 .volume_meshes
3145 .iter()
3146 .any(|i| !i.settings.hidden && i.transparency.is_some());
3147 if any_transparent {
3148 self.resources.ensure_pt_pipeline(device);
3149 for item in &frame.scene.volume_meshes {
3153 if item.settings.hidden || item.transparency.is_none() {
3154 continue;
3155 }
3156 self.resources
3157 .ensure_pt_lut_bind_group(device, item.colourmap_id);
3158 }
3159 if let Some(pipeline) = self.resources.pt_pipeline.as_ref() {
3160 oit_pass.set_pipeline(pipeline);
3161 oit_pass.set_bind_group(0, camera_bg, &[]);
3162 let resources = &self.resources;
3163 for item in &frame.scene.volume_meshes {
3164 if item.settings.hidden {
3165 continue;
3166 }
3167 let Some(transparency) = item.transparency else {
3168 continue;
3169 };
3170 if item.settings.wireframe || frame.viewport.wireframe_mode {
3171 continue;
3172 }
3173 let Some(pt_id) = item.projected_tet_id else {
3174 continue;
3175 };
3176 let Some(gpu) = resources.projected_tet_store.get(pt_id.0) else {
3177 continue;
3178 };
3179 let (scalar_min, scalar_max) =
3180 item.scalar_range.unwrap_or(gpu.scalar_range);
3181 let uniform = crate::resources::ProjectedTetUniform {
3182 density: transparency.density,
3183 scalar_min,
3184 scalar_max,
3185 threshold_min: transparency.threshold_min,
3186 threshold_max: transparency.threshold_max,
3187 unlit: if item.settings.unlit { 1 } else { 0 },
3188 opacity: item.settings.opacity,
3189 _pad: 0.0,
3190 };
3191 queue.write_buffer(
3192 &gpu.uniform_buffer,
3193 0,
3194 bytemuck::bytes_of(&uniform),
3195 );
3196 let lut_bg = item
3199 .colourmap_id
3200 .and_then(|id| {
3201 resources
3202 .colourmap_views
3203 .get(id.0)
3204 .and(resources.pt_lut_bind_groups.get(&id.0))
3205 })
3206 .or(resources.pt_fallback_lut_bind_group.as_ref());
3207 let Some(lut_bg) = lut_bg else { continue };
3208 oit_pass.set_bind_group(2, lut_bg, &[]);
3209 for chunk in &gpu.chunks {
3210 oit_pass.set_bind_group(1, &chunk.bind_group, &[]);
3211 oit_pass.draw(0..6, 0..chunk.tet_count);
3212 }
3213 }
3214 }
3215 }
3216
3217 self.dispatch_plugin_paint_transparent(&mut oit_pass, frame);
3219 }
3220 }
3221
3222 if has_transparent {
3227 if let (Some(pipeline), Some(bg)) = (
3228 self.resources.oit_composite_pipeline.as_ref(),
3229 slot_hdr.oit_composite_bind_group.as_ref(),
3230 ) {
3231 let hdr_view = &slot_hdr.hdr_view;
3232 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3233 label: Some("oit_composite_pass"),
3234 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3235 view: hdr_view,
3236 resolve_target: None,
3237 ops: wgpu::Operations {
3238 load: wgpu::LoadOp::Load,
3239 store: wgpu::StoreOp::Store,
3240 },
3241 depth_slice: None,
3242 })],
3243 depth_stencil_attachment: None,
3244 timestamp_writes: None,
3245 occlusion_query_set: None,
3246 });
3247 composite_pass.set_pipeline(pipeline);
3248 composite_pass.set_bind_group(0, bg, &[]);
3249 composite_pass.draw(0..3, 0..1);
3250 }
3251 }
3252
3253 if !self.prepared_scatter_volumes.is_empty() {
3261 let scatter = &frame.effects.scatter;
3262 let [sw, sh] = slot_hdr.scene_size;
3263 let want_downsample = scatter.downsample;
3264 let (tw, th) = if want_downsample {
3265 ((sw / 2).max(1), (sh / 2).max(1))
3266 } else {
3267 (sw.max(1), sh.max(1))
3268 };
3269
3270 while self.scatter_viewport_states.len() <= vp_idx {
3272 self.scatter_viewport_states.push(None);
3273 }
3274
3275 self.resources
3277 .ensure_scatter_pipeline(device, wgpu::TextureFormat::Rgba16Float);
3278 self.resources
3279 .ensure_scatter_composite_pipeline(device, wgpu::TextureFormat::Rgba16Float);
3280 self.resources
3281 .ensure_scatter_temporal_resolve_pipeline(device);
3282
3283 let needs_alloc = match self.scatter_viewport_states[vp_idx].as_ref() {
3285 None => true,
3286 Some(s) => s.size != [tw, th] || s.downsampled != want_downsample,
3287 };
3288 if needs_alloc {
3289 let make_tex = |label: &str| {
3290 device.create_texture(&wgpu::TextureDescriptor {
3291 label: Some(label),
3292 size: wgpu::Extent3d {
3293 width: tw,
3294 height: th,
3295 depth_or_array_layers: 1,
3296 },
3297 mip_level_count: 1,
3298 sample_count: 1,
3299 dimension: wgpu::TextureDimension::D2,
3300 format: wgpu::TextureFormat::Rgba16Float,
3301 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
3302 | wgpu::TextureUsages::TEXTURE_BINDING,
3303 view_formats: &[],
3304 })
3305 };
3306 let raw_tex = make_tex("scatter_raw_current");
3307 let hist_a_tex = make_tex("scatter_history_a");
3308 let hist_b_tex = make_tex("scatter_history_b");
3309 let raw_view = raw_tex.create_view(&wgpu::TextureViewDescriptor::default());
3310 let hist_a_view = hist_a_tex.create_view(&wgpu::TextureViewDescriptor::default());
3311 let hist_b_view = hist_b_tex.create_view(&wgpu::TextureViewDescriptor::default());
3312 let composite_bg_raw = self.resources.make_scatter_composite_bg(device, &raw_view);
3313 let composite_bg_history_a = self
3314 .resources
3315 .make_scatter_composite_bg(device, &hist_a_view);
3316 let composite_bg_history_b = self
3317 .resources
3318 .make_scatter_composite_bg(device, &hist_b_view);
3319 let temporal_resolve_bg_read_a = self.resources.make_scatter_temporal_resolve_bg(
3320 device,
3321 queue,
3322 &raw_view,
3323 &hist_a_view,
3324 &slot_hdr.hdr_depth_only_view,
3325 );
3326 let temporal_resolve_bg_read_b = self.resources.make_scatter_temporal_resolve_bg(
3327 device,
3328 queue,
3329 &raw_view,
3330 &hist_b_view,
3331 &slot_hdr.hdr_depth_only_view,
3332 );
3333 self.scatter_viewport_states[vp_idx] =
3334 Some(crate::resources::ScatterViewportState {
3335 raw_current_texture: raw_tex,
3336 raw_current_view: raw_view,
3337 history_a_texture: hist_a_tex,
3338 history_a_view: hist_a_view,
3339 history_b_texture: hist_b_tex,
3340 history_b_view: hist_b_view,
3341 composite_bg_raw,
3342 composite_bg_history_a,
3343 composite_bg_history_b,
3344 temporal_resolve_bg_read_a,
3345 temporal_resolve_bg_read_b,
3346 size: [tw, th],
3347 downsampled: want_downsample,
3348 parity: 0,
3349 history_valid: false,
3350 prev_view_proj: [[0.0; 4]; 4],
3351 refraction_source_texture: None,
3352 refraction_source_view: None,
3353 refraction_source_bg: None,
3354 refraction_blit_bg: None,
3355 refraction_source_size: [0, 0],
3356 });
3357 }
3358
3359 let (parity, history_valid, prev_view_proj) = {
3360 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3361 (s.parity, s.history_valid, s.prev_view_proj)
3362 };
3363
3364 if !self.prepared_refraction_volumes.is_empty() {
3371 self.resources
3372 .ensure_scatter_refraction_pipeline(device, wgpu::TextureFormat::Rgba16Float);
3373 self.resources.ensure_scatter_refraction_blit_pipeline(
3374 device,
3375 wgpu::TextureFormat::Rgba16Float,
3376 );
3377
3378 let need_realloc = {
3382 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3383 s.refraction_source_view.is_none() || s.refraction_source_size != [sw, sh]
3384 };
3385 if need_realloc {
3386 let src_tex = device.create_texture(&wgpu::TextureDescriptor {
3387 label: Some("scatter_refraction_source"),
3388 size: wgpu::Extent3d {
3389 width: sw.max(1),
3390 height: sh.max(1),
3391 depth_or_array_layers: 1,
3392 },
3393 mip_level_count: 1,
3394 sample_count: 1,
3395 dimension: wgpu::TextureDimension::D2,
3396 format: wgpu::TextureFormat::Rgba16Float,
3397 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
3398 | wgpu::TextureUsages::TEXTURE_BINDING,
3399 view_formats: &[],
3400 });
3401 let src_view = src_tex.create_view(&wgpu::TextureViewDescriptor::default());
3402 let blit_bg = self
3403 .resources
3404 .make_scatter_composite_bg(device, &slot_hdr.hdr_view);
3405 let source_bg = self.resources.make_scatter_refraction_source_bg(
3406 device,
3407 &src_view,
3408 &slot_hdr.hdr_depth_only_view,
3409 );
3410 let s = self.scatter_viewport_states[vp_idx].as_mut().unwrap();
3411 s.refraction_source_texture = Some(src_tex);
3412 s.refraction_source_view = Some(src_view);
3413 s.refraction_source_bg = Some(source_bg);
3414 s.refraction_blit_bg = Some(blit_bg);
3415 s.refraction_source_size = [sw, sh];
3416 }
3417
3418 let time_seconds = self.start_instant.elapsed().as_secs_f32();
3419 let n_ref = self.resources.write_scatter_refraction_per_volume_buffer(
3420 device,
3421 queue,
3422 &self.prepared_refraction_volumes,
3423 time_seconds,
3424 );
3425 let ref_stride = self.resources.scatter_refraction_per_volume_stride();
3426
3427 if n_ref > 0 {
3428 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3429 let src_view = s.refraction_source_view.as_ref().unwrap();
3430 let blit_bg = s.refraction_blit_bg.as_ref().unwrap();
3431 let source_bg = s.refraction_source_bg.as_ref().unwrap();
3432
3433 if let Some(blit_pipeline) =
3435 self.resources.scatter_refraction_blit_pipeline.as_ref()
3436 {
3437 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3438 label: Some("scatter_refraction_blit_pass"),
3439 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3440 view: src_view,
3441 resolve_target: None,
3442 ops: wgpu::Operations {
3443 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3444 store: wgpu::StoreOp::Store,
3445 },
3446 depth_slice: None,
3447 })],
3448 depth_stencil_attachment: None,
3449 timestamp_writes: None,
3450 occlusion_query_set: None,
3451 });
3452 pass.set_pipeline(blit_pipeline);
3453 pass.set_bind_group(0, blit_bg, &[]);
3454 pass.draw(0..3, 0..1);
3455 }
3456
3457 if let (Some(pipeline), Some(per_vol_bg)) = (
3460 self.resources.scatter_refraction_pipeline.as_ref(),
3461 self.resources.scatter_refraction_per_volume_bg.as_ref(),
3462 ) {
3463 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3464 label: Some("scatter_refraction_pass"),
3465 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3466 view: &slot_hdr.hdr_view,
3467 resolve_target: None,
3468 ops: wgpu::Operations {
3469 load: wgpu::LoadOp::Load,
3470 store: wgpu::StoreOp::Store,
3471 },
3472 depth_slice: None,
3473 })],
3474 depth_stencil_attachment: None,
3475 timestamp_writes: None,
3476 occlusion_query_set: None,
3477 });
3478 pass.set_pipeline(pipeline);
3479 pass.set_bind_group(0, camera_bg, &[]);
3480 pass.set_bind_group(2, source_bg, &[]);
3481 for i in 0..n_ref {
3482 let dyn_offset = i * ref_stride;
3483 pass.set_bind_group(1, per_vol_bg, &[dyn_offset]);
3484 pass.draw(0..6, 0..1);
3485 }
3486 }
3487 }
3488 }
3489
3490 self.resources.clear_scatter_per_volume_tex_cache();
3492 let n = self.resources.write_scatter_per_volume_buffer(
3493 device,
3494 queue,
3495 &self.prepared_scatter_volumes,
3496 );
3497 let stride = self.resources.scatter_per_volume_stride();
3498
3499 let time_seconds = self.start_instant.elapsed().as_secs_f32();
3501 let global_steps = scatter.quality.default_steps();
3502 let depth_token = (vp_idx as u64).wrapping_mul(1_000_003)
3503 ^ (tw as u64).wrapping_mul(7919)
3504 ^ (th as u64).wrapping_mul(31);
3505 self.resources.write_scatter_frame_uniform(
3506 device,
3507 queue,
3508 &slot_hdr.hdr_depth_only_view,
3509 depth_token,
3510 time_seconds,
3511 global_steps,
3512 scatter.blue_noise_jitter,
3513 self.frame_counter,
3514 );
3515
3516 if scatter.temporal {
3518 self.resources.write_scatter_temporal_uniform(
3519 device,
3520 queue,
3521 prev_view_proj,
3522 scatter.temporal_blend,
3523 history_valid,
3524 );
3525 }
3526
3527 let mut per_vol_tex_bgs: Vec<wgpu::BindGroup> = Vec::with_capacity(n as usize);
3529 for (volume, _, _) in self.prepared_scatter_volumes.iter().take(n as usize) {
3530 let (lut_id, density_id) =
3531 crate::resources::ViewportGpuResources::scatter_volume_tex_ids(volume);
3532 let bg = self
3533 .resources
3534 .ensure_scatter_per_volume_tex_bg(device, queue, lut_id, density_id);
3535 per_vol_tex_bgs.push(bg);
3536 }
3537
3538 if n > 0 {
3539 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3540 let raw_view = &s.raw_current_view;
3541 if let (Some(pipeline), Some(per_vol_bg), Some(frame_bg)) = (
3542 self.resources.scatter_pipeline.as_ref(),
3543 self.resources.scatter_per_volume_bg.as_ref(),
3544 self.resources.scatter_frame_bg.as_ref(),
3545 ) {
3546 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3547 label: Some("scatter_volume_pass"),
3548 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3549 view: raw_view,
3550 resolve_target: None,
3551 ops: wgpu::Operations {
3552 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3553 store: wgpu::StoreOp::Store,
3554 },
3555 depth_slice: None,
3556 })],
3557 depth_stencil_attachment: None,
3558 timestamp_writes: None,
3559 occlusion_query_set: None,
3560 });
3561 pass.set_pipeline(pipeline);
3562 pass.set_bind_group(0, camera_bg, &[]);
3563 pass.set_bind_group(3, frame_bg, &[]);
3564 for i in 0..n {
3565 let dyn_offset = i * stride;
3566 pass.set_bind_group(1, per_vol_bg, &[dyn_offset]);
3567 pass.set_bind_group(2, &per_vol_tex_bgs[i as usize], &[]);
3568 pass.draw(0..6, 0..1);
3569 }
3570 }
3571
3572 let source_for_composite: &wgpu::BindGroup = if scatter.temporal {
3574 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3575 let (resolve_bg, target_view, source_composite) = if parity == 0 {
3577 (
3579 &s.temporal_resolve_bg_read_b,
3580 &s.history_a_view,
3581 &s.composite_bg_history_a,
3582 )
3583 } else {
3584 (
3585 &s.temporal_resolve_bg_read_a,
3586 &s.history_b_view,
3587 &s.composite_bg_history_b,
3588 )
3589 };
3590 if let Some(resolve_pipeline) =
3591 self.resources.scatter_temporal_resolve_pipeline.as_ref()
3592 {
3593 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3594 label: Some("scatter_temporal_resolve_pass"),
3595 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3596 view: target_view,
3597 resolve_target: None,
3598 ops: wgpu::Operations {
3599 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3600 store: wgpu::StoreOp::Store,
3601 },
3602 depth_slice: None,
3603 })],
3604 depth_stencil_attachment: None,
3605 timestamp_writes: None,
3606 occlusion_query_set: None,
3607 });
3608 pass.set_pipeline(resolve_pipeline);
3609 pass.set_bind_group(0, resolve_bg, &[]);
3610 pass.draw(0..3, 0..1);
3611 }
3612 source_composite
3613 } else {
3614 &s.composite_bg_raw
3615 };
3616
3617 if let Some(composite_pipeline) = self.resources.scatter_composite_pipeline.as_ref()
3619 {
3620 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3621 label: Some("scatter_composite_pass"),
3622 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3623 view: &slot_hdr.hdr_view,
3624 resolve_target: None,
3625 ops: wgpu::Operations {
3626 load: wgpu::LoadOp::Load,
3627 store: wgpu::StoreOp::Store,
3628 },
3629 depth_slice: None,
3630 })],
3631 depth_stencil_attachment: None,
3632 timestamp_writes: None,
3633 occlusion_query_set: None,
3634 });
3635 pass.set_pipeline(composite_pipeline);
3636 pass.set_bind_group(0, source_for_composite, &[]);
3637 pass.draw(0..3, 0..1);
3638 }
3639
3640 let s = self.scatter_viewport_states[vp_idx].as_mut().unwrap();
3642 s.prev_view_proj = frame.camera.render_camera.view_proj().to_cols_array_2d();
3643 s.parity = 1 - s.parity;
3644 s.history_valid = scatter.temporal;
3645 } else if let Some(s) = self.scatter_viewport_states[vp_idx].as_mut() {
3646 s.history_valid = false;
3647 }
3648 }
3649
3650 if !self.lic_gpu_data.is_empty() {
3656 if let (Some(surface_pipeline), Some(advect_pipeline)) = (
3657 self.resources.lic_surface_pipeline.as_ref(),
3658 self.resources.lic_advect_pipeline.as_ref(),
3659 ) {
3660 let camera_bg = &slot.camera_bind_group;
3661 {
3663 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3664 label: Some("lic_surface_pass"),
3665 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3666 view: &slot_hdr.lic_vector_view,
3667 resolve_target: None,
3668 ops: wgpu::Operations {
3669 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3670 store: wgpu::StoreOp::Store,
3671 },
3672 depth_slice: None,
3673 })],
3674 depth_stencil_attachment: None,
3675 timestamp_writes: None,
3676 occlusion_query_set: None,
3677 });
3678 pass.set_pipeline(surface_pipeline);
3679 pass.set_bind_group(0, camera_bg, &[]);
3680 for gpu in &self.lic_gpu_data {
3681 let Some(mesh) = self.resources.mesh_store.get(gpu.mesh_id) else {
3682 continue;
3683 };
3684 let Some(vec_buf) =
3685 mesh.vector_attribute_buffers.get(&gpu.vector_attribute)
3686 else {
3687 continue;
3688 };
3689 pass.set_bind_group(1, &gpu.bind_group, &[]);
3690 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3691 pass.set_vertex_buffer(1, vec_buf.slice(..));
3692 pass.set_index_buffer(
3693 mesh.index_buffer.slice(..),
3694 wgpu::IndexFormat::Uint32,
3695 );
3696 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3697 }
3698 }
3699 {
3701 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3702 label: Some("lic_advect_pass"),
3703 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3704 view: &slot_hdr.lic_output_view,
3705 resolve_target: None,
3706 ops: wgpu::Operations {
3707 load: wgpu::LoadOp::Clear(wgpu::Color {
3708 r: 0.5,
3709 g: 0.0,
3710 b: 0.0,
3711 a: 1.0,
3712 }),
3713 store: wgpu::StoreOp::Store,
3714 },
3715 depth_slice: None,
3716 })],
3717 depth_stencil_attachment: None,
3718 timestamp_writes: None,
3719 occlusion_query_set: None,
3720 });
3721 pass.set_pipeline(advect_pipeline);
3722 pass.set_bind_group(0, &slot_hdr.lic_advect_bind_group, &[]);
3723 pass.draw(0..3, 0..1);
3724 }
3725 }
3726 }
3727
3728 if !slot.outline_object_buffers.is_empty()
3734 || !slot.splat_outline_buffers.is_empty()
3735 || !slot.streamtube_outline_items.is_empty()
3736 || !slot.tube_outline_items.is_empty()
3737 || !slot.ribbon_outline_items.is_empty()
3738 || !slot.polyline_outline_indices.is_empty()
3739 || !slot.volume_outline_indices.is_empty()
3740 || !slot.glyph_outline_indices.is_empty()
3741 || !slot.tensor_glyph_outline_indices.is_empty()
3742 || !slot.sprite_outline_indices.is_empty()
3743 || !slot.raw_geom_outline_buffers.is_empty()
3744 || !slot.screen_rect_outline_buffers.is_empty()
3745 || !slot.implicit_outline_indices.is_empty()
3746 || !slot.mc_outline_data.is_empty()
3747 {
3748 let hdr_pipeline = self
3750 .resources
3751 .outline_composite_pipeline_hdr
3752 .as_ref()
3753 .or(self.resources.outline_composite_pipeline_single.as_ref());
3754 if let Some(pipeline) = hdr_pipeline {
3755 let bg = &slot_hdr.outline_composite_bind_group;
3756 let hdr_view = &slot_hdr.hdr_view;
3757 let hdr_depth_view = &slot_hdr.hdr_depth_view;
3758 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3759 label: Some("hdr_outline_composite_pass"),
3760 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3761 view: hdr_view,
3762 resolve_target: None,
3763 ops: wgpu::Operations {
3764 load: wgpu::LoadOp::Load,
3765 store: wgpu::StoreOp::Store,
3766 },
3767 depth_slice: None,
3768 })],
3769 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3770 view: hdr_depth_view,
3771 depth_ops: Some(wgpu::Operations {
3772 load: wgpu::LoadOp::Load,
3773 store: wgpu::StoreOp::Store,
3774 }),
3775 stencil_ops: None,
3776 }),
3777 timestamp_writes: None,
3778 occlusion_query_set: None,
3779 });
3780 outline_pass.set_pipeline(pipeline);
3781 outline_pass.set_bind_group(0, bg, &[]);
3782 outline_pass.draw(0..3, 0..1);
3783 }
3784 }
3785
3786 let throttle_effects = self.degradation_effects_throttled;
3789
3790 if pp.ssao && !throttle_effects {
3794 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
3795 {
3796 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3797 label: Some("ssao_pass"),
3798 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3799 view: &slot_hdr.ssao_view,
3800 resolve_target: None,
3801 ops: wgpu::Operations {
3802 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3803 store: wgpu::StoreOp::Store,
3804 },
3805 depth_slice: None,
3806 })],
3807 depth_stencil_attachment: None,
3808 timestamp_writes: None,
3809 occlusion_query_set: None,
3810 });
3811 ssao_pass.set_pipeline(ssao_pipeline);
3812 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
3813 ssao_pass.draw(0..3, 0..1);
3814 }
3815
3816 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
3818 let mut ssao_blur_pass =
3819 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3820 label: Some("ssao_blur_pass"),
3821 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3822 view: &slot_hdr.ssao_blur_view,
3823 resolve_target: None,
3824 ops: wgpu::Operations {
3825 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3826 store: wgpu::StoreOp::Store,
3827 },
3828 depth_slice: None,
3829 })],
3830 depth_stencil_attachment: None,
3831 timestamp_writes: None,
3832 occlusion_query_set: None,
3833 });
3834 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
3835 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
3836 ssao_blur_pass.draw(0..3, 0..1);
3837 }
3838 }
3839 }
3840
3841 if pp.contact_shadows && !throttle_effects {
3845 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
3846 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3847 label: Some("contact_shadow_pass"),
3848 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3849 view: &slot_hdr.contact_shadow_view,
3850 resolve_target: None,
3851 depth_slice: None,
3852 ops: wgpu::Operations {
3853 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3854 store: wgpu::StoreOp::Store,
3855 },
3856 })],
3857 depth_stencil_attachment: None,
3858 timestamp_writes: None,
3859 occlusion_query_set: None,
3860 });
3861 cs_pass.set_pipeline(cs_pipeline);
3862 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
3863 cs_pass.draw(0..3, 0..1);
3864 }
3865 }
3866
3867 if pp.bloom && !throttle_effects {
3871 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
3873 {
3874 let mut threshold_pass =
3875 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3876 label: Some("bloom_threshold_pass"),
3877 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3878 view: &slot_hdr.bloom_threshold_view,
3879 resolve_target: None,
3880 ops: wgpu::Operations {
3881 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3882 store: wgpu::StoreOp::Store,
3883 },
3884 depth_slice: None,
3885 })],
3886 depth_stencil_attachment: None,
3887 timestamp_writes: None,
3888 occlusion_query_set: None,
3889 });
3890 threshold_pass.set_pipeline(bloom_threshold_pipeline);
3891 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
3892 threshold_pass.draw(0..3, 0..1);
3893 }
3894
3895 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
3898 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
3899 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
3900 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
3901 let bloom_ping_view = &slot_hdr.bloom_ping_view;
3902 let bloom_pong_view = &slot_hdr.bloom_pong_view;
3903 const BLUR_ITERATIONS: usize = 4;
3904 for i in 0..BLUR_ITERATIONS {
3905 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
3907 {
3908 let mut h_pass =
3909 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3910 label: Some("bloom_blur_h_pass"),
3911 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3912 view: bloom_ping_view,
3913 resolve_target: None,
3914 ops: wgpu::Operations {
3915 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3916 store: wgpu::StoreOp::Store,
3917 },
3918 depth_slice: None,
3919 })],
3920 depth_stencil_attachment: None,
3921 timestamp_writes: None,
3922 occlusion_query_set: None,
3923 });
3924 h_pass.set_pipeline(blur_pipeline);
3925 h_pass.set_bind_group(0, h_bg, &[]);
3926 h_pass.draw(0..3, 0..1);
3927 }
3928 {
3930 let mut v_pass =
3931 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3932 label: Some("bloom_blur_v_pass"),
3933 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3934 view: bloom_pong_view,
3935 resolve_target: None,
3936 ops: wgpu::Operations {
3937 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3938 store: wgpu::StoreOp::Store,
3939 },
3940 depth_slice: None,
3941 })],
3942 depth_stencil_attachment: None,
3943 timestamp_writes: None,
3944 occlusion_query_set: None,
3945 });
3946 v_pass.set_pipeline(blur_pipeline);
3947 v_pass.set_bind_group(0, blur_v_bg, &[]);
3948 v_pass.draw(0..3, 0..1);
3949 }
3950 }
3951 }
3952 }
3953 }
3954
3955 if pp.dof_enabled && !throttle_effects {
3959 if let Some(dof_pipeline) = &self.resources.dof_pipeline {
3960 let mut dof_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3961 label: Some("dof_pass"),
3962 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3963 view: &slot_hdr.dof_view,
3964 resolve_target: None,
3965 depth_slice: None,
3966 ops: wgpu::Operations {
3967 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3968 store: wgpu::StoreOp::Store,
3969 },
3970 })],
3971 depth_stencil_attachment: None,
3972 timestamp_writes: None,
3973 occlusion_query_set: None,
3974 });
3975 dof_pass.set_pipeline(dof_pipeline);
3976 dof_pass.set_bind_group(0, &slot_hdr.dof_bg, &[]);
3977 dof_pass.draw(0..3, 0..1);
3978 }
3979 }
3980
3981 let use_fxaa = pp.fxaa;
3989 let use_hdr_upscale = slot_hdr.upscale_bind_group.is_some();
3990 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
3991 let tone_target: &wgpu::TextureView = if use_fxaa {
3992 &slot_hdr.fxaa_view
3993 } else if use_hdr_upscale {
3994 slot_hdr.upscale_view.as_ref().unwrap()
3995 } else {
3996 output_view
3997 };
3998 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3999 label: Some("tone_map_pass"),
4000 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4001 view: tone_target,
4002 resolve_target: None,
4003 ops: wgpu::Operations {
4004 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4005 store: wgpu::StoreOp::Store,
4006 },
4007 depth_slice: None,
4008 })],
4009 depth_stencil_attachment: None,
4010 timestamp_writes: None,
4011 occlusion_query_set: None,
4012 });
4013 tone_pass.set_pipeline(tone_map_pipeline);
4014 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
4015 tone_pass.draw(0..3, 0..1);
4016 }
4017
4018 if use_fxaa {
4022 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
4023 let fxaa_target: &wgpu::TextureView = if use_hdr_upscale {
4024 slot_hdr.upscale_view.as_ref().unwrap()
4025 } else {
4026 output_view
4027 };
4028 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4029 label: Some("fxaa_pass"),
4030 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4031 view: fxaa_target,
4032 resolve_target: None,
4033 ops: wgpu::Operations {
4034 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4035 store: wgpu::StoreOp::Store,
4036 },
4037 depth_slice: None,
4038 })],
4039 depth_stencil_attachment: None,
4040 timestamp_writes: None,
4041 occlusion_query_set: None,
4042 });
4043 fxaa_pass.set_pipeline(fxaa_pipeline);
4044 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
4045 fxaa_pass.draw(0..3, 0..1);
4046 }
4047 }
4048
4049 if use_hdr_upscale {
4054 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
4055 if let Some(upscale_bg) = &slot_hdr.upscale_bind_group {
4056 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
4057 let mut upscale_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4058 label: Some("hdr_upscale_pass"),
4059 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4060 view: output_view,
4061 resolve_target: None,
4062 ops: wgpu::Operations {
4063 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4064 store: wgpu::StoreOp::Store,
4065 },
4066 depth_slice: None,
4067 })],
4068 depth_stencil_attachment: None,
4069 timestamp_writes: None,
4070 occlusion_query_set: None,
4071 });
4072 upscale_pass.set_pipeline(pipeline);
4073 upscale_pass.set_bind_group(0, upscale_bg, &[]);
4074 upscale_pass.draw(0..3, 0..1);
4075 }
4076 }
4077 }
4078
4079 {
4085 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
4086 if let Some(blit_bg) = &slot_hdr.depth_blit_bind_group {
4087 if let Some(blit_pipeline) = &self.resources.depth_blit_pipeline {
4088 let mut blit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4089 label: Some("depth_blit_pass"),
4090 color_attachments: &[],
4091 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4092 view: &slot_hdr.output_depth_view,
4093 depth_ops: Some(wgpu::Operations {
4094 load: wgpu::LoadOp::Clear(1.0),
4095 store: wgpu::StoreOp::Store,
4096 }),
4097 stencil_ops: None,
4098 }),
4099 timestamp_writes: None,
4100 occlusion_query_set: None,
4101 });
4102 blit_pass.set_pipeline(blit_pipeline);
4103 blit_pass.set_bind_group(0, blit_bg, &[]);
4104 blit_pass.draw(0..3, 0..1);
4105 }
4106 }
4107 }
4108
4109 if frame.viewport.show_grid {
4113 let slot = &self.viewport_slots[vp_idx];
4114 let slot_hdr = slot.hdr.as_ref().unwrap();
4115 let grid_bg = &slot.grid_bind_group;
4116 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4117 label: Some("hdr_grid_pass"),
4118 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4119 view: output_view,
4120 resolve_target: None,
4121 ops: wgpu::Operations {
4122 load: wgpu::LoadOp::Load,
4123 store: wgpu::StoreOp::Store,
4124 },
4125 depth_slice: None,
4126 })],
4127 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4128 view: &slot_hdr.output_depth_view,
4129 depth_ops: Some(wgpu::Operations {
4130 load: wgpu::LoadOp::Load,
4131 store: wgpu::StoreOp::Store,
4132 }),
4133 stencil_ops: None,
4134 }),
4135 timestamp_writes: None,
4136 occlusion_query_set: None,
4137 });
4138 grid_pass.set_pipeline(&self.resources.grid_pipeline);
4139 grid_pass.set_bind_group(0, grid_bg, &[]);
4140 grid_pass.draw(0..3, 0..1);
4141 }
4142
4143 if !matches!(
4146 frame.effects.ground_plane.mode,
4147 crate::renderer::types::GroundPlaneMode::None
4148 ) {
4149 let slot = &self.viewport_slots[vp_idx];
4150 let slot_hdr = slot.hdr.as_ref().unwrap();
4151 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4152 label: Some("hdr_ground_plane_pass"),
4153 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4154 view: output_view,
4155 resolve_target: None,
4156 ops: wgpu::Operations {
4157 load: wgpu::LoadOp::Load,
4158 store: wgpu::StoreOp::Store,
4159 },
4160 depth_slice: None,
4161 })],
4162 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4163 view: &slot_hdr.output_depth_view,
4164 depth_ops: Some(wgpu::Operations {
4165 load: wgpu::LoadOp::Load,
4166 store: wgpu::StoreOp::Store,
4167 }),
4168 stencil_ops: None,
4169 }),
4170 timestamp_writes: None,
4171 occlusion_query_set: None,
4172 });
4173 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
4174 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
4175 gp_pass.draw(0..3, 0..1);
4176 }
4177
4178 if !self.screen_image_gpu_data.is_empty() {
4186 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
4187 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
4188 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
4189 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4190 label: Some("screen_image_pass"),
4191 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4192 view: output_view,
4193 resolve_target: None,
4194 ops: wgpu::Operations {
4195 load: wgpu::LoadOp::Load,
4196 store: wgpu::StoreOp::Store,
4197 },
4198 depth_slice: None,
4199 })],
4200 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4201 view: &slot_hdr.output_depth_view,
4202 depth_ops: Some(wgpu::Operations {
4203 load: wgpu::LoadOp::Load,
4204 store: wgpu::StoreOp::Discard,
4205 }),
4206 stencil_ops: None,
4207 }),
4208 timestamp_writes: None,
4209 occlusion_query_set: None,
4210 });
4211 for gpu in &self.screen_image_gpu_data {
4212 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
4213 img_pass.set_pipeline(dc_pipe);
4214 img_pass.set_bind_group(0, dc_bg, &[]);
4215 } else {
4216 img_pass.set_pipeline(overlay_pipeline);
4217 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
4218 }
4219 img_pass.draw(0..6, 0..1);
4220 }
4221 }
4222 }
4223
4224 {
4228 let slot = &self.viewport_slots[vp_idx];
4229 let slot_hdr = slot.hdr.as_ref().unwrap();
4230 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
4231 && slot.gizmo_index_count > 0)
4232 || !slot.constraint_line_buffers.is_empty()
4233 || !slot.clip_plane_fill_buffers.is_empty()
4234 || !slot.clip_plane_line_buffers.is_empty()
4235 || !slot.xray_object_buffers.is_empty();
4236 if has_editor_overlays {
4237 let camera_bg = &slot.camera_bind_group;
4238 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4239 label: Some("hdr_editor_overlay_pass"),
4240 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4241 view: output_view,
4242 resolve_target: None,
4243 ops: wgpu::Operations {
4244 load: wgpu::LoadOp::Load,
4245 store: wgpu::StoreOp::Store,
4246 },
4247 depth_slice: None,
4248 })],
4249 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4250 view: &slot_hdr.output_depth_view,
4251 depth_ops: Some(wgpu::Operations {
4252 load: wgpu::LoadOp::Load,
4253 store: wgpu::StoreOp::Discard,
4254 }),
4255 stencil_ops: None,
4256 }),
4257 timestamp_writes: None,
4258 occlusion_query_set: None,
4259 });
4260
4261 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
4262 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
4263 overlay_pass.set_bind_group(0, camera_bg, &[]);
4264 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
4265 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
4266 overlay_pass.set_index_buffer(
4267 slot.gizmo_index_buffer.slice(..),
4268 wgpu::IndexFormat::Uint32,
4269 );
4270 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
4271 }
4272
4273 if !slot.constraint_line_buffers.is_empty() {
4274 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
4275 overlay_pass.set_bind_group(0, camera_bg, &[]);
4276 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
4277 overlay_pass.set_bind_group(1, bg, &[]);
4278 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
4279 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
4280 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
4281 }
4282 }
4283
4284 if !slot.clip_plane_fill_buffers.is_empty() {
4285 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
4286 overlay_pass.set_bind_group(0, camera_bg, &[]);
4287 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
4288 overlay_pass.set_bind_group(1, bg, &[]);
4289 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
4290 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
4291 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
4292 }
4293 }
4294
4295 if !slot.clip_plane_line_buffers.is_empty() {
4296 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
4297 overlay_pass.set_bind_group(0, camera_bg, &[]);
4298 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
4299 overlay_pass.set_bind_group(1, bg, &[]);
4300 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
4301 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
4302 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
4303 }
4304 }
4305
4306 if !slot.xray_object_buffers.is_empty() {
4307 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
4308 overlay_pass.set_bind_group(0, camera_bg, &[]);
4309 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
4310 let Some(mesh) = self.resources.mesh_store.get(*mesh_id) else {
4311 continue;
4312 };
4313 overlay_pass.set_bind_group(1, bg, &[]);
4314 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
4315 overlay_pass.set_index_buffer(
4316 mesh.index_buffer.slice(..),
4317 wgpu::IndexFormat::Uint32,
4318 );
4319 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
4320 }
4321 }
4322 }
4323 }
4324
4325 if frame.viewport.show_axes_indicator {
4328 let slot = &self.viewport_slots[vp_idx];
4329 if slot.axes_vertex_count > 0 {
4330 let slot_hdr = slot.hdr.as_ref().unwrap();
4331 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4332 label: Some("hdr_axes_pass"),
4333 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4334 view: output_view,
4335 resolve_target: None,
4336 ops: wgpu::Operations {
4337 load: wgpu::LoadOp::Load,
4338 store: wgpu::StoreOp::Store,
4339 },
4340 depth_slice: None,
4341 })],
4342 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4343 view: &slot_hdr.output_depth_view,
4344 depth_ops: Some(wgpu::Operations {
4345 load: wgpu::LoadOp::Load,
4346 store: wgpu::StoreOp::Discard,
4347 }),
4348 stencil_ops: None,
4349 }),
4350 timestamp_writes: None,
4351 occlusion_query_set: None,
4352 });
4353 axes_pass.set_pipeline(&self.resources.axes_pipeline);
4354 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
4355 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
4356 }
4357 }
4358
4359 let has_overlay = self.overlay_shape_gpu_data.is_some()
4361 || self.overlay_rect_gpu_data.is_some()
4362 || self.label_gpu_data.is_some()
4363 || self.scalar_bar_gpu_data.is_some()
4364 || self.ruler_gpu_data.is_some()
4365 || self.loading_bar_gpu_data.is_some()
4366 || !self.overlay_image_gpu_data.is_empty();
4367
4368 let needs_hdr_blur = self.has_backdrop_blur_shapes();
4373 let hdr_blur_bg: Option<wgpu::BindGroup> = if needs_hdr_blur && has_overlay {
4374 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
4375 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
4387 let source = &slot_hdr.hdr_view;
4388 let spread = self
4389 .overlay_shape_gpu_data
4390 .as_ref()
4391 .map(|d| d.max_blur_radius)
4392 .unwrap_or(8.0);
4393 Some(self.run_backdrop_blur(&mut encoder, device, queue, source, spread))
4394 } else {
4395 None
4396 };
4397
4398 if has_overlay {
4399 let hdr_depth_view = &self.viewport_slots[vp_idx]
4400 .hdr
4401 .as_ref()
4402 .unwrap()
4403 .output_depth_view;
4404 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4405 label: Some("overlay_pass"),
4406 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4407 view: output_view,
4408 resolve_target: None,
4409 ops: wgpu::Operations {
4410 load: wgpu::LoadOp::Load,
4411 store: wgpu::StoreOp::Store,
4412 },
4413 depth_slice: None,
4414 })],
4415 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4416 view: hdr_depth_view,
4417 depth_ops: Some(wgpu::Operations {
4418 load: wgpu::LoadOp::Load,
4419 store: wgpu::StoreOp::Discard,
4420 }),
4421 stencil_ops: None,
4422 }),
4423 timestamp_writes: None,
4424 occlusion_query_set: None,
4425 });
4426 if let Some(ref bg) = hdr_blur_bg {
4428 self.draw_blur_shapes(&mut overlay_pass, bg);
4429 }
4430 if let Some(ref sd) = self.overlay_shape_gpu_data {
4432 if sd.vertex_count > 0 {
4433 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
4434 if let Some(vbuf) = &sd.vertex_buf {
4435 overlay_pass.set_pipeline(pipeline);
4436 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
4437 overlay_pass.draw(0..sd.vertex_count, 0..1);
4438 }
4439 }
4440 }
4441 if !sd.tex_batches.is_empty() {
4442 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
4443 overlay_pass.set_pipeline(pipeline);
4444 for batch in &sd.tex_batches {
4445 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
4446 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
4447 overlay_pass.draw(0..batch.vertex_count, 0..1);
4448 }
4449 }
4450 }
4451 }
4452 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
4453 overlay_pass.set_pipeline(pipeline);
4454 if let Some(ref rr) = self.overlay_rect_gpu_data {
4455 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
4456 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
4457 overlay_pass.draw(0..rr.vertex_count, 0..1);
4458 }
4459 if let Some(ref ld) = self.label_gpu_data {
4460 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
4461 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
4462 overlay_pass.draw(0..ld.vertex_count, 0..1);
4463 }
4464 if let Some(ref sb) = self.scalar_bar_gpu_data {
4465 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
4466 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
4467 overlay_pass.draw(0..sb.vertex_count, 0..1);
4468 }
4469 if let Some(ref rd) = self.ruler_gpu_data {
4470 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
4471 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
4472 overlay_pass.draw(0..rd.vertex_count, 0..1);
4473 }
4474 if let Some(ref lb) = self.loading_bar_gpu_data {
4475 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
4476 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
4477 overlay_pass.draw(0..lb.vertex_count, 0..1);
4478 }
4479 }
4480 if !self.overlay_image_gpu_data.is_empty() {
4482 if let Some(pipeline) = &self.resources.screen_image_pipeline {
4483 overlay_pass.set_pipeline(pipeline);
4484 for gpu in &self.overlay_image_gpu_data {
4485 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
4486 overlay_pass.draw(0..6, 0..1);
4487 }
4488 }
4489 }
4490 }
4491
4492 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
4494 self.ts_query_set.as_ref(),
4495 self.ts_resolve_buf.as_ref(),
4496 self.ts_staging_buf.as_ref(),
4497 ) {
4498 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
4499 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
4500 self.ts_needs_readback = true;
4501 }
4502
4503 encoder.finish()
4504 }
4505
4506 pub fn render_offscreen(
4520 &mut self,
4521 device: &wgpu::Device,
4522 queue: &wgpu::Queue,
4523 frame: &FrameData,
4524 width: u32,
4525 height: u32,
4526 ) -> Vec<u8> {
4527 let target_format = self.resources.target_format;
4529 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
4530 label: Some("offscreen_target"),
4531 size: wgpu::Extent3d {
4532 width: width.max(1),
4533 height: height.max(1),
4534 depth_or_array_layers: 1,
4535 },
4536 mip_level_count: 1,
4537 sample_count: 1,
4538 dimension: wgpu::TextureDimension::D2,
4539 format: target_format,
4540 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
4541 view_formats: &[],
4542 });
4543
4544 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
4546
4547 let cmd_buf = self.render(device, queue, &output_view, frame);
4555 queue.submit(std::iter::once(cmd_buf));
4556
4557 let bytes_per_pixel = 4u32;
4559 let unpadded_row = width * bytes_per_pixel;
4560 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
4561 let padded_row = (unpadded_row + align - 1) & !(align - 1);
4562 let buffer_size = (padded_row * height.max(1)) as u64;
4563
4564 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
4565 label: Some("offscreen_staging"),
4566 size: buffer_size,
4567 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
4568 mapped_at_creation: false,
4569 });
4570
4571 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
4572 label: Some("offscreen_copy_encoder"),
4573 });
4574 copy_encoder.copy_texture_to_buffer(
4575 wgpu::TexelCopyTextureInfo {
4576 texture: &offscreen_texture,
4577 mip_level: 0,
4578 origin: wgpu::Origin3d::ZERO,
4579 aspect: wgpu::TextureAspect::All,
4580 },
4581 wgpu::TexelCopyBufferInfo {
4582 buffer: &staging_buf,
4583 layout: wgpu::TexelCopyBufferLayout {
4584 offset: 0,
4585 bytes_per_row: Some(padded_row),
4586 rows_per_image: Some(height.max(1)),
4587 },
4588 },
4589 wgpu::Extent3d {
4590 width: width.max(1),
4591 height: height.max(1),
4592 depth_or_array_layers: 1,
4593 },
4594 );
4595 queue.submit(std::iter::once(copy_encoder.finish()));
4596
4597 let (tx, rx) = std::sync::mpsc::channel();
4599 staging_buf
4600 .slice(..)
4601 .map_async(wgpu::MapMode::Read, move |result| {
4602 let _ = tx.send(result);
4603 });
4604 device
4605 .poll(wgpu::PollType::Wait {
4606 submission_index: None,
4607 timeout: Some(std::time::Duration::from_secs(5)),
4608 })
4609 .unwrap();
4610 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
4611
4612 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
4613 {
4614 let mapped = staging_buf.slice(..).get_mapped_range();
4615 let data: &[u8] = &mapped;
4616 if padded_row == unpadded_row {
4617 pixels.extend_from_slice(data);
4619 } else {
4620 for row in 0..height as usize {
4622 let start = row * padded_row as usize;
4623 let end = start + unpadded_row as usize;
4624 pixels.extend_from_slice(&data[start..end]);
4625 }
4626 }
4627 }
4628 staging_buf.unmap();
4629
4630 let is_bgra = matches!(
4632 target_format,
4633 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
4634 );
4635 if is_bgra {
4636 for pixel in pixels.chunks_exact_mut(4) {
4637 pixel.swap(0, 2); }
4639 }
4640
4641 pixels
4642 }
4643
4644 fn ensure_backdrop_blur_state(&mut self, device: &wgpu::Device, w: u32, h: u32) {
4650 let need_recreate = match &self.backdrop_blur_state {
4651 Some(s) => s.size != [w, h] || s.format != self.resources.target_format,
4652 None => true,
4653 };
4654 if !need_recreate {
4655 return;
4656 }
4657
4658 let format = self.resources.target_format;
4659 let blur_w = (w / 2).max(1);
4660 let blur_h = (h / 2).max(1);
4661
4662 let intermediate_texture = device.create_texture(&wgpu::TextureDescriptor {
4663 label: Some("backdrop_intermediate"),
4664 size: wgpu::Extent3d {
4665 width: w,
4666 height: h,
4667 depth_or_array_layers: 1,
4668 },
4669 mip_level_count: 1,
4670 sample_count: 1,
4671 dimension: wgpu::TextureDimension::D2,
4672 format,
4673 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
4674 view_formats: &[],
4675 });
4676 let intermediate_view = intermediate_texture.create_view(&Default::default());
4677
4678 let make_blur_tex = |label: &str| {
4679 let t = device.create_texture(&wgpu::TextureDescriptor {
4680 label: Some(label),
4681 size: wgpu::Extent3d {
4682 width: blur_w,
4683 height: blur_h,
4684 depth_or_array_layers: 1,
4685 },
4686 mip_level_count: 1,
4687 sample_count: 1,
4688 dimension: wgpu::TextureDimension::D2,
4689 format,
4690 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
4691 | wgpu::TextureUsages::TEXTURE_BINDING,
4692 view_formats: &[],
4693 });
4694 let v = t.create_view(&Default::default());
4695 (t, v)
4696 };
4697 let (blur_a_texture, blur_a_view) = make_blur_tex("backdrop_blur_a");
4698 let (blur_b_texture, blur_b_view) = make_blur_tex("backdrop_blur_b");
4699
4700 self.backdrop_blur_state = Some(crate::resources::BackdropBlurState {
4701 intermediate_texture,
4702 intermediate_view,
4703 blur_a_texture,
4704 blur_a_view,
4705 blur_b_texture,
4706 blur_b_view,
4707 size: [w, h],
4708 format,
4709 });
4710 }
4711
4712 fn run_backdrop_blur(
4716 &self,
4717 encoder: &mut wgpu::CommandEncoder,
4718 device: &wgpu::Device,
4719 _queue: &wgpu::Queue,
4720 source_view: &wgpu::TextureView,
4721 spread: f32,
4722 ) -> wgpu::BindGroup {
4723 let bs = self.backdrop_blur_state.as_ref().unwrap();
4724 let blur_bgl = self.resources.backdrop_blur_bgl.as_ref().unwrap();
4725 let blur_sampler = self.resources.backdrop_blur_sampler.as_ref().unwrap();
4726 let blur_pipeline = self.resources.backdrop_blur_pipeline.as_ref().unwrap();
4727 let blit_pipeline = self.resources.dyn_res_upscale_pipeline.as_ref().unwrap();
4729 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
4730 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
4731
4732 let downsample_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
4734 label: Some("backdrop_downsample_bg"),
4735 layout: blit_bgl,
4736 entries: &[
4737 wgpu::BindGroupEntry {
4738 binding: 0,
4739 resource: wgpu::BindingResource::TextureView(source_view),
4740 },
4741 wgpu::BindGroupEntry {
4742 binding: 1,
4743 resource: wgpu::BindingResource::Sampler(blit_sampler),
4744 },
4745 ],
4746 });
4747 {
4748 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4749 label: Some("backdrop_downsample"),
4750 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4751 view: &bs.blur_a_view,
4752 resolve_target: None,
4753 ops: wgpu::Operations {
4754 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4755 store: wgpu::StoreOp::Store,
4756 },
4757 depth_slice: None,
4758 })],
4759 depth_stencil_attachment: None,
4760 timestamp_writes: None,
4761 occlusion_query_set: None,
4762 });
4763 pass.set_pipeline(blit_pipeline);
4764 pass.set_bind_group(0, &downsample_bg, &[]);
4765 pass.draw(0..3, 0..1);
4766 }
4767
4768 let effective_spread = (spread / 2.0).max(1.0);
4770
4771 let h_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
4773 label: Some("blur_h_uniform"),
4774 contents: bytemuck::cast_slice(&[1u32, effective_spread.to_bits(), 0u32, 0u32]),
4775 usage: wgpu::BufferUsages::UNIFORM,
4776 });
4777 let h_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
4778 label: Some("blur_h_bg"),
4779 layout: blur_bgl,
4780 entries: &[
4781 wgpu::BindGroupEntry {
4782 binding: 0,
4783 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
4784 },
4785 wgpu::BindGroupEntry {
4786 binding: 1,
4787 resource: wgpu::BindingResource::Sampler(blur_sampler),
4788 },
4789 wgpu::BindGroupEntry {
4790 binding: 2,
4791 resource: h_uniform.as_entire_binding(),
4792 },
4793 ],
4794 });
4795 {
4796 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4797 label: Some("backdrop_blur_h"),
4798 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4799 view: &bs.blur_b_view,
4800 resolve_target: None,
4801 ops: wgpu::Operations {
4802 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4803 store: wgpu::StoreOp::Store,
4804 },
4805 depth_slice: None,
4806 })],
4807 depth_stencil_attachment: None,
4808 timestamp_writes: None,
4809 occlusion_query_set: None,
4810 });
4811 pass.set_pipeline(blur_pipeline);
4812 pass.set_bind_group(0, &h_bg, &[]);
4813 pass.draw(0..3, 0..1);
4814 }
4815
4816 let v_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
4818 label: Some("blur_v_uniform"),
4819 contents: bytemuck::cast_slice(&[0u32, effective_spread.to_bits(), 0u32, 0u32]),
4820 usage: wgpu::BufferUsages::UNIFORM,
4821 });
4822 let v_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
4823 label: Some("blur_v_bg"),
4824 layout: blur_bgl,
4825 entries: &[
4826 wgpu::BindGroupEntry {
4827 binding: 0,
4828 resource: wgpu::BindingResource::TextureView(&bs.blur_b_view),
4829 },
4830 wgpu::BindGroupEntry {
4831 binding: 1,
4832 resource: wgpu::BindingResource::Sampler(blur_sampler),
4833 },
4834 wgpu::BindGroupEntry {
4835 binding: 2,
4836 resource: v_uniform.as_entire_binding(),
4837 },
4838 ],
4839 });
4840 {
4841 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4842 label: Some("backdrop_blur_v"),
4843 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4844 view: &bs.blur_a_view,
4845 resolve_target: None,
4846 ops: wgpu::Operations {
4847 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4848 store: wgpu::StoreOp::Store,
4849 },
4850 depth_slice: None,
4851 })],
4852 depth_stencil_attachment: None,
4853 timestamp_writes: None,
4854 occlusion_query_set: None,
4855 });
4856 pass.set_pipeline(blur_pipeline);
4857 pass.set_bind_group(0, &v_bg, &[]);
4858 pass.draw(0..3, 0..1);
4859 }
4860
4861 let tex_bgl = self.resources.overlay_shape_tex_bgl.as_ref().unwrap();
4865 let tex_sampler = self.resources.overlay_shape_tex_sampler.as_ref().unwrap();
4866 device.create_bind_group(&wgpu::BindGroupDescriptor {
4867 label: Some("backdrop_blur_overlay_bg"),
4868 layout: tex_bgl,
4869 entries: &[
4870 wgpu::BindGroupEntry {
4871 binding: 0,
4872 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
4873 },
4874 wgpu::BindGroupEntry {
4875 binding: 1,
4876 resource: wgpu::BindingResource::Sampler(tex_sampler),
4877 },
4878 ],
4879 })
4880 }
4881
4882 fn has_backdrop_blur_shapes(&self) -> bool {
4884 self.overlay_shape_gpu_data
4885 .as_ref()
4886 .map_or(false, |sd| sd.blur_vertex_count > 0)
4887 }
4888
4889 fn draw_blur_shapes<'rp>(
4891 &'rp self,
4892 render_pass: &mut wgpu::RenderPass<'rp>,
4893 blur_bind_group: &'rp wgpu::BindGroup,
4894 ) {
4895 if let Some(ref sd) = self.overlay_shape_gpu_data {
4896 if sd.blur_vertex_count > 0 {
4897 if let (Some(pipeline), Some(vbuf)) = (
4898 &self.resources.overlay_shape_tex_pipeline,
4899 &sd.blur_vertex_buf,
4900 ) {
4901 render_pass.set_pipeline(pipeline);
4902 render_pass.set_bind_group(0, blur_bind_group, &[]);
4903 render_pass.set_vertex_buffer(0, vbuf.slice(..));
4904 render_pass.draw(0..sd.blur_vertex_count, 0..1);
4905 }
4906 }
4907 }
4908 }
4909}