1use super::*;
2use wgpu::util::DeviceExt;
3
4impl ViewportRenderer {
5 pub(crate) fn paint_to<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
6 let vp_idx = frame.camera.viewport_index;
7 let camera_bg = self.viewport_camera_bind_group(vp_idx);
8 let grid_bg = self.viewport_grid_bind_group(vp_idx);
9 let vp_slot = self.viewport_slots.get(vp_idx);
10 emit_draw_calls!(
11 &self.resources,
12 &mut *render_pass,
13 frame,
14 self.use_instancing,
15 &self.instanced_batches,
16 camera_bg,
17 grid_bg,
18 &self.compute_filter_results,
19 vp_slot,
20 &self.wireframe_bind_groups
21 );
22 emit_scivis_draw_calls!(
23 &self.resources,
24 &mut *render_pass,
25 &self.point_cloud_gpu_data,
26 &self.glyph_gpu_data,
27 &self.polyline_gpu_data,
28 &self.volume_gpu_data,
29 &self.streamtube_gpu_data,
30 camera_bg,
31 &self.tube_gpu_data,
32 &self.image_slice_gpu_data,
33 &self.tensor_glyph_gpu_data,
34 &self.ribbon_gpu_data,
35 &self.volume_surface_slice_gpu_data,
36 &self.sprite_gpu_data,
37 false
38 );
39 if !self.gaussian_splat_draw_data.is_empty() {
41 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
42 render_pass.set_pipeline(dual.for_format(false));
43 render_pass.set_bind_group(0, camera_bg, &[]);
44 for dd in &self.gaussian_splat_draw_data {
45 if dd.wireframe {
46 continue;
47 }
48 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
49 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
50 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
51 render_pass.draw(0..6, 0..dd.count);
52 }
53 }
54 }
55 }
56 }
57 if !self.tvm_wireframe_draws.is_empty() {
59 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
60 render_pass.set_bind_group(0, camera_bg, &[]);
61 for mesh_id in &self.tvm_wireframe_draws {
62 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
63 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
64 render_pass.set_bind_group(1, tvm_bg, &[]);
65 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
66 render_pass.set_index_buffer(
67 mesh.edge_index_buffer.slice(..),
68 wgpu::IndexFormat::Uint32,
69 );
70 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
71 }
72 }
73 }
74 }
75 if !self.implicit_gpu_data.is_empty() {
77 if let Some(ref dual) = self.resources.implicit_pipeline {
78 render_pass.set_pipeline(dual.for_format(false));
79 render_pass.set_bind_group(0, camera_bg, &[]);
80 for gpu in &self.implicit_gpu_data {
81 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
82 render_pass.draw(0..6, 0..1);
83 }
84 }
85 }
86 if !self.mc_gpu_data.is_empty() {
88 render_pass.set_bind_group(0, camera_bg, &[]);
89 for mc in &self.mc_gpu_data {
90 let vol = &self.resources.mc_volumes[mc.volume_idx];
91 if mc.wireframe || frame.viewport.wireframe_mode {
92 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
93 render_pass.set_pipeline(dual.for_format(false));
94 for (slab, wire_bg) in
95 vol.slabs.iter().zip(mc.wire_slab_bgs.iter())
96 {
97 render_pass.set_bind_group(1, wire_bg, &[]);
98 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
99 }
100 }
101 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
102 render_pass.set_pipeline(dual.for_format(false));
103 render_pass.set_bind_group(1, &mc.render_bg, &[]);
104 for slab in &vol.slabs {
105 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
106 render_pass.draw_indirect(&slab.indirect_buf, 0);
107 }
108 }
109 }
110 }
111 emit_outline_composite!(&self.resources, &mut *render_pass, vp_slot);
113 if let Some(sub_hl) = self
115 .viewport_slots
116 .get(vp_idx)
117 .and_then(|s| s.sub_highlight.as_ref())
118 {
119 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
120 &self.resources.sub_highlight_fill_ldr_pipeline,
121 &self.resources.sub_highlight_edge_ldr_pipeline,
122 &self.resources.sub_highlight_sprite_ldr_pipeline,
123 ) {
124 if sub_hl.fill_vertex_count > 0 {
125 render_pass.set_pipeline(fill_pl);
126 render_pass.set_bind_group(0, camera_bg, &[]);
127 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
128 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
129 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
130 }
131 if sub_hl.edge_segment_count > 0 {
132 render_pass.set_pipeline(edge_pl);
133 render_pass.set_bind_group(0, camera_bg, &[]);
134 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
135 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
136 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
137 }
138 if sub_hl.sprite_point_count > 0 {
139 render_pass.set_pipeline(sprite_pl);
140 render_pass.set_bind_group(0, camera_bg, &[]);
141 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
142 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
143 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
144 }
145 }
146 }
147 if !self.screen_image_gpu_data.is_empty() {
149 if let Some(pipeline) = &self.resources.screen_image_pipeline {
150 render_pass.set_pipeline(pipeline);
151 for gpu in &self.screen_image_gpu_data {
152 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
153 render_pass.draw(0..6, 0..1);
154 }
155 }
156 }
157 if let Some(ref sd) = self.overlay_shape_gpu_data {
159 if sd.vertex_count > 0 {
160 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
161 if let Some(vbuf) = &sd.vertex_buf {
162 render_pass.set_pipeline(pipeline);
163 render_pass.set_vertex_buffer(0, vbuf.slice(..));
164 render_pass.draw(0..sd.vertex_count, 0..1);
165 }
166 }
167 }
168 if !sd.tex_batches.is_empty() {
169 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
170 render_pass.set_pipeline(pipeline);
171 for batch in &sd.tex_batches {
172 render_pass.set_bind_group(0, &batch.bind_group, &[]);
173 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
174 render_pass.draw(0..batch.vertex_count, 0..1);
175 }
176 }
177 }
178 }
179 if let Some(ref rr) = self.overlay_rect_gpu_data {
181 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
182 render_pass.set_pipeline(pipeline);
183 render_pass.set_bind_group(0, &rr.bind_group, &[]);
184 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
185 render_pass.draw(0..rr.vertex_count, 0..1);
186 }
187 }
188 if let Some(ref ld) = self.label_gpu_data {
190 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
191 render_pass.set_pipeline(pipeline);
192 render_pass.set_bind_group(0, &ld.bind_group, &[]);
193 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
194 render_pass.draw(0..ld.vertex_count, 0..1);
195 }
196 }
197 if let Some(ref sb) = self.scalar_bar_gpu_data {
199 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
200 render_pass.set_pipeline(pipeline);
201 render_pass.set_bind_group(0, &sb.bind_group, &[]);
202 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
203 render_pass.draw(0..sb.vertex_count, 0..1);
204 }
205 }
206 if let Some(ref rd) = self.ruler_gpu_data {
208 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
209 render_pass.set_pipeline(pipeline);
210 render_pass.set_bind_group(0, &rd.bind_group, &[]);
211 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
212 render_pass.draw(0..rd.vertex_count, 0..1);
213 }
214 }
215 if let Some(ref lb) = self.loading_bar_gpu_data {
217 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
218 render_pass.set_pipeline(pipeline);
219 render_pass.set_bind_group(0, &lb.bind_group, &[]);
220 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
221 render_pass.draw(0..lb.vertex_count, 0..1);
222 }
223 }
224 if !self.overlay_image_gpu_data.is_empty() {
226 if let Some(pipeline) = &self.resources.screen_image_pipeline {
227 render_pass.set_pipeline(pipeline);
228 for gpu in &self.overlay_image_gpu_data {
229 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
230 render_pass.draw(0..6, 0..1);
231 }
232 }
233 }
234 }
235
236 pub(crate) fn prepare_ldr_dyn_res(
251 &mut self,
252 encoder: &mut wgpu::CommandEncoder,
253 device: &wgpu::Device,
254 frame: &FrameData,
255 ) -> bool {
256 if self.current_render_scale >= 1.0 - 0.001 {
257 return false;
258 }
259
260 let vp_idx = frame.camera.viewport_index;
261 let w = (frame.camera.viewport_size[0] as u32).max(1);
262 let h = (frame.camera.viewport_size[1] as u32).max(1);
263 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
264 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
265
266 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
267 self.resources.ensure_dyn_res_ds_pipeline(device);
268
269 let bg_colour = frame.viewport.background_colour.unwrap_or([
270 65.0 / 255.0,
271 65.0 / 255.0,
272 65.0 / 255.0,
273 1.0,
274 ]);
275
276 {
277 let slot = &self.viewport_slots[vp_idx];
278 let dr = slot.dyn_res.as_ref().unwrap();
279 let colour_view = &dr.colour_view;
280 let depth_view = &dr.depth_view;
281 let camera_bg = &slot.camera_bind_group;
282 let grid_bg = &slot.grid_bind_group;
283
284 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
285 label: Some("ldr_dyn_res_render_pass"),
286 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
287 view: colour_view,
288 resolve_target: None,
289 ops: wgpu::Operations {
290 load: wgpu::LoadOp::Clear(wgpu::Color {
291 r: bg_colour[0] as f64,
292 g: bg_colour[1] as f64,
293 b: bg_colour[2] as f64,
294 a: bg_colour[3] as f64,
295 }),
296 store: wgpu::StoreOp::Store,
297 },
298 depth_slice: None,
299 })],
300 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
301 view: depth_view,
302 depth_ops: Some(wgpu::Operations {
303 load: wgpu::LoadOp::Clear(1.0),
304 store: wgpu::StoreOp::Discard,
305 }),
306 stencil_ops: None,
307 }),
308 timestamp_writes: None,
309 occlusion_query_set: None,
310 });
311 emit_draw_calls!(
312 &self.resources,
313 &mut render_pass,
314 frame,
315 self.use_instancing,
316 &self.instanced_batches,
317 camera_bg,
318 grid_bg,
319 &self.compute_filter_results,
320 Some(slot),
321 &self.wireframe_bind_groups
322 );
323 emit_scivis_draw_calls!(
324 &self.resources,
325 &mut render_pass,
326 &self.point_cloud_gpu_data,
327 &self.glyph_gpu_data,
328 &self.polyline_gpu_data,
329 &self.volume_gpu_data,
330 &self.streamtube_gpu_data,
331 camera_bg,
332 &self.tube_gpu_data,
333 &self.image_slice_gpu_data,
334 &self.tensor_glyph_gpu_data,
335 &self.ribbon_gpu_data,
336 &self.volume_surface_slice_gpu_data,
337 &self.sprite_gpu_data,
338 false
339 );
340 if !self.tvm_wireframe_draws.is_empty() {
342 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
343 render_pass.set_bind_group(0, camera_bg, &[]);
344 for mesh_id in &self.tvm_wireframe_draws {
345 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
346 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
347 render_pass.set_bind_group(1, tvm_bg, &[]);
348 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
349 render_pass.set_index_buffer(
350 mesh.edge_index_buffer.slice(..),
351 wgpu::IndexFormat::Uint32,
352 );
353 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
354 }
355 }
356 }
357 }
358 if !self.implicit_gpu_data.is_empty() {
360 if let Some(ref dual) = self.resources.implicit_pipeline {
361 render_pass.set_pipeline(dual.for_format(false));
362 render_pass.set_bind_group(0, camera_bg, &[]);
363 for gpu in &self.implicit_gpu_data {
364 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
365 render_pass.draw(0..6, 0..1);
366 }
367 }
368 }
369 if !self.mc_gpu_data.is_empty() {
371 if let Some(ref dual) = self.resources.mc_surface_pipeline {
372 render_pass.set_pipeline(dual.for_format(false));
373 render_pass.set_bind_group(0, camera_bg, &[]);
374 for mc in &self.mc_gpu_data {
375 let vol = &self.resources.mc_volumes[mc.volume_idx];
376 render_pass.set_bind_group(1, &mc.render_bg, &[]);
377 for slab in &vol.slabs {
378 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
379 render_pass.draw_indirect(&slab.indirect_buf, 0);
380 }
381 }
382 }
383 }
384 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
386 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
388 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
389 &self.resources.sub_highlight_fill_ldr_pipeline,
390 &self.resources.sub_highlight_edge_ldr_pipeline,
391 &self.resources.sub_highlight_sprite_ldr_pipeline,
392 ) {
393 if sub_hl.fill_vertex_count > 0 {
394 render_pass.set_pipeline(fill_pl);
395 render_pass.set_bind_group(0, camera_bg, &[]);
396 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
397 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
398 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
399 }
400 if sub_hl.edge_segment_count > 0 {
401 render_pass.set_pipeline(edge_pl);
402 render_pass.set_bind_group(0, camera_bg, &[]);
403 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
404 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
405 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
406 }
407 if sub_hl.sprite_point_count > 0 {
408 render_pass.set_pipeline(sprite_pl);
409 render_pass.set_bind_group(0, camera_bg, &[]);
410 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
411 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
412 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
413 }
414 }
415 }
416 if !self.screen_image_gpu_data.is_empty() {
418 if let Some(pipeline) = &self.resources.screen_image_pipeline {
419 render_pass.set_pipeline(pipeline);
420 for gpu in &self.screen_image_gpu_data {
421 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
422 render_pass.draw(0..6, 0..1);
423 }
424 }
425 }
426 if let Some(ref sd) = self.overlay_shape_gpu_data {
428 if sd.vertex_count > 0 {
429 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
430 if let Some(vbuf) = &sd.vertex_buf {
431 render_pass.set_pipeline(pipeline);
432 render_pass.set_vertex_buffer(0, vbuf.slice(..));
433 render_pass.draw(0..sd.vertex_count, 0..1);
434 }
435 }
436 }
437 if !sd.tex_batches.is_empty() {
438 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
439 render_pass.set_pipeline(pipeline);
440 for batch in &sd.tex_batches {
441 render_pass.set_bind_group(0, &batch.bind_group, &[]);
442 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
443 render_pass.draw(0..batch.vertex_count, 0..1);
444 }
445 }
446 }
447 }
448 if let Some(ref rr) = self.overlay_rect_gpu_data {
450 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
451 render_pass.set_pipeline(pipeline);
452 render_pass.set_bind_group(0, &rr.bind_group, &[]);
453 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
454 render_pass.draw(0..rr.vertex_count, 0..1);
455 }
456 }
457 if let Some(ref ld) = self.label_gpu_data {
459 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
460 render_pass.set_pipeline(pipeline);
461 render_pass.set_bind_group(0, &ld.bind_group, &[]);
462 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
463 render_pass.draw(0..ld.vertex_count, 0..1);
464 }
465 }
466 if let Some(ref sb) = self.scalar_bar_gpu_data {
468 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
469 render_pass.set_pipeline(pipeline);
470 render_pass.set_bind_group(0, &sb.bind_group, &[]);
471 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
472 render_pass.draw(0..sb.vertex_count, 0..1);
473 }
474 }
475 if let Some(ref rd) = self.ruler_gpu_data {
477 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
478 render_pass.set_pipeline(pipeline);
479 render_pass.set_bind_group(0, &rd.bind_group, &[]);
480 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
481 render_pass.draw(0..rd.vertex_count, 0..1);
482 }
483 }
484 if let Some(ref lb) = self.loading_bar_gpu_data {
486 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
487 render_pass.set_pipeline(pipeline);
488 render_pass.set_bind_group(0, &lb.bind_group, &[]);
489 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
490 render_pass.draw(0..lb.vertex_count, 0..1);
491 }
492 }
493 if !self.overlay_image_gpu_data.is_empty() {
495 if let Some(pipeline) = &self.resources.screen_image_pipeline {
496 render_pass.set_pipeline(pipeline);
497 for gpu in &self.overlay_image_gpu_data {
498 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
499 render_pass.draw(0..6, 0..1);
500 }
501 }
502 }
503 }
504
505 true
506 }
507
508 pub(crate) fn paint_dyn_res_blit<'rp>(
514 &self,
515 render_pass: &mut wgpu::RenderPass<'rp>,
516 frame: &FrameData,
517 ) {
518 let vp_idx = frame.camera.viewport_index;
519 if let Some(dr) = self
520 .viewport_slots
521 .get(vp_idx)
522 .and_then(|s| s.dyn_res.as_ref())
523 {
524 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
525 render_pass.set_pipeline(pipeline);
526 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
527 render_pass.draw(0..3, 0..1);
528 }
529 }
530 }
531
532 pub(crate) fn prepare_hdr_callback(
549 &mut self,
550 device: &wgpu::Device,
551 queue: &wgpu::Queue,
552 frame: &FrameData,
553 ) -> wgpu::CommandBuffer {
554 self.prepare(device, queue, frame);
555
556 let vp_idx = frame.camera.viewport_index;
557 let ppp = frame.camera.pixels_per_point;
563 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
564 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
565
566 self.resources.ensure_dyn_res_pipeline(device);
568 self.resources.ensure_dyn_res_ds_pipeline(device);
569
570 self.ensure_viewport_slot(device, vp_idx);
572 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
573 None => true,
574 Some(t) => t.size != [w, h],
575 };
576 if needs_create {
577 let target = self.resources.create_hdr_callback_target(device, [w, h]);
578 self.viewport_slots[vp_idx].hdr_callback = Some(target);
579 }
580
581 let output_view = self.viewport_slots[vp_idx]
585 .hdr_callback
586 .as_ref()
587 .unwrap()
588 .texture
589 .create_view(&wgpu::TextureViewDescriptor::default());
590
591 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
592 }
593
594 pub(crate) fn paint_hdr_blit<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
600 let vp_idx = frame.camera.viewport_index;
601 if let Some(hc) = self
602 .viewport_slots
603 .get(vp_idx)
604 .and_then(|s| s.hdr_callback.as_ref())
605 {
606 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
607 render_pass.set_pipeline(pipeline);
608 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
609 render_pass.draw(0..3, 0..1);
610 }
611 }
612 }
613
614 pub(crate) fn prepare_callback(
628 &mut self,
629 device: &wgpu::Device,
630 queue: &wgpu::Queue,
631 frame: &FrameData,
632 ) -> Vec<wgpu::CommandBuffer> {
633 if frame.effects.post_process.enabled {
634 let cb = self.prepare_hdr_callback(device, queue, frame);
635 vec![cb]
636 } else {
637 self.prepare(device, queue, frame);
638 if self.current_render_scale < 1.0 - 0.001 {
639 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
640 label: Some("ldr_dyn_res_callback_encoder"),
641 });
642 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
643 vec![encoder.finish()]
644 } else {
645 Vec::new()
646 }
647 }
648 }
649
650 pub(crate) fn paint_callback<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
656 let vp_idx = frame.camera.viewport_index;
657 if frame.effects.post_process.enabled {
658 if self
659 .viewport_slots
660 .get(vp_idx)
661 .and_then(|s| s.hdr_callback.as_ref())
662 .is_some()
663 {
664 self.paint_hdr_blit(render_pass, frame);
665 return;
666 }
667 }
668 if self.current_render_scale < 1.0 - 0.001
669 && self
670 .viewport_slots
671 .get(vp_idx)
672 .and_then(|s| s.dyn_res.as_ref())
673 .is_some()
674 {
675 self.paint_dyn_res_blit(render_pass, frame);
676 } else {
677 self.paint_to(render_pass, frame);
678 }
679 }
680
681 pub(crate) fn render_viewport(
695 &mut self,
696 device: &wgpu::Device,
697 queue: &wgpu::Queue,
698 output_view: &wgpu::TextureView,
699 id: ViewportId,
700 frame: &FrameData,
701 ) -> wgpu::CommandBuffer {
702 self.render_frame_internal(device, queue, output_view, id.0, frame)
703 }
704
705 pub(crate) fn render(
713 &mut self,
714 device: &wgpu::Device,
715 queue: &wgpu::Queue,
716 output_view: &wgpu::TextureView,
717 frame: &FrameData,
718 ) -> wgpu::CommandBuffer {
719 self.prepare(device, queue, frame);
721 self.render_frame_internal(
722 device,
723 queue,
724 output_view,
725 frame.camera.viewport_index,
726 frame,
727 )
728 }
729
730 fn render_frame_internal(
735 &mut self,
736 device: &wgpu::Device,
737 queue: &wgpu::Queue,
738 output_view: &wgpu::TextureView,
739 vp_idx: usize,
740 frame: &FrameData,
741 ) -> wgpu::CommandBuffer {
742 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
744 SurfaceSubmission::Flat(items) => items.as_ref(),
745 };
746
747 let bg_colour = frame.viewport.background_colour.unwrap_or([
748 65.0 / 255.0,
749 65.0 / 255.0,
750 65.0 / 255.0,
751 1.0,
752 ]);
753 let ppp = frame.camera.pixels_per_point;
754 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
755 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
756
757 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
759 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor, self.current_render_scale);
760
761 if self.ts_query_set.is_none()
763 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
764 {
765 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
766 label: Some("ts_query_set"),
767 ty: wgpu::QueryType::Timestamp,
768 count: 2,
769 }));
770 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
771 label: Some("ts_resolve_buf"),
772 size: 16,
773 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
774 mapped_at_creation: false,
775 }));
776 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
777 label: Some("ts_staging_buf"),
778 size: 16,
779 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
780 mapped_at_creation: false,
781 }));
782 self.ts_period = queue.get_timestamp_period();
783 }
784
785 if !frame.effects.post_process.enabled {
786 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
790 label: Some("ldr_encoder"),
791 });
792
793 let use_dyn_res = self.current_render_scale < 1.0 - 0.001;
794 let needs_blur = self.has_backdrop_blur_shapes();
795
796 if use_dyn_res {
797 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
798 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
799 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
800 }
801
802 if needs_blur && !use_dyn_res {
806 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
807 }
808
809 {
810 let slot = &self.viewport_slots[vp_idx];
811 let slot_hdr = slot.hdr.as_ref().expect(
812 "HDR state missing in LDR path; ensure_viewport_hdr must have been called",
813 );
814 let camera_bg = &slot.camera_bind_group;
815 let grid_bg = &slot.grid_bind_group;
816 let (scene_colour_view, scene_depth_view): (&wgpu::TextureView, &wgpu::TextureView) =
818 if use_dyn_res {
819 let dr = slot.dyn_res.as_ref().unwrap();
820 (&dr.colour_view, &dr.depth_view)
821 } else if needs_blur {
822 let bs = self.backdrop_blur_state.as_ref().unwrap();
823 (&bs.intermediate_view, &slot_hdr.outline_depth_view)
824 } else {
825 (output_view, &slot_hdr.outline_depth_view)
826 };
827 let ts_writes =
828 self.ts_query_set
829 .as_ref()
830 .map(|qs| wgpu::RenderPassTimestampWrites {
831 query_set: qs,
832 beginning_of_pass_write_index: Some(0),
833 end_of_pass_write_index: Some(1),
834 });
835 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
836 label: Some("ldr_render_pass"),
837 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
838 view: scene_colour_view,
839 resolve_target: None,
840 ops: wgpu::Operations {
841 load: wgpu::LoadOp::Clear(wgpu::Color {
842 r: bg_colour[0] as f64,
843 g: bg_colour[1] as f64,
844 b: bg_colour[2] as f64,
845 a: bg_colour[3] as f64,
846 }),
847 store: wgpu::StoreOp::Store,
848 },
849 depth_slice: None,
850 })],
851 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
852 view: scene_depth_view,
853 depth_ops: Some(wgpu::Operations {
854 load: wgpu::LoadOp::Clear(1.0),
855 store: wgpu::StoreOp::Discard,
856 }),
857 stencil_ops: None,
858 }),
859 timestamp_writes: ts_writes,
860 occlusion_query_set: None,
861 });
862 emit_draw_calls!(
863 &self.resources,
864 &mut render_pass,
865 frame,
866 self.use_instancing,
867 &self.instanced_batches,
868 camera_bg,
869 grid_bg,
870 &self.compute_filter_results,
871 Some(slot),
872 &self.wireframe_bind_groups
873 );
874 emit_scivis_draw_calls!(
875 &self.resources,
876 &mut render_pass,
877 &self.point_cloud_gpu_data,
878 &self.glyph_gpu_data,
879 &self.polyline_gpu_data,
880 &self.volume_gpu_data,
881 &self.streamtube_gpu_data,
882 camera_bg,
883 &self.tube_gpu_data,
884 &self.image_slice_gpu_data,
885 &self.tensor_glyph_gpu_data,
886 &self.ribbon_gpu_data,
887 &self.volume_surface_slice_gpu_data,
888 &self.sprite_gpu_data,
889 false
890 );
891 if !self.tvm_wireframe_draws.is_empty() {
893 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
894 render_pass.set_bind_group(0, camera_bg, &[]);
895 for mesh_id in &self.tvm_wireframe_draws {
896 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
897 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
898 render_pass.set_bind_group(1, tvm_bg, &[]);
899 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
900 render_pass.set_index_buffer(
901 mesh.edge_index_buffer.slice(..),
902 wgpu::IndexFormat::Uint32,
903 );
904 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
905 }
906 }
907 }
908 }
909 if !self.implicit_gpu_data.is_empty() {
911 if let Some(ref dual) = self.resources.implicit_pipeline {
912 render_pass.set_pipeline(dual.for_format(false));
913 render_pass.set_bind_group(0, camera_bg, &[]);
914 for gpu in &self.implicit_gpu_data {
915 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
916 render_pass.draw(0..6, 0..1);
917 }
918 }
919 }
920 if !self.mc_gpu_data.is_empty() {
922 if let Some(ref dual) = self.resources.mc_surface_pipeline {
923 render_pass.set_pipeline(dual.for_format(false));
924 render_pass.set_bind_group(0, camera_bg, &[]);
925 for mc in &self.mc_gpu_data {
926 let vol = &self.resources.mc_volumes[mc.volume_idx];
927 render_pass.set_bind_group(1, &mc.render_bg, &[]);
928 for slab in &vol.slabs {
929 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
930 render_pass.draw_indirect(&slab.indirect_buf, 0);
931 }
932 }
933 }
934 }
935 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
937 if !self.screen_image_gpu_data.is_empty() {
942 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
943 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
944 for gpu in &self.screen_image_gpu_data {
945 if let (Some(dc_bg), Some(dc_pipe)) =
946 (&gpu.depth_bind_group, dc_pipeline)
947 {
948 render_pass.set_pipeline(dc_pipe);
949 render_pass.set_bind_group(0, dc_bg, &[]);
950 } else {
951 render_pass.set_pipeline(overlay_pipeline);
952 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
953 }
954 render_pass.draw(0..6, 0..1);
955 }
956 }
957 }
958 if !needs_blur {
961 if let Some(ref sd) = self.overlay_shape_gpu_data {
963 if sd.vertex_count > 0 {
964 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
965 if let Some(vbuf) = &sd.vertex_buf {
966 render_pass.set_pipeline(pipeline);
967 render_pass.set_vertex_buffer(0, vbuf.slice(..));
968 render_pass.draw(0..sd.vertex_count, 0..1);
969 }
970 }
971 }
972 if !sd.tex_batches.is_empty() {
973 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
974 render_pass.set_pipeline(pipeline);
975 for batch in &sd.tex_batches {
976 render_pass.set_bind_group(0, &batch.bind_group, &[]);
977 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
978 render_pass.draw(0..batch.vertex_count, 0..1);
979 }
980 }
981 }
982 }
983 if let Some(ref rr) = self.overlay_rect_gpu_data {
985 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
986 render_pass.set_pipeline(pipeline);
987 render_pass.set_bind_group(0, &rr.bind_group, &[]);
988 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
989 render_pass.draw(0..rr.vertex_count, 0..1);
990 }
991 }
992 if let Some(ref ld) = self.label_gpu_data {
994 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
995 render_pass.set_pipeline(pipeline);
996 render_pass.set_bind_group(0, &ld.bind_group, &[]);
997 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
998 render_pass.draw(0..ld.vertex_count, 0..1);
999 }
1000 }
1001 if let Some(ref sb) = self.scalar_bar_gpu_data {
1003 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1004 render_pass.set_pipeline(pipeline);
1005 render_pass.set_bind_group(0, &sb.bind_group, &[]);
1006 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1007 render_pass.draw(0..sb.vertex_count, 0..1);
1008 }
1009 }
1010 if let Some(ref rd) = self.ruler_gpu_data {
1012 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1013 render_pass.set_pipeline(pipeline);
1014 render_pass.set_bind_group(0, &rd.bind_group, &[]);
1015 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1016 render_pass.draw(0..rd.vertex_count, 0..1);
1017 }
1018 }
1019 if !self.overlay_image_gpu_data.is_empty() {
1021 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1022 render_pass.set_pipeline(pipeline);
1023 for gpu in &self.overlay_image_gpu_data {
1024 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1025 render_pass.draw(0..6, 0..1);
1026 }
1027 }
1028 }
1029 }
1030 }
1031 if needs_blur {
1036 let spread = self.overlay_shape_gpu_data.as_ref()
1037 .map(|d| d.max_blur_radius)
1038 .unwrap_or(1.0);
1039 let blur_bg = {
1040 let source = if use_dyn_res {
1041 &self.viewport_slots[vp_idx].dyn_res.as_ref().unwrap().colour_view
1042 } else {
1043 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1044 };
1045 self.run_backdrop_blur(&mut encoder, device, queue, source, spread)
1046 };
1047
1048 let slot = &self.viewport_slots[vp_idx];
1050 let slot_hdr = slot.hdr.as_ref().unwrap();
1051 let overlay_colour_view: &wgpu::TextureView = if use_dyn_res {
1052 &slot.dyn_res.as_ref().unwrap().colour_view
1053 } else {
1054 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1055 };
1056 let overlay_depth_view: &wgpu::TextureView = if use_dyn_res {
1057 &slot.dyn_res.as_ref().unwrap().depth_view
1058 } else {
1059 &slot_hdr.outline_depth_view
1060 };
1061 {
1062 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1063 label: Some("ldr_overlay_blur_pass"),
1064 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1065 view: overlay_colour_view,
1066 resolve_target: None,
1067 ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store },
1068 depth_slice: None,
1069 })],
1070 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1071 view: overlay_depth_view,
1072 depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Discard }),
1073 stencil_ops: None,
1074 }),
1075 timestamp_writes: None,
1076 occlusion_query_set: None,
1077 });
1078 self.draw_blur_shapes(&mut overlay_pass, &blur_bg);
1080 if let Some(ref sd) = self.overlay_shape_gpu_data {
1082 if sd.vertex_count > 0 {
1083 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1084 if let Some(vbuf) = &sd.vertex_buf {
1085 overlay_pass.set_pipeline(pipeline);
1086 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1087 overlay_pass.draw(0..sd.vertex_count, 0..1);
1088 }
1089 }
1090 }
1091 if !sd.tex_batches.is_empty() {
1092 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1093 overlay_pass.set_pipeline(pipeline);
1094 for batch in &sd.tex_batches {
1095 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
1096 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1097 overlay_pass.draw(0..batch.vertex_count, 0..1);
1098 }
1099 }
1100 }
1101 }
1102 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1103 if let Some(ref rr) = self.overlay_rect_gpu_data {
1104 overlay_pass.set_pipeline(pipeline);
1105 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
1106 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1107 overlay_pass.draw(0..rr.vertex_count, 0..1);
1108 }
1109 if let Some(ref ld) = self.label_gpu_data {
1110 overlay_pass.set_pipeline(pipeline);
1111 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
1112 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1113 overlay_pass.draw(0..ld.vertex_count, 0..1);
1114 }
1115 if let Some(ref sb) = self.scalar_bar_gpu_data {
1116 overlay_pass.set_pipeline(pipeline);
1117 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
1118 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1119 overlay_pass.draw(0..sb.vertex_count, 0..1);
1120 }
1121 if let Some(ref rd) = self.ruler_gpu_data {
1122 overlay_pass.set_pipeline(pipeline);
1123 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
1124 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1125 overlay_pass.draw(0..rd.vertex_count, 0..1);
1126 }
1127 }
1128 if !self.overlay_image_gpu_data.is_empty() {
1129 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1130 overlay_pass.set_pipeline(pipeline);
1131 for gpu in &self.overlay_image_gpu_data {
1132 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
1133 overlay_pass.draw(0..6, 0..1);
1134 }
1135 }
1136 }
1137 }
1138 }
1139
1140 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
1142 self.ts_query_set.as_ref(),
1143 self.ts_resolve_buf.as_ref(),
1144 self.ts_staging_buf.as_ref(),
1145 ) {
1146 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
1147 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
1148 self.ts_needs_readback = true;
1149 }
1150
1151 if use_dyn_res {
1153 let upscale_bg = &self.viewport_slots[vp_idx]
1154 .dyn_res
1155 .as_ref()
1156 .unwrap()
1157 .upscale_bind_group;
1158 let mut upscale_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1159 label: Some("dyn_res_upscale_pass"),
1160 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1161 view: output_view,
1162 resolve_target: None,
1163 ops: wgpu::Operations {
1164 load: wgpu::LoadOp::Load,
1165 store: wgpu::StoreOp::Store,
1166 },
1167 depth_slice: None,
1168 })],
1169 depth_stencil_attachment: None,
1170 timestamp_writes: None,
1171 occlusion_query_set: None,
1172 });
1173 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1174 upscale_pass.set_pipeline(pipeline);
1175 upscale_pass.set_bind_group(0, upscale_bg, &[]);
1176 upscale_pass.draw(0..3, 0..1);
1177 }
1178 } else if needs_blur {
1179 let bs = self.backdrop_blur_state.as_ref().unwrap();
1181 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
1182 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
1183 let blit_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1184 label: Some("backdrop_blit_bg"),
1185 layout: blit_bgl,
1186 entries: &[
1187 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.intermediate_view) },
1188 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blit_sampler) },
1189 ],
1190 });
1191 let mut blit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1192 label: Some("backdrop_blit_pass"),
1193 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1194 view: output_view,
1195 resolve_target: None,
1196 ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store },
1197 depth_slice: None,
1198 })],
1199 depth_stencil_attachment: None,
1200 timestamp_writes: None,
1201 occlusion_query_set: None,
1202 });
1203 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1204 blit_pass.set_pipeline(pipeline);
1205 blit_pass.set_bind_group(0, &blit_bg, &[]);
1206 blit_pass.draw(0..3, 0..1);
1207 }
1208 }
1209
1210 return encoder.finish();
1211 }
1212
1213 let pp = &frame.effects.post_process;
1215
1216 let hdr_clear_rgb = [
1217 bg_colour[0].powf(2.2),
1218 bg_colour[1].powf(2.2),
1219 bg_colour[2].powf(2.2),
1220 ];
1221
1222 let mode = match pp.tone_mapping {
1224 crate::renderer::ToneMapping::Reinhard => 0u32,
1225 crate::renderer::ToneMapping::Aces => 1u32,
1226 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
1227 };
1228 let tm_uniform = crate::resources::ToneMapUniform {
1229 exposure: pp.exposure,
1230 mode,
1231 bloom_enabled: if pp.bloom { 1 } else { 0 },
1232 ssao_enabled: if pp.ssao { 1 } else { 0 },
1233 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
1234 edl_enabled: if pp.edl_enabled { 1 } else { 0 },
1235 edl_radius: pp.edl_radius,
1236 edl_strength: pp.edl_strength,
1237 background_colour: bg_colour,
1238 near_plane: frame.camera.render_camera.near,
1239 far_plane: frame.camera.render_camera.far,
1240 lic_enabled: if frame.scene.lic_items.is_empty() {
1241 0
1242 } else {
1243 1
1244 },
1245 lic_strength: frame
1246 .scene
1247 .lic_items
1248 .first()
1249 .map(|i| i.config.strength)
1250 .unwrap_or(0.5),
1251 };
1252 {
1253 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1254 queue.write_buffer(
1255 &hdr.tone_map_uniform_buf,
1256 0,
1257 bytemuck::cast_slice(&[tm_uniform]),
1258 );
1259
1260 if pp.ssao {
1262 let proj = frame.camera.render_camera.projection;
1263 let inv_proj = proj.inverse();
1264 let ssao_uniform = crate::resources::SsaoUniform {
1265 inv_proj: inv_proj.to_cols_array_2d(),
1266 proj: proj.to_cols_array_2d(),
1267 radius: 0.5,
1268 bias: 0.025,
1269 _pad: [0.0; 2],
1270 };
1271 queue.write_buffer(
1272 &hdr.ssao_uniform_buf,
1273 0,
1274 bytemuck::cast_slice(&[ssao_uniform]),
1275 );
1276 }
1277
1278 if pp.contact_shadows {
1280 let proj = frame.camera.render_camera.projection;
1281 let inv_proj = proj.inverse();
1282 let light_dir_world: glam::Vec3 =
1283 if let Some(l) = frame.effects.lighting.lights.first() {
1284 match l.kind {
1285 LightKind::Directional { direction } => {
1286 glam::Vec3::from(direction).normalize()
1287 }
1288 LightKind::Spot { direction, .. } => {
1289 glam::Vec3::from(direction).normalize()
1290 }
1291 _ => glam::Vec3::new(0.0, -1.0, 0.0),
1292 }
1293 } else {
1294 glam::Vec3::new(0.0, -1.0, 0.0)
1295 };
1296 let view = frame.camera.render_camera.view;
1297 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
1298 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
1299 let cs_uniform = crate::resources::ContactShadowUniform {
1300 inv_proj: inv_proj.to_cols_array_2d(),
1301 proj: proj.to_cols_array_2d(),
1302 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
1303 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
1304 params: [
1305 pp.contact_shadow_max_distance,
1306 pp.contact_shadow_steps as f32,
1307 pp.contact_shadow_thickness,
1308 0.0,
1309 ],
1310 };
1311 queue.write_buffer(
1312 &hdr.contact_shadow_uniform_buf,
1313 0,
1314 bytemuck::cast_slice(&[cs_uniform]),
1315 );
1316 }
1317
1318 if pp.bloom {
1320 let bloom_u = crate::resources::BloomUniform {
1321 threshold: pp.bloom_threshold,
1322 intensity: pp.bloom_intensity,
1323 horizontal: 0,
1324 _pad: 0,
1325 };
1326 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
1327 }
1328 }
1329
1330 if pp.dof_enabled {
1332 let (w, h) = {
1333 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1334 (hdr.scene_size[0] as f32, hdr.scene_size[1] as f32)
1335 };
1336 let dof_uniform = crate::resources::DofUniform {
1337 focal_distance: pp.dof_focal_distance,
1338 focal_range: pp.dof_focal_range,
1339 max_blur_radius: pp.dof_max_blur_radius,
1340 near_plane: frame.camera.render_camera.near,
1341 far_plane: frame.camera.render_camera.far,
1342 viewport_width: w,
1343 viewport_height: h,
1344 _pad: 0.0,
1345 };
1346 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1347 queue.write_buffer(
1348 &hdr.dof_uniform_buf,
1349 0,
1350 bytemuck::cast_slice(&[dof_uniform]),
1351 );
1352 }
1353
1354 {
1356 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1357 self.resources.rebuild_tone_map_bind_group(
1358 device,
1359 hdr,
1360 pp.bloom,
1361 pp.ssao,
1362 pp.contact_shadows,
1363 !frame.scene.lic_items.is_empty(),
1364 pp.dof_enabled,
1365 );
1366 }
1367
1368 {
1373 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
1374 self.instanced_batches.iter().any(|b| b.is_transparent)
1375 } else {
1376 scene_items
1377 .iter()
1378 .any(|i| !i.appearance.hidden && i.appearance.opacity < 1.0)
1379 } || frame
1380 .scene
1381 .transparent_volume_meshes
1382 .iter()
1383 .any(|i| !i.appearance.hidden);
1384 if needs_oit {
1385 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1386 let [sw, sh] = hdr.scene_size;
1387 self.resources
1388 .ensure_viewport_oit(device, hdr, sw, sh);
1389 }
1390 }
1391
1392 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1396 label: Some("hdr_encoder"),
1397 });
1398
1399 let slot = &self.viewport_slots[vp_idx];
1401 let camera_bg = &slot.camera_bind_group;
1402 let slot_hdr = slot.hdr.as_ref().expect(
1403 "HDR state missing; ensure_viewport_hdr must be called before render_frame_internal",
1404 );
1405
1406 {
1410 let use_ssaa = ssaa_factor > 1
1412 && slot_hdr.ssaa_colour_view.is_some()
1413 && slot_hdr.ssaa_depth_view.is_some();
1414 let scene_colour_view = if use_ssaa {
1415 slot_hdr.ssaa_colour_view.as_ref().unwrap()
1416 } else {
1417 &slot_hdr.hdr_view
1418 };
1419 let scene_depth_view = if use_ssaa {
1420 slot_hdr.ssaa_depth_view.as_ref().unwrap()
1421 } else {
1422 &slot_hdr.hdr_depth_view
1423 };
1424
1425 let clear_wgpu = wgpu::Color {
1426 r: hdr_clear_rgb[0] as f64,
1427 g: hdr_clear_rgb[1] as f64,
1428 b: hdr_clear_rgb[2] as f64,
1429 a: 0.0,
1432 };
1433
1434 let hdr_ts_writes =
1435 self.ts_query_set
1436 .as_ref()
1437 .map(|qs| wgpu::RenderPassTimestampWrites {
1438 query_set: qs,
1439 beginning_of_pass_write_index: Some(0),
1440 end_of_pass_write_index: Some(1),
1441 });
1442 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1443 label: Some("hdr_scene_pass"),
1444 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1445 view: scene_colour_view,
1446 resolve_target: None,
1447 ops: wgpu::Operations {
1448 load: wgpu::LoadOp::Clear(clear_wgpu),
1449 store: wgpu::StoreOp::Store,
1450 },
1451 depth_slice: None,
1452 })],
1453 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1454 view: scene_depth_view,
1455 depth_ops: Some(wgpu::Operations {
1456 load: wgpu::LoadOp::Clear(1.0),
1457 store: wgpu::StoreOp::Store,
1458 }),
1459 stencil_ops: Some(wgpu::Operations {
1460 load: wgpu::LoadOp::Clear(0),
1461 store: wgpu::StoreOp::Store,
1462 }),
1463 }),
1464 timestamp_writes: hdr_ts_writes,
1465 occlusion_query_set: None,
1466 });
1467
1468 let resources = &self.resources;
1469 render_pass.set_bind_group(0, camera_bg, &[]);
1470
1471 let show_skybox = frame
1473 .effects
1474 .environment
1475 .as_ref()
1476 .is_some_and(|e| e.show_skybox)
1477 && resources.ibl_skybox_view.is_some();
1478
1479 let use_instancing = self.use_instancing;
1480 let batches = &self.instanced_batches;
1481 let compute_filter_results = &self.compute_filter_results;
1482
1483 if !scene_items.is_empty() {
1484 if use_instancing && !batches.is_empty() {
1485 let excluded_items: Vec<&SceneRenderItem> = scene_items
1486 .iter()
1487 .filter(|item| {
1488 !item.appearance.hidden
1489 && (item.active_attribute.is_some()
1490 || item.material.is_two_sided()
1491 || item.material.matcap_id.is_some())
1492 && resources.mesh_store.get(item.mesh_id).is_some()
1493 })
1494 .collect();
1495
1496 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1500 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1501 for (batch_global_idx, batch) in batches.iter().enumerate() {
1502 if batch.is_transparent {
1503 transparent_batches.push((batch_global_idx, batch));
1504 } else {
1505 opaque_batches.push((batch_global_idx, batch));
1506 }
1507 }
1508
1509 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
1510 let use_indirect = self.gpu_culling_enabled
1511 && resources.hdr_solid_instanced_cull_pipeline.is_some()
1512 && resources.indirect_args_buf.is_some();
1513
1514 if use_indirect {
1515 if let (Some(pipeline), Some(indirect_buf)) = (
1516 &resources.hdr_solid_instanced_cull_pipeline,
1517 &resources.indirect_args_buf,
1518 ) {
1519 render_pass.set_pipeline(pipeline);
1520 for (batch_global_idx, batch) in &opaque_batches {
1521 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1522 continue;
1523 };
1524 let mat_key = (
1525 batch.texture_id.unwrap_or(u64::MAX),
1526 batch.normal_map_id.unwrap_or(u64::MAX),
1527 batch.ao_map_id.unwrap_or(u64::MAX),
1528 );
1529 let Some(inst_tex_bg) =
1530 resources.instance_cull_bind_groups.get(&mat_key)
1531 else {
1532 continue;
1533 };
1534 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1535 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1536 render_pass.set_index_buffer(
1537 mesh.index_buffer.slice(..),
1538 wgpu::IndexFormat::Uint32,
1539 );
1540 render_pass.draw_indexed_indirect(
1543 indirect_buf,
1544 *batch_global_idx as u64 * 20,
1545 );
1546 }
1547 }
1548 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
1549 render_pass.set_pipeline(pipeline);
1550 for (_, batch) in &opaque_batches {
1551 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1552 continue;
1553 };
1554 let mat_key = (
1555 batch.texture_id.unwrap_or(u64::MAX),
1556 batch.normal_map_id.unwrap_or(u64::MAX),
1557 batch.ao_map_id.unwrap_or(u64::MAX),
1558 );
1559 let Some(inst_tex_bg) =
1560 resources.instance_bind_groups.get(&mat_key)
1561 else {
1562 continue;
1563 };
1564 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1565 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1566 render_pass.set_index_buffer(
1567 mesh.index_buffer.slice(..),
1568 wgpu::IndexFormat::Uint32,
1569 );
1570 render_pass.draw_indexed(
1571 0..mesh.index_count,
1572 0,
1573 batch.instance_offset
1574 ..batch.instance_offset + batch.instance_count,
1575 );
1576 }
1577 }
1578 }
1579
1580 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
1585 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
1586 let mut wf_idx = 0usize;
1587 for item in scene_items {
1588 if item.appearance.hidden {
1589 continue;
1590 }
1591 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1592 continue;
1593 };
1594 let skin_bg = item.skin_instance.and_then(|inst| {
1595 resources.skin_instance_bind_group(item.mesh_id, inst)
1596 });
1597 if let (Some(bg), Some(hdr_skinned_wf)) =
1598 (skin_bg, resources.hdr_skinned_wireframe_pipeline.as_ref())
1599 {
1600 render_pass.set_pipeline(hdr_skinned_wf);
1601 render_pass.set_bind_group(2, bg, &[]);
1602 } else {
1603 render_pass.set_pipeline(hdr_wf);
1604 }
1605 let bg = self
1606 .wireframe_bind_groups
1607 .get(wf_idx)
1608 .unwrap_or(&mesh.object_bind_group);
1609 render_pass.set_bind_group(1, bg, &[]);
1610 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1611 render_pass.set_index_buffer(
1612 mesh.edge_index_buffer.slice(..),
1613 wgpu::IndexFormat::Uint32,
1614 );
1615 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1616 wf_idx += 1;
1617 }
1618 }
1619 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
1620 &resources.hdr_solid_pipeline,
1621 &resources.hdr_solid_two_sided_pipeline,
1622 ) {
1623 for item in excluded_items
1629 .into_iter()
1630 .filter(|item| item.appearance.opacity >= 1.0 && !item.material.is_blend())
1631 {
1632 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1633 continue;
1634 };
1635 let skin_bg = item.skin_instance.and_then(|inst| {
1636 resources.skin_instance_bind_group(item.mesh_id, inst)
1637 });
1638 if let (Some(bg), Some(hdr_skinned_pl)) = (
1639 skin_bg,
1640 if item.material.is_two_sided() {
1641 resources.hdr_skinned_solid_two_sided_pipeline.as_ref()
1642 } else {
1643 resources.hdr_skinned_solid_pipeline.as_ref()
1644 },
1645 ) {
1646 render_pass.set_pipeline(hdr_skinned_pl);
1647 render_pass.set_bind_group(2, bg, &[]);
1648 } else {
1649 let pipeline = if item.material.is_two_sided() {
1650 hdr_solid_two_sided
1651 } else {
1652 hdr_solid
1653 };
1654 render_pass.set_pipeline(pipeline);
1655 }
1656 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1657 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1658 let filter = compute_filter_results
1659 .iter()
1660 .find(|r| r.mesh_id == item.mesh_id);
1661 if let Some(fr) = filter {
1662 render_pass.set_index_buffer(
1663 fr.index_buffer.slice(..),
1664 wgpu::IndexFormat::Uint32,
1665 );
1666 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1667 } else {
1668 render_pass.set_index_buffer(
1669 mesh.index_buffer.slice(..),
1670 wgpu::IndexFormat::Uint32,
1671 );
1672 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1673 }
1674 }
1675 }
1676
1677 if let Some(hdr_wf) = &resources.hdr_wireframe_pipeline {
1681 for item in scene_items.iter().filter(|i| i.show_normals && !i.appearance.hidden) {
1682 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1683 continue;
1684 };
1685 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1686 if mesh.normal_line_count > 0 {
1687 render_pass.set_pipeline(hdr_wf);
1688 render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
1689 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1690 render_pass.draw(0..mesh.normal_line_count, 0..1);
1691 }
1692 }
1693 }
1694 }
1695 } else {
1696 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
1698 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
1699 let pos =
1700 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
1701 (pos - eye).length()
1702 };
1703
1704 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
1705 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
1706 for item in scene_items {
1707 if item.appearance.hidden || resources.mesh_store.get(item.mesh_id).is_none() {
1708 continue;
1709 }
1710 if item.appearance.opacity < 1.0 || item.material.is_blend() {
1711 transparent.push(item);
1712 } else {
1713 opaque.push(item);
1714 }
1715 }
1716 opaque.sort_by(|a, b| {
1717 dist_from_eye(a)
1718 .partial_cmp(&dist_from_eye(b))
1719 .unwrap_or(std::cmp::Ordering::Equal)
1720 });
1721 transparent.sort_by(|a, b| {
1722 dist_from_eye(b)
1723 .partial_cmp(&dist_from_eye(a))
1724 .unwrap_or(std::cmp::Ordering::Equal)
1725 });
1726
1727 let draw_item_hdr =
1728 |render_pass: &mut wgpu::RenderPass<'_>,
1729 item: &SceneRenderItem,
1730 solid_pl: &wgpu::RenderPipeline,
1731 trans_pl: &wgpu::RenderPipeline,
1732 wf_pl: &wgpu::RenderPipeline| {
1733 let mesh = resources.mesh_store.get(item.mesh_id).unwrap();
1734 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1737
1738 let skin_bg = item.skin_instance.and_then(|inst| {
1743 resources.skin_instance_bind_group(item.mesh_id, inst)
1744 });
1745 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1746 matches!(
1747 a.kind,
1748 crate::resources::AttributeKind::Face
1749 | crate::resources::AttributeKind::FaceColour
1750 | crate::resources::AttributeKind::Halfedge
1751 | crate::resources::AttributeKind::Corner
1752 )
1753 });
1754 if frame.viewport.wireframe_mode {
1755 if let (Some(bg), Some(hdr_skinned_wf)) =
1756 (skin_bg, resources.hdr_skinned_wireframe_pipeline.as_ref())
1757 {
1758 render_pass.set_pipeline(hdr_skinned_wf);
1759 render_pass.set_bind_group(2, bg, &[]);
1760 } else {
1761 render_pass.set_pipeline(wf_pl);
1762 }
1763 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1764 render_pass.set_index_buffer(
1765 mesh.edge_index_buffer.slice(..),
1766 wgpu::IndexFormat::Uint32,
1767 );
1768 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1769 } else if is_face_attr {
1770 if let Some(ref fvb) = mesh.face_vertex_buffer {
1771 let pl = if item.appearance.opacity < 1.0 {
1772 trans_pl
1773 } else {
1774 solid_pl
1775 };
1776 render_pass.set_pipeline(pl);
1777 render_pass.set_vertex_buffer(0, fvb.slice(..));
1778 render_pass.draw(0..mesh.index_count, 0..1);
1779 }
1780 } else {
1781 let filter = compute_filter_results
1782 .iter()
1783 .find(|r| r.mesh_id == item.mesh_id);
1784 let pl = if item.appearance.opacity < 1.0 {
1785 trans_pl
1786 } else {
1787 solid_pl
1788 };
1789 let is_blended = item.appearance.opacity < 1.0
1790 || item.material.is_blend();
1791 let hdr_skinned_pl = if is_blended {
1792 resources.hdr_skinned_transparent_pipeline.as_ref()
1793 } else if item.material.is_two_sided() {
1794 resources.hdr_skinned_solid_two_sided_pipeline.as_ref()
1795 } else {
1796 resources.hdr_skinned_solid_pipeline.as_ref()
1797 };
1798 if let (Some(bg), Some(hdr_skinned)) = (skin_bg, hdr_skinned_pl)
1799 {
1800 render_pass.set_pipeline(hdr_skinned);
1801 render_pass.set_bind_group(2, bg, &[]);
1802 } else {
1803 render_pass.set_pipeline(pl);
1804 }
1805 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1806 if let Some(fr) = filter {
1807 render_pass.set_index_buffer(
1808 fr.index_buffer.slice(..),
1809 wgpu::IndexFormat::Uint32,
1810 );
1811 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1812 } else {
1813 render_pass.set_index_buffer(
1814 mesh.index_buffer.slice(..),
1815 wgpu::IndexFormat::Uint32,
1816 );
1817 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1818 }
1819 }
1820 if item.show_normals {
1821 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1822 if mesh.normal_line_count > 0 {
1823 render_pass.set_pipeline(wf_pl);
1824 render_pass.set_bind_group(
1825 1,
1826 &mesh.normal_bind_group,
1827 &[],
1828 );
1829 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1830 render_pass.draw(0..mesh.normal_line_count, 0..1);
1831 }
1832 }
1833 }
1834 };
1835
1836 let _ = &transparent; if let (
1840 Some(hdr_solid),
1841 Some(hdr_solid_two_sided),
1842 Some(hdr_trans),
1843 Some(hdr_wf),
1844 ) = (
1845 &resources.hdr_solid_pipeline,
1846 &resources.hdr_solid_two_sided_pipeline,
1847 &resources.hdr_transparent_pipeline,
1848 &resources.hdr_wireframe_pipeline,
1849 ) {
1850 for item in &opaque {
1851 let solid_pl = if item.material.is_two_sided() {
1852 hdr_solid_two_sided
1853 } else {
1854 hdr_solid
1855 };
1856 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
1857 }
1858 }
1859 }
1860 }
1861
1862 if !slot.cap_buffers.is_empty() {
1864 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
1865 render_pass.set_pipeline(hdr_overlay);
1866 render_pass.set_bind_group(0, camera_bg, &[]);
1867 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
1868 render_pass.set_bind_group(1, bg, &[]);
1869 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1870 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1871 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
1872 }
1873 }
1874 }
1875
1876 emit_scivis_draw_calls!(
1878 &self.resources,
1879 &mut render_pass,
1880 &self.point_cloud_gpu_data,
1881 &self.glyph_gpu_data,
1882 &self.polyline_gpu_data,
1883 &self.volume_gpu_data,
1884 &self.streamtube_gpu_data,
1885 camera_bg,
1886 &self.tube_gpu_data,
1887 &self.image_slice_gpu_data,
1888 &self.tensor_glyph_gpu_data,
1889 &self.ribbon_gpu_data,
1890 &self.volume_surface_slice_gpu_data,
1891 &self.sprite_gpu_data,
1892 true
1893 );
1894
1895 if !self.implicit_gpu_data.is_empty() {
1897 if let Some(ref dual) = self.resources.implicit_pipeline {
1898 render_pass.set_pipeline(dual.for_format(true));
1899 render_pass.set_bind_group(0, camera_bg, &[]);
1900 for gpu in &self.implicit_gpu_data {
1901 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1902 render_pass.draw(0..6, 0..1);
1903 }
1904 }
1905 }
1906 if !self.mc_gpu_data.is_empty() {
1908 render_pass.set_bind_group(0, camera_bg, &[]);
1909 for mc in &self.mc_gpu_data {
1910 let vol = &self.resources.mc_volumes[mc.volume_idx];
1911 if mc.wireframe || frame.viewport.wireframe_mode {
1912 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
1913 render_pass.set_pipeline(dual.for_format(true));
1914 for (slab, wire_bg) in
1915 vol.slabs.iter().zip(mc.wire_slab_bgs.iter())
1916 {
1917 render_pass.set_bind_group(1, wire_bg, &[]);
1918 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
1919 }
1920 }
1921 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
1922 render_pass.set_pipeline(dual.for_format(true));
1923 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1924 for slab in &vol.slabs {
1925 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1926 render_pass.draw_indirect(&slab.indirect_buf, 0);
1927 }
1928 }
1929 }
1930 }
1931
1932 if !self.gaussian_splat_draw_data.is_empty() {
1934 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
1935 render_pass.set_pipeline(dual.for_format(true));
1936 render_pass.set_bind_group(0, camera_bg, &[]);
1937 for dd in &self.gaussian_splat_draw_data {
1938 if dd.wireframe {
1939 continue;
1940 }
1941 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
1942 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
1943 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
1944 render_pass.draw(0..6, 0..dd.count);
1945 }
1946 }
1947 }
1948 }
1949 }
1950 if !self.tvm_wireframe_draws.is_empty() {
1952 if let (Some(tvm_bg), Some(hdr_wf)) = (&self.tvm_wireframe_bg, &resources.hdr_wireframe_pipeline) {
1953 for mesh_id in &self.tvm_wireframe_draws {
1954 if let Some(mesh) = resources.mesh_store.get(*mesh_id) {
1955 render_pass.set_pipeline(hdr_wf);
1956 render_pass.set_bind_group(1, tvm_bg, &[]);
1957 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1958 render_pass.set_index_buffer(
1959 mesh.edge_index_buffer.slice(..),
1960 wgpu::IndexFormat::Uint32,
1961 );
1962 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1963 }
1964 }
1965 }
1966 }
1967
1968 if show_skybox {
1970 render_pass.set_bind_group(0, camera_bg, &[]);
1971 render_pass.set_pipeline(&resources.skybox_pipeline);
1972 render_pass.draw(0..3, 0..1);
1973 }
1974 }
1975
1976 if ssaa_factor > 1 {
1981 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1982 if let (Some(pipeline), Some(bg)) = (
1983 &self.resources.ssaa_resolve_pipeline,
1984 &slot_hdr.ssaa_resolve_bind_group,
1985 ) {
1986 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1987 label: Some("ssaa_resolve_pass"),
1988 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1989 view: &slot_hdr.hdr_view,
1990 resolve_target: None,
1991 ops: wgpu::Operations {
1992 load: wgpu::LoadOp::Load,
1993 store: wgpu::StoreOp::Store,
1994 },
1995 depth_slice: None,
1996 })],
1997 depth_stencil_attachment: None,
1998 timestamp_writes: None,
1999 occlusion_query_set: None,
2000 });
2001 resolve_pass.set_pipeline(pipeline);
2002 resolve_pass.set_bind_group(0, bg, &[]);
2003 resolve_pass.draw(0..3, 0..1);
2004 }
2005 }
2006
2007 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
2013 let resources = &self.resources;
2014 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
2015 &resources.sub_highlight_fill_pipeline,
2016 &resources.sub_highlight_edge_pipeline,
2017 &resources.sub_highlight_sprite_pipeline,
2018 ) {
2019 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2020 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2021 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2022 label: Some("sub_highlight_pass"),
2023 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2024 view: &slot_hdr.hdr_view,
2025 resolve_target: None,
2026 ops: wgpu::Operations {
2027 load: wgpu::LoadOp::Load,
2028 store: wgpu::StoreOp::Store,
2029 },
2030 depth_slice: None,
2031 })],
2032 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2033 view: &slot_hdr.hdr_depth_view,
2034 depth_ops: Some(wgpu::Operations {
2035 load: wgpu::LoadOp::Load,
2036 store: wgpu::StoreOp::Store,
2042 }),
2043 stencil_ops: None,
2044 }),
2045 timestamp_writes: None,
2046 occlusion_query_set: None,
2047 });
2048
2049 if sub_hl.fill_vertex_count > 0 {
2050 pass.set_pipeline(fill_pl);
2051 pass.set_bind_group(0, camera_bg, &[]);
2052 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
2053 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
2054 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
2055 }
2056 if sub_hl.edge_segment_count > 0 {
2057 pass.set_pipeline(edge_pl);
2058 pass.set_bind_group(0, camera_bg, &[]);
2059 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
2060 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
2061 pass.draw(0..6, 0..sub_hl.edge_segment_count);
2062 }
2063 if sub_hl.sprite_point_count > 0 {
2064 pass.set_pipeline(sprite_pl);
2065 pass.set_bind_group(0, camera_bg, &[]);
2066 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
2067 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
2068 pass.draw(0..6, 0..sub_hl.sprite_point_count);
2069 }
2070 }
2071 }
2072
2073 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
2078 self.instanced_batches.iter().any(|b| b.is_transparent)
2083 || scene_items.iter().any(|i| {
2084 !i.appearance.hidden
2085 && (i.appearance.opacity < 1.0 || i.material.is_blend())
2086 && (i.active_attribute.is_some()
2087 || i.material.is_two_sided()
2088 || i.material.matcap_id.is_some())
2089 })
2090 } else {
2091 scene_items
2092 .iter()
2093 .any(|i| !i.appearance.hidden && i.appearance.opacity < 1.0)
2094 } || frame
2095 .scene
2096 .transparent_volume_meshes
2097 .iter()
2098 .any(|i| !i.appearance.hidden);
2099
2100 if has_transparent {
2101 if let (Some(accum_view), Some(reveal_view)) = (
2103 slot_hdr.oit_accum_view.as_ref(),
2104 slot_hdr.oit_reveal_view.as_ref(),
2105 ) {
2106 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2107 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2109 label: Some("oit_pass"),
2110 color_attachments: &[
2111 Some(wgpu::RenderPassColorAttachment {
2112 view: accum_view,
2113 resolve_target: None,
2114 ops: wgpu::Operations {
2115 load: wgpu::LoadOp::Clear(wgpu::Color {
2116 r: 0.0,
2117 g: 0.0,
2118 b: 0.0,
2119 a: 0.0,
2120 }),
2121 store: wgpu::StoreOp::Store,
2122 },
2123 depth_slice: None,
2124 }),
2125 Some(wgpu::RenderPassColorAttachment {
2126 view: reveal_view,
2127 resolve_target: None,
2128 ops: wgpu::Operations {
2129 load: wgpu::LoadOp::Clear(wgpu::Color {
2130 r: 1.0,
2131 g: 1.0,
2132 b: 1.0,
2133 a: 1.0,
2134 }),
2135 store: wgpu::StoreOp::Store,
2136 },
2137 depth_slice: None,
2138 }),
2139 ],
2140 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2141 view: hdr_depth_view,
2142 depth_ops: Some(wgpu::Operations {
2143 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
2145 }),
2146 stencil_ops: None,
2147 }),
2148 timestamp_writes: None,
2149 occlusion_query_set: None,
2150 });
2151
2152 oit_pass.set_bind_group(0, camera_bg, &[]);
2153
2154 if self.use_instancing && !self.instanced_batches.is_empty() {
2155 let use_indirect_oit = self.gpu_culling_enabled
2156 && self.resources.oit_instanced_cull_pipeline.is_some()
2157 && self.resources.indirect_args_buf.is_some();
2158
2159 if use_indirect_oit {
2160 if let (Some(pipeline), Some(indirect_buf)) = (
2161 &self.resources.oit_instanced_cull_pipeline,
2162 &self.resources.indirect_args_buf,
2163 ) {
2164 oit_pass.set_pipeline(pipeline);
2165 for (batch_global_idx, batch) in
2166 self.instanced_batches.iter().enumerate()
2167 {
2168 if !batch.is_transparent {
2169 continue;
2170 }
2171 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id)
2172 else {
2173 continue;
2174 };
2175 let mat_key = (
2176 batch.texture_id.unwrap_or(u64::MAX),
2177 batch.normal_map_id.unwrap_or(u64::MAX),
2178 batch.ao_map_id.unwrap_or(u64::MAX),
2179 );
2180 let Some(inst_tex_bg) =
2181 self.resources.instance_cull_bind_groups.get(&mat_key)
2182 else {
2183 continue;
2184 };
2185 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
2186 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2187 oit_pass.set_index_buffer(
2188 mesh.index_buffer.slice(..),
2189 wgpu::IndexFormat::Uint32,
2190 );
2191 oit_pass.draw_indexed_indirect(
2192 indirect_buf,
2193 batch_global_idx as u64 * 20,
2194 );
2195 }
2196 }
2197 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
2198 oit_pass.set_pipeline(pipeline);
2199 for batch in &self.instanced_batches {
2200 if !batch.is_transparent {
2201 continue;
2202 }
2203 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id) else {
2204 continue;
2205 };
2206 let mat_key = (
2207 batch.texture_id.unwrap_or(u64::MAX),
2208 batch.normal_map_id.unwrap_or(u64::MAX),
2209 batch.ao_map_id.unwrap_or(u64::MAX),
2210 );
2211 let Some(inst_tex_bg) =
2212 self.resources.instance_bind_groups.get(&mat_key)
2213 else {
2214 continue;
2215 };
2216 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
2217 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2218 oit_pass.set_index_buffer(
2219 mesh.index_buffer.slice(..),
2220 wgpu::IndexFormat::Uint32,
2221 );
2222 oit_pass.draw_indexed(
2223 0..mesh.index_count,
2224 0,
2225 batch.instance_offset..batch.instance_offset + batch.instance_count,
2226 );
2227 }
2228 }
2229
2230 if let Some(ref pipeline) = self.resources.oit_pipeline {
2234 oit_pass.set_pipeline(pipeline);
2235 let mut last_skinned = false;
2236 for item in scene_items {
2237 if item.appearance.hidden || (item.appearance.opacity >= 1.0 && !item.material.is_blend()) {
2238 continue;
2239 }
2240 let skin_bg = item.skin_instance.and_then(|inst| {
2241 self.resources.skin_instance_bind_group(item.mesh_id, inst)
2242 });
2243 if skin_bg.is_none()
2244 && item.active_attribute.is_none()
2245 && !item.material.is_two_sided()
2246 && item.material.matcap_id.is_none()
2247 {
2248 continue;
2249 }
2250 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
2251 continue;
2252 };
2253 let want_skinned = skin_bg.is_some()
2254 && self.resources.skinned_oit_pipeline.is_some();
2255 if want_skinned != last_skinned {
2256 if want_skinned {
2257 oit_pass.set_pipeline(
2258 self.resources.skinned_oit_pipeline.as_ref().unwrap(),
2259 );
2260 } else {
2261 oit_pass.set_pipeline(pipeline);
2262 }
2263 last_skinned = want_skinned;
2264 }
2265 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
2266 if let Some(bg) = skin_bg {
2267 oit_pass.set_bind_group(2, bg, &[]);
2268 }
2269 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2270 oit_pass.set_index_buffer(
2271 mesh.index_buffer.slice(..),
2272 wgpu::IndexFormat::Uint32,
2273 );
2274 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2275 }
2276 }
2277 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
2278 oit_pass.set_pipeline(pipeline);
2279 let mut last_skinned = false;
2280 for item in scene_items {
2281 if item.appearance.hidden || item.appearance.opacity >= 1.0 {
2282 continue;
2283 }
2284 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
2285 continue;
2286 };
2287 let skin_bg = item.skin_instance.and_then(|inst| {
2288 self.resources.skin_instance_bind_group(item.mesh_id, inst)
2289 });
2290 let want_skinned = skin_bg.is_some()
2291 && self.resources.skinned_oit_pipeline.is_some();
2292 if want_skinned != last_skinned {
2293 if want_skinned {
2294 oit_pass.set_pipeline(
2295 self.resources.skinned_oit_pipeline.as_ref().unwrap(),
2296 );
2297 } else {
2298 oit_pass.set_pipeline(pipeline);
2299 }
2300 last_skinned = want_skinned;
2301 }
2302 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
2303 if let Some(bg) = skin_bg {
2304 oit_pass.set_bind_group(2, bg, &[]);
2305 }
2306 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2307 oit_pass.set_index_buffer(
2308 mesh.index_buffer.slice(..),
2309 wgpu::IndexFormat::Uint32,
2310 );
2311 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2312 }
2313 }
2314
2315 if !frame.scene.transparent_volume_meshes.is_empty() {
2319 self.resources.ensure_pt_pipeline(device);
2320 if let Some(pipeline) = self.resources.pt_pipeline.as_ref() {
2321 oit_pass.set_pipeline(pipeline);
2322 oit_pass.set_bind_group(0, camera_bg, &[]);
2323 for item in &frame.scene.transparent_volume_meshes {
2324 if item.appearance.hidden {
2325 continue;
2326 }
2327 if item.appearance.wireframe || frame.viewport.wireframe_mode {
2328 continue;
2329 }
2330 let Some(gpu) = self.resources.projected_tet_store.get(item.id.0)
2331 else {
2332 continue;
2333 };
2334 let (scalar_min, scalar_max) =
2335 item.scalar_range.unwrap_or(gpu.scalar_range);
2336 let uniform = crate::resources::ProjectedTetUniform {
2337 density: item.density,
2338 scalar_min,
2339 scalar_max,
2340 threshold_min: item.threshold_min,
2341 threshold_max: item.threshold_max,
2342 _pad: 0.0,
2343 };
2344 queue.write_buffer(
2345 &gpu.uniform_buffer,
2346 0,
2347 bytemuck::bytes_of(&uniform),
2348 );
2349 for chunk in &gpu.chunks {
2350 oit_pass.set_bind_group(1, &chunk.bind_group, &[]);
2351 oit_pass.draw(0..6, 0..chunk.tet_count);
2352 }
2353 }
2354 }
2355 }
2356 }
2357 }
2358
2359 if has_transparent {
2364 if let (Some(pipeline), Some(bg)) = (
2365 self.resources.oit_composite_pipeline.as_ref(),
2366 slot_hdr.oit_composite_bind_group.as_ref(),
2367 ) {
2368 let hdr_view = &slot_hdr.hdr_view;
2369 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2370 label: Some("oit_composite_pass"),
2371 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2372 view: hdr_view,
2373 resolve_target: None,
2374 ops: wgpu::Operations {
2375 load: wgpu::LoadOp::Load,
2376 store: wgpu::StoreOp::Store,
2377 },
2378 depth_slice: None,
2379 })],
2380 depth_stencil_attachment: None,
2381 timestamp_writes: None,
2382 occlusion_query_set: None,
2383 });
2384 composite_pass.set_pipeline(pipeline);
2385 composite_pass.set_bind_group(0, bg, &[]);
2386 composite_pass.draw(0..3, 0..1);
2387 }
2388 }
2389
2390 if !self.lic_gpu_data.is_empty() {
2396 if let (Some(surface_pipeline), Some(advect_pipeline)) = (
2397 self.resources.lic_surface_pipeline.as_ref(),
2398 self.resources.lic_advect_pipeline.as_ref(),
2399 ) {
2400 let camera_bg = &slot.camera_bind_group;
2401 {
2403 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2404 label: Some("lic_surface_pass"),
2405 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2406 view: &slot_hdr.lic_vector_view,
2407 resolve_target: None,
2408 ops: wgpu::Operations {
2409 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2410 store: wgpu::StoreOp::Store,
2411 },
2412 depth_slice: None,
2413 })],
2414 depth_stencil_attachment: None,
2415 timestamp_writes: None,
2416 occlusion_query_set: None,
2417 });
2418 pass.set_pipeline(surface_pipeline);
2419 pass.set_bind_group(0, camera_bg, &[]);
2420 for gpu in &self.lic_gpu_data {
2421 let Some(mesh) = self.resources.mesh_store.get(gpu.mesh_id) else {
2422 continue;
2423 };
2424 let Some(vec_buf) =
2425 mesh.vector_attribute_buffers.get(&gpu.vector_attribute)
2426 else {
2427 continue;
2428 };
2429 pass.set_bind_group(1, &gpu.bind_group, &[]);
2430 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2431 pass.set_vertex_buffer(1, vec_buf.slice(..));
2432 pass.set_index_buffer(
2433 mesh.index_buffer.slice(..),
2434 wgpu::IndexFormat::Uint32,
2435 );
2436 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2437 }
2438 }
2439 {
2441 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2442 label: Some("lic_advect_pass"),
2443 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2444 view: &slot_hdr.lic_output_view,
2445 resolve_target: None,
2446 ops: wgpu::Operations {
2447 load: wgpu::LoadOp::Clear(wgpu::Color {
2448 r: 0.5,
2449 g: 0.0,
2450 b: 0.0,
2451 a: 1.0,
2452 }),
2453 store: wgpu::StoreOp::Store,
2454 },
2455 depth_slice: None,
2456 })],
2457 depth_stencil_attachment: None,
2458 timestamp_writes: None,
2459 occlusion_query_set: None,
2460 });
2461 pass.set_pipeline(advect_pipeline);
2462 pass.set_bind_group(0, &slot_hdr.lic_advect_bind_group, &[]);
2463 pass.draw(0..3, 0..1);
2464 }
2465 }
2466 }
2467
2468 if !slot.outline_object_buffers.is_empty()
2474 || !slot.splat_outline_buffers.is_empty()
2475 || !slot.streamtube_outline_items.is_empty()
2476 || !slot.tube_outline_items.is_empty()
2477 || !slot.ribbon_outline_items.is_empty()
2478 || !slot.polyline_outline_indices.is_empty()
2479 || !slot.volume_outline_indices.is_empty()
2480 || !slot.glyph_outline_indices.is_empty()
2481 || !slot.tensor_glyph_outline_indices.is_empty()
2482 || !slot.sprite_outline_indices.is_empty()
2483 || !slot.raw_geom_outline_buffers.is_empty()
2484 || !slot.screen_rect_outline_buffers.is_empty()
2485 || !slot.implicit_outline_indices.is_empty()
2486 || !slot.mc_outline_data.is_empty()
2487 {
2488 let hdr_pipeline = self
2490 .resources
2491 .outline_composite_pipeline_hdr
2492 .as_ref()
2493 .or(self.resources.outline_composite_pipeline_single.as_ref());
2494 if let Some(pipeline) = hdr_pipeline {
2495 let bg = &slot_hdr.outline_composite_bind_group;
2496 let hdr_view = &slot_hdr.hdr_view;
2497 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2498 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2499 label: Some("hdr_outline_composite_pass"),
2500 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2501 view: hdr_view,
2502 resolve_target: None,
2503 ops: wgpu::Operations {
2504 load: wgpu::LoadOp::Load,
2505 store: wgpu::StoreOp::Store,
2506 },
2507 depth_slice: None,
2508 })],
2509 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2510 view: hdr_depth_view,
2511 depth_ops: Some(wgpu::Operations {
2512 load: wgpu::LoadOp::Load,
2513 store: wgpu::StoreOp::Store,
2514 }),
2515 stencil_ops: None,
2516 }),
2517 timestamp_writes: None,
2518 occlusion_query_set: None,
2519 });
2520 outline_pass.set_pipeline(pipeline);
2521 outline_pass.set_bind_group(0, bg, &[]);
2522 outline_pass.draw(0..3, 0..1);
2523 }
2524 }
2525
2526 let throttle_effects = self.degradation_effects_throttled;
2529
2530 if pp.ssao && !throttle_effects {
2534 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
2535 {
2536 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2537 label: Some("ssao_pass"),
2538 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2539 view: &slot_hdr.ssao_view,
2540 resolve_target: None,
2541 ops: wgpu::Operations {
2542 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2543 store: wgpu::StoreOp::Store,
2544 },
2545 depth_slice: None,
2546 })],
2547 depth_stencil_attachment: None,
2548 timestamp_writes: None,
2549 occlusion_query_set: None,
2550 });
2551 ssao_pass.set_pipeline(ssao_pipeline);
2552 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
2553 ssao_pass.draw(0..3, 0..1);
2554 }
2555
2556 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
2558 let mut ssao_blur_pass =
2559 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2560 label: Some("ssao_blur_pass"),
2561 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2562 view: &slot_hdr.ssao_blur_view,
2563 resolve_target: None,
2564 ops: wgpu::Operations {
2565 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2566 store: wgpu::StoreOp::Store,
2567 },
2568 depth_slice: None,
2569 })],
2570 depth_stencil_attachment: None,
2571 timestamp_writes: None,
2572 occlusion_query_set: None,
2573 });
2574 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
2575 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
2576 ssao_blur_pass.draw(0..3, 0..1);
2577 }
2578 }
2579 }
2580
2581 if pp.contact_shadows && !throttle_effects {
2585 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
2586 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2587 label: Some("contact_shadow_pass"),
2588 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2589 view: &slot_hdr.contact_shadow_view,
2590 resolve_target: None,
2591 depth_slice: None,
2592 ops: wgpu::Operations {
2593 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2594 store: wgpu::StoreOp::Store,
2595 },
2596 })],
2597 depth_stencil_attachment: None,
2598 timestamp_writes: None,
2599 occlusion_query_set: None,
2600 });
2601 cs_pass.set_pipeline(cs_pipeline);
2602 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
2603 cs_pass.draw(0..3, 0..1);
2604 }
2605 }
2606
2607 if pp.bloom && !throttle_effects {
2611 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
2613 {
2614 let mut threshold_pass =
2615 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2616 label: Some("bloom_threshold_pass"),
2617 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2618 view: &slot_hdr.bloom_threshold_view,
2619 resolve_target: None,
2620 ops: wgpu::Operations {
2621 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2622 store: wgpu::StoreOp::Store,
2623 },
2624 depth_slice: None,
2625 })],
2626 depth_stencil_attachment: None,
2627 timestamp_writes: None,
2628 occlusion_query_set: None,
2629 });
2630 threshold_pass.set_pipeline(bloom_threshold_pipeline);
2631 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
2632 threshold_pass.draw(0..3, 0..1);
2633 }
2634
2635 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
2638 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
2639 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
2640 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
2641 let bloom_ping_view = &slot_hdr.bloom_ping_view;
2642 let bloom_pong_view = &slot_hdr.bloom_pong_view;
2643 const BLUR_ITERATIONS: usize = 4;
2644 for i in 0..BLUR_ITERATIONS {
2645 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
2647 {
2648 let mut h_pass =
2649 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2650 label: Some("bloom_blur_h_pass"),
2651 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2652 view: bloom_ping_view,
2653 resolve_target: None,
2654 ops: wgpu::Operations {
2655 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2656 store: wgpu::StoreOp::Store,
2657 },
2658 depth_slice: None,
2659 })],
2660 depth_stencil_attachment: None,
2661 timestamp_writes: None,
2662 occlusion_query_set: None,
2663 });
2664 h_pass.set_pipeline(blur_pipeline);
2665 h_pass.set_bind_group(0, h_bg, &[]);
2666 h_pass.draw(0..3, 0..1);
2667 }
2668 {
2670 let mut v_pass =
2671 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2672 label: Some("bloom_blur_v_pass"),
2673 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2674 view: bloom_pong_view,
2675 resolve_target: None,
2676 ops: wgpu::Operations {
2677 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2678 store: wgpu::StoreOp::Store,
2679 },
2680 depth_slice: None,
2681 })],
2682 depth_stencil_attachment: None,
2683 timestamp_writes: None,
2684 occlusion_query_set: None,
2685 });
2686 v_pass.set_pipeline(blur_pipeline);
2687 v_pass.set_bind_group(0, blur_v_bg, &[]);
2688 v_pass.draw(0..3, 0..1);
2689 }
2690 }
2691 }
2692 }
2693 }
2694
2695 if pp.dof_enabled && !throttle_effects {
2699 if let Some(dof_pipeline) = &self.resources.dof_pipeline {
2700 let mut dof_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2701 label: Some("dof_pass"),
2702 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2703 view: &slot_hdr.dof_view,
2704 resolve_target: None,
2705 depth_slice: None,
2706 ops: wgpu::Operations {
2707 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2708 store: wgpu::StoreOp::Store,
2709 },
2710 })],
2711 depth_stencil_attachment: None,
2712 timestamp_writes: None,
2713 occlusion_query_set: None,
2714 });
2715 dof_pass.set_pipeline(dof_pipeline);
2716 dof_pass.set_bind_group(0, &slot_hdr.dof_bg, &[]);
2717 dof_pass.draw(0..3, 0..1);
2718 }
2719 }
2720
2721 let use_fxaa = pp.fxaa;
2729 let use_hdr_upscale = slot_hdr.upscale_bind_group.is_some();
2730 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
2731 let tone_target: &wgpu::TextureView = if use_fxaa {
2732 &slot_hdr.fxaa_view
2733 } else if use_hdr_upscale {
2734 slot_hdr.upscale_view.as_ref().unwrap()
2735 } else {
2736 output_view
2737 };
2738 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2739 label: Some("tone_map_pass"),
2740 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2741 view: tone_target,
2742 resolve_target: None,
2743 ops: wgpu::Operations {
2744 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2745 store: wgpu::StoreOp::Store,
2746 },
2747 depth_slice: None,
2748 })],
2749 depth_stencil_attachment: None,
2750 timestamp_writes: None,
2751 occlusion_query_set: None,
2752 });
2753 tone_pass.set_pipeline(tone_map_pipeline);
2754 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
2755 tone_pass.draw(0..3, 0..1);
2756 }
2757
2758 if use_fxaa {
2762 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
2763 let fxaa_target: &wgpu::TextureView = if use_hdr_upscale {
2764 slot_hdr.upscale_view.as_ref().unwrap()
2765 } else {
2766 output_view
2767 };
2768 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2769 label: Some("fxaa_pass"),
2770 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2771 view: fxaa_target,
2772 resolve_target: None,
2773 ops: wgpu::Operations {
2774 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2775 store: wgpu::StoreOp::Store,
2776 },
2777 depth_slice: None,
2778 })],
2779 depth_stencil_attachment: None,
2780 timestamp_writes: None,
2781 occlusion_query_set: None,
2782 });
2783 fxaa_pass.set_pipeline(fxaa_pipeline);
2784 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
2785 fxaa_pass.draw(0..3, 0..1);
2786 }
2787 }
2788
2789 if use_hdr_upscale {
2794 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2795 if let Some(upscale_bg) = &slot_hdr.upscale_bind_group {
2796 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
2797 let mut upscale_pass =
2798 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2799 label: Some("hdr_upscale_pass"),
2800 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2801 view: output_view,
2802 resolve_target: None,
2803 ops: wgpu::Operations {
2804 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2805 store: wgpu::StoreOp::Store,
2806 },
2807 depth_slice: None,
2808 })],
2809 depth_stencil_attachment: None,
2810 timestamp_writes: None,
2811 occlusion_query_set: None,
2812 });
2813 upscale_pass.set_pipeline(pipeline);
2814 upscale_pass.set_bind_group(0, upscale_bg, &[]);
2815 upscale_pass.draw(0..3, 0..1);
2816 }
2817 }
2818 }
2819
2820 {
2826 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2827 if let Some(blit_bg) = &slot_hdr.depth_blit_bind_group {
2828 if let Some(blit_pipeline) = &self.resources.depth_blit_pipeline {
2829 let mut blit_pass =
2830 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2831 label: Some("depth_blit_pass"),
2832 color_attachments: &[],
2833 depth_stencil_attachment: Some(
2834 wgpu::RenderPassDepthStencilAttachment {
2835 view: &slot_hdr.output_depth_view,
2836 depth_ops: Some(wgpu::Operations {
2837 load: wgpu::LoadOp::Clear(1.0),
2838 store: wgpu::StoreOp::Store,
2839 }),
2840 stencil_ops: None,
2841 },
2842 ),
2843 timestamp_writes: None,
2844 occlusion_query_set: None,
2845 });
2846 blit_pass.set_pipeline(blit_pipeline);
2847 blit_pass.set_bind_group(0, blit_bg, &[]);
2848 blit_pass.draw(0..3, 0..1);
2849 }
2850 }
2851 }
2852
2853 if frame.viewport.show_grid {
2857 let slot = &self.viewport_slots[vp_idx];
2858 let slot_hdr = slot.hdr.as_ref().unwrap();
2859 let grid_bg = &slot.grid_bind_group;
2860 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2861 label: Some("hdr_grid_pass"),
2862 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2863 view: output_view,
2864 resolve_target: None,
2865 ops: wgpu::Operations {
2866 load: wgpu::LoadOp::Load,
2867 store: wgpu::StoreOp::Store,
2868 },
2869 depth_slice: None,
2870 })],
2871 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2872 view: &slot_hdr.output_depth_view,
2873 depth_ops: Some(wgpu::Operations {
2874 load: wgpu::LoadOp::Load,
2875 store: wgpu::StoreOp::Store,
2876 }),
2877 stencil_ops: None,
2878 }),
2879 timestamp_writes: None,
2880 occlusion_query_set: None,
2881 });
2882 grid_pass.set_pipeline(&self.resources.grid_pipeline);
2883 grid_pass.set_bind_group(0, grid_bg, &[]);
2884 grid_pass.draw(0..3, 0..1);
2885 }
2886
2887 if !matches!(
2890 frame.effects.ground_plane.mode,
2891 crate::renderer::types::GroundPlaneMode::None
2892 ) {
2893 let slot = &self.viewport_slots[vp_idx];
2894 let slot_hdr = slot.hdr.as_ref().unwrap();
2895 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2896 label: Some("hdr_ground_plane_pass"),
2897 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2898 view: output_view,
2899 resolve_target: None,
2900 ops: wgpu::Operations {
2901 load: wgpu::LoadOp::Load,
2902 store: wgpu::StoreOp::Store,
2903 },
2904 depth_slice: None,
2905 })],
2906 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2907 view: &slot_hdr.output_depth_view,
2908 depth_ops: Some(wgpu::Operations {
2909 load: wgpu::LoadOp::Load,
2910 store: wgpu::StoreOp::Store,
2911 }),
2912 stencil_ops: None,
2913 }),
2914 timestamp_writes: None,
2915 occlusion_query_set: None,
2916 });
2917 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
2918 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
2919 gp_pass.draw(0..3, 0..1);
2920 }
2921
2922 if !self.screen_image_gpu_data.is_empty() {
2930 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
2931 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2932 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
2933 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2934 label: Some("screen_image_pass"),
2935 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2936 view: output_view,
2937 resolve_target: None,
2938 ops: wgpu::Operations {
2939 load: wgpu::LoadOp::Load,
2940 store: wgpu::StoreOp::Store,
2941 },
2942 depth_slice: None,
2943 })],
2944 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2945 view: &slot_hdr.output_depth_view,
2946 depth_ops: Some(wgpu::Operations {
2947 load: wgpu::LoadOp::Load,
2948 store: wgpu::StoreOp::Discard,
2949 }),
2950 stencil_ops: None,
2951 }),
2952 timestamp_writes: None,
2953 occlusion_query_set: None,
2954 });
2955 for gpu in &self.screen_image_gpu_data {
2956 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
2957 img_pass.set_pipeline(dc_pipe);
2958 img_pass.set_bind_group(0, dc_bg, &[]);
2959 } else {
2960 img_pass.set_pipeline(overlay_pipeline);
2961 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
2962 }
2963 img_pass.draw(0..6, 0..1);
2964 }
2965 }
2966 }
2967
2968 {
2972 let slot = &self.viewport_slots[vp_idx];
2973 let slot_hdr = slot.hdr.as_ref().unwrap();
2974 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
2975 && slot.gizmo_index_count > 0)
2976 || !slot.constraint_line_buffers.is_empty()
2977 || !slot.clip_plane_fill_buffers.is_empty()
2978 || !slot.clip_plane_line_buffers.is_empty()
2979 || !slot.xray_object_buffers.is_empty();
2980 if has_editor_overlays {
2981 let camera_bg = &slot.camera_bind_group;
2982 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2983 label: Some("hdr_editor_overlay_pass"),
2984 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2985 view: output_view,
2986 resolve_target: None,
2987 ops: wgpu::Operations {
2988 load: wgpu::LoadOp::Load,
2989 store: wgpu::StoreOp::Store,
2990 },
2991 depth_slice: None,
2992 })],
2993 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2994 view: &slot_hdr.output_depth_view,
2995 depth_ops: Some(wgpu::Operations {
2996 load: wgpu::LoadOp::Load,
2997 store: wgpu::StoreOp::Discard,
2998 }),
2999 stencil_ops: None,
3000 }),
3001 timestamp_writes: None,
3002 occlusion_query_set: None,
3003 });
3004
3005 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
3006 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
3007 overlay_pass.set_bind_group(0, camera_bg, &[]);
3008 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
3009 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
3010 overlay_pass.set_index_buffer(
3011 slot.gizmo_index_buffer.slice(..),
3012 wgpu::IndexFormat::Uint32,
3013 );
3014 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
3015 }
3016
3017 if !slot.constraint_line_buffers.is_empty() {
3018 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
3019 overlay_pass.set_bind_group(0, camera_bg, &[]);
3020 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
3021 overlay_pass.set_bind_group(1, bg, &[]);
3022 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3023 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3024 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
3025 }
3026 }
3027
3028 if !slot.clip_plane_fill_buffers.is_empty() {
3029 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
3030 overlay_pass.set_bind_group(0, camera_bg, &[]);
3031 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
3032 overlay_pass.set_bind_group(1, bg, &[]);
3033 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3034 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3035 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
3036 }
3037 }
3038
3039 if !slot.clip_plane_line_buffers.is_empty() {
3040 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
3041 overlay_pass.set_bind_group(0, camera_bg, &[]);
3042 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
3043 overlay_pass.set_bind_group(1, bg, &[]);
3044 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3045 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3046 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
3047 }
3048 }
3049
3050 if !slot.xray_object_buffers.is_empty() {
3051 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
3052 overlay_pass.set_bind_group(0, camera_bg, &[]);
3053 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
3054 let Some(mesh) = self.resources.mesh_store.get(*mesh_id) else {
3055 continue;
3056 };
3057 overlay_pass.set_bind_group(1, bg, &[]);
3058 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3059 overlay_pass.set_index_buffer(
3060 mesh.index_buffer.slice(..),
3061 wgpu::IndexFormat::Uint32,
3062 );
3063 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3064 }
3065 }
3066 }
3067 }
3068
3069 if frame.viewport.show_axes_indicator {
3072 let slot = &self.viewport_slots[vp_idx];
3073 if slot.axes_vertex_count > 0 {
3074 let slot_hdr = slot.hdr.as_ref().unwrap();
3075 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3076 label: Some("hdr_axes_pass"),
3077 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3078 view: output_view,
3079 resolve_target: None,
3080 ops: wgpu::Operations {
3081 load: wgpu::LoadOp::Load,
3082 store: wgpu::StoreOp::Store,
3083 },
3084 depth_slice: None,
3085 })],
3086 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3087 view: &slot_hdr.output_depth_view,
3088 depth_ops: Some(wgpu::Operations {
3089 load: wgpu::LoadOp::Load,
3090 store: wgpu::StoreOp::Discard,
3091 }),
3092 stencil_ops: None,
3093 }),
3094 timestamp_writes: None,
3095 occlusion_query_set: None,
3096 });
3097 axes_pass.set_pipeline(&self.resources.axes_pipeline);
3098 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
3099 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
3100 }
3101 }
3102
3103 let has_overlay = self.overlay_shape_gpu_data.is_some()
3105 || self.overlay_rect_gpu_data.is_some()
3106 || self.label_gpu_data.is_some()
3107 || self.scalar_bar_gpu_data.is_some()
3108 || self.ruler_gpu_data.is_some()
3109 || self.loading_bar_gpu_data.is_some()
3110 || !self.overlay_image_gpu_data.is_empty();
3111
3112 let needs_hdr_blur = self.has_backdrop_blur_shapes();
3117 let hdr_blur_bg: Option<wgpu::BindGroup> = if needs_hdr_blur && has_overlay {
3118 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
3119 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3131 let source = &slot_hdr.hdr_view;
3132 let spread = self.overlay_shape_gpu_data.as_ref()
3133 .map(|d| d.max_blur_radius)
3134 .unwrap_or(8.0);
3135 Some(self.run_backdrop_blur(&mut encoder, device, queue, source, spread))
3136 } else {
3137 None
3138 };
3139
3140 if has_overlay {
3141 let hdr_depth_view = &self.viewport_slots[vp_idx]
3142 .hdr
3143 .as_ref()
3144 .unwrap()
3145 .output_depth_view;
3146 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3147 label: Some("overlay_pass"),
3148 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3149 view: output_view,
3150 resolve_target: None,
3151 ops: wgpu::Operations {
3152 load: wgpu::LoadOp::Load,
3153 store: wgpu::StoreOp::Store,
3154 },
3155 depth_slice: None,
3156 })],
3157 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3158 view: hdr_depth_view,
3159 depth_ops: Some(wgpu::Operations {
3160 load: wgpu::LoadOp::Load,
3161 store: wgpu::StoreOp::Discard,
3162 }),
3163 stencil_ops: None,
3164 }),
3165 timestamp_writes: None,
3166 occlusion_query_set: None,
3167 });
3168 if let Some(ref bg) = hdr_blur_bg {
3170 self.draw_blur_shapes(&mut overlay_pass, bg);
3171 }
3172 if let Some(ref sd) = self.overlay_shape_gpu_data {
3174 if sd.vertex_count > 0 {
3175 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
3176 if let Some(vbuf) = &sd.vertex_buf {
3177 overlay_pass.set_pipeline(pipeline);
3178 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3179 overlay_pass.draw(0..sd.vertex_count, 0..1);
3180 }
3181 }
3182 }
3183 if !sd.tex_batches.is_empty() {
3184 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
3185 overlay_pass.set_pipeline(pipeline);
3186 for batch in &sd.tex_batches {
3187 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
3188 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
3189 overlay_pass.draw(0..batch.vertex_count, 0..1);
3190 }
3191 }
3192 }
3193 }
3194 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
3195 overlay_pass.set_pipeline(pipeline);
3196 if let Some(ref rr) = self.overlay_rect_gpu_data {
3197 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
3198 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
3199 overlay_pass.draw(0..rr.vertex_count, 0..1);
3200 }
3201 if let Some(ref ld) = self.label_gpu_data {
3202 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
3203 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
3204 overlay_pass.draw(0..ld.vertex_count, 0..1);
3205 }
3206 if let Some(ref sb) = self.scalar_bar_gpu_data {
3207 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
3208 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
3209 overlay_pass.draw(0..sb.vertex_count, 0..1);
3210 }
3211 if let Some(ref rd) = self.ruler_gpu_data {
3212 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
3213 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
3214 overlay_pass.draw(0..rd.vertex_count, 0..1);
3215 }
3216 if let Some(ref lb) = self.loading_bar_gpu_data {
3217 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
3218 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
3219 overlay_pass.draw(0..lb.vertex_count, 0..1);
3220 }
3221 }
3222 if !self.overlay_image_gpu_data.is_empty() {
3224 if let Some(pipeline) = &self.resources.screen_image_pipeline {
3225 overlay_pass.set_pipeline(pipeline);
3226 for gpu in &self.overlay_image_gpu_data {
3227 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
3228 overlay_pass.draw(0..6, 0..1);
3229 }
3230 }
3231 }
3232 }
3233
3234 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
3236 self.ts_query_set.as_ref(),
3237 self.ts_resolve_buf.as_ref(),
3238 self.ts_staging_buf.as_ref(),
3239 ) {
3240 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
3241 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
3242 self.ts_needs_readback = true;
3243 }
3244
3245 encoder.finish()
3246 }
3247
3248 pub fn render_offscreen(
3262 &mut self,
3263 device: &wgpu::Device,
3264 queue: &wgpu::Queue,
3265 frame: &FrameData,
3266 width: u32,
3267 height: u32,
3268 ) -> Vec<u8> {
3269 let target_format = self.resources.target_format;
3271 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
3272 label: Some("offscreen_target"),
3273 size: wgpu::Extent3d {
3274 width: width.max(1),
3275 height: height.max(1),
3276 depth_or_array_layers: 1,
3277 },
3278 mip_level_count: 1,
3279 sample_count: 1,
3280 dimension: wgpu::TextureDimension::D2,
3281 format: target_format,
3282 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
3283 view_formats: &[],
3284 });
3285
3286 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
3288
3289 let cmd_buf = self.render(device, queue, &output_view, frame);
3297 queue.submit(std::iter::once(cmd_buf));
3298
3299 let bytes_per_pixel = 4u32;
3301 let unpadded_row = width * bytes_per_pixel;
3302 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
3303 let padded_row = (unpadded_row + align - 1) & !(align - 1);
3304 let buffer_size = (padded_row * height.max(1)) as u64;
3305
3306 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
3307 label: Some("offscreen_staging"),
3308 size: buffer_size,
3309 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
3310 mapped_at_creation: false,
3311 });
3312
3313 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
3314 label: Some("offscreen_copy_encoder"),
3315 });
3316 copy_encoder.copy_texture_to_buffer(
3317 wgpu::TexelCopyTextureInfo {
3318 texture: &offscreen_texture,
3319 mip_level: 0,
3320 origin: wgpu::Origin3d::ZERO,
3321 aspect: wgpu::TextureAspect::All,
3322 },
3323 wgpu::TexelCopyBufferInfo {
3324 buffer: &staging_buf,
3325 layout: wgpu::TexelCopyBufferLayout {
3326 offset: 0,
3327 bytes_per_row: Some(padded_row),
3328 rows_per_image: Some(height.max(1)),
3329 },
3330 },
3331 wgpu::Extent3d {
3332 width: width.max(1),
3333 height: height.max(1),
3334 depth_or_array_layers: 1,
3335 },
3336 );
3337 queue.submit(std::iter::once(copy_encoder.finish()));
3338
3339 let (tx, rx) = std::sync::mpsc::channel();
3341 staging_buf
3342 .slice(..)
3343 .map_async(wgpu::MapMode::Read, move |result| {
3344 let _ = tx.send(result);
3345 });
3346 device
3347 .poll(wgpu::PollType::Wait {
3348 submission_index: None,
3349 timeout: Some(std::time::Duration::from_secs(5)),
3350 })
3351 .unwrap();
3352 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
3353
3354 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
3355 {
3356 let mapped = staging_buf.slice(..).get_mapped_range();
3357 let data: &[u8] = &mapped;
3358 if padded_row == unpadded_row {
3359 pixels.extend_from_slice(data);
3361 } else {
3362 for row in 0..height as usize {
3364 let start = row * padded_row as usize;
3365 let end = start + unpadded_row as usize;
3366 pixels.extend_from_slice(&data[start..end]);
3367 }
3368 }
3369 }
3370 staging_buf.unmap();
3371
3372 let is_bgra = matches!(
3374 target_format,
3375 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
3376 );
3377 if is_bgra {
3378 for pixel in pixels.chunks_exact_mut(4) {
3379 pixel.swap(0, 2); }
3381 }
3382
3383 pixels
3384 }
3385
3386 fn ensure_backdrop_blur_state(
3392 &mut self,
3393 device: &wgpu::Device,
3394 w: u32,
3395 h: u32,
3396 ) {
3397 let need_recreate = match &self.backdrop_blur_state {
3398 Some(s) => s.size != [w, h] || s.format != self.resources.target_format,
3399 None => true,
3400 };
3401 if !need_recreate {
3402 return;
3403 }
3404
3405 let format = self.resources.target_format;
3406 let blur_w = (w / 2).max(1);
3407 let blur_h = (h / 2).max(1);
3408
3409 let intermediate_texture = device.create_texture(&wgpu::TextureDescriptor {
3410 label: Some("backdrop_intermediate"),
3411 size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
3412 mip_level_count: 1,
3413 sample_count: 1,
3414 dimension: wgpu::TextureDimension::D2,
3415 format,
3416 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
3417 view_formats: &[],
3418 });
3419 let intermediate_view = intermediate_texture.create_view(&Default::default());
3420
3421 let make_blur_tex = |label: &str| {
3422 let t = device.create_texture(&wgpu::TextureDescriptor {
3423 label: Some(label),
3424 size: wgpu::Extent3d { width: blur_w, height: blur_h, depth_or_array_layers: 1 },
3425 mip_level_count: 1,
3426 sample_count: 1,
3427 dimension: wgpu::TextureDimension::D2,
3428 format,
3429 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
3430 view_formats: &[],
3431 });
3432 let v = t.create_view(&Default::default());
3433 (t, v)
3434 };
3435 let (blur_a_texture, blur_a_view) = make_blur_tex("backdrop_blur_a");
3436 let (blur_b_texture, blur_b_view) = make_blur_tex("backdrop_blur_b");
3437
3438 self.backdrop_blur_state = Some(crate::resources::BackdropBlurState {
3439 intermediate_texture,
3440 intermediate_view,
3441 blur_a_texture,
3442 blur_a_view,
3443 blur_b_texture,
3444 blur_b_view,
3445 size: [w, h],
3446 format,
3447 });
3448 }
3449
3450 fn run_backdrop_blur(
3454 &self,
3455 encoder: &mut wgpu::CommandEncoder,
3456 device: &wgpu::Device,
3457 _queue: &wgpu::Queue,
3458 source_view: &wgpu::TextureView,
3459 spread: f32,
3460 ) -> wgpu::BindGroup {
3461 let bs = self.backdrop_blur_state.as_ref().unwrap();
3462 let blur_bgl = self.resources.backdrop_blur_bgl.as_ref().unwrap();
3463 let blur_sampler = self.resources.backdrop_blur_sampler.as_ref().unwrap();
3464 let blur_pipeline = self.resources.backdrop_blur_pipeline.as_ref().unwrap();
3465 let blit_pipeline = self.resources.dyn_res_upscale_pipeline.as_ref().unwrap();
3467 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
3468 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
3469
3470 let downsample_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
3472 label: Some("backdrop_downsample_bg"),
3473 layout: blit_bgl,
3474 entries: &[
3475 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(source_view) },
3476 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blit_sampler) },
3477 ],
3478 });
3479 {
3480 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3481 label: Some("backdrop_downsample"),
3482 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3483 view: &bs.blur_a_view,
3484 resolve_target: None,
3485 ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store },
3486 depth_slice: None,
3487 })],
3488 depth_stencil_attachment: None,
3489 timestamp_writes: None,
3490 occlusion_query_set: None,
3491 });
3492 pass.set_pipeline(blit_pipeline);
3493 pass.set_bind_group(0, &downsample_bg, &[]);
3494 pass.draw(0..3, 0..1);
3495 }
3496
3497 let effective_spread = (spread / 2.0).max(1.0);
3499
3500 let h_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
3502 label: Some("blur_h_uniform"),
3503 contents: bytemuck::cast_slice(&[1u32, effective_spread.to_bits(), 0u32, 0u32]),
3504 usage: wgpu::BufferUsages::UNIFORM,
3505 });
3506 let h_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
3507 label: Some("blur_h_bg"),
3508 layout: blur_bgl,
3509 entries: &[
3510 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.blur_a_view) },
3511 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blur_sampler) },
3512 wgpu::BindGroupEntry { binding: 2, resource: h_uniform.as_entire_binding() },
3513 ],
3514 });
3515 {
3516 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3517 label: Some("backdrop_blur_h"),
3518 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3519 view: &bs.blur_b_view,
3520 resolve_target: None,
3521 ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store },
3522 depth_slice: None,
3523 })],
3524 depth_stencil_attachment: None,
3525 timestamp_writes: None,
3526 occlusion_query_set: None,
3527 });
3528 pass.set_pipeline(blur_pipeline);
3529 pass.set_bind_group(0, &h_bg, &[]);
3530 pass.draw(0..3, 0..1);
3531 }
3532
3533 let v_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
3535 label: Some("blur_v_uniform"),
3536 contents: bytemuck::cast_slice(&[0u32, effective_spread.to_bits(), 0u32, 0u32]),
3537 usage: wgpu::BufferUsages::UNIFORM,
3538 });
3539 let v_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
3540 label: Some("blur_v_bg"),
3541 layout: blur_bgl,
3542 entries: &[
3543 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.blur_b_view) },
3544 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(blur_sampler) },
3545 wgpu::BindGroupEntry { binding: 2, resource: v_uniform.as_entire_binding() },
3546 ],
3547 });
3548 {
3549 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3550 label: Some("backdrop_blur_v"),
3551 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3552 view: &bs.blur_a_view,
3553 resolve_target: None,
3554 ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store },
3555 depth_slice: None,
3556 })],
3557 depth_stencil_attachment: None,
3558 timestamp_writes: None,
3559 occlusion_query_set: None,
3560 });
3561 pass.set_pipeline(blur_pipeline);
3562 pass.set_bind_group(0, &v_bg, &[]);
3563 pass.draw(0..3, 0..1);
3564 }
3565
3566 let tex_bgl = self.resources.overlay_shape_tex_bgl.as_ref().unwrap();
3570 let tex_sampler = self.resources.overlay_shape_tex_sampler.as_ref().unwrap();
3571 device.create_bind_group(&wgpu::BindGroupDescriptor {
3572 label: Some("backdrop_blur_overlay_bg"),
3573 layout: tex_bgl,
3574 entries: &[
3575 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&bs.blur_a_view) },
3576 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(tex_sampler) },
3577 ],
3578 })
3579 }
3580
3581 fn has_backdrop_blur_shapes(&self) -> bool {
3583 self.overlay_shape_gpu_data
3584 .as_ref()
3585 .map_or(false, |sd| sd.blur_vertex_count > 0)
3586 }
3587
3588 fn draw_blur_shapes<'rp>(
3590 &'rp self,
3591 render_pass: &mut wgpu::RenderPass<'rp>,
3592 blur_bind_group: &'rp wgpu::BindGroup,
3593 ) {
3594 if let Some(ref sd) = self.overlay_shape_gpu_data {
3595 if sd.blur_vertex_count > 0 {
3596 if let (Some(pipeline), Some(vbuf)) = (
3597 &self.resources.overlay_shape_tex_pipeline,
3598 &sd.blur_vertex_buf,
3599 ) {
3600 render_pass.set_pipeline(pipeline);
3601 render_pass.set_bind_group(0, blur_bind_group, &[]);
3602 render_pass.set_vertex_buffer(0, vbuf.slice(..));
3603 render_pass.draw(0..sd.blur_vertex_count, 0..1);
3604 }
3605 }
3606 }
3607 }
3608}