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