1use super::*;
2use wgpu::util::DeviceExt;
3
4impl ViewportRenderer {
5 pub(crate) fn paint_to<'rp>(&self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
6 let vp_idx = frame.camera.viewport_index;
7 let camera_bg = self.viewport_camera_bind_group(vp_idx);
8 let grid_bg = self.viewport_grid_bind_group(vp_idx);
9 let vp_slot = self.viewport_slots.get(vp_idx);
10 emit_draw_calls!(
11 &self.resources,
12 &mut *render_pass,
13 frame,
14 self.use_instancing,
15 &self.instanced_batches,
16 camera_bg,
17 grid_bg,
18 &self.compute_filter_results,
19 vp_slot,
20 &self.wireframe_bind_groups,
21 &self.per_item_object_bind_groups
22 );
23 emit_scivis_draw_calls!(
24 &self.resources,
25 &mut *render_pass,
26 &self.point_cloud_gpu_data,
27 &self.glyph_gpu_data,
28 &self.polyline_gpu_data,
29 &self.volume_gpu_data,
30 &self.streamtube_gpu_data,
31 camera_bg,
32 &self.tube_gpu_data,
33 &self.image_slice_gpu_data,
34 &self.tensor_glyph_gpu_data,
35 &self.ribbon_gpu_data,
36 &self.volume_surface_slice_gpu_data,
37 &self.sprite_gpu_data,
38 &self.mesh_instance_gpu_data,
39 false
40 );
41 if !self.gaussian_splat_draw_data.is_empty() {
43 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
44 render_pass.set_pipeline(dual.for_format(false));
45 render_pass.set_bind_group(0, camera_bg, &[]);
46 for dd in &self.gaussian_splat_draw_data {
47 if dd.wireframe {
48 continue;
49 }
50 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
51 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
52 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
53 render_pass.draw(0..6, 0..dd.count);
54 }
55 }
56 }
57 }
58 }
59 if !self.tvm_wireframe_draws.is_empty() {
61 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
62 render_pass.set_bind_group(0, camera_bg, &[]);
63 for mesh_id in &self.tvm_wireframe_draws {
64 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
65 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
66 render_pass.set_bind_group(1, tvm_bg, &[]);
67 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
68 render_pass.set_index_buffer(
69 mesh.edge_index_buffer.slice(..),
70 wgpu::IndexFormat::Uint32,
71 );
72 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
73 }
74 }
75 }
76 }
77 if !self.implicit_gpu_data.is_empty() {
79 if let Some(ref dual) = self.resources.implicit_pipeline {
80 render_pass.set_pipeline(dual.for_format(false));
81 render_pass.set_bind_group(0, camera_bg, &[]);
82 for gpu in &self.implicit_gpu_data {
83 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
84 render_pass.draw(0..6, 0..1);
85 }
86 }
87 }
88 if !self.mc_gpu_data.is_empty() {
90 render_pass.set_bind_group(0, camera_bg, &[]);
91 for mc in &self.mc_gpu_data {
92 let vol = &self.resources.mc_volumes[mc.volume_idx];
93 if mc.wireframe || frame.viewport.wireframe_mode {
94 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
95 render_pass.set_pipeline(dual.for_format(false));
96 for (slab, wire_bg) in vol.slabs.iter().zip(mc.wire_slab_bgs.iter()) {
97 render_pass.set_bind_group(1, wire_bg, &[]);
98 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
99 }
100 }
101 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
102 render_pass.set_pipeline(dual.for_format(false));
103 render_pass.set_bind_group(1, &mc.render_bg, &[]);
104 for slab in &vol.slabs {
105 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
106 render_pass.draw_indirect(&slab.indirect_buf, 0);
107 }
108 }
109 }
110 }
111 emit_outline_composite!(&self.resources, &mut *render_pass, vp_slot);
113 if let Some(sub_hl) = self
115 .viewport_slots
116 .get(vp_idx)
117 .and_then(|s| s.sub_highlight.as_ref())
118 {
119 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
120 &self.resources.sub_highlight_fill_ldr_pipeline,
121 &self.resources.sub_highlight_edge_ldr_pipeline,
122 &self.resources.sub_highlight_sprite_ldr_pipeline,
123 ) {
124 if sub_hl.fill_vertex_count > 0 {
125 render_pass.set_pipeline(fill_pl);
126 render_pass.set_bind_group(0, camera_bg, &[]);
127 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
128 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
129 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
130 }
131 if sub_hl.edge_segment_count > 0 {
132 render_pass.set_pipeline(edge_pl);
133 render_pass.set_bind_group(0, camera_bg, &[]);
134 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
135 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
136 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
137 }
138 if sub_hl.sprite_point_count > 0 {
139 render_pass.set_pipeline(sprite_pl);
140 render_pass.set_bind_group(0, camera_bg, &[]);
141 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
142 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
143 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
144 }
145 }
146 }
147 if !self.screen_image_gpu_data.is_empty() {
149 if let Some(pipeline) = &self.resources.screen_image_pipeline {
150 render_pass.set_pipeline(pipeline);
151 for gpu in &self.screen_image_gpu_data {
152 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
153 render_pass.draw(0..6, 0..1);
154 }
155 }
156 }
157 if let Some(ref sd) = self.overlay_shape_gpu_data {
159 if sd.vertex_count > 0 {
160 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
161 if let Some(vbuf) = &sd.vertex_buf {
162 render_pass.set_pipeline(pipeline);
163 render_pass.set_vertex_buffer(0, vbuf.slice(..));
164 render_pass.draw(0..sd.vertex_count, 0..1);
165 }
166 }
167 }
168 if !sd.tex_batches.is_empty() {
169 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
170 render_pass.set_pipeline(pipeline);
171 for batch in &sd.tex_batches {
172 render_pass.set_bind_group(0, &batch.bind_group, &[]);
173 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
174 render_pass.draw(0..batch.vertex_count, 0..1);
175 }
176 }
177 }
178 }
179 if let Some(ref rr) = self.overlay_rect_gpu_data {
181 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
182 render_pass.set_pipeline(pipeline);
183 render_pass.set_bind_group(0, &rr.bind_group, &[]);
184 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
185 render_pass.draw(0..rr.vertex_count, 0..1);
186 }
187 }
188 if let Some(ref ld) = self.label_gpu_data {
190 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
191 render_pass.set_pipeline(pipeline);
192 render_pass.set_bind_group(0, &ld.bind_group, &[]);
193 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
194 render_pass.draw(0..ld.vertex_count, 0..1);
195 }
196 }
197 if let Some(ref sb) = self.scalar_bar_gpu_data {
199 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
200 render_pass.set_pipeline(pipeline);
201 render_pass.set_bind_group(0, &sb.bind_group, &[]);
202 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
203 render_pass.draw(0..sb.vertex_count, 0..1);
204 }
205 }
206 if let Some(ref rd) = self.ruler_gpu_data {
208 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
209 render_pass.set_pipeline(pipeline);
210 render_pass.set_bind_group(0, &rd.bind_group, &[]);
211 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
212 render_pass.draw(0..rd.vertex_count, 0..1);
213 }
214 }
215 if let Some(ref lb) = self.loading_bar_gpu_data {
217 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
218 render_pass.set_pipeline(pipeline);
219 render_pass.set_bind_group(0, &lb.bind_group, &[]);
220 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
221 render_pass.draw(0..lb.vertex_count, 0..1);
222 }
223 }
224 if !self.overlay_image_gpu_data.is_empty() {
226 if let Some(pipeline) = &self.resources.screen_image_pipeline {
227 render_pass.set_pipeline(pipeline);
228 for gpu in &self.overlay_image_gpu_data {
229 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
230 render_pass.draw(0..6, 0..1);
231 }
232 }
233 }
234 if frame.effects.show_shadow_atlas {
236 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
237 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
238 render_pass.draw(0..6, 0..1);
239 }
240
241 }
242
243 pub(crate) fn prepare_ldr_dyn_res(
258 &mut self,
259 encoder: &mut wgpu::CommandEncoder,
260 device: &wgpu::Device,
261 frame: &FrameData,
262 ) -> bool {
263 if self.current_render_scale >= 1.0 - 0.001 {
264 return false;
265 }
266
267 let vp_idx = frame.camera.viewport_index;
268 let w = (frame.camera.viewport_size[0] as u32).max(1);
269 let h = (frame.camera.viewport_size[1] as u32).max(1);
270 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
271 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
272
273 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
274 self.resources.ensure_dyn_res_ds_pipeline(device);
275
276 let bg_colour = frame.viewport.background_colour.unwrap_or([
277 65.0 / 255.0,
278 65.0 / 255.0,
279 65.0 / 255.0,
280 1.0,
281 ]);
282
283 {
284 let slot = &self.viewport_slots[vp_idx];
285 let dr = slot.dyn_res.as_ref().unwrap();
286 let colour_view = &dr.colour_view;
287 let depth_view = &dr.depth_view;
288 let camera_bg = &slot.camera_bind_group;
289 let grid_bg = &slot.grid_bind_group;
290
291 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
292 label: Some("ldr_dyn_res_render_pass"),
293 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
294 view: colour_view,
295 resolve_target: None,
296 ops: wgpu::Operations {
297 load: wgpu::LoadOp::Clear(wgpu::Color {
298 r: bg_colour[0] as f64,
299 g: bg_colour[1] as f64,
300 b: bg_colour[2] as f64,
301 a: bg_colour[3] as f64,
302 }),
303 store: wgpu::StoreOp::Store,
304 },
305 depth_slice: None,
306 })],
307 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
308 view: depth_view,
309 depth_ops: Some(wgpu::Operations {
310 load: wgpu::LoadOp::Clear(1.0),
311 store: wgpu::StoreOp::Discard,
312 }),
313 stencil_ops: None,
314 }),
315 timestamp_writes: None,
316 occlusion_query_set: None,
317 });
318 emit_draw_calls!(
319 &self.resources,
320 &mut render_pass,
321 frame,
322 self.use_instancing,
323 &self.instanced_batches,
324 camera_bg,
325 grid_bg,
326 &self.compute_filter_results,
327 Some(slot),
328 &self.wireframe_bind_groups,
329 &self.per_item_object_bind_groups
330 );
331 emit_scivis_draw_calls!(
332 &self.resources,
333 &mut render_pass,
334 &self.point_cloud_gpu_data,
335 &self.glyph_gpu_data,
336 &self.polyline_gpu_data,
337 &self.volume_gpu_data,
338 &self.streamtube_gpu_data,
339 camera_bg,
340 &self.tube_gpu_data,
341 &self.image_slice_gpu_data,
342 &self.tensor_glyph_gpu_data,
343 &self.ribbon_gpu_data,
344 &self.volume_surface_slice_gpu_data,
345 &self.sprite_gpu_data,
346 &self.mesh_instance_gpu_data,
347 false
348 );
349 if !self.tvm_wireframe_draws.is_empty() {
351 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
352 render_pass.set_bind_group(0, camera_bg, &[]);
353 for mesh_id in &self.tvm_wireframe_draws {
354 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
355 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
356 render_pass.set_bind_group(1, tvm_bg, &[]);
357 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
358 render_pass.set_index_buffer(
359 mesh.edge_index_buffer.slice(..),
360 wgpu::IndexFormat::Uint32,
361 );
362 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
363 }
364 }
365 }
366 }
367 if !self.implicit_gpu_data.is_empty() {
369 if let Some(ref dual) = self.resources.implicit_pipeline {
370 render_pass.set_pipeline(dual.for_format(false));
371 render_pass.set_bind_group(0, camera_bg, &[]);
372 for gpu in &self.implicit_gpu_data {
373 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
374 render_pass.draw(0..6, 0..1);
375 }
376 }
377 }
378 if !self.mc_gpu_data.is_empty() {
380 if let Some(ref dual) = self.resources.mc_surface_pipeline {
381 render_pass.set_pipeline(dual.for_format(false));
382 render_pass.set_bind_group(0, camera_bg, &[]);
383 for mc in &self.mc_gpu_data {
384 let vol = &self.resources.mc_volumes[mc.volume_idx];
385 render_pass.set_bind_group(1, &mc.render_bg, &[]);
386 for slab in &vol.slabs {
387 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
388 render_pass.draw_indirect(&slab.indirect_buf, 0);
389 }
390 }
391 }
392 }
393 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
395 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
397 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
398 &self.resources.sub_highlight_fill_ldr_pipeline,
399 &self.resources.sub_highlight_edge_ldr_pipeline,
400 &self.resources.sub_highlight_sprite_ldr_pipeline,
401 ) {
402 if sub_hl.fill_vertex_count > 0 {
403 render_pass.set_pipeline(fill_pl);
404 render_pass.set_bind_group(0, camera_bg, &[]);
405 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
406 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
407 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
408 }
409 if sub_hl.edge_segment_count > 0 {
410 render_pass.set_pipeline(edge_pl);
411 render_pass.set_bind_group(0, camera_bg, &[]);
412 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
413 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
414 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
415 }
416 if sub_hl.sprite_point_count > 0 {
417 render_pass.set_pipeline(sprite_pl);
418 render_pass.set_bind_group(0, camera_bg, &[]);
419 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
420 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
421 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
422 }
423 }
424 }
425 if !self.screen_image_gpu_data.is_empty() {
427 if let Some(pipeline) = &self.resources.screen_image_pipeline {
428 render_pass.set_pipeline(pipeline);
429 for gpu in &self.screen_image_gpu_data {
430 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
431 render_pass.draw(0..6, 0..1);
432 }
433 }
434 }
435 if let Some(ref sd) = self.overlay_shape_gpu_data {
437 if sd.vertex_count > 0 {
438 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
439 if let Some(vbuf) = &sd.vertex_buf {
440 render_pass.set_pipeline(pipeline);
441 render_pass.set_vertex_buffer(0, vbuf.slice(..));
442 render_pass.draw(0..sd.vertex_count, 0..1);
443 }
444 }
445 }
446 if !sd.tex_batches.is_empty() {
447 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
448 render_pass.set_pipeline(pipeline);
449 for batch in &sd.tex_batches {
450 render_pass.set_bind_group(0, &batch.bind_group, &[]);
451 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
452 render_pass.draw(0..batch.vertex_count, 0..1);
453 }
454 }
455 }
456 }
457 if let Some(ref rr) = self.overlay_rect_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, &rr.bind_group, &[]);
462 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
463 render_pass.draw(0..rr.vertex_count, 0..1);
464 }
465 }
466 if let Some(ref ld) = self.label_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, &ld.bind_group, &[]);
471 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
472 render_pass.draw(0..ld.vertex_count, 0..1);
473 }
474 }
475 if let Some(ref sb) = self.scalar_bar_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, &sb.bind_group, &[]);
480 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
481 render_pass.draw(0..sb.vertex_count, 0..1);
482 }
483 }
484 if let Some(ref rd) = self.ruler_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, &rd.bind_group, &[]);
489 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
490 render_pass.draw(0..rd.vertex_count, 0..1);
491 }
492 }
493 if let Some(ref lb) = self.loading_bar_gpu_data {
495 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
496 render_pass.set_pipeline(pipeline);
497 render_pass.set_bind_group(0, &lb.bind_group, &[]);
498 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
499 render_pass.draw(0..lb.vertex_count, 0..1);
500 }
501 }
502 if !self.overlay_image_gpu_data.is_empty() {
504 if let Some(pipeline) = &self.resources.screen_image_pipeline {
505 render_pass.set_pipeline(pipeline);
506 for gpu in &self.overlay_image_gpu_data {
507 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
508 render_pass.draw(0..6, 0..1);
509 }
510 }
511 }
512 }
513
514 true
515 }
516
517 pub(crate) fn paint_dyn_res_blit<'rp>(
523 &self,
524 render_pass: &mut wgpu::RenderPass<'rp>,
525 frame: &FrameData,
526 ) {
527 let vp_idx = frame.camera.viewport_index;
528 if let Some(dr) = self
529 .viewport_slots
530 .get(vp_idx)
531 .and_then(|s| s.dyn_res.as_ref())
532 {
533 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
534 render_pass.set_pipeline(pipeline);
535 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
536 render_pass.draw(0..3, 0..1);
537 }
538 }
539 }
540
541 pub(crate) fn prepare_hdr_callback(
558 &mut self,
559 device: &wgpu::Device,
560 queue: &wgpu::Queue,
561 frame: &FrameData,
562 ) -> wgpu::CommandBuffer {
563 self.prepare(device, queue, frame);
564
565 let vp_idx = frame.camera.viewport_index;
566 let ppp = frame.camera.pixels_per_point;
572 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
573 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
574
575 self.resources.ensure_dyn_res_pipeline(device);
577 self.resources.ensure_dyn_res_ds_pipeline(device);
578
579 self.ensure_viewport_slot(device, vp_idx);
581 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
582 None => true,
583 Some(t) => t.size != [w, h],
584 };
585 if needs_create {
586 let target = self.resources.create_hdr_callback_target(device, [w, h]);
587 self.viewport_slots[vp_idx].hdr_callback = Some(target);
588 }
589
590 let output_view = self.viewport_slots[vp_idx]
594 .hdr_callback
595 .as_ref()
596 .unwrap()
597 .texture
598 .create_view(&wgpu::TextureViewDescriptor::default());
599
600 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
601 }
602
603 pub(crate) fn prepare_hdr_callback_viewport(
619 &mut self,
620 device: &wgpu::Device,
621 queue: &wgpu::Queue,
622 id: ViewportId,
623 frame: &FrameData,
624 ) -> wgpu::CommandBuffer {
625 let vp_idx = id.0;
626 let ppp = frame.camera.pixels_per_point;
627 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
628 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
629
630 self.resources.ensure_dyn_res_pipeline(device);
631 self.resources.ensure_dyn_res_ds_pipeline(device);
632
633 self.ensure_viewport_slot(device, vp_idx);
634 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
635 None => true,
636 Some(t) => t.size != [w, h],
637 };
638 if needs_create {
639 let target = self.resources.create_hdr_callback_target(device, [w, h]);
640 self.viewport_slots[vp_idx].hdr_callback = Some(target);
641 }
642
643 let output_view = self.viewport_slots[vp_idx]
644 .hdr_callback
645 .as_ref()
646 .unwrap()
647 .texture
648 .create_view(&wgpu::TextureViewDescriptor::default());
649
650 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
651 }
652
653 pub(crate) fn paint_hdr_blit<'rp>(
659 &self,
660 render_pass: &mut wgpu::RenderPass<'rp>,
661 frame: &FrameData,
662 ) {
663 let vp_idx = frame.camera.viewport_index;
664 if let Some(hc) = self
665 .viewport_slots
666 .get(vp_idx)
667 .and_then(|s| s.hdr_callback.as_ref())
668 {
669 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
670 render_pass.set_pipeline(pipeline);
671 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
672 render_pass.draw(0..3, 0..1);
673 }
674 }
675 if frame.effects.show_shadow_atlas {
677 render_pass.set_pipeline(&self.resources.shadow_atlas_viewer_pipeline);
678 render_pass.set_bind_group(0, &self.resources.shadow_atlas_viewer_bg, &[]);
679 render_pass.draw(0..6, 0..1);
680 }
681 }
682
683 pub(crate) fn paint_hdr_blit_no_ds<'rp>(
687 &self,
688 render_pass: &mut wgpu::RenderPass<'rp>,
689 frame: &FrameData,
690 ) {
691 let vp_idx = frame.camera.viewport_index;
692 if let Some(hc) = self
693 .viewport_slots
694 .get(vp_idx)
695 .and_then(|s| s.hdr_callback.as_ref())
696 {
697 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
698 render_pass.set_pipeline(pipeline);
699 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
700 render_pass.draw(0..3, 0..1);
701 }
702 }
703 }
704
705 pub(crate) fn prepare_callback(
719 &mut self,
720 device: &wgpu::Device,
721 queue: &wgpu::Queue,
722 frame: &FrameData,
723 ) -> Vec<wgpu::CommandBuffer> {
724 if frame.effects.post_process.enabled {
725 let cb = self.prepare_hdr_callback(device, queue, frame);
726 vec![cb]
727 } else {
728 self.prepare(device, queue, frame);
729 if self.current_render_scale < 1.0 - 0.001 {
730 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
731 label: Some("ldr_dyn_res_callback_encoder"),
732 });
733 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
734 vec![encoder.finish()]
735 } else {
736 Vec::new()
737 }
738 }
739 }
740
741 pub(crate) fn paint_callback<'rp>(
747 &self,
748 render_pass: &mut wgpu::RenderPass<'rp>,
749 frame: &FrameData,
750 ) {
751 let vp_idx = frame.camera.viewport_index;
752 if frame.effects.post_process.enabled {
753 if self
754 .viewport_slots
755 .get(vp_idx)
756 .and_then(|s| s.hdr_callback.as_ref())
757 .is_some()
758 {
759 self.paint_hdr_blit(render_pass, frame);
760 return;
761 }
762 }
763 if self.current_render_scale < 1.0 - 0.001
764 && self
765 .viewport_slots
766 .get(vp_idx)
767 .and_then(|s| s.dyn_res.as_ref())
768 .is_some()
769 {
770 self.paint_dyn_res_blit(render_pass, frame);
771 } else {
772 self.paint_to(render_pass, frame);
773 }
774 }
775
776 pub(crate) fn render_viewport(
790 &mut self,
791 device: &wgpu::Device,
792 queue: &wgpu::Queue,
793 output_view: &wgpu::TextureView,
794 id: ViewportId,
795 frame: &FrameData,
796 ) -> wgpu::CommandBuffer {
797 self.render_frame_internal(device, queue, output_view, id.0, frame)
798 }
799
800 pub(crate) fn render(
808 &mut self,
809 device: &wgpu::Device,
810 queue: &wgpu::Queue,
811 output_view: &wgpu::TextureView,
812 frame: &FrameData,
813 ) -> wgpu::CommandBuffer {
814 self.prepare(device, queue, frame);
816 self.render_frame_internal(
817 device,
818 queue,
819 output_view,
820 frame.camera.viewport_index,
821 frame,
822 )
823 }
824
825 fn render_frame_internal(
830 &mut self,
831 device: &wgpu::Device,
832 queue: &wgpu::Queue,
833 output_view: &wgpu::TextureView,
834 vp_idx: usize,
835 frame: &FrameData,
836 ) -> wgpu::CommandBuffer {
837 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
839 SurfaceSubmission::Flat(items) => items.as_ref(),
840 };
841
842 let bg_colour = frame.viewport.background_colour.unwrap_or([
843 65.0 / 255.0,
844 65.0 / 255.0,
845 65.0 / 255.0,
846 1.0,
847 ]);
848 let ppp = frame.camera.pixels_per_point;
849 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
850 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
851
852 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
854 self.ensure_viewport_hdr(
855 device,
856 queue,
857 vp_idx,
858 w.max(1),
859 h.max(1),
860 ssaa_factor,
861 self.current_render_scale,
862 );
863
864 if self.ts_query_set.is_none()
866 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
867 {
868 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
869 label: Some("ts_query_set"),
870 ty: wgpu::QueryType::Timestamp,
871 count: 2,
872 }));
873 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
874 label: Some("ts_resolve_buf"),
875 size: 16,
876 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
877 mapped_at_creation: false,
878 }));
879 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
880 label: Some("ts_staging_buf"),
881 size: 16,
882 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
883 mapped_at_creation: false,
884 }));
885 self.ts_period = queue.get_timestamp_period();
886 }
887
888 if !frame.effects.post_process.enabled {
889 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
893 label: Some("ldr_encoder"),
894 });
895
896 let use_dyn_res = self.current_render_scale < 1.0 - 0.001;
897 let needs_blur = self.has_backdrop_blur_shapes();
898
899 if use_dyn_res {
900 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
901 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
902 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
903 }
904
905 if needs_blur && !use_dyn_res {
909 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
910 }
911
912 {
913 let slot = &self.viewport_slots[vp_idx];
914 let slot_hdr = slot.hdr.as_ref().expect(
915 "HDR state missing in LDR path; ensure_viewport_hdr must have been called",
916 );
917 let camera_bg = &slot.camera_bind_group;
918 let grid_bg = &slot.grid_bind_group;
919 let (scene_colour_view, scene_depth_view): (
921 &wgpu::TextureView,
922 &wgpu::TextureView,
923 ) = if use_dyn_res {
924 let dr = slot.dyn_res.as_ref().unwrap();
925 (&dr.colour_view, &dr.depth_view)
926 } else if needs_blur {
927 let bs = self.backdrop_blur_state.as_ref().unwrap();
928 (&bs.intermediate_view, &slot_hdr.outline_depth_view)
929 } else {
930 (output_view, &slot_hdr.outline_depth_view)
931 };
932 let ts_writes =
933 self.ts_query_set
934 .as_ref()
935 .map(|qs| wgpu::RenderPassTimestampWrites {
936 query_set: qs,
937 beginning_of_pass_write_index: Some(0),
938 end_of_pass_write_index: Some(1),
939 });
940 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
941 label: Some("ldr_render_pass"),
942 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
943 view: scene_colour_view,
944 resolve_target: None,
945 ops: wgpu::Operations {
946 load: wgpu::LoadOp::Clear(wgpu::Color {
947 r: bg_colour[0] as f64,
948 g: bg_colour[1] as f64,
949 b: bg_colour[2] as f64,
950 a: bg_colour[3] as f64,
951 }),
952 store: wgpu::StoreOp::Store,
953 },
954 depth_slice: None,
955 })],
956 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
957 view: scene_depth_view,
958 depth_ops: Some(wgpu::Operations {
959 load: wgpu::LoadOp::Clear(1.0),
960 store: wgpu::StoreOp::Discard,
961 }),
962 stencil_ops: None,
963 }),
964 timestamp_writes: ts_writes,
965 occlusion_query_set: None,
966 });
967 emit_draw_calls!(
968 &self.resources,
969 &mut render_pass,
970 frame,
971 self.use_instancing,
972 &self.instanced_batches,
973 camera_bg,
974 grid_bg,
975 &self.compute_filter_results,
976 Some(slot),
977 &self.wireframe_bind_groups,
978 &self.per_item_object_bind_groups
979 );
980 emit_scivis_draw_calls!(
981 &self.resources,
982 &mut render_pass,
983 &self.point_cloud_gpu_data,
984 &self.glyph_gpu_data,
985 &self.polyline_gpu_data,
986 &self.volume_gpu_data,
987 &self.streamtube_gpu_data,
988 camera_bg,
989 &self.tube_gpu_data,
990 &self.image_slice_gpu_data,
991 &self.tensor_glyph_gpu_data,
992 &self.ribbon_gpu_data,
993 &self.volume_surface_slice_gpu_data,
994 &self.sprite_gpu_data,
995 &self.mesh_instance_gpu_data,
996 false
997 );
998 if !self.tvm_wireframe_draws.is_empty() {
1000 if let Some(ref tvm_bg) = self.tvm_wireframe_bg {
1001 render_pass.set_bind_group(0, camera_bg, &[]);
1002 for mesh_id in &self.tvm_wireframe_draws {
1003 if let Some(mesh) = self.resources.mesh_store.get(*mesh_id) {
1004 render_pass.set_pipeline(&self.resources.wireframe_pipeline);
1005 render_pass.set_bind_group(1, tvm_bg, &[]);
1006 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1007 render_pass.set_index_buffer(
1008 mesh.edge_index_buffer.slice(..),
1009 wgpu::IndexFormat::Uint32,
1010 );
1011 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1012 }
1013 }
1014 }
1015 }
1016 if !self.implicit_gpu_data.is_empty() {
1018 if let Some(ref dual) = self.resources.implicit_pipeline {
1019 render_pass.set_pipeline(dual.for_format(false));
1020 render_pass.set_bind_group(0, camera_bg, &[]);
1021 for gpu in &self.implicit_gpu_data {
1022 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1023 render_pass.draw(0..6, 0..1);
1024 }
1025 }
1026 }
1027 if !self.mc_gpu_data.is_empty() {
1029 if let Some(ref dual) = self.resources.mc_surface_pipeline {
1030 render_pass.set_pipeline(dual.for_format(false));
1031 render_pass.set_bind_group(0, camera_bg, &[]);
1032 for mc in &self.mc_gpu_data {
1033 let vol = &self.resources.mc_volumes[mc.volume_idx];
1034 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1035 for slab in &vol.slabs {
1036 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1037 render_pass.draw_indirect(&slab.indirect_buf, 0);
1038 }
1039 }
1040 }
1041 }
1042 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
1044 if !self.screen_image_gpu_data.is_empty() {
1049 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
1050 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
1051 for gpu in &self.screen_image_gpu_data {
1052 if let (Some(dc_bg), Some(dc_pipe)) =
1053 (&gpu.depth_bind_group, dc_pipeline)
1054 {
1055 render_pass.set_pipeline(dc_pipe);
1056 render_pass.set_bind_group(0, dc_bg, &[]);
1057 } else {
1058 render_pass.set_pipeline(overlay_pipeline);
1059 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1060 }
1061 render_pass.draw(0..6, 0..1);
1062 }
1063 }
1064 }
1065 if !needs_blur {
1068 if let Some(ref sd) = self.overlay_shape_gpu_data {
1070 if sd.vertex_count > 0 {
1071 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1072 if let Some(vbuf) = &sd.vertex_buf {
1073 render_pass.set_pipeline(pipeline);
1074 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1075 render_pass.draw(0..sd.vertex_count, 0..1);
1076 }
1077 }
1078 }
1079 if !sd.tex_batches.is_empty() {
1080 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1081 render_pass.set_pipeline(pipeline);
1082 for batch in &sd.tex_batches {
1083 render_pass.set_bind_group(0, &batch.bind_group, &[]);
1084 render_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1085 render_pass.draw(0..batch.vertex_count, 0..1);
1086 }
1087 }
1088 }
1089 }
1090 if let Some(ref rr) = self.overlay_rect_gpu_data {
1092 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1093 render_pass.set_pipeline(pipeline);
1094 render_pass.set_bind_group(0, &rr.bind_group, &[]);
1095 render_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1096 render_pass.draw(0..rr.vertex_count, 0..1);
1097 }
1098 }
1099 if let Some(ref ld) = self.label_gpu_data {
1101 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1102 render_pass.set_pipeline(pipeline);
1103 render_pass.set_bind_group(0, &ld.bind_group, &[]);
1104 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1105 render_pass.draw(0..ld.vertex_count, 0..1);
1106 }
1107 }
1108 if let Some(ref sb) = self.scalar_bar_gpu_data {
1110 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1111 render_pass.set_pipeline(pipeline);
1112 render_pass.set_bind_group(0, &sb.bind_group, &[]);
1113 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1114 render_pass.draw(0..sb.vertex_count, 0..1);
1115 }
1116 }
1117 if let Some(ref rd) = self.ruler_gpu_data {
1119 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1120 render_pass.set_pipeline(pipeline);
1121 render_pass.set_bind_group(0, &rd.bind_group, &[]);
1122 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1123 render_pass.draw(0..rd.vertex_count, 0..1);
1124 }
1125 }
1126 if !self.overlay_image_gpu_data.is_empty() {
1128 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1129 render_pass.set_pipeline(pipeline);
1130 for gpu in &self.overlay_image_gpu_data {
1131 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1132 render_pass.draw(0..6, 0..1);
1133 }
1134 }
1135 }
1136 }
1137 }
1138 if needs_blur {
1143 let spread = self
1144 .overlay_shape_gpu_data
1145 .as_ref()
1146 .map(|d| d.max_blur_radius)
1147 .unwrap_or(1.0);
1148 let blur_bg = {
1149 let source = if use_dyn_res {
1150 &self.viewport_slots[vp_idx]
1151 .dyn_res
1152 .as_ref()
1153 .unwrap()
1154 .colour_view
1155 } else {
1156 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1157 };
1158 self.run_backdrop_blur(&mut encoder, device, queue, source, spread)
1159 };
1160
1161 let slot = &self.viewport_slots[vp_idx];
1163 let slot_hdr = slot.hdr.as_ref().unwrap();
1164 let overlay_colour_view: &wgpu::TextureView = if use_dyn_res {
1165 &slot.dyn_res.as_ref().unwrap().colour_view
1166 } else {
1167 &self.backdrop_blur_state.as_ref().unwrap().intermediate_view
1168 };
1169 let overlay_depth_view: &wgpu::TextureView = if use_dyn_res {
1170 &slot.dyn_res.as_ref().unwrap().depth_view
1171 } else {
1172 &slot_hdr.outline_depth_view
1173 };
1174 {
1175 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1176 label: Some("ldr_overlay_blur_pass"),
1177 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1178 view: overlay_colour_view,
1179 resolve_target: None,
1180 ops: wgpu::Operations {
1181 load: wgpu::LoadOp::Load,
1182 store: wgpu::StoreOp::Store,
1183 },
1184 depth_slice: None,
1185 })],
1186 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1187 view: overlay_depth_view,
1188 depth_ops: Some(wgpu::Operations {
1189 load: wgpu::LoadOp::Load,
1190 store: wgpu::StoreOp::Discard,
1191 }),
1192 stencil_ops: None,
1193 }),
1194 timestamp_writes: None,
1195 occlusion_query_set: None,
1196 });
1197 self.draw_blur_shapes(&mut overlay_pass, &blur_bg);
1199 if let Some(ref sd) = self.overlay_shape_gpu_data {
1201 if sd.vertex_count > 0 {
1202 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
1203 if let Some(vbuf) = &sd.vertex_buf {
1204 overlay_pass.set_pipeline(pipeline);
1205 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1206 overlay_pass.draw(0..sd.vertex_count, 0..1);
1207 }
1208 }
1209 }
1210 if !sd.tex_batches.is_empty() {
1211 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
1212 overlay_pass.set_pipeline(pipeline);
1213 for batch in &sd.tex_batches {
1214 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
1215 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
1216 overlay_pass.draw(0..batch.vertex_count, 0..1);
1217 }
1218 }
1219 }
1220 }
1221 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1222 if let Some(ref rr) = self.overlay_rect_gpu_data {
1223 overlay_pass.set_pipeline(pipeline);
1224 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
1225 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
1226 overlay_pass.draw(0..rr.vertex_count, 0..1);
1227 }
1228 if let Some(ref ld) = self.label_gpu_data {
1229 overlay_pass.set_pipeline(pipeline);
1230 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
1231 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1232 overlay_pass.draw(0..ld.vertex_count, 0..1);
1233 }
1234 if let Some(ref sb) = self.scalar_bar_gpu_data {
1235 overlay_pass.set_pipeline(pipeline);
1236 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
1237 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1238 overlay_pass.draw(0..sb.vertex_count, 0..1);
1239 }
1240 if let Some(ref rd) = self.ruler_gpu_data {
1241 overlay_pass.set_pipeline(pipeline);
1242 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
1243 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1244 overlay_pass.draw(0..rd.vertex_count, 0..1);
1245 }
1246 }
1247 if !self.overlay_image_gpu_data.is_empty() {
1248 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1249 overlay_pass.set_pipeline(pipeline);
1250 for gpu in &self.overlay_image_gpu_data {
1251 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
1252 overlay_pass.draw(0..6, 0..1);
1253 }
1254 }
1255 }
1256 }
1257 }
1258
1259 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
1261 self.ts_query_set.as_ref(),
1262 self.ts_resolve_buf.as_ref(),
1263 self.ts_staging_buf.as_ref(),
1264 ) {
1265 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
1266 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
1267 self.ts_needs_readback = true;
1268 }
1269
1270 if use_dyn_res {
1272 let upscale_bg = &self.viewport_slots[vp_idx]
1273 .dyn_res
1274 .as_ref()
1275 .unwrap()
1276 .upscale_bind_group;
1277 let mut upscale_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1278 label: Some("dyn_res_upscale_pass"),
1279 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1280 view: output_view,
1281 resolve_target: None,
1282 ops: wgpu::Operations {
1283 load: wgpu::LoadOp::Load,
1284 store: wgpu::StoreOp::Store,
1285 },
1286 depth_slice: None,
1287 })],
1288 depth_stencil_attachment: None,
1289 timestamp_writes: None,
1290 occlusion_query_set: None,
1291 });
1292 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1293 upscale_pass.set_pipeline(pipeline);
1294 upscale_pass.set_bind_group(0, upscale_bg, &[]);
1295 upscale_pass.draw(0..3, 0..1);
1296 }
1297 } else if needs_blur {
1298 let bs = self.backdrop_blur_state.as_ref().unwrap();
1300 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
1301 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
1302 let blit_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
1303 label: Some("backdrop_blit_bg"),
1304 layout: blit_bgl,
1305 entries: &[
1306 wgpu::BindGroupEntry {
1307 binding: 0,
1308 resource: wgpu::BindingResource::TextureView(&bs.intermediate_view),
1309 },
1310 wgpu::BindGroupEntry {
1311 binding: 1,
1312 resource: wgpu::BindingResource::Sampler(blit_sampler),
1313 },
1314 ],
1315 });
1316 let mut blit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1317 label: Some("backdrop_blit_pass"),
1318 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1319 view: output_view,
1320 resolve_target: None,
1321 ops: wgpu::Operations {
1322 load: wgpu::LoadOp::Load,
1323 store: wgpu::StoreOp::Store,
1324 },
1325 depth_slice: None,
1326 })],
1327 depth_stencil_attachment: None,
1328 timestamp_writes: None,
1329 occlusion_query_set: None,
1330 });
1331 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1332 blit_pass.set_pipeline(pipeline);
1333 blit_pass.set_bind_group(0, &blit_bg, &[]);
1334 blit_pass.draw(0..3, 0..1);
1335 }
1336 }
1337
1338 return encoder.finish();
1339 }
1340
1341 let pp = &frame.effects.post_process;
1343
1344 let hdr_clear_rgb = [
1345 bg_colour[0].powf(2.2),
1346 bg_colour[1].powf(2.2),
1347 bg_colour[2].powf(2.2),
1348 ];
1349
1350 let mode = match pp.tone_mapping {
1352 crate::renderer::ToneMapping::Reinhard => 0u32,
1353 crate::renderer::ToneMapping::Aces => 1u32,
1354 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
1355 };
1356 let tm_uniform = crate::resources::ToneMapUniform {
1357 exposure: pp.exposure,
1358 mode,
1359 bloom_enabled: if pp.bloom { 1 } else { 0 },
1360 ssao_enabled: if pp.ssao { 1 } else { 0 },
1361 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
1362 edl_enabled: if pp.edl_enabled { 1 } else { 0 },
1363 edl_radius: pp.edl_radius,
1364 edl_strength: pp.edl_strength,
1365 background_colour: bg_colour,
1366 near_plane: frame.camera.render_camera.near,
1367 far_plane: frame.camera.render_camera.far,
1368 lic_enabled: if scene_items
1369 .iter()
1370 .any(|i| i.lic.is_some() && !i.settings.hidden)
1371 {
1372 1
1373 } else {
1374 0
1375 },
1376 lic_strength: scene_items
1377 .iter()
1378 .filter(|i| !i.settings.hidden)
1379 .find_map(|i| i.lic.as_ref().map(|l| l.config.strength))
1380 .unwrap_or(0.5),
1381 };
1382 {
1383 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1384 queue.write_buffer(
1385 &hdr.tone_map_uniform_buf,
1386 0,
1387 bytemuck::cast_slice(&[tm_uniform]),
1388 );
1389
1390 if pp.ssao {
1392 let proj = frame.camera.render_camera.projection;
1393 let inv_proj = proj.inverse();
1394 let ssao_uniform = crate::resources::SsaoUniform {
1395 inv_proj: inv_proj.to_cols_array_2d(),
1396 proj: proj.to_cols_array_2d(),
1397 radius: 0.5,
1398 bias: 0.025,
1399 _pad: [0.0; 2],
1400 };
1401 queue.write_buffer(
1402 &hdr.ssao_uniform_buf,
1403 0,
1404 bytemuck::cast_slice(&[ssao_uniform]),
1405 );
1406 }
1407
1408 if pp.contact_shadows {
1410 let proj = frame.camera.render_camera.projection;
1411 let inv_proj = proj.inverse();
1412 let light_dir_world: glam::Vec3 =
1413 if let Some(l) = frame.effects.lighting.lights.first() {
1414 match l.kind {
1415 LightKind::Directional { direction } => {
1416 glam::Vec3::from(direction).normalize()
1417 }
1418 LightKind::Spot { direction, .. } => {
1419 glam::Vec3::from(direction).normalize()
1420 }
1421 _ => glam::Vec3::new(0.0, -1.0, 0.0),
1422 }
1423 } else {
1424 glam::Vec3::new(0.0, -1.0, 0.0)
1425 };
1426 let view = frame.camera.render_camera.view;
1427 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
1428 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
1429 let cs_uniform = crate::resources::ContactShadowUniform {
1430 inv_proj: inv_proj.to_cols_array_2d(),
1431 proj: proj.to_cols_array_2d(),
1432 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
1433 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
1434 params: [
1435 pp.contact_shadow_max_distance,
1436 pp.contact_shadow_steps as f32,
1437 pp.contact_shadow_thickness,
1438 0.0,
1439 ],
1440 };
1441 queue.write_buffer(
1442 &hdr.contact_shadow_uniform_buf,
1443 0,
1444 bytemuck::cast_slice(&[cs_uniform]),
1445 );
1446 }
1447
1448 if pp.bloom {
1450 let bloom_u = crate::resources::BloomUniform {
1451 threshold: pp.bloom_threshold,
1452 intensity: pp.bloom_intensity,
1453 horizontal: 0,
1454 _pad: 0,
1455 };
1456 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
1457 }
1458 }
1459
1460 if pp.dof_enabled {
1462 let (w, h) = {
1463 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1464 (hdr.scene_size[0] as f32, hdr.scene_size[1] as f32)
1465 };
1466 let dof_uniform = crate::resources::DofUniform {
1467 focal_distance: pp.dof_focal_distance,
1468 focal_range: pp.dof_focal_range,
1469 max_blur_radius: pp.dof_max_blur_radius,
1470 near_plane: frame.camera.render_camera.near,
1471 far_plane: frame.camera.render_camera.far,
1472 viewport_width: w,
1473 viewport_height: h,
1474 _pad: 0.0,
1475 };
1476 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1477 queue.write_buffer(
1478 &hdr.dof_uniform_buf,
1479 0,
1480 bytemuck::cast_slice(&[dof_uniform]),
1481 );
1482 }
1483
1484 {
1486 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1487 self.resources.rebuild_tone_map_bind_group(
1488 device,
1489 hdr,
1490 pp.bloom,
1491 pp.ssao,
1492 pp.contact_shadows,
1493 scene_items
1494 .iter()
1495 .any(|i| i.lic.is_some() && !i.settings.hidden),
1496 pp.dof_enabled,
1497 );
1498 }
1499
1500 {
1505 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
1506 self.instanced_batches.iter().any(|b| b.is_transparent)
1507 } else {
1508 scene_items
1509 .iter()
1510 .any(|i| !i.settings.hidden && i.settings.opacity < 1.0)
1511 } || frame
1512 .scene
1513 .transparent_volume_meshes
1514 .iter()
1515 .any(|i| !i.settings.hidden);
1516 if needs_oit {
1517 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1518 let [sw, sh] = hdr.scene_size;
1519 self.resources.ensure_viewport_oit(device, hdr, sw, sh);
1520 }
1521 }
1522
1523 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1527 label: Some("hdr_encoder"),
1528 });
1529
1530 let slot = &self.viewport_slots[vp_idx];
1532 let camera_bg = &slot.camera_bind_group;
1533 let slot_hdr = slot.hdr.as_ref().expect(
1534 "HDR state missing; ensure_viewport_hdr must be called before render_frame_internal",
1535 );
1536
1537 {
1541 let use_ssaa = ssaa_factor > 1
1543 && slot_hdr.ssaa_colour_view.is_some()
1544 && slot_hdr.ssaa_depth_view.is_some();
1545 let scene_colour_view = if use_ssaa {
1546 slot_hdr.ssaa_colour_view.as_ref().unwrap()
1547 } else {
1548 &slot_hdr.hdr_view
1549 };
1550 let scene_depth_view = if use_ssaa {
1551 slot_hdr.ssaa_depth_view.as_ref().unwrap()
1552 } else {
1553 &slot_hdr.hdr_depth_view
1554 };
1555
1556 let clear_wgpu = wgpu::Color {
1557 r: hdr_clear_rgb[0] as f64,
1558 g: hdr_clear_rgb[1] as f64,
1559 b: hdr_clear_rgb[2] as f64,
1560 a: 0.0,
1563 };
1564
1565 let hdr_ts_writes =
1566 self.ts_query_set
1567 .as_ref()
1568 .map(|qs| wgpu::RenderPassTimestampWrites {
1569 query_set: qs,
1570 beginning_of_pass_write_index: Some(0),
1571 end_of_pass_write_index: Some(1),
1572 });
1573 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1574 label: Some("hdr_scene_pass"),
1575 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1576 view: scene_colour_view,
1577 resolve_target: None,
1578 ops: wgpu::Operations {
1579 load: wgpu::LoadOp::Clear(clear_wgpu),
1580 store: wgpu::StoreOp::Store,
1581 },
1582 depth_slice: None,
1583 })],
1584 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1585 view: scene_depth_view,
1586 depth_ops: Some(wgpu::Operations {
1587 load: wgpu::LoadOp::Clear(1.0),
1588 store: wgpu::StoreOp::Store,
1589 }),
1590 stencil_ops: Some(wgpu::Operations {
1591 load: wgpu::LoadOp::Clear(1),
1592 store: wgpu::StoreOp::Store,
1593 }),
1594 }),
1595 timestamp_writes: hdr_ts_writes,
1596 occlusion_query_set: None,
1597 });
1598
1599 let resources = &self.resources;
1600 render_pass.set_bind_group(0, camera_bg, &[]);
1601
1602 let show_skybox = frame
1604 .effects
1605 .environment
1606 .as_ref()
1607 .is_some_and(|e| e.show_skybox)
1608 && resources.ibl_skybox_view.is_some();
1609
1610 let use_instancing = self.use_instancing;
1611 let batches = &self.instanced_batches;
1612 let compute_filter_results = &self.compute_filter_results;
1613
1614 if !scene_items.is_empty() {
1615 if use_instancing && !batches.is_empty() {
1616 let excluded_items: Vec<(usize, &SceneRenderItem)> = scene_items
1617 .iter()
1618 .enumerate()
1619 .filter(|(_, item)| {
1620 !item.settings.hidden
1621 && (item.active_attribute.is_some()
1622 || item.material.is_two_sided()
1623 || item.material.matcap_id().is_some()
1624 || item.material.param_vis.is_some())
1625 && resources.mesh_store.get(item.mesh_id).is_some()
1626 })
1627 .collect();
1628
1629 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1633 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1634 for (batch_global_idx, batch) in batches.iter().enumerate() {
1635 if batch.is_transparent {
1636 transparent_batches.push((batch_global_idx, batch));
1637 } else {
1638 opaque_batches.push((batch_global_idx, batch));
1639 }
1640 }
1641
1642 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
1643 let use_indirect = self.gpu_culling_enabled
1644 && resources.hdr_solid_instanced_cull_pipeline.is_some()
1645 && resources.indirect_args_buf.is_some();
1646
1647 if use_indirect {
1648 if let (Some(pipeline), Some(indirect_buf)) = (
1649 &resources.hdr_solid_instanced_cull_pipeline,
1650 &resources.indirect_args_buf,
1651 ) {
1652 render_pass.set_pipeline(pipeline);
1653 for (batch_global_idx, batch) in &opaque_batches {
1654 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1655 continue;
1656 };
1657 let mat_key = (
1658 batch.texture_id.unwrap_or(u64::MAX),
1659 batch.normal_map_id.unwrap_or(u64::MAX),
1660 batch.ao_map_id.unwrap_or(u64::MAX),
1661 );
1662 let Some(inst_tex_bg) =
1663 resources.instance_cull_bind_groups.get(&mat_key)
1664 else {
1665 continue;
1666 };
1667 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1668 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1669 render_pass.set_index_buffer(
1670 mesh.index_buffer.slice(..),
1671 wgpu::IndexFormat::Uint32,
1672 );
1673 render_pass.draw_indexed_indirect(
1676 indirect_buf,
1677 *batch_global_idx as u64 * 20,
1678 );
1679 }
1680 }
1681 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
1682 render_pass.set_pipeline(pipeline);
1683 for (_, batch) in &opaque_batches {
1684 let Some(mesh) = resources.mesh_store.get(batch.mesh_id) else {
1685 continue;
1686 };
1687 let mat_key = (
1688 batch.texture_id.unwrap_or(u64::MAX),
1689 batch.normal_map_id.unwrap_or(u64::MAX),
1690 batch.ao_map_id.unwrap_or(u64::MAX),
1691 );
1692 let Some(inst_tex_bg) =
1693 resources.instance_bind_groups.get(&mat_key)
1694 else {
1695 continue;
1696 };
1697 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1698 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1699 render_pass.set_index_buffer(
1700 mesh.index_buffer.slice(..),
1701 wgpu::IndexFormat::Uint32,
1702 );
1703 render_pass.draw_indexed(
1704 0..mesh.index_count,
1705 0,
1706 batch.instance_offset
1707 ..batch.instance_offset + batch.instance_count,
1708 );
1709 }
1710 }
1711 }
1712
1713 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
1718 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
1719 let mut wf_idx = 0usize;
1720 for item in scene_items {
1721 if item.settings.hidden {
1722 continue;
1723 }
1724 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1725 continue;
1726 };
1727 let skin_bg = item.skin_instance.and_then(|inst| {
1728 resources.skin_instance_bind_group(item.mesh_id, inst)
1729 });
1730 if let (Some(bg), Some(hdr_skinned_wf)) =
1731 (skin_bg, resources.hdr_skinned_wireframe_pipeline.as_ref())
1732 {
1733 render_pass.set_pipeline(hdr_skinned_wf);
1734 render_pass.set_bind_group(2, bg, &[]);
1735 } else {
1736 render_pass.set_pipeline(hdr_wf);
1737 }
1738 let bg = self
1739 .wireframe_bind_groups
1740 .get(wf_idx)
1741 .unwrap_or(&mesh.object_bind_group);
1742 render_pass.set_bind_group(1, bg, &[]);
1743 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1744 render_pass.set_index_buffer(
1745 mesh.edge_index_buffer.slice(..),
1746 wgpu::IndexFormat::Uint32,
1747 );
1748 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1749 wf_idx += 1;
1750 }
1751 }
1752 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
1753 &resources.hdr_solid_pipeline,
1754 &resources.hdr_solid_two_sided_pipeline,
1755 ) {
1756 for (item_idx, item) in excluded_items.iter().copied().filter(|(_, item)| {
1762 item.settings.opacity >= 1.0 && !item.material.is_blend()
1763 }) {
1764 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1765 continue;
1766 };
1767 let skin_bg = item.skin_instance.and_then(|inst| {
1768 resources.skin_instance_bind_group(item.mesh_id, inst)
1769 });
1770 if let (Some(bg), Some(hdr_skinned_pl)) = (
1771 skin_bg,
1772 if item.material.is_two_sided() {
1773 resources.hdr_skinned_solid_two_sided_pipeline.as_ref()
1774 } else {
1775 resources.hdr_skinned_solid_pipeline.as_ref()
1776 },
1777 ) {
1778 render_pass.set_pipeline(hdr_skinned_pl);
1779 render_pass.set_bind_group(2, bg, &[]);
1780 } else {
1781 let pipeline = if item.material.is_two_sided() {
1782 hdr_solid_two_sided
1783 } else {
1784 hdr_solid
1785 };
1786 render_pass.set_pipeline(pipeline);
1787 }
1788 let obj_bg = self
1789 .per_item_object_bind_groups
1790 .get(item_idx)
1791 .and_then(|opt| opt.as_ref())
1792 .unwrap_or(&mesh.object_bind_group);
1793 render_pass.set_bind_group(1, obj_bg, &[]);
1794 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1795 let filter = compute_filter_results
1796 .iter()
1797 .find(|r| r.mesh_id == item.mesh_id);
1798 if let Some(fr) = filter {
1799 render_pass.set_index_buffer(
1800 fr.index_buffer.slice(..),
1801 wgpu::IndexFormat::Uint32,
1802 );
1803 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1804 } else {
1805 render_pass.set_index_buffer(
1806 mesh.index_buffer.slice(..),
1807 wgpu::IndexFormat::Uint32,
1808 );
1809 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1810 }
1811 }
1812 }
1813
1814 if let Some(hdr_wf) = &resources.hdr_wireframe_pipeline {
1818 for item in scene_items
1819 .iter()
1820 .filter(|i| i.show_normals && !i.settings.hidden)
1821 {
1822 let Some(mesh) = resources.mesh_store.get(item.mesh_id) else {
1823 continue;
1824 };
1825 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1826 if mesh.normal_line_count > 0 {
1827 render_pass.set_pipeline(hdr_wf);
1828 render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
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 } else {
1836 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
1838 let dist_from_eye = |entry: &(usize, &SceneRenderItem)| -> f32 {
1839 let item = entry.1;
1840 let pos =
1841 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
1842 (pos - eye).length()
1843 };
1844
1845 let mut opaque: Vec<(usize, &SceneRenderItem)> = Vec::new();
1846 let mut transparent: Vec<(usize, &SceneRenderItem)> = Vec::new();
1847 for (idx, item) in scene_items.iter().enumerate() {
1848 if item.settings.hidden || resources.mesh_store.get(item.mesh_id).is_none()
1849 {
1850 continue;
1851 }
1852 if item.settings.opacity < 1.0 || item.material.is_blend() {
1853 transparent.push((idx, item));
1854 } else {
1855 opaque.push((idx, item));
1856 }
1857 }
1858 opaque.sort_by(|a, b| {
1859 dist_from_eye(a)
1860 .partial_cmp(&dist_from_eye(b))
1861 .unwrap_or(std::cmp::Ordering::Equal)
1862 });
1863 transparent.sort_by(|a, b| {
1864 dist_from_eye(b)
1865 .partial_cmp(&dist_from_eye(a))
1866 .unwrap_or(std::cmp::Ordering::Equal)
1867 });
1868
1869 let per_item_bgs = &self.per_item_object_bind_groups;
1870 let draw_item_hdr =
1871 |render_pass: &mut wgpu::RenderPass<'_>,
1872 item_idx: usize,
1873 item: &SceneRenderItem,
1874 solid_pl: &wgpu::RenderPipeline,
1875 trans_pl: &wgpu::RenderPipeline,
1876 wf_pl: &wgpu::RenderPipeline| {
1877 let mesh = resources.mesh_store.get(item.mesh_id).unwrap();
1878 let obj_bg = per_item_bgs
1879 .get(item_idx)
1880 .and_then(|opt| opt.as_ref())
1881 .unwrap_or(&mesh.object_bind_group);
1882 render_pass.set_bind_group(1, obj_bg, &[]);
1883
1884 let skin_bg = item.skin_instance.and_then(|inst| {
1889 resources.skin_instance_bind_group(item.mesh_id, inst)
1890 });
1891 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1892 matches!(
1893 a.kind,
1894 crate::resources::AttributeKind::Face
1895 | crate::resources::AttributeKind::FaceColour
1896 | crate::resources::AttributeKind::Halfedge
1897 | crate::resources::AttributeKind::Corner
1898 )
1899 });
1900 if frame.viewport.wireframe_mode {
1901 if let (Some(bg), Some(hdr_skinned_wf)) =
1902 (skin_bg, resources.hdr_skinned_wireframe_pipeline.as_ref())
1903 {
1904 render_pass.set_pipeline(hdr_skinned_wf);
1905 render_pass.set_bind_group(2, bg, &[]);
1906 } else {
1907 render_pass.set_pipeline(wf_pl);
1908 }
1909 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1910 render_pass.set_index_buffer(
1911 mesh.edge_index_buffer.slice(..),
1912 wgpu::IndexFormat::Uint32,
1913 );
1914 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1915 } else if is_face_attr {
1916 if let Some(ref fvb) = mesh.face_vertex_buffer {
1917 let pl = if item.settings.opacity < 1.0 {
1918 trans_pl
1919 } else {
1920 solid_pl
1921 };
1922 render_pass.set_pipeline(pl);
1923 render_pass.set_vertex_buffer(0, fvb.slice(..));
1924 render_pass.draw(0..mesh.index_count, 0..1);
1925 }
1926 } else {
1927 let filter = compute_filter_results
1928 .iter()
1929 .find(|r| r.mesh_id == item.mesh_id);
1930 let pl = if item.settings.opacity < 1.0 {
1931 trans_pl
1932 } else {
1933 solid_pl
1934 };
1935 let is_blended =
1936 item.settings.opacity < 1.0 || item.material.is_blend();
1937 let hdr_skinned_pl = if is_blended {
1938 resources.hdr_skinned_transparent_pipeline.as_ref()
1939 } else if item.material.is_two_sided() {
1940 resources.hdr_skinned_solid_two_sided_pipeline.as_ref()
1941 } else {
1942 resources.hdr_skinned_solid_pipeline.as_ref()
1943 };
1944 if let (Some(bg), Some(hdr_skinned)) = (skin_bg, hdr_skinned_pl) {
1945 render_pass.set_pipeline(hdr_skinned);
1946 render_pass.set_bind_group(2, bg, &[]);
1947 } else {
1948 render_pass.set_pipeline(pl);
1949 }
1950 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1951 if let Some(fr) = filter {
1952 render_pass.set_index_buffer(
1953 fr.index_buffer.slice(..),
1954 wgpu::IndexFormat::Uint32,
1955 );
1956 render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
1957 } else {
1958 render_pass.set_index_buffer(
1959 mesh.index_buffer.slice(..),
1960 wgpu::IndexFormat::Uint32,
1961 );
1962 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1963 }
1964 }
1965 if item.show_normals {
1966 if let Some(ref nl_buf) = mesh.normal_line_buffer {
1967 if mesh.normal_line_count > 0 {
1968 render_pass.set_pipeline(wf_pl);
1969 render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
1970 render_pass.set_vertex_buffer(0, nl_buf.slice(..));
1971 render_pass.draw(0..mesh.normal_line_count, 0..1);
1972 }
1973 }
1974 }
1975 };
1976
1977 let _ = &transparent; if let (
1981 Some(hdr_solid),
1982 Some(hdr_solid_two_sided),
1983 Some(hdr_trans),
1984 Some(hdr_wf),
1985 ) = (
1986 &resources.hdr_solid_pipeline,
1987 &resources.hdr_solid_two_sided_pipeline,
1988 &resources.hdr_transparent_pipeline,
1989 &resources.hdr_wireframe_pipeline,
1990 ) {
1991 for (item_idx, item) in &opaque {
1992 let solid_pl = if item.material.is_two_sided() {
1993 hdr_solid_two_sided
1994 } else {
1995 hdr_solid
1996 };
1997 draw_item_hdr(&mut render_pass, *item_idx, item, solid_pl, hdr_trans, hdr_wf);
1998 }
1999 }
2000 }
2001 }
2002
2003 if !slot.cap_buffers.is_empty() {
2005 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
2006 render_pass.set_pipeline(hdr_overlay);
2007 render_pass.set_bind_group(0, camera_bg, &[]);
2008 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
2009 render_pass.set_bind_group(1, bg, &[]);
2010 render_pass.set_vertex_buffer(0, vbuf.slice(..));
2011 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2012 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
2013 }
2014 }
2015 }
2016
2017 let sprite_slice_for_inline: &[crate::resources::SpriteGpuData] = &[];
2025 emit_scivis_draw_calls!(
2026 &self.resources,
2027 &mut render_pass,
2028 &self.point_cloud_gpu_data,
2029 &self.glyph_gpu_data,
2030 &self.polyline_gpu_data,
2031 &self.volume_gpu_data,
2032 &self.streamtube_gpu_data,
2033 camera_bg,
2034 &self.tube_gpu_data,
2035 &self.image_slice_gpu_data,
2036 &self.tensor_glyph_gpu_data,
2037 &self.ribbon_gpu_data,
2038 &self.volume_surface_slice_gpu_data,
2039 sprite_slice_for_inline,
2040 &self.mesh_instance_gpu_data,
2041 true
2042 );
2043
2044 if !self.implicit_gpu_data.is_empty() {
2046 if let Some(ref dual) = self.resources.implicit_pipeline {
2047 render_pass.set_pipeline(dual.for_format(true));
2048 render_pass.set_bind_group(0, camera_bg, &[]);
2049 for gpu in &self.implicit_gpu_data {
2050 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
2051 render_pass.draw(0..6, 0..1);
2052 }
2053 }
2054 }
2055 if !self.mc_gpu_data.is_empty() {
2057 render_pass.set_bind_group(0, camera_bg, &[]);
2058 for mc in &self.mc_gpu_data {
2059 let vol = &self.resources.mc_volumes[mc.volume_idx];
2060 if mc.wireframe || frame.viewport.wireframe_mode {
2061 if let Some(ref dual) = self.resources.mc_wireframe_pipeline {
2062 render_pass.set_pipeline(dual.for_format(true));
2063 for (slab, wire_bg) in vol.slabs.iter().zip(mc.wire_slab_bgs.iter()) {
2064 render_pass.set_bind_group(1, wire_bg, &[]);
2065 render_pass.draw_indirect(&slab.wire_indirect_buf, 0);
2066 }
2067 }
2068 } else if let Some(ref dual) = self.resources.mc_surface_pipeline {
2069 render_pass.set_pipeline(dual.for_format(true));
2070 render_pass.set_bind_group(1, &mc.render_bg, &[]);
2071 for slab in &vol.slabs {
2072 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
2073 render_pass.draw_indirect(&slab.indirect_buf, 0);
2074 }
2075 }
2076 }
2077 }
2078
2079 if !self.gaussian_splat_draw_data.is_empty() {
2081 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
2082 render_pass.set_pipeline(dual.for_format(true));
2083 render_pass.set_bind_group(0, camera_bg, &[]);
2084 for dd in &self.gaussian_splat_draw_data {
2085 if dd.wireframe {
2086 continue;
2087 }
2088 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
2089 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
2090 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
2091 render_pass.draw(0..6, 0..dd.count);
2092 }
2093 }
2094 }
2095 }
2096 }
2097 if !self.tvm_wireframe_draws.is_empty() {
2099 if let (Some(tvm_bg), Some(hdr_wf)) =
2100 (&self.tvm_wireframe_bg, &resources.hdr_wireframe_pipeline)
2101 {
2102 for mesh_id in &self.tvm_wireframe_draws {
2103 if let Some(mesh) = resources.mesh_store.get(*mesh_id) {
2104 render_pass.set_pipeline(hdr_wf);
2105 render_pass.set_bind_group(1, tvm_bg, &[]);
2106 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2107 render_pass.set_index_buffer(
2108 mesh.edge_index_buffer.slice(..),
2109 wgpu::IndexFormat::Uint32,
2110 );
2111 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
2112 }
2113 }
2114 }
2115 }
2116
2117 self.dispatch_plugin_paint(&mut render_pass, frame);
2120
2121 if show_skybox {
2123 render_pass.set_bind_group(0, camera_bg, &[]);
2124 render_pass.set_pipeline(&resources.skybox_pipeline);
2125 render_pass.draw(0..3, 0..1);
2126 }
2127 }
2128
2129 if !self.sprite_gpu_data.is_empty() {
2146 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2147 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2148 let resources = &self.resources;
2149
2150 let use_ssaa = ssaa_factor > 1
2151 && slot_hdr.ssaa_colour_view.is_some()
2152 && slot_hdr.ssaa_depth_view.is_some()
2153 && slot_hdr.ssaa_depth_only_view.is_some();
2154 let colour_view = if use_ssaa {
2155 slot_hdr.ssaa_colour_view.as_ref().unwrap()
2156 } else {
2157 &slot_hdr.hdr_view
2158 };
2159 let depth_view = if use_ssaa {
2160 slot_hdr.ssaa_depth_view.as_ref().unwrap()
2161 } else {
2162 &slot_hdr.hdr_depth_view
2163 };
2164 let depth_only_view = if use_ssaa {
2165 slot_hdr.ssaa_depth_only_view.as_ref().unwrap()
2166 } else {
2167 &slot_hdr.hdr_depth_only_view
2168 };
2169
2170 let any_depth_write = self.sprite_gpu_data.iter().any(|s| s.depth_write);
2171 let any_transparent = self.sprite_gpu_data.iter().any(|s| !s.depth_write);
2172
2173 let buckets = [
2174 (
2175 true,
2176 crate::renderer::SpriteBlend::AlphaBlend,
2177 resources.sprite_pipeline_depth_write.as_ref(),
2178 ),
2179 (
2180 true,
2181 crate::renderer::SpriteBlend::Additive,
2182 resources.sprite_pipeline_additive_depth_write.as_ref(),
2183 ),
2184 (
2185 true,
2186 crate::renderer::SpriteBlend::Premultiplied,
2187 resources.sprite_pipeline_premultiplied_depth_write.as_ref(),
2188 ),
2189 (
2190 false,
2191 crate::renderer::SpriteBlend::AlphaBlend,
2192 resources.sprite_pipeline.as_ref(),
2193 ),
2194 (
2195 false,
2196 crate::renderer::SpriteBlend::Additive,
2197 resources.sprite_pipeline_additive.as_ref(),
2198 ),
2199 (
2200 false,
2201 crate::renderer::SpriteBlend::Premultiplied,
2202 resources.sprite_pipeline_premultiplied.as_ref(),
2203 ),
2204 ];
2205
2206 let fallback_soft_bg = resources.sprite_soft_fallback_bg.as_ref();
2207
2208 if any_depth_write {
2212 if let Some(fallback_soft_bg) = fallback_soft_bg {
2213 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2214 label: Some("sprite_depth_write_pass"),
2215 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2216 view: colour_view,
2217 resolve_target: None,
2218 ops: wgpu::Operations {
2219 load: wgpu::LoadOp::Load,
2220 store: wgpu::StoreOp::Store,
2221 },
2222 depth_slice: None,
2223 })],
2224 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2225 view: depth_view,
2226 depth_ops: Some(wgpu::Operations {
2227 load: wgpu::LoadOp::Load,
2228 store: wgpu::StoreOp::Store,
2229 }),
2230 stencil_ops: Some(wgpu::Operations {
2231 load: wgpu::LoadOp::Load,
2232 store: wgpu::StoreOp::Store,
2233 }),
2234 }),
2235 timestamp_writes: None,
2236 occlusion_query_set: None,
2237 });
2238 for (depth_write, blend, pipeline) in &buckets {
2239 if !*depth_write {
2240 continue;
2241 }
2242 let Some(dual) = pipeline else { continue };
2243 let mut bound = false;
2244 for sprite in self.sprite_gpu_data.iter() {
2245 if sprite.wireframe
2246 || !sprite.depth_write
2247 || sprite.blend != *blend
2248 {
2249 continue;
2250 }
2251 if !bound {
2252 pass.set_pipeline(dual.for_format(true));
2253 pass.set_bind_group(0, camera_bg, &[]);
2254 pass.set_bind_group(2, fallback_soft_bg, &[]);
2255 bound = true;
2256 }
2257 pass.set_bind_group(1, &sprite.bind_group, &[]);
2258 pass.set_vertex_buffer(0, sprite.vertex_buffer.slice(..));
2259 pass.draw(0..6, 0..sprite.sprite_count);
2260 }
2261 }
2262 }
2263 }
2264
2265 if any_transparent {
2268 let real_soft_bg = if let (Some(bgl), Some(sampler)) = (
2269 resources.sprite_soft_bgl.as_ref(),
2270 resources.sprite_soft_sampler.as_ref(),
2271 ) {
2272 Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
2273 label: Some("sprite_soft_bg"),
2274 layout: bgl,
2275 entries: &[
2276 wgpu::BindGroupEntry {
2277 binding: 0,
2278 resource: wgpu::BindingResource::TextureView(depth_only_view),
2279 },
2280 wgpu::BindGroupEntry {
2281 binding: 1,
2282 resource: wgpu::BindingResource::Sampler(sampler),
2283 },
2284 ],
2285 }))
2286 } else {
2287 None
2288 };
2289
2290 if let Some(real_soft_bg) = real_soft_bg.as_ref() {
2291 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2292 label: Some("sprite_transparent_pass"),
2293 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2294 view: colour_view,
2295 resolve_target: None,
2296 ops: wgpu::Operations {
2297 load: wgpu::LoadOp::Load,
2298 store: wgpu::StoreOp::Store,
2299 },
2300 depth_slice: None,
2301 })],
2302 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2303 view: depth_view,
2304 depth_ops: None,
2305 stencil_ops: None,
2306 }),
2307 timestamp_writes: None,
2308 occlusion_query_set: None,
2309 });
2310 for (depth_write, blend, pipeline) in &buckets {
2311 if *depth_write {
2312 continue;
2313 }
2314 let Some(dual) = pipeline else { continue };
2315 let mut bound = false;
2316 for sprite in self.sprite_gpu_data.iter() {
2317 if sprite.wireframe
2318 || sprite.depth_write
2319 || sprite.blend != *blend
2320 {
2321 continue;
2322 }
2323 if !bound {
2324 pass.set_pipeline(dual.for_format(true));
2325 pass.set_bind_group(0, camera_bg, &[]);
2326 pass.set_bind_group(2, real_soft_bg, &[]);
2327 bound = true;
2328 }
2329 pass.set_bind_group(1, &sprite.bind_group, &[]);
2330 pass.set_vertex_buffer(0, sprite.vertex_buffer.slice(..));
2331 pass.draw(0..6, 0..sprite.sprite_count);
2332 }
2333 }
2334 }
2335 }
2336 }
2337
2338 if ssaa_factor > 1 {
2343 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2344 if let (Some(pipeline), Some(bg)) = (
2345 &self.resources.ssaa_resolve_pipeline,
2346 &slot_hdr.ssaa_resolve_bind_group,
2347 ) {
2348 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2349 label: Some("ssaa_resolve_pass"),
2350 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2351 view: &slot_hdr.hdr_view,
2352 resolve_target: None,
2353 ops: wgpu::Operations {
2354 load: wgpu::LoadOp::Load,
2355 store: wgpu::StoreOp::Store,
2356 },
2357 depth_slice: None,
2358 })],
2359 depth_stencil_attachment: None,
2360 timestamp_writes: None,
2361 occlusion_query_set: None,
2362 });
2363 resolve_pass.set_pipeline(pipeline);
2364 resolve_pass.set_bind_group(0, bg, &[]);
2365 resolve_pass.draw(0..3, 0..1);
2366 }
2367 }
2368
2369 if !self.decal_exclude_items.is_empty() {
2374 if let Some(exclude_pl) = self.resources.decal_exclude_pipeline.as_ref() {
2375 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2376 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2377 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2378 label: Some("decal_exclude_pass"),
2379 color_attachments: &[],
2380 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2381 view: &slot_hdr.hdr_depth_view,
2382 depth_ops: Some(wgpu::Operations {
2383 load: wgpu::LoadOp::Load,
2384 store: wgpu::StoreOp::Store,
2385 }),
2386 stencil_ops: Some(wgpu::Operations {
2387 load: wgpu::LoadOp::Load,
2388 store: wgpu::StoreOp::Store,
2389 }),
2390 }),
2391 timestamp_writes: None,
2392 occlusion_query_set: None,
2393 });
2394 pass.set_pipeline(exclude_pl);
2395 pass.set_stencil_reference(0);
2396 pass.set_bind_group(0, camera_bg, &[]);
2397 for item in &self.decal_exclude_items {
2398 if let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) {
2399 pass.set_bind_group(1, &item.bind_group, &[]);
2400 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2401 if mesh.index_count > 0 {
2402 pass.set_index_buffer(
2403 mesh.index_buffer.slice(..),
2404 wgpu::IndexFormat::Uint32,
2405 );
2406 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2407 }
2408 }
2409 }
2410 }
2411 }
2412
2413 if !self.decal_gpu_data.is_empty() {
2419 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2420 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2421 let depth_bg = &slot_hdr.decal_depth_bg;
2422 let replace_pipeline = self.resources.decal_replace_pipeline.as_ref();
2423 let multiply_pipeline = self.resources.decal_multiply_pipeline.as_ref();
2424 let additive_pipeline = self.resources.decal_additive_pipeline.as_ref();
2425 if replace_pipeline.is_some()
2426 || multiply_pipeline.is_some()
2427 || additive_pipeline.is_some()
2428 {
2429 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2430 label: Some("decal_pass"),
2431 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2432 view: &slot_hdr.hdr_view,
2433 resolve_target: None,
2434 ops: wgpu::Operations {
2435 load: wgpu::LoadOp::Load,
2436 store: wgpu::StoreOp::Store,
2437 },
2438 depth_slice: None,
2439 })],
2440 depth_stencil_attachment: None,
2441 timestamp_writes: None,
2442 occlusion_query_set: None,
2443 });
2444 pass.set_bind_group(0, camera_bg, &[]);
2445 pass.set_bind_group(1, depth_bg, &[]);
2446 for gpu in &self.decal_gpu_data {
2447 let pipeline = match gpu.blend_mode {
2448 crate::renderer::DecalBlendMode::Replace => replace_pipeline,
2449 crate::renderer::DecalBlendMode::Multiply => multiply_pipeline,
2450 crate::renderer::DecalBlendMode::Additive => additive_pipeline,
2451 };
2452 if let Some(pl) = pipeline {
2453 pass.set_pipeline(&pl.hdr);
2454 pass.set_bind_group(2, &gpu.bind_group, &[]);
2455 pass.draw(0..6, 0..1);
2456 }
2457 }
2458 }
2459 }
2460
2461 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
2467 let resources = &self.resources;
2468 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
2469 &resources.sub_highlight_fill_pipeline,
2470 &resources.sub_highlight_edge_pipeline,
2471 &resources.sub_highlight_sprite_pipeline,
2472 ) {
2473 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2474 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
2475 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2476 label: Some("sub_highlight_pass"),
2477 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2478 view: &slot_hdr.hdr_view,
2479 resolve_target: None,
2480 ops: wgpu::Operations {
2481 load: wgpu::LoadOp::Load,
2482 store: wgpu::StoreOp::Store,
2483 },
2484 depth_slice: None,
2485 })],
2486 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2487 view: &slot_hdr.hdr_depth_view,
2488 depth_ops: Some(wgpu::Operations {
2489 load: wgpu::LoadOp::Load,
2490 store: wgpu::StoreOp::Store,
2496 }),
2497 stencil_ops: None,
2498 }),
2499 timestamp_writes: None,
2500 occlusion_query_set: None,
2501 });
2502
2503 if sub_hl.fill_vertex_count > 0 {
2504 pass.set_pipeline(fill_pl);
2505 pass.set_bind_group(0, camera_bg, &[]);
2506 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
2507 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
2508 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
2509 }
2510 if sub_hl.edge_segment_count > 0 {
2511 pass.set_pipeline(edge_pl);
2512 pass.set_bind_group(0, camera_bg, &[]);
2513 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
2514 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
2515 pass.draw(0..6, 0..sub_hl.edge_segment_count);
2516 }
2517 if sub_hl.sprite_point_count > 0 {
2518 pass.set_pipeline(sprite_pl);
2519 pass.set_bind_group(0, camera_bg, &[]);
2520 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
2521 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
2522 pass.draw(0..6, 0..sub_hl.sprite_point_count);
2523 }
2524 }
2525 }
2526
2527 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
2532 self.instanced_batches.iter().any(|b| b.is_transparent)
2537 || scene_items.iter().any(|i| {
2538 !i.settings.hidden
2539 && (i.settings.opacity < 1.0 || i.material.is_blend())
2540 && (i.active_attribute.is_some()
2541 || i.material.is_two_sided()
2542 || i.material.matcap_id().is_some()
2543 || i.material.param_vis.is_some())
2544 })
2545 } else {
2546 scene_items
2547 .iter()
2548 .any(|i| !i.settings.hidden && (i.settings.opacity < 1.0 || i.material.is_blend()))
2549 } || frame
2550 .scene
2551 .transparent_volume_meshes
2552 .iter()
2553 .any(|i| !i.settings.hidden);
2554
2555 if has_transparent {
2556 if let (Some(accum_view), Some(reveal_view)) = (
2558 slot_hdr.oit_accum_view.as_ref(),
2559 slot_hdr.oit_reveal_view.as_ref(),
2560 ) {
2561 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2562 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2564 label: Some("oit_pass"),
2565 color_attachments: &[
2566 Some(wgpu::RenderPassColorAttachment {
2567 view: accum_view,
2568 resolve_target: None,
2569 ops: wgpu::Operations {
2570 load: wgpu::LoadOp::Clear(wgpu::Color {
2571 r: 0.0,
2572 g: 0.0,
2573 b: 0.0,
2574 a: 0.0,
2575 }),
2576 store: wgpu::StoreOp::Store,
2577 },
2578 depth_slice: None,
2579 }),
2580 Some(wgpu::RenderPassColorAttachment {
2581 view: reveal_view,
2582 resolve_target: None,
2583 ops: wgpu::Operations {
2584 load: wgpu::LoadOp::Clear(wgpu::Color {
2585 r: 1.0,
2586 g: 1.0,
2587 b: 1.0,
2588 a: 1.0,
2589 }),
2590 store: wgpu::StoreOp::Store,
2591 },
2592 depth_slice: None,
2593 }),
2594 ],
2595 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2596 view: hdr_depth_view,
2597 depth_ops: Some(wgpu::Operations {
2598 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
2600 }),
2601 stencil_ops: None,
2602 }),
2603 timestamp_writes: None,
2604 occlusion_query_set: None,
2605 });
2606
2607 oit_pass.set_bind_group(0, camera_bg, &[]);
2608
2609 if self.use_instancing && !self.instanced_batches.is_empty() {
2610 let use_indirect_oit = self.gpu_culling_enabled
2611 && self.resources.oit_instanced_cull_pipeline.is_some()
2612 && self.resources.indirect_args_buf.is_some();
2613
2614 if use_indirect_oit {
2615 if let (Some(pipeline), Some(indirect_buf)) = (
2616 &self.resources.oit_instanced_cull_pipeline,
2617 &self.resources.indirect_args_buf,
2618 ) {
2619 oit_pass.set_pipeline(pipeline);
2620 for (batch_global_idx, batch) in
2621 self.instanced_batches.iter().enumerate()
2622 {
2623 if !batch.is_transparent {
2624 continue;
2625 }
2626 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id)
2627 else {
2628 continue;
2629 };
2630 let mat_key = (
2631 batch.texture_id.unwrap_or(u64::MAX),
2632 batch.normal_map_id.unwrap_or(u64::MAX),
2633 batch.ao_map_id.unwrap_or(u64::MAX),
2634 );
2635 let Some(inst_tex_bg) =
2636 self.resources.instance_cull_bind_groups.get(&mat_key)
2637 else {
2638 continue;
2639 };
2640 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
2641 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2642 oit_pass.set_index_buffer(
2643 mesh.index_buffer.slice(..),
2644 wgpu::IndexFormat::Uint32,
2645 );
2646 oit_pass.draw_indexed_indirect(
2647 indirect_buf,
2648 batch_global_idx as u64 * 20,
2649 );
2650 }
2651 }
2652 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
2653 oit_pass.set_pipeline(pipeline);
2654 for batch in &self.instanced_batches {
2655 if !batch.is_transparent {
2656 continue;
2657 }
2658 let Some(mesh) = self.resources.mesh_store.get(batch.mesh_id) else {
2659 continue;
2660 };
2661 let mat_key = (
2662 batch.texture_id.unwrap_or(u64::MAX),
2663 batch.normal_map_id.unwrap_or(u64::MAX),
2664 batch.ao_map_id.unwrap_or(u64::MAX),
2665 );
2666 let Some(inst_tex_bg) =
2667 self.resources.instance_bind_groups.get(&mat_key)
2668 else {
2669 continue;
2670 };
2671 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
2672 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2673 oit_pass.set_index_buffer(
2674 mesh.index_buffer.slice(..),
2675 wgpu::IndexFormat::Uint32,
2676 );
2677 oit_pass.draw_indexed(
2678 0..mesh.index_count,
2679 0,
2680 batch.instance_offset..batch.instance_offset + batch.instance_count,
2681 );
2682 }
2683 }
2684
2685 if let Some(ref pipeline) = self.resources.oit_pipeline {
2689 oit_pass.set_pipeline(pipeline);
2690 let mut last_skinned = false;
2691 for (item_idx, item) in scene_items.iter().enumerate() {
2692 if item.settings.hidden
2693 || (item.settings.opacity >= 1.0 && !item.material.is_blend())
2694 {
2695 continue;
2696 }
2697 let skin_bg = item.skin_instance.and_then(|inst| {
2698 self.resources.skin_instance_bind_group(item.mesh_id, inst)
2699 });
2700 if skin_bg.is_none()
2701 && item.active_attribute.is_none()
2702 && !item.material.is_two_sided()
2703 && item.material.matcap_id().is_none()
2704 {
2705 continue;
2706 }
2707 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
2708 continue;
2709 };
2710 let want_skinned =
2711 skin_bg.is_some() && self.resources.skinned_oit_pipeline.is_some();
2712 if want_skinned != last_skinned {
2713 if want_skinned {
2714 oit_pass.set_pipeline(
2715 self.resources.skinned_oit_pipeline.as_ref().unwrap(),
2716 );
2717 } else {
2718 oit_pass.set_pipeline(pipeline);
2719 }
2720 last_skinned = want_skinned;
2721 }
2722 let obj_bg = self
2723 .per_item_object_bind_groups
2724 .get(item_idx)
2725 .and_then(|opt| opt.as_ref())
2726 .unwrap_or(&mesh.object_bind_group);
2727 oit_pass.set_bind_group(1, obj_bg, &[]);
2728 if let Some(bg) = skin_bg {
2729 oit_pass.set_bind_group(2, bg, &[]);
2730 }
2731 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2732 oit_pass.set_index_buffer(
2733 mesh.index_buffer.slice(..),
2734 wgpu::IndexFormat::Uint32,
2735 );
2736 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2737 }
2738 }
2739 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
2740 oit_pass.set_pipeline(pipeline);
2741 let mut last_skinned = false;
2742 for (item_idx, item) in scene_items.iter().enumerate() {
2743 if item.settings.hidden
2744 || (item.settings.opacity >= 1.0 && !item.material.is_blend())
2745 {
2746 continue;
2747 }
2748 let Some(mesh) = self.resources.mesh_store.get(item.mesh_id) else {
2749 continue;
2750 };
2751 let skin_bg = item.skin_instance.and_then(|inst| {
2752 self.resources.skin_instance_bind_group(item.mesh_id, inst)
2753 });
2754 let want_skinned =
2755 skin_bg.is_some() && self.resources.skinned_oit_pipeline.is_some();
2756 if want_skinned != last_skinned {
2757 if want_skinned {
2758 oit_pass.set_pipeline(
2759 self.resources.skinned_oit_pipeline.as_ref().unwrap(),
2760 );
2761 } else {
2762 oit_pass.set_pipeline(pipeline);
2763 }
2764 last_skinned = want_skinned;
2765 }
2766 let obj_bg = self
2767 .per_item_object_bind_groups
2768 .get(item_idx)
2769 .and_then(|opt| opt.as_ref())
2770 .unwrap_or(&mesh.object_bind_group);
2771 oit_pass.set_bind_group(1, obj_bg, &[]);
2772 if let Some(bg) = skin_bg {
2773 oit_pass.set_bind_group(2, bg, &[]);
2774 }
2775 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2776 oit_pass.set_index_buffer(
2777 mesh.index_buffer.slice(..),
2778 wgpu::IndexFormat::Uint32,
2779 );
2780 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2781 }
2782 }
2783
2784 if !frame.scene.transparent_volume_meshes.is_empty() {
2788 self.resources.ensure_pt_pipeline(device);
2789 if let Some(pipeline) = self.resources.pt_pipeline.as_ref() {
2790 oit_pass.set_pipeline(pipeline);
2791 oit_pass.set_bind_group(0, camera_bg, &[]);
2792 for item in &frame.scene.transparent_volume_meshes {
2793 if item.settings.hidden {
2794 continue;
2795 }
2796 if item.settings.wireframe || frame.viewport.wireframe_mode {
2797 continue;
2798 }
2799 let Some(gpu) = self.resources.projected_tet_store.get(item.id.0)
2800 else {
2801 continue;
2802 };
2803 let (scalar_min, scalar_max) =
2804 item.scalar_range.unwrap_or(gpu.scalar_range);
2805 let uniform = crate::resources::ProjectedTetUniform {
2806 density: item.density,
2807 scalar_min,
2808 scalar_max,
2809 threshold_min: item.threshold_min,
2810 threshold_max: item.threshold_max,
2811 unlit: if item.settings.unlit { 1 } else { 0 },
2812 };
2813 queue.write_buffer(
2814 &gpu.uniform_buffer,
2815 0,
2816 bytemuck::bytes_of(&uniform),
2817 );
2818 for chunk in &gpu.chunks {
2819 oit_pass.set_bind_group(1, &chunk.bind_group, &[]);
2820 oit_pass.draw(0..6, 0..chunk.tet_count);
2821 }
2822 }
2823 }
2824 }
2825
2826 self.dispatch_plugin_paint_transparent(&mut oit_pass, frame);
2828 }
2829 }
2830
2831 if has_transparent {
2836 if let (Some(pipeline), Some(bg)) = (
2837 self.resources.oit_composite_pipeline.as_ref(),
2838 slot_hdr.oit_composite_bind_group.as_ref(),
2839 ) {
2840 let hdr_view = &slot_hdr.hdr_view;
2841 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2842 label: Some("oit_composite_pass"),
2843 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2844 view: hdr_view,
2845 resolve_target: None,
2846 ops: wgpu::Operations {
2847 load: wgpu::LoadOp::Load,
2848 store: wgpu::StoreOp::Store,
2849 },
2850 depth_slice: None,
2851 })],
2852 depth_stencil_attachment: None,
2853 timestamp_writes: None,
2854 occlusion_query_set: None,
2855 });
2856 composite_pass.set_pipeline(pipeline);
2857 composite_pass.set_bind_group(0, bg, &[]);
2858 composite_pass.draw(0..3, 0..1);
2859 }
2860 }
2861
2862 if !self.prepared_scatter_volumes.is_empty() {
2870 let scatter = &frame.effects.scatter;
2871 let [sw, sh] = slot_hdr.scene_size;
2872 let want_downsample = scatter.downsample;
2873 let (tw, th) = if want_downsample {
2874 ((sw / 2).max(1), (sh / 2).max(1))
2875 } else {
2876 (sw.max(1), sh.max(1))
2877 };
2878
2879 while self.scatter_viewport_states.len() <= vp_idx {
2881 self.scatter_viewport_states.push(None);
2882 }
2883
2884 self.resources
2886 .ensure_scatter_pipeline(device, wgpu::TextureFormat::Rgba16Float);
2887 self.resources
2888 .ensure_scatter_composite_pipeline(device, wgpu::TextureFormat::Rgba16Float);
2889 self.resources
2890 .ensure_scatter_temporal_resolve_pipeline(device);
2891
2892 let needs_alloc = match self.scatter_viewport_states[vp_idx].as_ref() {
2894 None => true,
2895 Some(s) => s.size != [tw, th] || s.downsampled != want_downsample,
2896 };
2897 if needs_alloc {
2898 let make_tex = |label: &str| {
2899 device.create_texture(&wgpu::TextureDescriptor {
2900 label: Some(label),
2901 size: wgpu::Extent3d {
2902 width: tw,
2903 height: th,
2904 depth_or_array_layers: 1,
2905 },
2906 mip_level_count: 1,
2907 sample_count: 1,
2908 dimension: wgpu::TextureDimension::D2,
2909 format: wgpu::TextureFormat::Rgba16Float,
2910 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
2911 | wgpu::TextureUsages::TEXTURE_BINDING,
2912 view_formats: &[],
2913 })
2914 };
2915 let raw_tex = make_tex("scatter_raw_current");
2916 let hist_a_tex = make_tex("scatter_history_a");
2917 let hist_b_tex = make_tex("scatter_history_b");
2918 let raw_view = raw_tex.create_view(&wgpu::TextureViewDescriptor::default());
2919 let hist_a_view = hist_a_tex.create_view(&wgpu::TextureViewDescriptor::default());
2920 let hist_b_view = hist_b_tex.create_view(&wgpu::TextureViewDescriptor::default());
2921 let composite_bg_raw = self.resources.make_scatter_composite_bg(device, &raw_view);
2922 let composite_bg_history_a = self
2923 .resources
2924 .make_scatter_composite_bg(device, &hist_a_view);
2925 let composite_bg_history_b = self
2926 .resources
2927 .make_scatter_composite_bg(device, &hist_b_view);
2928 let temporal_resolve_bg_read_a = self.resources.make_scatter_temporal_resolve_bg(
2929 device,
2930 queue,
2931 &raw_view,
2932 &hist_a_view,
2933 &slot_hdr.hdr_depth_only_view,
2934 );
2935 let temporal_resolve_bg_read_b = self.resources.make_scatter_temporal_resolve_bg(
2936 device,
2937 queue,
2938 &raw_view,
2939 &hist_b_view,
2940 &slot_hdr.hdr_depth_only_view,
2941 );
2942 self.scatter_viewport_states[vp_idx] =
2943 Some(crate::resources::ScatterViewportState {
2944 raw_current_texture: raw_tex,
2945 raw_current_view: raw_view,
2946 history_a_texture: hist_a_tex,
2947 history_a_view: hist_a_view,
2948 history_b_texture: hist_b_tex,
2949 history_b_view: hist_b_view,
2950 composite_bg_raw,
2951 composite_bg_history_a,
2952 composite_bg_history_b,
2953 temporal_resolve_bg_read_a,
2954 temporal_resolve_bg_read_b,
2955 size: [tw, th],
2956 downsampled: want_downsample,
2957 parity: 0,
2958 history_valid: false,
2959 prev_view_proj: [[0.0; 4]; 4],
2960 refraction_source_texture: None,
2961 refraction_source_view: None,
2962 refraction_source_bg: None,
2963 refraction_blit_bg: None,
2964 refraction_source_size: [0, 0],
2965 });
2966 }
2967
2968 let (parity, history_valid, prev_view_proj) = {
2969 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
2970 (s.parity, s.history_valid, s.prev_view_proj)
2971 };
2972
2973 if !self.prepared_refraction_volumes.is_empty() {
2980 self.resources.ensure_scatter_refraction_pipeline(
2981 device,
2982 wgpu::TextureFormat::Rgba16Float,
2983 );
2984 self.resources.ensure_scatter_refraction_blit_pipeline(
2985 device,
2986 wgpu::TextureFormat::Rgba16Float,
2987 );
2988
2989 let need_realloc = {
2993 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
2994 s.refraction_source_view.is_none() || s.refraction_source_size != [sw, sh]
2995 };
2996 if need_realloc {
2997 let src_tex = device.create_texture(&wgpu::TextureDescriptor {
2998 label: Some("scatter_refraction_source"),
2999 size: wgpu::Extent3d {
3000 width: sw.max(1),
3001 height: sh.max(1),
3002 depth_or_array_layers: 1,
3003 },
3004 mip_level_count: 1,
3005 sample_count: 1,
3006 dimension: wgpu::TextureDimension::D2,
3007 format: wgpu::TextureFormat::Rgba16Float,
3008 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
3009 | wgpu::TextureUsages::TEXTURE_BINDING,
3010 view_formats: &[],
3011 });
3012 let src_view = src_tex.create_view(&wgpu::TextureViewDescriptor::default());
3013 let blit_bg = self
3014 .resources
3015 .make_scatter_composite_bg(device, &slot_hdr.hdr_view);
3016 let source_bg = self.resources.make_scatter_refraction_source_bg(
3017 device,
3018 &src_view,
3019 &slot_hdr.hdr_depth_only_view,
3020 );
3021 let s = self.scatter_viewport_states[vp_idx].as_mut().unwrap();
3022 s.refraction_source_texture = Some(src_tex);
3023 s.refraction_source_view = Some(src_view);
3024 s.refraction_source_bg = Some(source_bg);
3025 s.refraction_blit_bg = Some(blit_bg);
3026 s.refraction_source_size = [sw, sh];
3027 }
3028
3029 let time_seconds = self.start_instant.elapsed().as_secs_f32();
3030 let n_ref = self
3031 .resources
3032 .write_scatter_refraction_per_volume_buffer(
3033 device,
3034 queue,
3035 &self.prepared_refraction_volumes,
3036 time_seconds,
3037 );
3038 let ref_stride = self.resources.scatter_refraction_per_volume_stride();
3039
3040 if n_ref > 0 {
3041 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3042 let src_view = s.refraction_source_view.as_ref().unwrap();
3043 let blit_bg = s.refraction_blit_bg.as_ref().unwrap();
3044 let source_bg = s.refraction_source_bg.as_ref().unwrap();
3045
3046 if let Some(blit_pipeline) =
3048 self.resources.scatter_refraction_blit_pipeline.as_ref()
3049 {
3050 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3051 label: Some("scatter_refraction_blit_pass"),
3052 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3053 view: src_view,
3054 resolve_target: None,
3055 ops: wgpu::Operations {
3056 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3057 store: wgpu::StoreOp::Store,
3058 },
3059 depth_slice: None,
3060 })],
3061 depth_stencil_attachment: None,
3062 timestamp_writes: None,
3063 occlusion_query_set: None,
3064 });
3065 pass.set_pipeline(blit_pipeline);
3066 pass.set_bind_group(0, blit_bg, &[]);
3067 pass.draw(0..3, 0..1);
3068 }
3069
3070 if let (Some(pipeline), Some(per_vol_bg)) = (
3073 self.resources.scatter_refraction_pipeline.as_ref(),
3074 self.resources.scatter_refraction_per_volume_bg.as_ref(),
3075 ) {
3076 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3077 label: Some("scatter_refraction_pass"),
3078 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3079 view: &slot_hdr.hdr_view,
3080 resolve_target: None,
3081 ops: wgpu::Operations {
3082 load: wgpu::LoadOp::Load,
3083 store: wgpu::StoreOp::Store,
3084 },
3085 depth_slice: None,
3086 })],
3087 depth_stencil_attachment: None,
3088 timestamp_writes: None,
3089 occlusion_query_set: None,
3090 });
3091 pass.set_pipeline(pipeline);
3092 pass.set_bind_group(0, camera_bg, &[]);
3093 pass.set_bind_group(2, source_bg, &[]);
3094 for i in 0..n_ref {
3095 let dyn_offset = i * ref_stride;
3096 pass.set_bind_group(1, per_vol_bg, &[dyn_offset]);
3097 pass.draw(0..6, 0..1);
3098 }
3099 }
3100 }
3101 }
3102
3103 self.resources.clear_scatter_per_volume_tex_cache();
3105 let n = self.resources.write_scatter_per_volume_buffer(
3106 device,
3107 queue,
3108 &self.prepared_scatter_volumes,
3109 );
3110 let stride = self.resources.scatter_per_volume_stride();
3111
3112 let time_seconds = self.start_instant.elapsed().as_secs_f32();
3114 let global_steps = scatter.quality.default_steps();
3115 let depth_token = (vp_idx as u64).wrapping_mul(1_000_003)
3116 ^ (tw as u64).wrapping_mul(7919)
3117 ^ (th as u64).wrapping_mul(31);
3118 self.resources.write_scatter_frame_uniform(
3119 device,
3120 queue,
3121 &slot_hdr.hdr_depth_only_view,
3122 depth_token,
3123 time_seconds,
3124 global_steps,
3125 scatter.blue_noise_jitter,
3126 self.frame_counter,
3127 );
3128
3129 if scatter.temporal {
3131 self.resources.write_scatter_temporal_uniform(
3132 device,
3133 queue,
3134 prev_view_proj,
3135 scatter.temporal_blend,
3136 history_valid,
3137 );
3138 }
3139
3140 let mut per_vol_tex_bgs: Vec<wgpu::BindGroup> = Vec::with_capacity(n as usize);
3142 for (volume, _, _) in self.prepared_scatter_volumes.iter().take(n as usize) {
3143 let (lut_id, density_id) =
3144 crate::resources::ViewportGpuResources::scatter_volume_tex_ids(volume);
3145 let bg = self
3146 .resources
3147 .ensure_scatter_per_volume_tex_bg(device, queue, lut_id, density_id);
3148 per_vol_tex_bgs.push(bg);
3149 }
3150
3151 if n > 0 {
3152 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3153 let raw_view = &s.raw_current_view;
3154 if let (Some(pipeline), Some(per_vol_bg), Some(frame_bg)) = (
3155 self.resources.scatter_pipeline.as_ref(),
3156 self.resources.scatter_per_volume_bg.as_ref(),
3157 self.resources.scatter_frame_bg.as_ref(),
3158 ) {
3159 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3160 label: Some("scatter_volume_pass"),
3161 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3162 view: raw_view,
3163 resolve_target: None,
3164 ops: wgpu::Operations {
3165 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3166 store: wgpu::StoreOp::Store,
3167 },
3168 depth_slice: None,
3169 })],
3170 depth_stencil_attachment: None,
3171 timestamp_writes: None,
3172 occlusion_query_set: None,
3173 });
3174 pass.set_pipeline(pipeline);
3175 pass.set_bind_group(0, camera_bg, &[]);
3176 pass.set_bind_group(3, frame_bg, &[]);
3177 for i in 0..n {
3178 let dyn_offset = i * stride;
3179 pass.set_bind_group(1, per_vol_bg, &[dyn_offset]);
3180 pass.set_bind_group(2, &per_vol_tex_bgs[i as usize], &[]);
3181 pass.draw(0..6, 0..1);
3182 }
3183 }
3184
3185 let source_for_composite: &wgpu::BindGroup = if scatter.temporal {
3187 let s = self.scatter_viewport_states[vp_idx].as_ref().unwrap();
3188 let (resolve_bg, target_view, source_composite) = if parity == 0 {
3190 (
3192 &s.temporal_resolve_bg_read_b,
3193 &s.history_a_view,
3194 &s.composite_bg_history_a,
3195 )
3196 } else {
3197 (
3198 &s.temporal_resolve_bg_read_a,
3199 &s.history_b_view,
3200 &s.composite_bg_history_b,
3201 )
3202 };
3203 if let Some(resolve_pipeline) =
3204 self.resources.scatter_temporal_resolve_pipeline.as_ref()
3205 {
3206 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3207 label: Some("scatter_temporal_resolve_pass"),
3208 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3209 view: target_view,
3210 resolve_target: None,
3211 ops: wgpu::Operations {
3212 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3213 store: wgpu::StoreOp::Store,
3214 },
3215 depth_slice: None,
3216 })],
3217 depth_stencil_attachment: None,
3218 timestamp_writes: None,
3219 occlusion_query_set: None,
3220 });
3221 pass.set_pipeline(resolve_pipeline);
3222 pass.set_bind_group(0, resolve_bg, &[]);
3223 pass.draw(0..3, 0..1);
3224 }
3225 source_composite
3226 } else {
3227 &s.composite_bg_raw
3228 };
3229
3230 if let Some(composite_pipeline) = self.resources.scatter_composite_pipeline.as_ref()
3232 {
3233 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3234 label: Some("scatter_composite_pass"),
3235 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3236 view: &slot_hdr.hdr_view,
3237 resolve_target: None,
3238 ops: wgpu::Operations {
3239 load: wgpu::LoadOp::Load,
3240 store: wgpu::StoreOp::Store,
3241 },
3242 depth_slice: None,
3243 })],
3244 depth_stencil_attachment: None,
3245 timestamp_writes: None,
3246 occlusion_query_set: None,
3247 });
3248 pass.set_pipeline(composite_pipeline);
3249 pass.set_bind_group(0, source_for_composite, &[]);
3250 pass.draw(0..3, 0..1);
3251 }
3252
3253 let s = self.scatter_viewport_states[vp_idx].as_mut().unwrap();
3255 s.prev_view_proj = frame.camera.render_camera.view_proj().to_cols_array_2d();
3256 s.parity = 1 - s.parity;
3257 s.history_valid = scatter.temporal;
3258 } else if let Some(s) = self.scatter_viewport_states[vp_idx].as_mut() {
3259 s.history_valid = false;
3260 }
3261 }
3262
3263 if !self.lic_gpu_data.is_empty() {
3269 if let (Some(surface_pipeline), Some(advect_pipeline)) = (
3270 self.resources.lic_surface_pipeline.as_ref(),
3271 self.resources.lic_advect_pipeline.as_ref(),
3272 ) {
3273 let camera_bg = &slot.camera_bind_group;
3274 {
3276 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3277 label: Some("lic_surface_pass"),
3278 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3279 view: &slot_hdr.lic_vector_view,
3280 resolve_target: None,
3281 ops: wgpu::Operations {
3282 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
3283 store: wgpu::StoreOp::Store,
3284 },
3285 depth_slice: None,
3286 })],
3287 depth_stencil_attachment: None,
3288 timestamp_writes: None,
3289 occlusion_query_set: None,
3290 });
3291 pass.set_pipeline(surface_pipeline);
3292 pass.set_bind_group(0, camera_bg, &[]);
3293 for gpu in &self.lic_gpu_data {
3294 let Some(mesh) = self.resources.mesh_store.get(gpu.mesh_id) else {
3295 continue;
3296 };
3297 let Some(vec_buf) =
3298 mesh.vector_attribute_buffers.get(&gpu.vector_attribute)
3299 else {
3300 continue;
3301 };
3302 pass.set_bind_group(1, &gpu.bind_group, &[]);
3303 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3304 pass.set_vertex_buffer(1, vec_buf.slice(..));
3305 pass.set_index_buffer(
3306 mesh.index_buffer.slice(..),
3307 wgpu::IndexFormat::Uint32,
3308 );
3309 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3310 }
3311 }
3312 {
3314 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3315 label: Some("lic_advect_pass"),
3316 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3317 view: &slot_hdr.lic_output_view,
3318 resolve_target: None,
3319 ops: wgpu::Operations {
3320 load: wgpu::LoadOp::Clear(wgpu::Color {
3321 r: 0.5,
3322 g: 0.0,
3323 b: 0.0,
3324 a: 1.0,
3325 }),
3326 store: wgpu::StoreOp::Store,
3327 },
3328 depth_slice: None,
3329 })],
3330 depth_stencil_attachment: None,
3331 timestamp_writes: None,
3332 occlusion_query_set: None,
3333 });
3334 pass.set_pipeline(advect_pipeline);
3335 pass.set_bind_group(0, &slot_hdr.lic_advect_bind_group, &[]);
3336 pass.draw(0..3, 0..1);
3337 }
3338 }
3339 }
3340
3341 if !slot.outline_object_buffers.is_empty()
3347 || !slot.splat_outline_buffers.is_empty()
3348 || !slot.streamtube_outline_items.is_empty()
3349 || !slot.tube_outline_items.is_empty()
3350 || !slot.ribbon_outline_items.is_empty()
3351 || !slot.polyline_outline_indices.is_empty()
3352 || !slot.volume_outline_indices.is_empty()
3353 || !slot.glyph_outline_indices.is_empty()
3354 || !slot.tensor_glyph_outline_indices.is_empty()
3355 || !slot.sprite_outline_indices.is_empty()
3356 || !slot.raw_geom_outline_buffers.is_empty()
3357 || !slot.screen_rect_outline_buffers.is_empty()
3358 || !slot.implicit_outline_indices.is_empty()
3359 || !slot.mc_outline_data.is_empty()
3360 {
3361 let hdr_pipeline = self
3363 .resources
3364 .outline_composite_pipeline_hdr
3365 .as_ref()
3366 .or(self.resources.outline_composite_pipeline_single.as_ref());
3367 if let Some(pipeline) = hdr_pipeline {
3368 let bg = &slot_hdr.outline_composite_bind_group;
3369 let hdr_view = &slot_hdr.hdr_view;
3370 let hdr_depth_view = &slot_hdr.hdr_depth_view;
3371 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3372 label: Some("hdr_outline_composite_pass"),
3373 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3374 view: hdr_view,
3375 resolve_target: None,
3376 ops: wgpu::Operations {
3377 load: wgpu::LoadOp::Load,
3378 store: wgpu::StoreOp::Store,
3379 },
3380 depth_slice: None,
3381 })],
3382 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3383 view: hdr_depth_view,
3384 depth_ops: Some(wgpu::Operations {
3385 load: wgpu::LoadOp::Load,
3386 store: wgpu::StoreOp::Store,
3387 }),
3388 stencil_ops: None,
3389 }),
3390 timestamp_writes: None,
3391 occlusion_query_set: None,
3392 });
3393 outline_pass.set_pipeline(pipeline);
3394 outline_pass.set_bind_group(0, bg, &[]);
3395 outline_pass.draw(0..3, 0..1);
3396 }
3397 }
3398
3399 let throttle_effects = self.degradation_effects_throttled;
3402
3403 if pp.ssao && !throttle_effects {
3407 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
3408 {
3409 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3410 label: Some("ssao_pass"),
3411 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3412 view: &slot_hdr.ssao_view,
3413 resolve_target: None,
3414 ops: wgpu::Operations {
3415 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3416 store: wgpu::StoreOp::Store,
3417 },
3418 depth_slice: None,
3419 })],
3420 depth_stencil_attachment: None,
3421 timestamp_writes: None,
3422 occlusion_query_set: None,
3423 });
3424 ssao_pass.set_pipeline(ssao_pipeline);
3425 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
3426 ssao_pass.draw(0..3, 0..1);
3427 }
3428
3429 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
3431 let mut ssao_blur_pass =
3432 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3433 label: Some("ssao_blur_pass"),
3434 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3435 view: &slot_hdr.ssao_blur_view,
3436 resolve_target: None,
3437 ops: wgpu::Operations {
3438 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3439 store: wgpu::StoreOp::Store,
3440 },
3441 depth_slice: None,
3442 })],
3443 depth_stencil_attachment: None,
3444 timestamp_writes: None,
3445 occlusion_query_set: None,
3446 });
3447 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
3448 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
3449 ssao_blur_pass.draw(0..3, 0..1);
3450 }
3451 }
3452 }
3453
3454 if pp.contact_shadows && !throttle_effects {
3458 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
3459 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3460 label: Some("contact_shadow_pass"),
3461 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3462 view: &slot_hdr.contact_shadow_view,
3463 resolve_target: None,
3464 depth_slice: None,
3465 ops: wgpu::Operations {
3466 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
3467 store: wgpu::StoreOp::Store,
3468 },
3469 })],
3470 depth_stencil_attachment: None,
3471 timestamp_writes: None,
3472 occlusion_query_set: None,
3473 });
3474 cs_pass.set_pipeline(cs_pipeline);
3475 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
3476 cs_pass.draw(0..3, 0..1);
3477 }
3478 }
3479
3480 if pp.bloom && !throttle_effects {
3484 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
3486 {
3487 let mut threshold_pass =
3488 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3489 label: Some("bloom_threshold_pass"),
3490 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3491 view: &slot_hdr.bloom_threshold_view,
3492 resolve_target: None,
3493 ops: wgpu::Operations {
3494 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3495 store: wgpu::StoreOp::Store,
3496 },
3497 depth_slice: None,
3498 })],
3499 depth_stencil_attachment: None,
3500 timestamp_writes: None,
3501 occlusion_query_set: None,
3502 });
3503 threshold_pass.set_pipeline(bloom_threshold_pipeline);
3504 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
3505 threshold_pass.draw(0..3, 0..1);
3506 }
3507
3508 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
3511 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
3512 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
3513 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
3514 let bloom_ping_view = &slot_hdr.bloom_ping_view;
3515 let bloom_pong_view = &slot_hdr.bloom_pong_view;
3516 const BLUR_ITERATIONS: usize = 4;
3517 for i in 0..BLUR_ITERATIONS {
3518 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
3520 {
3521 let mut h_pass =
3522 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3523 label: Some("bloom_blur_h_pass"),
3524 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3525 view: bloom_ping_view,
3526 resolve_target: None,
3527 ops: wgpu::Operations {
3528 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3529 store: wgpu::StoreOp::Store,
3530 },
3531 depth_slice: None,
3532 })],
3533 depth_stencil_attachment: None,
3534 timestamp_writes: None,
3535 occlusion_query_set: None,
3536 });
3537 h_pass.set_pipeline(blur_pipeline);
3538 h_pass.set_bind_group(0, h_bg, &[]);
3539 h_pass.draw(0..3, 0..1);
3540 }
3541 {
3543 let mut v_pass =
3544 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3545 label: Some("bloom_blur_v_pass"),
3546 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3547 view: bloom_pong_view,
3548 resolve_target: None,
3549 ops: wgpu::Operations {
3550 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3551 store: wgpu::StoreOp::Store,
3552 },
3553 depth_slice: None,
3554 })],
3555 depth_stencil_attachment: None,
3556 timestamp_writes: None,
3557 occlusion_query_set: None,
3558 });
3559 v_pass.set_pipeline(blur_pipeline);
3560 v_pass.set_bind_group(0, blur_v_bg, &[]);
3561 v_pass.draw(0..3, 0..1);
3562 }
3563 }
3564 }
3565 }
3566 }
3567
3568 if pp.dof_enabled && !throttle_effects {
3572 if let Some(dof_pipeline) = &self.resources.dof_pipeline {
3573 let mut dof_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3574 label: Some("dof_pass"),
3575 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3576 view: &slot_hdr.dof_view,
3577 resolve_target: None,
3578 depth_slice: None,
3579 ops: wgpu::Operations {
3580 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3581 store: wgpu::StoreOp::Store,
3582 },
3583 })],
3584 depth_stencil_attachment: None,
3585 timestamp_writes: None,
3586 occlusion_query_set: None,
3587 });
3588 dof_pass.set_pipeline(dof_pipeline);
3589 dof_pass.set_bind_group(0, &slot_hdr.dof_bg, &[]);
3590 dof_pass.draw(0..3, 0..1);
3591 }
3592 }
3593
3594 let use_fxaa = pp.fxaa;
3602 let use_hdr_upscale = slot_hdr.upscale_bind_group.is_some();
3603 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
3604 let tone_target: &wgpu::TextureView = if use_fxaa {
3605 &slot_hdr.fxaa_view
3606 } else if use_hdr_upscale {
3607 slot_hdr.upscale_view.as_ref().unwrap()
3608 } else {
3609 output_view
3610 };
3611 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3612 label: Some("tone_map_pass"),
3613 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3614 view: tone_target,
3615 resolve_target: None,
3616 ops: wgpu::Operations {
3617 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3618 store: wgpu::StoreOp::Store,
3619 },
3620 depth_slice: None,
3621 })],
3622 depth_stencil_attachment: None,
3623 timestamp_writes: None,
3624 occlusion_query_set: None,
3625 });
3626 tone_pass.set_pipeline(tone_map_pipeline);
3627 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
3628 tone_pass.draw(0..3, 0..1);
3629 }
3630
3631 if use_fxaa {
3635 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
3636 let fxaa_target: &wgpu::TextureView = if use_hdr_upscale {
3637 slot_hdr.upscale_view.as_ref().unwrap()
3638 } else {
3639 output_view
3640 };
3641 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3642 label: Some("fxaa_pass"),
3643 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3644 view: fxaa_target,
3645 resolve_target: None,
3646 ops: wgpu::Operations {
3647 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3648 store: wgpu::StoreOp::Store,
3649 },
3650 depth_slice: None,
3651 })],
3652 depth_stencil_attachment: None,
3653 timestamp_writes: None,
3654 occlusion_query_set: None,
3655 });
3656 fxaa_pass.set_pipeline(fxaa_pipeline);
3657 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
3658 fxaa_pass.draw(0..3, 0..1);
3659 }
3660 }
3661
3662 if use_hdr_upscale {
3667 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3668 if let Some(upscale_bg) = &slot_hdr.upscale_bind_group {
3669 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
3670 let mut upscale_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3671 label: Some("hdr_upscale_pass"),
3672 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3673 view: output_view,
3674 resolve_target: None,
3675 ops: wgpu::Operations {
3676 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
3677 store: wgpu::StoreOp::Store,
3678 },
3679 depth_slice: None,
3680 })],
3681 depth_stencil_attachment: None,
3682 timestamp_writes: None,
3683 occlusion_query_set: None,
3684 });
3685 upscale_pass.set_pipeline(pipeline);
3686 upscale_pass.set_bind_group(0, upscale_bg, &[]);
3687 upscale_pass.draw(0..3, 0..1);
3688 }
3689 }
3690 }
3691
3692 {
3698 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3699 if let Some(blit_bg) = &slot_hdr.depth_blit_bind_group {
3700 if let Some(blit_pipeline) = &self.resources.depth_blit_pipeline {
3701 let mut blit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3702 label: Some("depth_blit_pass"),
3703 color_attachments: &[],
3704 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3705 view: &slot_hdr.output_depth_view,
3706 depth_ops: Some(wgpu::Operations {
3707 load: wgpu::LoadOp::Clear(1.0),
3708 store: wgpu::StoreOp::Store,
3709 }),
3710 stencil_ops: None,
3711 }),
3712 timestamp_writes: None,
3713 occlusion_query_set: None,
3714 });
3715 blit_pass.set_pipeline(blit_pipeline);
3716 blit_pass.set_bind_group(0, blit_bg, &[]);
3717 blit_pass.draw(0..3, 0..1);
3718 }
3719 }
3720 }
3721
3722 if frame.viewport.show_grid {
3726 let slot = &self.viewport_slots[vp_idx];
3727 let slot_hdr = slot.hdr.as_ref().unwrap();
3728 let grid_bg = &slot.grid_bind_group;
3729 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3730 label: Some("hdr_grid_pass"),
3731 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3732 view: output_view,
3733 resolve_target: None,
3734 ops: wgpu::Operations {
3735 load: wgpu::LoadOp::Load,
3736 store: wgpu::StoreOp::Store,
3737 },
3738 depth_slice: None,
3739 })],
3740 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3741 view: &slot_hdr.output_depth_view,
3742 depth_ops: Some(wgpu::Operations {
3743 load: wgpu::LoadOp::Load,
3744 store: wgpu::StoreOp::Store,
3745 }),
3746 stencil_ops: None,
3747 }),
3748 timestamp_writes: None,
3749 occlusion_query_set: None,
3750 });
3751 grid_pass.set_pipeline(&self.resources.grid_pipeline);
3752 grid_pass.set_bind_group(0, grid_bg, &[]);
3753 grid_pass.draw(0..3, 0..1);
3754 }
3755
3756 if !matches!(
3759 frame.effects.ground_plane.mode,
3760 crate::renderer::types::GroundPlaneMode::None
3761 ) {
3762 let slot = &self.viewport_slots[vp_idx];
3763 let slot_hdr = slot.hdr.as_ref().unwrap();
3764 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3765 label: Some("hdr_ground_plane_pass"),
3766 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3767 view: output_view,
3768 resolve_target: None,
3769 ops: wgpu::Operations {
3770 load: wgpu::LoadOp::Load,
3771 store: wgpu::StoreOp::Store,
3772 },
3773 depth_slice: None,
3774 })],
3775 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3776 view: &slot_hdr.output_depth_view,
3777 depth_ops: Some(wgpu::Operations {
3778 load: wgpu::LoadOp::Load,
3779 store: wgpu::StoreOp::Store,
3780 }),
3781 stencil_ops: None,
3782 }),
3783 timestamp_writes: None,
3784 occlusion_query_set: None,
3785 });
3786 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
3787 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
3788 gp_pass.draw(0..3, 0..1);
3789 }
3790
3791 if !self.screen_image_gpu_data.is_empty() {
3799 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
3800 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
3801 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
3802 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3803 label: Some("screen_image_pass"),
3804 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3805 view: output_view,
3806 resolve_target: None,
3807 ops: wgpu::Operations {
3808 load: wgpu::LoadOp::Load,
3809 store: wgpu::StoreOp::Store,
3810 },
3811 depth_slice: None,
3812 })],
3813 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3814 view: &slot_hdr.output_depth_view,
3815 depth_ops: Some(wgpu::Operations {
3816 load: wgpu::LoadOp::Load,
3817 store: wgpu::StoreOp::Discard,
3818 }),
3819 stencil_ops: None,
3820 }),
3821 timestamp_writes: None,
3822 occlusion_query_set: None,
3823 });
3824 for gpu in &self.screen_image_gpu_data {
3825 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
3826 img_pass.set_pipeline(dc_pipe);
3827 img_pass.set_bind_group(0, dc_bg, &[]);
3828 } else {
3829 img_pass.set_pipeline(overlay_pipeline);
3830 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
3831 }
3832 img_pass.draw(0..6, 0..1);
3833 }
3834 }
3835 }
3836
3837 {
3841 let slot = &self.viewport_slots[vp_idx];
3842 let slot_hdr = slot.hdr.as_ref().unwrap();
3843 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
3844 && slot.gizmo_index_count > 0)
3845 || !slot.constraint_line_buffers.is_empty()
3846 || !slot.clip_plane_fill_buffers.is_empty()
3847 || !slot.clip_plane_line_buffers.is_empty()
3848 || !slot.xray_object_buffers.is_empty();
3849 if has_editor_overlays {
3850 let camera_bg = &slot.camera_bind_group;
3851 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3852 label: Some("hdr_editor_overlay_pass"),
3853 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3854 view: output_view,
3855 resolve_target: None,
3856 ops: wgpu::Operations {
3857 load: wgpu::LoadOp::Load,
3858 store: wgpu::StoreOp::Store,
3859 },
3860 depth_slice: None,
3861 })],
3862 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3863 view: &slot_hdr.output_depth_view,
3864 depth_ops: Some(wgpu::Operations {
3865 load: wgpu::LoadOp::Load,
3866 store: wgpu::StoreOp::Discard,
3867 }),
3868 stencil_ops: None,
3869 }),
3870 timestamp_writes: None,
3871 occlusion_query_set: None,
3872 });
3873
3874 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
3875 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
3876 overlay_pass.set_bind_group(0, camera_bg, &[]);
3877 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
3878 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
3879 overlay_pass.set_index_buffer(
3880 slot.gizmo_index_buffer.slice(..),
3881 wgpu::IndexFormat::Uint32,
3882 );
3883 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
3884 }
3885
3886 if !slot.constraint_line_buffers.is_empty() {
3887 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
3888 overlay_pass.set_bind_group(0, camera_bg, &[]);
3889 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
3890 overlay_pass.set_bind_group(1, bg, &[]);
3891 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3892 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3893 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
3894 }
3895 }
3896
3897 if !slot.clip_plane_fill_buffers.is_empty() {
3898 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
3899 overlay_pass.set_bind_group(0, camera_bg, &[]);
3900 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
3901 overlay_pass.set_bind_group(1, bg, &[]);
3902 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3903 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3904 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
3905 }
3906 }
3907
3908 if !slot.clip_plane_line_buffers.is_empty() {
3909 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
3910 overlay_pass.set_bind_group(0, camera_bg, &[]);
3911 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
3912 overlay_pass.set_bind_group(1, bg, &[]);
3913 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
3914 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
3915 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
3916 }
3917 }
3918
3919 if !slot.xray_object_buffers.is_empty() {
3920 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
3921 overlay_pass.set_bind_group(0, camera_bg, &[]);
3922 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
3923 let Some(mesh) = self.resources.mesh_store.get(*mesh_id) else {
3924 continue;
3925 };
3926 overlay_pass.set_bind_group(1, bg, &[]);
3927 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
3928 overlay_pass.set_index_buffer(
3929 mesh.index_buffer.slice(..),
3930 wgpu::IndexFormat::Uint32,
3931 );
3932 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
3933 }
3934 }
3935 }
3936 }
3937
3938 if frame.viewport.show_axes_indicator {
3941 let slot = &self.viewport_slots[vp_idx];
3942 if slot.axes_vertex_count > 0 {
3943 let slot_hdr = slot.hdr.as_ref().unwrap();
3944 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3945 label: Some("hdr_axes_pass"),
3946 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3947 view: output_view,
3948 resolve_target: None,
3949 ops: wgpu::Operations {
3950 load: wgpu::LoadOp::Load,
3951 store: wgpu::StoreOp::Store,
3952 },
3953 depth_slice: None,
3954 })],
3955 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
3956 view: &slot_hdr.output_depth_view,
3957 depth_ops: Some(wgpu::Operations {
3958 load: wgpu::LoadOp::Load,
3959 store: wgpu::StoreOp::Discard,
3960 }),
3961 stencil_ops: None,
3962 }),
3963 timestamp_writes: None,
3964 occlusion_query_set: None,
3965 });
3966 axes_pass.set_pipeline(&self.resources.axes_pipeline);
3967 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
3968 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
3969 }
3970 }
3971
3972 let has_overlay = self.overlay_shape_gpu_data.is_some()
3974 || self.overlay_rect_gpu_data.is_some()
3975 || self.label_gpu_data.is_some()
3976 || self.scalar_bar_gpu_data.is_some()
3977 || self.ruler_gpu_data.is_some()
3978 || self.loading_bar_gpu_data.is_some()
3979 || !self.overlay_image_gpu_data.is_empty();
3980
3981 let needs_hdr_blur = self.has_backdrop_blur_shapes();
3986 let hdr_blur_bg: Option<wgpu::BindGroup> = if needs_hdr_blur && has_overlay {
3987 self.ensure_backdrop_blur_state(device, w.max(1), h.max(1));
3988 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
4000 let source = &slot_hdr.hdr_view;
4001 let spread = self
4002 .overlay_shape_gpu_data
4003 .as_ref()
4004 .map(|d| d.max_blur_radius)
4005 .unwrap_or(8.0);
4006 Some(self.run_backdrop_blur(&mut encoder, device, queue, source, spread))
4007 } else {
4008 None
4009 };
4010
4011 if has_overlay {
4012 let hdr_depth_view = &self.viewport_slots[vp_idx]
4013 .hdr
4014 .as_ref()
4015 .unwrap()
4016 .output_depth_view;
4017 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4018 label: Some("overlay_pass"),
4019 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4020 view: output_view,
4021 resolve_target: None,
4022 ops: wgpu::Operations {
4023 load: wgpu::LoadOp::Load,
4024 store: wgpu::StoreOp::Store,
4025 },
4026 depth_slice: None,
4027 })],
4028 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
4029 view: hdr_depth_view,
4030 depth_ops: Some(wgpu::Operations {
4031 load: wgpu::LoadOp::Load,
4032 store: wgpu::StoreOp::Discard,
4033 }),
4034 stencil_ops: None,
4035 }),
4036 timestamp_writes: None,
4037 occlusion_query_set: None,
4038 });
4039 if let Some(ref bg) = hdr_blur_bg {
4041 self.draw_blur_shapes(&mut overlay_pass, bg);
4042 }
4043 if let Some(ref sd) = self.overlay_shape_gpu_data {
4045 if sd.vertex_count > 0 {
4046 if let Some(pipeline) = &self.resources.overlay_shape_pipeline {
4047 if let Some(vbuf) = &sd.vertex_buf {
4048 overlay_pass.set_pipeline(pipeline);
4049 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
4050 overlay_pass.draw(0..sd.vertex_count, 0..1);
4051 }
4052 }
4053 }
4054 if !sd.tex_batches.is_empty() {
4055 if let Some(pipeline) = &self.resources.overlay_shape_tex_pipeline {
4056 overlay_pass.set_pipeline(pipeline);
4057 for batch in &sd.tex_batches {
4058 overlay_pass.set_bind_group(0, &batch.bind_group, &[]);
4059 overlay_pass.set_vertex_buffer(0, batch.vertex_buf.slice(..));
4060 overlay_pass.draw(0..batch.vertex_count, 0..1);
4061 }
4062 }
4063 }
4064 }
4065 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
4066 overlay_pass.set_pipeline(pipeline);
4067 if let Some(ref rr) = self.overlay_rect_gpu_data {
4068 overlay_pass.set_bind_group(0, &rr.bind_group, &[]);
4069 overlay_pass.set_vertex_buffer(0, rr.vertex_buf.slice(..));
4070 overlay_pass.draw(0..rr.vertex_count, 0..1);
4071 }
4072 if let Some(ref ld) = self.label_gpu_data {
4073 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
4074 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
4075 overlay_pass.draw(0..ld.vertex_count, 0..1);
4076 }
4077 if let Some(ref sb) = self.scalar_bar_gpu_data {
4078 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
4079 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
4080 overlay_pass.draw(0..sb.vertex_count, 0..1);
4081 }
4082 if let Some(ref rd) = self.ruler_gpu_data {
4083 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
4084 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
4085 overlay_pass.draw(0..rd.vertex_count, 0..1);
4086 }
4087 if let Some(ref lb) = self.loading_bar_gpu_data {
4088 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
4089 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
4090 overlay_pass.draw(0..lb.vertex_count, 0..1);
4091 }
4092 }
4093 if !self.overlay_image_gpu_data.is_empty() {
4095 if let Some(pipeline) = &self.resources.screen_image_pipeline {
4096 overlay_pass.set_pipeline(pipeline);
4097 for gpu in &self.overlay_image_gpu_data {
4098 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
4099 overlay_pass.draw(0..6, 0..1);
4100 }
4101 }
4102 }
4103 }
4104
4105 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
4107 self.ts_query_set.as_ref(),
4108 self.ts_resolve_buf.as_ref(),
4109 self.ts_staging_buf.as_ref(),
4110 ) {
4111 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
4112 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
4113 self.ts_needs_readback = true;
4114 }
4115
4116 encoder.finish()
4117 }
4118
4119 pub fn render_offscreen(
4133 &mut self,
4134 device: &wgpu::Device,
4135 queue: &wgpu::Queue,
4136 frame: &FrameData,
4137 width: u32,
4138 height: u32,
4139 ) -> Vec<u8> {
4140 let target_format = self.resources.target_format;
4142 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
4143 label: Some("offscreen_target"),
4144 size: wgpu::Extent3d {
4145 width: width.max(1),
4146 height: height.max(1),
4147 depth_or_array_layers: 1,
4148 },
4149 mip_level_count: 1,
4150 sample_count: 1,
4151 dimension: wgpu::TextureDimension::D2,
4152 format: target_format,
4153 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
4154 view_formats: &[],
4155 });
4156
4157 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
4159
4160 let cmd_buf = self.render(device, queue, &output_view, frame);
4168 queue.submit(std::iter::once(cmd_buf));
4169
4170 let bytes_per_pixel = 4u32;
4172 let unpadded_row = width * bytes_per_pixel;
4173 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
4174 let padded_row = (unpadded_row + align - 1) & !(align - 1);
4175 let buffer_size = (padded_row * height.max(1)) as u64;
4176
4177 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
4178 label: Some("offscreen_staging"),
4179 size: buffer_size,
4180 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
4181 mapped_at_creation: false,
4182 });
4183
4184 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
4185 label: Some("offscreen_copy_encoder"),
4186 });
4187 copy_encoder.copy_texture_to_buffer(
4188 wgpu::TexelCopyTextureInfo {
4189 texture: &offscreen_texture,
4190 mip_level: 0,
4191 origin: wgpu::Origin3d::ZERO,
4192 aspect: wgpu::TextureAspect::All,
4193 },
4194 wgpu::TexelCopyBufferInfo {
4195 buffer: &staging_buf,
4196 layout: wgpu::TexelCopyBufferLayout {
4197 offset: 0,
4198 bytes_per_row: Some(padded_row),
4199 rows_per_image: Some(height.max(1)),
4200 },
4201 },
4202 wgpu::Extent3d {
4203 width: width.max(1),
4204 height: height.max(1),
4205 depth_or_array_layers: 1,
4206 },
4207 );
4208 queue.submit(std::iter::once(copy_encoder.finish()));
4209
4210 let (tx, rx) = std::sync::mpsc::channel();
4212 staging_buf
4213 .slice(..)
4214 .map_async(wgpu::MapMode::Read, move |result| {
4215 let _ = tx.send(result);
4216 });
4217 device
4218 .poll(wgpu::PollType::Wait {
4219 submission_index: None,
4220 timeout: Some(std::time::Duration::from_secs(5)),
4221 })
4222 .unwrap();
4223 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
4224
4225 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
4226 {
4227 let mapped = staging_buf.slice(..).get_mapped_range();
4228 let data: &[u8] = &mapped;
4229 if padded_row == unpadded_row {
4230 pixels.extend_from_slice(data);
4232 } else {
4233 for row in 0..height as usize {
4235 let start = row * padded_row as usize;
4236 let end = start + unpadded_row as usize;
4237 pixels.extend_from_slice(&data[start..end]);
4238 }
4239 }
4240 }
4241 staging_buf.unmap();
4242
4243 let is_bgra = matches!(
4245 target_format,
4246 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
4247 );
4248 if is_bgra {
4249 for pixel in pixels.chunks_exact_mut(4) {
4250 pixel.swap(0, 2); }
4252 }
4253
4254 pixels
4255 }
4256
4257 fn ensure_backdrop_blur_state(&mut self, device: &wgpu::Device, w: u32, h: u32) {
4263 let need_recreate = match &self.backdrop_blur_state {
4264 Some(s) => s.size != [w, h] || s.format != self.resources.target_format,
4265 None => true,
4266 };
4267 if !need_recreate {
4268 return;
4269 }
4270
4271 let format = self.resources.target_format;
4272 let blur_w = (w / 2).max(1);
4273 let blur_h = (h / 2).max(1);
4274
4275 let intermediate_texture = device.create_texture(&wgpu::TextureDescriptor {
4276 label: Some("backdrop_intermediate"),
4277 size: wgpu::Extent3d {
4278 width: w,
4279 height: h,
4280 depth_or_array_layers: 1,
4281 },
4282 mip_level_count: 1,
4283 sample_count: 1,
4284 dimension: wgpu::TextureDimension::D2,
4285 format,
4286 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
4287 view_formats: &[],
4288 });
4289 let intermediate_view = intermediate_texture.create_view(&Default::default());
4290
4291 let make_blur_tex = |label: &str| {
4292 let t = device.create_texture(&wgpu::TextureDescriptor {
4293 label: Some(label),
4294 size: wgpu::Extent3d {
4295 width: blur_w,
4296 height: blur_h,
4297 depth_or_array_layers: 1,
4298 },
4299 mip_level_count: 1,
4300 sample_count: 1,
4301 dimension: wgpu::TextureDimension::D2,
4302 format,
4303 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
4304 | wgpu::TextureUsages::TEXTURE_BINDING,
4305 view_formats: &[],
4306 });
4307 let v = t.create_view(&Default::default());
4308 (t, v)
4309 };
4310 let (blur_a_texture, blur_a_view) = make_blur_tex("backdrop_blur_a");
4311 let (blur_b_texture, blur_b_view) = make_blur_tex("backdrop_blur_b");
4312
4313 self.backdrop_blur_state = Some(crate::resources::BackdropBlurState {
4314 intermediate_texture,
4315 intermediate_view,
4316 blur_a_texture,
4317 blur_a_view,
4318 blur_b_texture,
4319 blur_b_view,
4320 size: [w, h],
4321 format,
4322 });
4323 }
4324
4325 fn run_backdrop_blur(
4329 &self,
4330 encoder: &mut wgpu::CommandEncoder,
4331 device: &wgpu::Device,
4332 _queue: &wgpu::Queue,
4333 source_view: &wgpu::TextureView,
4334 spread: f32,
4335 ) -> wgpu::BindGroup {
4336 let bs = self.backdrop_blur_state.as_ref().unwrap();
4337 let blur_bgl = self.resources.backdrop_blur_bgl.as_ref().unwrap();
4338 let blur_sampler = self.resources.backdrop_blur_sampler.as_ref().unwrap();
4339 let blur_pipeline = self.resources.backdrop_blur_pipeline.as_ref().unwrap();
4340 let blit_pipeline = self.resources.dyn_res_upscale_pipeline.as_ref().unwrap();
4342 let blit_bgl = self.resources.dyn_res_upscale_bgl.as_ref().unwrap();
4343 let blit_sampler = self.resources.dyn_res_linear_sampler.as_ref().unwrap();
4344
4345 let downsample_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
4347 label: Some("backdrop_downsample_bg"),
4348 layout: blit_bgl,
4349 entries: &[
4350 wgpu::BindGroupEntry {
4351 binding: 0,
4352 resource: wgpu::BindingResource::TextureView(source_view),
4353 },
4354 wgpu::BindGroupEntry {
4355 binding: 1,
4356 resource: wgpu::BindingResource::Sampler(blit_sampler),
4357 },
4358 ],
4359 });
4360 {
4361 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4362 label: Some("backdrop_downsample"),
4363 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4364 view: &bs.blur_a_view,
4365 resolve_target: None,
4366 ops: wgpu::Operations {
4367 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4368 store: wgpu::StoreOp::Store,
4369 },
4370 depth_slice: None,
4371 })],
4372 depth_stencil_attachment: None,
4373 timestamp_writes: None,
4374 occlusion_query_set: None,
4375 });
4376 pass.set_pipeline(blit_pipeline);
4377 pass.set_bind_group(0, &downsample_bg, &[]);
4378 pass.draw(0..3, 0..1);
4379 }
4380
4381 let effective_spread = (spread / 2.0).max(1.0);
4383
4384 let h_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
4386 label: Some("blur_h_uniform"),
4387 contents: bytemuck::cast_slice(&[1u32, effective_spread.to_bits(), 0u32, 0u32]),
4388 usage: wgpu::BufferUsages::UNIFORM,
4389 });
4390 let h_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
4391 label: Some("blur_h_bg"),
4392 layout: blur_bgl,
4393 entries: &[
4394 wgpu::BindGroupEntry {
4395 binding: 0,
4396 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
4397 },
4398 wgpu::BindGroupEntry {
4399 binding: 1,
4400 resource: wgpu::BindingResource::Sampler(blur_sampler),
4401 },
4402 wgpu::BindGroupEntry {
4403 binding: 2,
4404 resource: h_uniform.as_entire_binding(),
4405 },
4406 ],
4407 });
4408 {
4409 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4410 label: Some("backdrop_blur_h"),
4411 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4412 view: &bs.blur_b_view,
4413 resolve_target: None,
4414 ops: wgpu::Operations {
4415 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4416 store: wgpu::StoreOp::Store,
4417 },
4418 depth_slice: None,
4419 })],
4420 depth_stencil_attachment: None,
4421 timestamp_writes: None,
4422 occlusion_query_set: None,
4423 });
4424 pass.set_pipeline(blur_pipeline);
4425 pass.set_bind_group(0, &h_bg, &[]);
4426 pass.draw(0..3, 0..1);
4427 }
4428
4429 let v_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
4431 label: Some("blur_v_uniform"),
4432 contents: bytemuck::cast_slice(&[0u32, effective_spread.to_bits(), 0u32, 0u32]),
4433 usage: wgpu::BufferUsages::UNIFORM,
4434 });
4435 let v_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
4436 label: Some("blur_v_bg"),
4437 layout: blur_bgl,
4438 entries: &[
4439 wgpu::BindGroupEntry {
4440 binding: 0,
4441 resource: wgpu::BindingResource::TextureView(&bs.blur_b_view),
4442 },
4443 wgpu::BindGroupEntry {
4444 binding: 1,
4445 resource: wgpu::BindingResource::Sampler(blur_sampler),
4446 },
4447 wgpu::BindGroupEntry {
4448 binding: 2,
4449 resource: v_uniform.as_entire_binding(),
4450 },
4451 ],
4452 });
4453 {
4454 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4455 label: Some("backdrop_blur_v"),
4456 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4457 view: &bs.blur_a_view,
4458 resolve_target: None,
4459 ops: wgpu::Operations {
4460 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
4461 store: wgpu::StoreOp::Store,
4462 },
4463 depth_slice: None,
4464 })],
4465 depth_stencil_attachment: None,
4466 timestamp_writes: None,
4467 occlusion_query_set: None,
4468 });
4469 pass.set_pipeline(blur_pipeline);
4470 pass.set_bind_group(0, &v_bg, &[]);
4471 pass.draw(0..3, 0..1);
4472 }
4473
4474 let tex_bgl = self.resources.overlay_shape_tex_bgl.as_ref().unwrap();
4478 let tex_sampler = self.resources.overlay_shape_tex_sampler.as_ref().unwrap();
4479 device.create_bind_group(&wgpu::BindGroupDescriptor {
4480 label: Some("backdrop_blur_overlay_bg"),
4481 layout: tex_bgl,
4482 entries: &[
4483 wgpu::BindGroupEntry {
4484 binding: 0,
4485 resource: wgpu::BindingResource::TextureView(&bs.blur_a_view),
4486 },
4487 wgpu::BindGroupEntry {
4488 binding: 1,
4489 resource: wgpu::BindingResource::Sampler(tex_sampler),
4490 },
4491 ],
4492 })
4493 }
4494
4495 fn has_backdrop_blur_shapes(&self) -> bool {
4497 self.overlay_shape_gpu_data
4498 .as_ref()
4499 .map_or(false, |sd| sd.blur_vertex_count > 0)
4500 }
4501
4502 fn draw_blur_shapes<'rp>(
4504 &'rp self,
4505 render_pass: &mut wgpu::RenderPass<'rp>,
4506 blur_bind_group: &'rp wgpu::BindGroup,
4507 ) {
4508 if let Some(ref sd) = self.overlay_shape_gpu_data {
4509 if sd.blur_vertex_count > 0 {
4510 if let (Some(pipeline), Some(vbuf)) = (
4511 &self.resources.overlay_shape_tex_pipeline,
4512 &sd.blur_vertex_buf,
4513 ) {
4514 render_pass.set_pipeline(pipeline);
4515 render_pass.set_bind_group(0, blur_bind_group, &[]);
4516 render_pass.set_vertex_buffer(0, vbuf.slice(..));
4517 render_pass.draw(0..sd.blur_vertex_count, 0..1);
4518 }
4519 }
4520 }
4521 }
4522}