1use super::*;
2
3impl ViewportRenderer {
4 pub fn paint(&self, render_pass: &mut wgpu::RenderPass<'static>, frame: &FrameData) {
10 let vp_idx = frame.camera.viewport_index;
11 let camera_bg = self.viewport_camera_bind_group(vp_idx);
12 let grid_bg = self.viewport_grid_bind_group(vp_idx);
13 let vp_slot = self.viewport_slots.get(vp_idx);
14 emit_draw_calls!(
15 &self.resources,
16 &mut *render_pass,
17 frame,
18 self.use_instancing,
19 &self.instanced_batches,
20 camera_bg,
21 grid_bg,
22 &self.compute_filter_results,
23 vp_slot,
24 &self.wireframe_bind_groups
25 );
26 emit_scivis_draw_calls!(
27 &self.resources,
28 &mut *render_pass,
29 &self.point_cloud_gpu_data,
30 &self.glyph_gpu_data,
31 &self.polyline_gpu_data,
32 &self.volume_gpu_data,
33 &self.streamtube_gpu_data,
34 camera_bg,
35 &self.tube_gpu_data,
36 &self.image_slice_gpu_data,
37 &self.tensor_glyph_gpu_data,
38 &self.ribbon_gpu_data,
39 &self.volume_surface_slice_gpu_data,
40 &self.sprite_gpu_data,
41 false
42 );
43 if !self.gaussian_splat_draw_data.is_empty() {
45 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
46 render_pass.set_pipeline(dual.for_format(false));
47 render_pass.set_bind_group(0, camera_bg, &[]);
48 for dd in &self.gaussian_splat_draw_data {
49 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
50 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
51 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
52 render_pass.draw(0..6, 0..dd.count);
53 }
54 }
55 }
56 }
57 }
58 if !self.implicit_gpu_data.is_empty() {
60 if let Some(ref dual) = self.resources.implicit_pipeline {
61 render_pass.set_pipeline(dual.for_format(false));
62 render_pass.set_bind_group(0, camera_bg, &[]);
63 for gpu in &self.implicit_gpu_data {
64 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
65 render_pass.draw(0..6, 0..1);
66 }
67 }
68 }
69 if !self.mc_gpu_data.is_empty() {
71 if let Some(ref dual) = self.resources.mc_surface_pipeline {
72 render_pass.set_pipeline(dual.for_format(false));
73 render_pass.set_bind_group(0, camera_bg, &[]);
74 for mc in &self.mc_gpu_data {
75 let vol = &self.resources.mc_volumes[mc.volume_idx];
76 render_pass.set_bind_group(1, &mc.render_bg, &[]);
77 for slab in &vol.slabs {
78 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
79 render_pass.draw_indirect(&slab.indirect_buf, 0);
80 }
81 }
82 }
83 }
84 emit_outline_composite!(&self.resources, &mut *render_pass, vp_slot);
86 if let Some(sub_hl) = self.viewport_slots.get(vp_idx).and_then(|s| s.sub_highlight.as_ref()) {
88 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
89 &self.resources.sub_highlight_fill_ldr_pipeline,
90 &self.resources.sub_highlight_edge_ldr_pipeline,
91 &self.resources.sub_highlight_sprite_ldr_pipeline,
92 ) {
93 if sub_hl.fill_vertex_count > 0 {
94 render_pass.set_pipeline(fill_pl);
95 render_pass.set_bind_group(0, camera_bg, &[]);
96 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
97 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
98 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
99 }
100 if sub_hl.edge_segment_count > 0 {
101 render_pass.set_pipeline(edge_pl);
102 render_pass.set_bind_group(0, camera_bg, &[]);
103 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
104 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
105 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
106 }
107 if sub_hl.sprite_point_count > 0 {
108 render_pass.set_pipeline(sprite_pl);
109 render_pass.set_bind_group(0, camera_bg, &[]);
110 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
111 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
112 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
113 }
114 }
115 }
116 if !self.screen_image_gpu_data.is_empty() {
118 if let Some(pipeline) = &self.resources.screen_image_pipeline {
119 render_pass.set_pipeline(pipeline);
120 for gpu in &self.screen_image_gpu_data {
121 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
122 render_pass.draw(0..6, 0..1);
123 }
124 }
125 }
126 if let Some(ref ld) = self.label_gpu_data {
128 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
129 render_pass.set_pipeline(pipeline);
130 render_pass.set_bind_group(0, &ld.bind_group, &[]);
131 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
132 render_pass.draw(0..ld.vertex_count, 0..1);
133 }
134 }
135 if let Some(ref sb) = self.scalar_bar_gpu_data {
137 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
138 render_pass.set_pipeline(pipeline);
139 render_pass.set_bind_group(0, &sb.bind_group, &[]);
140 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
141 render_pass.draw(0..sb.vertex_count, 0..1);
142 }
143 }
144 if let Some(ref rd) = self.ruler_gpu_data {
146 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
147 render_pass.set_pipeline(pipeline);
148 render_pass.set_bind_group(0, &rd.bind_group, &[]);
149 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
150 render_pass.draw(0..rd.vertex_count, 0..1);
151 }
152 }
153 if let Some(ref lb) = self.loading_bar_gpu_data {
155 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
156 render_pass.set_pipeline(pipeline);
157 render_pass.set_bind_group(0, &lb.bind_group, &[]);
158 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
159 render_pass.draw(0..lb.vertex_count, 0..1);
160 }
161 }
162 if !self.overlay_image_gpu_data.is_empty() {
164 if let Some(pipeline) = &self.resources.screen_image_pipeline {
165 render_pass.set_pipeline(pipeline);
166 for gpu in &self.overlay_image_gpu_data {
167 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
168 render_pass.draw(0..6, 0..1);
169 }
170 }
171 }
172 }
173
174 pub fn paint_to<'rp>(&'rp self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
180 let vp_idx = frame.camera.viewport_index;
181 let camera_bg = self.viewport_camera_bind_group(vp_idx);
182 let grid_bg = self.viewport_grid_bind_group(vp_idx);
183 let vp_slot = self.viewport_slots.get(vp_idx);
184 emit_draw_calls!(
185 &self.resources,
186 &mut *render_pass,
187 frame,
188 self.use_instancing,
189 &self.instanced_batches,
190 camera_bg,
191 grid_bg,
192 &self.compute_filter_results,
193 vp_slot,
194 &self.wireframe_bind_groups
195 );
196 emit_scivis_draw_calls!(
197 &self.resources,
198 &mut *render_pass,
199 &self.point_cloud_gpu_data,
200 &self.glyph_gpu_data,
201 &self.polyline_gpu_data,
202 &self.volume_gpu_data,
203 &self.streamtube_gpu_data,
204 camera_bg,
205 &self.tube_gpu_data,
206 &self.image_slice_gpu_data,
207 &self.tensor_glyph_gpu_data,
208 &self.ribbon_gpu_data,
209 &self.volume_surface_slice_gpu_data,
210 &self.sprite_gpu_data,
211 false
212 );
213 if !self.gaussian_splat_draw_data.is_empty() {
215 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
216 render_pass.set_pipeline(dual.for_format(false));
217 render_pass.set_bind_group(0, camera_bg, &[]);
218 for dd in &self.gaussian_splat_draw_data {
219 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
220 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
221 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
222 render_pass.draw(0..6, 0..dd.count);
223 }
224 }
225 }
226 }
227 }
228 if !self.implicit_gpu_data.is_empty() {
230 if let Some(ref dual) = self.resources.implicit_pipeline {
231 render_pass.set_pipeline(dual.for_format(false));
232 render_pass.set_bind_group(0, camera_bg, &[]);
233 for gpu in &self.implicit_gpu_data {
234 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
235 render_pass.draw(0..6, 0..1);
236 }
237 }
238 }
239 if !self.mc_gpu_data.is_empty() {
241 if let Some(ref dual) = self.resources.mc_surface_pipeline {
242 render_pass.set_pipeline(dual.for_format(false));
243 render_pass.set_bind_group(0, camera_bg, &[]);
244 for mc in &self.mc_gpu_data {
245 let vol = &self.resources.mc_volumes[mc.volume_idx];
246 render_pass.set_bind_group(1, &mc.render_bg, &[]);
247 for slab in &vol.slabs {
248 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
249 render_pass.draw_indirect(&slab.indirect_buf, 0);
250 }
251 }
252 }
253 }
254 emit_outline_composite!(&self.resources, &mut *render_pass, vp_slot);
256 if let Some(sub_hl) = self.viewport_slots.get(vp_idx).and_then(|s| s.sub_highlight.as_ref()) {
258 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
259 &self.resources.sub_highlight_fill_ldr_pipeline,
260 &self.resources.sub_highlight_edge_ldr_pipeline,
261 &self.resources.sub_highlight_sprite_ldr_pipeline,
262 ) {
263 if sub_hl.fill_vertex_count > 0 {
264 render_pass.set_pipeline(fill_pl);
265 render_pass.set_bind_group(0, camera_bg, &[]);
266 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
267 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
268 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
269 }
270 if sub_hl.edge_segment_count > 0 {
271 render_pass.set_pipeline(edge_pl);
272 render_pass.set_bind_group(0, camera_bg, &[]);
273 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
274 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
275 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
276 }
277 if sub_hl.sprite_point_count > 0 {
278 render_pass.set_pipeline(sprite_pl);
279 render_pass.set_bind_group(0, camera_bg, &[]);
280 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
281 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
282 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
283 }
284 }
285 }
286 if !self.screen_image_gpu_data.is_empty() {
288 if let Some(pipeline) = &self.resources.screen_image_pipeline {
289 render_pass.set_pipeline(pipeline);
290 for gpu in &self.screen_image_gpu_data {
291 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
292 render_pass.draw(0..6, 0..1);
293 }
294 }
295 }
296 if let Some(ref ld) = self.label_gpu_data {
298 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
299 render_pass.set_pipeline(pipeline);
300 render_pass.set_bind_group(0, &ld.bind_group, &[]);
301 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
302 render_pass.draw(0..ld.vertex_count, 0..1);
303 }
304 }
305 if let Some(ref sb) = self.scalar_bar_gpu_data {
307 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
308 render_pass.set_pipeline(pipeline);
309 render_pass.set_bind_group(0, &sb.bind_group, &[]);
310 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
311 render_pass.draw(0..sb.vertex_count, 0..1);
312 }
313 }
314 if let Some(ref rd) = self.ruler_gpu_data {
316 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
317 render_pass.set_pipeline(pipeline);
318 render_pass.set_bind_group(0, &rd.bind_group, &[]);
319 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
320 render_pass.draw(0..rd.vertex_count, 0..1);
321 }
322 }
323 if let Some(ref lb) = self.loading_bar_gpu_data {
325 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
326 render_pass.set_pipeline(pipeline);
327 render_pass.set_bind_group(0, &lb.bind_group, &[]);
328 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
329 render_pass.draw(0..lb.vertex_count, 0..1);
330 }
331 }
332 if !self.overlay_image_gpu_data.is_empty() {
334 if let Some(pipeline) = &self.resources.screen_image_pipeline {
335 render_pass.set_pipeline(pipeline);
336 for gpu in &self.overlay_image_gpu_data {
337 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
338 render_pass.draw(0..6, 0..1);
339 }
340 }
341 }
342 }
343
344 pub fn prepare_ldr_dyn_res(
359 &mut self,
360 encoder: &mut wgpu::CommandEncoder,
361 device: &wgpu::Device,
362 frame: &FrameData,
363 ) -> bool {
364 if self.current_render_scale >= 1.0 - 0.001 {
365 return false;
366 }
367
368 let vp_idx = frame.camera.viewport_index;
369 let w = (frame.camera.viewport_size[0] as u32).max(1);
370 let h = (frame.camera.viewport_size[1] as u32).max(1);
371 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
372 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
373
374 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
375 self.resources.ensure_dyn_res_ds_pipeline(device);
376
377 let bg_color = frame.viewport.background_color.unwrap_or([
378 65.0 / 255.0,
379 65.0 / 255.0,
380 65.0 / 255.0,
381 1.0,
382 ]);
383
384 {
385 let slot = &self.viewport_slots[vp_idx];
386 let dr = slot.dyn_res.as_ref().unwrap();
387 let color_view = &dr.color_view;
388 let depth_view = &dr.depth_view;
389 let camera_bg = &slot.camera_bind_group;
390 let grid_bg = &slot.grid_bind_group;
391
392 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
393 label: Some("ldr_dyn_res_render_pass"),
394 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
395 view: color_view,
396 resolve_target: None,
397 ops: wgpu::Operations {
398 load: wgpu::LoadOp::Clear(wgpu::Color {
399 r: bg_color[0] as f64,
400 g: bg_color[1] as f64,
401 b: bg_color[2] as f64,
402 a: bg_color[3] as f64,
403 }),
404 store: wgpu::StoreOp::Store,
405 },
406 depth_slice: None,
407 })],
408 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
409 view: depth_view,
410 depth_ops: Some(wgpu::Operations {
411 load: wgpu::LoadOp::Clear(1.0),
412 store: wgpu::StoreOp::Discard,
413 }),
414 stencil_ops: None,
415 }),
416 timestamp_writes: None,
417 occlusion_query_set: None,
418 });
419 emit_draw_calls!(
420 &self.resources,
421 &mut render_pass,
422 frame,
423 self.use_instancing,
424 &self.instanced_batches,
425 camera_bg,
426 grid_bg,
427 &self.compute_filter_results,
428 Some(slot),
429 &self.wireframe_bind_groups
430 );
431 emit_scivis_draw_calls!(
432 &self.resources,
433 &mut render_pass,
434 &self.point_cloud_gpu_data,
435 &self.glyph_gpu_data,
436 &self.polyline_gpu_data,
437 &self.volume_gpu_data,
438 &self.streamtube_gpu_data,
439 camera_bg,
440 &self.tube_gpu_data,
441 &self.image_slice_gpu_data,
442 &self.tensor_glyph_gpu_data,
443 &self.ribbon_gpu_data,
444 &self.volume_surface_slice_gpu_data,
445 &self.sprite_gpu_data,
446 false
447 );
448 if !self.implicit_gpu_data.is_empty() {
450 if let Some(ref dual) = self.resources.implicit_pipeline {
451 render_pass.set_pipeline(dual.for_format(false));
452 render_pass.set_bind_group(0, camera_bg, &[]);
453 for gpu in &self.implicit_gpu_data {
454 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
455 render_pass.draw(0..6, 0..1);
456 }
457 }
458 }
459 if !self.mc_gpu_data.is_empty() {
461 if let Some(ref dual) = self.resources.mc_surface_pipeline {
462 render_pass.set_pipeline(dual.for_format(false));
463 render_pass.set_bind_group(0, camera_bg, &[]);
464 for mc in &self.mc_gpu_data {
465 let vol = &self.resources.mc_volumes[mc.volume_idx];
466 render_pass.set_bind_group(1, &mc.render_bg, &[]);
467 for slab in &vol.slabs {
468 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
469 render_pass.draw_indirect(&slab.indirect_buf, 0);
470 }
471 }
472 }
473 }
474 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
476 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
478 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
479 &self.resources.sub_highlight_fill_ldr_pipeline,
480 &self.resources.sub_highlight_edge_ldr_pipeline,
481 &self.resources.sub_highlight_sprite_ldr_pipeline,
482 ) {
483 if sub_hl.fill_vertex_count > 0 {
484 render_pass.set_pipeline(fill_pl);
485 render_pass.set_bind_group(0, camera_bg, &[]);
486 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
487 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
488 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
489 }
490 if sub_hl.edge_segment_count > 0 {
491 render_pass.set_pipeline(edge_pl);
492 render_pass.set_bind_group(0, camera_bg, &[]);
493 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
494 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
495 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
496 }
497 if sub_hl.sprite_point_count > 0 {
498 render_pass.set_pipeline(sprite_pl);
499 render_pass.set_bind_group(0, camera_bg, &[]);
500 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
501 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
502 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
503 }
504 }
505 }
506 if !self.screen_image_gpu_data.is_empty() {
508 if let Some(pipeline) = &self.resources.screen_image_pipeline {
509 render_pass.set_pipeline(pipeline);
510 for gpu in &self.screen_image_gpu_data {
511 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
512 render_pass.draw(0..6, 0..1);
513 }
514 }
515 }
516 if let Some(ref ld) = self.label_gpu_data {
518 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
519 render_pass.set_pipeline(pipeline);
520 render_pass.set_bind_group(0, &ld.bind_group, &[]);
521 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
522 render_pass.draw(0..ld.vertex_count, 0..1);
523 }
524 }
525 if let Some(ref sb) = self.scalar_bar_gpu_data {
527 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
528 render_pass.set_pipeline(pipeline);
529 render_pass.set_bind_group(0, &sb.bind_group, &[]);
530 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
531 render_pass.draw(0..sb.vertex_count, 0..1);
532 }
533 }
534 if let Some(ref rd) = self.ruler_gpu_data {
536 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
537 render_pass.set_pipeline(pipeline);
538 render_pass.set_bind_group(0, &rd.bind_group, &[]);
539 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
540 render_pass.draw(0..rd.vertex_count, 0..1);
541 }
542 }
543 if let Some(ref lb) = self.loading_bar_gpu_data {
545 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
546 render_pass.set_pipeline(pipeline);
547 render_pass.set_bind_group(0, &lb.bind_group, &[]);
548 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
549 render_pass.draw(0..lb.vertex_count, 0..1);
550 }
551 }
552 if !self.overlay_image_gpu_data.is_empty() {
554 if let Some(pipeline) = &self.resources.screen_image_pipeline {
555 render_pass.set_pipeline(pipeline);
556 for gpu in &self.overlay_image_gpu_data {
557 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
558 render_pass.draw(0..6, 0..1);
559 }
560 }
561 }
562 }
563
564 true
565 }
566
567 pub fn paint_dyn_res_blit(
573 &self,
574 render_pass: &mut wgpu::RenderPass<'static>,
575 frame: &FrameData,
576 ) {
577 let vp_idx = frame.camera.viewport_index;
578 if let Some(dr) = self.viewport_slots.get(vp_idx).and_then(|s| s.dyn_res.as_ref()) {
579 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
580 render_pass.set_pipeline(pipeline);
581 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
582 render_pass.draw(0..3, 0..1);
583 }
584 }
585 }
586
587 pub fn prepare_hdr_callback(
604 &mut self,
605 device: &wgpu::Device,
606 queue: &wgpu::Queue,
607 frame: &FrameData,
608 ) -> wgpu::CommandBuffer {
609 self.prepare(device, queue, frame);
610
611 let vp_idx = frame.camera.viewport_index;
612 let w = (frame.camera.viewport_size[0] as u32).max(1);
613 let h = (frame.camera.viewport_size[1] as u32).max(1);
614
615 self.resources.ensure_dyn_res_pipeline(device);
617 self.resources.ensure_dyn_res_ds_pipeline(device);
618
619 self.ensure_viewport_slot(device, vp_idx);
621 let needs_create = match self.viewport_slots[vp_idx].hdr_callback.as_ref() {
622 None => true,
623 Some(t) => t.size != [w, h],
624 };
625 if needs_create {
626 let target = self.resources.create_hdr_callback_target(device, [w, h]);
627 self.viewport_slots[vp_idx].hdr_callback = Some(target);
628 }
629
630 let output_view = self.viewport_slots[vp_idx]
634 .hdr_callback
635 .as_ref()
636 .unwrap()
637 .texture
638 .create_view(&wgpu::TextureViewDescriptor::default());
639
640 self.render_frame_internal(device, queue, &output_view, vp_idx, frame)
641 }
642
643 pub fn paint_hdr_blit(
649 &self,
650 render_pass: &mut wgpu::RenderPass<'static>,
651 frame: &FrameData,
652 ) {
653 let vp_idx = frame.camera.viewport_index;
654 if let Some(hc) = self.viewport_slots.get(vp_idx).and_then(|s| s.hdr_callback.as_ref()) {
655 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
656 render_pass.set_pipeline(pipeline);
657 render_pass.set_bind_group(0, &hc.blit_bind_group, &[]);
658 render_pass.draw(0..3, 0..1);
659 }
660 }
661 }
662
663 pub fn prepare_callback(
677 &mut self,
678 device: &wgpu::Device,
679 queue: &wgpu::Queue,
680 frame: &FrameData,
681 ) -> Vec<wgpu::CommandBuffer> {
682 if frame.effects.post_process.enabled {
683 let cb = self.prepare_hdr_callback(device, queue, frame);
684 vec![cb]
685 } else {
686 self.prepare(device, queue, frame);
687 if self.current_render_scale < 1.0 - 0.001 {
688 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
689 label: Some("ldr_dyn_res_callback_encoder"),
690 });
691 self.prepare_ldr_dyn_res(&mut encoder, device, frame);
692 vec![encoder.finish()]
693 } else {
694 Vec::new()
695 }
696 }
697 }
698
699 pub fn paint_callback(
705 &self,
706 render_pass: &mut wgpu::RenderPass<'static>,
707 frame: &FrameData,
708 ) {
709 let vp_idx = frame.camera.viewport_index;
710 if frame.effects.post_process.enabled {
711 if self.viewport_slots.get(vp_idx).and_then(|s| s.hdr_callback.as_ref()).is_some() {
712 self.paint_hdr_blit(render_pass, frame);
713 return;
714 }
715 }
716 if self.current_render_scale < 1.0 - 0.001
717 && self.viewport_slots.get(vp_idx).and_then(|s| s.dyn_res.as_ref()).is_some()
718 {
719 self.paint_dyn_res_blit(render_pass, frame);
720 } else {
721 self.paint(render_pass, frame);
722 }
723 }
724
725 pub fn render_viewport(
739 &mut self,
740 device: &wgpu::Device,
741 queue: &wgpu::Queue,
742 output_view: &wgpu::TextureView,
743 id: ViewportId,
744 frame: &FrameData,
745 ) -> wgpu::CommandBuffer {
746 self.render_frame_internal(device, queue, output_view, id.0, frame)
747 }
748
749 pub fn render(
757 &mut self,
758 device: &wgpu::Device,
759 queue: &wgpu::Queue,
760 output_view: &wgpu::TextureView,
761 frame: &FrameData,
762 ) -> wgpu::CommandBuffer {
763 self.prepare(device, queue, frame);
765 self.render_frame_internal(
766 device,
767 queue,
768 output_view,
769 frame.camera.viewport_index,
770 frame,
771 )
772 }
773
774 fn render_frame_internal(
779 &mut self,
780 device: &wgpu::Device,
781 queue: &wgpu::Queue,
782 output_view: &wgpu::TextureView,
783 vp_idx: usize,
784 frame: &FrameData,
785 ) -> wgpu::CommandBuffer {
786 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
788 SurfaceSubmission::Flat(items) => items.as_ref(),
789 };
790
791 let bg_color = frame.viewport.background_color.unwrap_or([
792 65.0 / 255.0,
793 65.0 / 255.0,
794 65.0 / 255.0,
795 1.0,
796 ]);
797 let ppp = frame.camera.pixels_per_point;
798 let w = (frame.camera.viewport_size[0] * ppp).round() as u32;
799 let h = (frame.camera.viewport_size[1] * ppp).round() as u32;
800
801 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
803 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor);
804
805 if self.ts_query_set.is_none()
807 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
808 {
809 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
810 label: Some("ts_query_set"),
811 ty: wgpu::QueryType::Timestamp,
812 count: 2,
813 }));
814 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
815 label: Some("ts_resolve_buf"),
816 size: 16,
817 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
818 mapped_at_creation: false,
819 }));
820 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
821 label: Some("ts_staging_buf"),
822 size: 16,
823 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
824 mapped_at_creation: false,
825 }));
826 self.ts_period = queue.get_timestamp_period();
827 }
828
829 if !frame.effects.post_process.enabled {
830 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
834 label: Some("ldr_encoder"),
835 });
836
837 let use_dyn_res = self.current_render_scale < 1.0 - 0.001;
838
839 if use_dyn_res {
840 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
841 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
842 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
843 }
844
845 {
846 let slot = &self.viewport_slots[vp_idx];
847 let slot_hdr = slot.hdr.as_ref().unwrap();
848 let camera_bg = &slot.camera_bind_group;
849 let grid_bg = &slot.grid_bind_group;
850 let (scene_color_view, scene_depth_view): (&wgpu::TextureView, &wgpu::TextureView) =
852 if use_dyn_res {
853 let dr = slot.dyn_res.as_ref().unwrap();
854 (&dr.color_view, &dr.depth_view)
855 } else {
856 (output_view, &slot_hdr.outline_depth_view)
857 };
858 let ts_writes = self.ts_query_set.as_ref().map(|qs| {
859 wgpu::RenderPassTimestampWrites {
860 query_set: qs,
861 beginning_of_pass_write_index: Some(0),
862 end_of_pass_write_index: Some(1),
863 }
864 });
865 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
866 label: Some("ldr_render_pass"),
867 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
868 view: scene_color_view,
869 resolve_target: None,
870 ops: wgpu::Operations {
871 load: wgpu::LoadOp::Clear(wgpu::Color {
872 r: bg_color[0] as f64,
873 g: bg_color[1] as f64,
874 b: bg_color[2] as f64,
875 a: bg_color[3] as f64,
876 }),
877 store: wgpu::StoreOp::Store,
878 },
879 depth_slice: None,
880 })],
881 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
882 view: scene_depth_view,
883 depth_ops: Some(wgpu::Operations {
884 load: wgpu::LoadOp::Clear(1.0),
885 store: wgpu::StoreOp::Discard,
886 }),
887 stencil_ops: None,
888 }),
889 timestamp_writes: ts_writes,
890 occlusion_query_set: None,
891 });
892 emit_draw_calls!(
893 &self.resources,
894 &mut render_pass,
895 frame,
896 self.use_instancing,
897 &self.instanced_batches,
898 camera_bg,
899 grid_bg,
900 &self.compute_filter_results,
901 Some(slot),
902 &self.wireframe_bind_groups
903 );
904 emit_scivis_draw_calls!(
905 &self.resources,
906 &mut render_pass,
907 &self.point_cloud_gpu_data,
908 &self.glyph_gpu_data,
909 &self.polyline_gpu_data,
910 &self.volume_gpu_data,
911 &self.streamtube_gpu_data,
912 camera_bg,
913 &self.tube_gpu_data,
914 &self.image_slice_gpu_data,
915 &self.tensor_glyph_gpu_data,
916 &self.ribbon_gpu_data,
917 &self.volume_surface_slice_gpu_data,
918 &self.sprite_gpu_data,
919 false
920 );
921 if !self.implicit_gpu_data.is_empty() {
923 if let Some(ref dual) = self.resources.implicit_pipeline {
924 render_pass.set_pipeline(dual.for_format(false));
925 render_pass.set_bind_group(0, camera_bg, &[]);
926 for gpu in &self.implicit_gpu_data {
927 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
928 render_pass.draw(0..6, 0..1);
929 }
930 }
931 }
932 if !self.mc_gpu_data.is_empty() {
934 if let Some(ref dual) = self.resources.mc_surface_pipeline {
935 render_pass.set_pipeline(dual.for_format(false));
936 render_pass.set_bind_group(0, camera_bg, &[]);
937 for mc in &self.mc_gpu_data {
938 let vol = &self.resources.mc_volumes[mc.volume_idx];
939 render_pass.set_bind_group(1, &mc.render_bg, &[]);
940 for slab in &vol.slabs {
941 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
942 render_pass.draw_indirect(&slab.indirect_buf, 0);
943 }
944 }
945 }
946 }
947 emit_outline_composite!(&self.resources, &mut render_pass, Some(slot));
949 if !self.screen_image_gpu_data.is_empty() {
954 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
955 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
956 for gpu in &self.screen_image_gpu_data {
957 if let (Some(dc_bg), Some(dc_pipe)) =
958 (&gpu.depth_bind_group, dc_pipeline)
959 {
960 render_pass.set_pipeline(dc_pipe);
961 render_pass.set_bind_group(0, dc_bg, &[]);
962 } else {
963 render_pass.set_pipeline(overlay_pipeline);
964 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
965 }
966 render_pass.draw(0..6, 0..1);
967 }
968 }
969 }
970 if let Some(ref ld) = self.label_gpu_data {
972 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
973 render_pass.set_pipeline(pipeline);
974 render_pass.set_bind_group(0, &ld.bind_group, &[]);
975 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
976 render_pass.draw(0..ld.vertex_count, 0..1);
977 }
978 }
979 if let Some(ref sb) = self.scalar_bar_gpu_data {
981 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
982 render_pass.set_pipeline(pipeline);
983 render_pass.set_bind_group(0, &sb.bind_group, &[]);
984 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
985 render_pass.draw(0..sb.vertex_count, 0..1);
986 }
987 }
988 if let Some(ref rd) = self.ruler_gpu_data {
990 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
991 render_pass.set_pipeline(pipeline);
992 render_pass.set_bind_group(0, &rd.bind_group, &[]);
993 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
994 render_pass.draw(0..rd.vertex_count, 0..1);
995 }
996 }
997 if !self.overlay_image_gpu_data.is_empty() {
999 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1000 render_pass.set_pipeline(pipeline);
1001 for gpu in &self.overlay_image_gpu_data {
1002 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
1003 render_pass.draw(0..6, 0..1);
1004 }
1005 }
1006 }
1007 }
1008
1009 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
1011 self.ts_query_set.as_ref(),
1012 self.ts_resolve_buf.as_ref(),
1013 self.ts_staging_buf.as_ref(),
1014 ) {
1015 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
1016 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
1017 self.ts_needs_readback = true;
1018 }
1019
1020 if use_dyn_res {
1022 let upscale_bg =
1023 &self.viewport_slots[vp_idx].dyn_res.as_ref().unwrap().upscale_bind_group;
1024 let mut upscale_pass =
1025 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1026 label: Some("dyn_res_upscale_pass"),
1027 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1028 view: output_view,
1029 resolve_target: None,
1030 ops: wgpu::Operations {
1031 load: wgpu::LoadOp::Load,
1032 store: wgpu::StoreOp::Store,
1033 },
1034 depth_slice: None,
1035 })],
1036 depth_stencil_attachment: None,
1037 timestamp_writes: None,
1038 occlusion_query_set: None,
1039 });
1040 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
1041 upscale_pass.set_pipeline(pipeline);
1042 upscale_pass.set_bind_group(0, upscale_bg, &[]);
1043 upscale_pass.draw(0..3, 0..1);
1044 }
1045 }
1046
1047 return encoder.finish();
1048 }
1049
1050 let pp = &frame.effects.post_process;
1052
1053 let hdr_clear_rgb = [
1054 bg_color[0].powf(2.2),
1055 bg_color[1].powf(2.2),
1056 bg_color[2].powf(2.2),
1057 ];
1058
1059 let mode = match pp.tone_mapping {
1061 crate::renderer::ToneMapping::Reinhard => 0u32,
1062 crate::renderer::ToneMapping::Aces => 1u32,
1063 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
1064 };
1065 let tm_uniform = crate::resources::ToneMapUniform {
1066 exposure: pp.exposure,
1067 mode,
1068 bloom_enabled: if pp.bloom { 1 } else { 0 },
1069 ssao_enabled: if pp.ssao { 1 } else { 0 },
1070 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
1071 edl_enabled: if pp.edl_enabled { 1 } else { 0 },
1072 edl_radius: pp.edl_radius,
1073 edl_strength: pp.edl_strength,
1074 background_color: bg_color,
1075 near_plane: frame.camera.render_camera.near,
1076 far_plane: frame.camera.render_camera.far,
1077 lic_enabled: if frame.scene.lic_items.is_empty() { 0 } else { 1 },
1078 lic_strength: frame.scene.lic_items.first().map(|i| i.config.strength).unwrap_or(0.5),
1079 };
1080 {
1081 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1082 queue.write_buffer(
1083 &hdr.tone_map_uniform_buf,
1084 0,
1085 bytemuck::cast_slice(&[tm_uniform]),
1086 );
1087
1088 if pp.ssao {
1090 let proj = frame.camera.render_camera.projection;
1091 let inv_proj = proj.inverse();
1092 let ssao_uniform = crate::resources::SsaoUniform {
1093 inv_proj: inv_proj.to_cols_array_2d(),
1094 proj: proj.to_cols_array_2d(),
1095 radius: 0.5,
1096 bias: 0.025,
1097 _pad: [0.0; 2],
1098 };
1099 queue.write_buffer(
1100 &hdr.ssao_uniform_buf,
1101 0,
1102 bytemuck::cast_slice(&[ssao_uniform]),
1103 );
1104 }
1105
1106 if pp.contact_shadows {
1108 let proj = frame.camera.render_camera.projection;
1109 let inv_proj = proj.inverse();
1110 let light_dir_world: glam::Vec3 =
1111 if let Some(l) = frame.effects.lighting.lights.first() {
1112 match l.kind {
1113 LightKind::Directional { direction } => {
1114 glam::Vec3::from(direction).normalize()
1115 }
1116 LightKind::Spot { direction, .. } => {
1117 glam::Vec3::from(direction).normalize()
1118 }
1119 _ => glam::Vec3::new(0.0, -1.0, 0.0),
1120 }
1121 } else {
1122 glam::Vec3::new(0.0, -1.0, 0.0)
1123 };
1124 let view = frame.camera.render_camera.view;
1125 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
1126 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
1127 let cs_uniform = crate::resources::ContactShadowUniform {
1128 inv_proj: inv_proj.to_cols_array_2d(),
1129 proj: proj.to_cols_array_2d(),
1130 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
1131 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
1132 params: [
1133 pp.contact_shadow_max_distance,
1134 pp.contact_shadow_steps as f32,
1135 pp.contact_shadow_thickness,
1136 0.0,
1137 ],
1138 };
1139 queue.write_buffer(
1140 &hdr.contact_shadow_uniform_buf,
1141 0,
1142 bytemuck::cast_slice(&[cs_uniform]),
1143 );
1144 }
1145
1146 if pp.bloom {
1148 let bloom_u = crate::resources::BloomUniform {
1149 threshold: pp.bloom_threshold,
1150 intensity: pp.bloom_intensity,
1151 horizontal: 0,
1152 _pad: 0,
1153 };
1154 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
1155 }
1156 }
1157
1158 if pp.dof_enabled {
1160 let (w, h) = {
1161 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1162 (hdr.size[0] as f32, hdr.size[1] as f32)
1163 };
1164 let dof_uniform = crate::resources::DofUniform {
1165 focal_distance: pp.dof_focal_distance,
1166 focal_range: pp.dof_focal_range,
1167 max_blur_radius: pp.dof_max_blur_radius,
1168 near_plane: frame.camera.render_camera.near,
1169 far_plane: frame.camera.render_camera.far,
1170 viewport_width: w,
1171 viewport_height: h,
1172 _pad: 0.0,
1173 };
1174 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1175 queue.write_buffer(
1176 &hdr.dof_uniform_buf,
1177 0,
1178 bytemuck::cast_slice(&[dof_uniform]),
1179 );
1180 }
1181
1182 {
1184 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1185 self.resources.rebuild_tone_map_bind_group(
1186 device,
1187 hdr,
1188 pp.bloom,
1189 pp.ssao,
1190 pp.contact_shadows,
1191 !frame.scene.lic_items.is_empty(),
1192 pp.dof_enabled,
1193 );
1194 }
1195
1196 {
1201 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
1202 self.instanced_batches.iter().any(|b| b.is_transparent)
1203 } else {
1204 scene_items
1205 .iter()
1206 .any(|i| i.visible && i.material.opacity < 1.0)
1207 } || frame.scene.transparent_volume_meshes.iter().any(|i| i.visible);
1208 if needs_oit {
1209 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
1210 self.resources
1211 .ensure_viewport_oit(device, hdr, w.max(1), h.max(1));
1212 }
1213 }
1214
1215 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1219 label: Some("hdr_encoder"),
1220 });
1221
1222 let slot = &self.viewport_slots[vp_idx];
1224 let camera_bg = &slot.camera_bind_group;
1225 let slot_hdr = slot.hdr.as_ref().unwrap();
1226
1227 {
1231 let use_ssaa = ssaa_factor > 1
1233 && slot_hdr.ssaa_color_view.is_some()
1234 && slot_hdr.ssaa_depth_view.is_some();
1235 let scene_color_view = if use_ssaa {
1236 slot_hdr.ssaa_color_view.as_ref().unwrap()
1237 } else {
1238 &slot_hdr.hdr_view
1239 };
1240 let scene_depth_view = if use_ssaa {
1241 slot_hdr.ssaa_depth_view.as_ref().unwrap()
1242 } else {
1243 &slot_hdr.hdr_depth_view
1244 };
1245
1246 let clear_wgpu = wgpu::Color {
1247 r: hdr_clear_rgb[0] as f64,
1248 g: hdr_clear_rgb[1] as f64,
1249 b: hdr_clear_rgb[2] as f64,
1250 a: 0.0,
1253 };
1254
1255 let hdr_ts_writes = self.ts_query_set.as_ref().map(|qs| {
1256 wgpu::RenderPassTimestampWrites {
1257 query_set: qs,
1258 beginning_of_pass_write_index: Some(0),
1259 end_of_pass_write_index: Some(1),
1260 }
1261 });
1262 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1263 label: Some("hdr_scene_pass"),
1264 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1265 view: scene_color_view,
1266 resolve_target: None,
1267 ops: wgpu::Operations {
1268 load: wgpu::LoadOp::Clear(clear_wgpu),
1269 store: wgpu::StoreOp::Store,
1270 },
1271 depth_slice: None,
1272 })],
1273 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1274 view: scene_depth_view,
1275 depth_ops: Some(wgpu::Operations {
1276 load: wgpu::LoadOp::Clear(1.0),
1277 store: wgpu::StoreOp::Store,
1278 }),
1279 stencil_ops: Some(wgpu::Operations {
1280 load: wgpu::LoadOp::Clear(0),
1281 store: wgpu::StoreOp::Store,
1282 }),
1283 }),
1284 timestamp_writes: hdr_ts_writes,
1285 occlusion_query_set: None,
1286 });
1287
1288 let resources = &self.resources;
1289 render_pass.set_bind_group(0, camera_bg, &[]);
1290
1291 let show_skybox = frame
1293 .effects
1294 .environment
1295 .as_ref()
1296 .is_some_and(|e| e.show_skybox)
1297 && resources.ibl_skybox_view.is_some();
1298
1299 let use_instancing = self.use_instancing;
1300 let batches = &self.instanced_batches;
1301
1302 if !scene_items.is_empty() {
1303 if use_instancing && !batches.is_empty() {
1304 let excluded_items: Vec<&SceneRenderItem> = scene_items
1305 .iter()
1306 .filter(|item| {
1307 item.visible
1308 && (item.active_attribute.is_some()
1309 || item.material.is_two_sided()
1310 || item.material.matcap_id.is_some())
1311 && resources
1312 .mesh_store
1313 .get(item.mesh_id)
1314 .is_some()
1315 })
1316 .collect();
1317
1318 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1322 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1323 for (batch_global_idx, batch) in batches.iter().enumerate() {
1324 if batch.is_transparent {
1325 transparent_batches.push((batch_global_idx, batch));
1326 } else {
1327 opaque_batches.push((batch_global_idx, batch));
1328 }
1329 }
1330
1331 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
1332 let use_indirect = self.gpu_culling_enabled
1333 && resources.hdr_solid_instanced_cull_pipeline.is_some()
1334 && resources.indirect_args_buf.is_some();
1335
1336 if use_indirect {
1337 if let (
1338 Some(pipeline),
1339 Some(indirect_buf),
1340 ) = (
1341 &resources.hdr_solid_instanced_cull_pipeline,
1342 &resources.indirect_args_buf,
1343 ) {
1344 render_pass.set_pipeline(pipeline);
1345 for (batch_global_idx, batch) in &opaque_batches {
1346 let Some(mesh) = resources.mesh_store.get(batch.mesh_id)
1347 else {
1348 continue;
1349 };
1350 let mat_key = (
1351 batch.texture_id.unwrap_or(u64::MAX),
1352 batch.normal_map_id.unwrap_or(u64::MAX),
1353 batch.ao_map_id.unwrap_or(u64::MAX),
1354 );
1355 let Some(inst_tex_bg) =
1356 resources.instance_cull_bind_groups.get(&mat_key)
1357 else {
1358 continue;
1359 };
1360 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1361 render_pass.set_vertex_buffer(
1362 0,
1363 mesh.vertex_buffer.slice(..),
1364 );
1365 render_pass.set_index_buffer(
1366 mesh.index_buffer.slice(..),
1367 wgpu::IndexFormat::Uint32,
1368 );
1369 render_pass.draw_indexed_indirect(
1372 indirect_buf,
1373 *batch_global_idx as u64 * 20,
1374 );
1375 }
1376 }
1377 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
1378 render_pass.set_pipeline(pipeline);
1379 for (_, batch) in &opaque_batches {
1380 let Some(mesh) = resources
1381 .mesh_store
1382 .get(batch.mesh_id)
1383 else {
1384 continue;
1385 };
1386 let mat_key = (
1387 batch.texture_id.unwrap_or(u64::MAX),
1388 batch.normal_map_id.unwrap_or(u64::MAX),
1389 batch.ao_map_id.unwrap_or(u64::MAX),
1390 );
1391 let Some(inst_tex_bg) =
1392 resources.instance_bind_groups.get(&mat_key)
1393 else {
1394 continue;
1395 };
1396 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1397 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1398 render_pass.set_index_buffer(
1399 mesh.index_buffer.slice(..),
1400 wgpu::IndexFormat::Uint32,
1401 );
1402 render_pass.draw_indexed(
1403 0..mesh.index_count,
1404 0,
1405 batch.instance_offset
1406 ..batch.instance_offset + batch.instance_count,
1407 );
1408 }
1409 }
1410 }
1411
1412 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
1417 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
1418 render_pass.set_pipeline(hdr_wf);
1419 let mut wf_idx = 0usize;
1420 for item in scene_items {
1421 if !item.visible {
1422 continue;
1423 }
1424 let Some(mesh) = resources
1425 .mesh_store
1426 .get(item.mesh_id)
1427 else {
1428 continue;
1429 };
1430 let bg = self.wireframe_bind_groups.get(wf_idx)
1431 .unwrap_or(&mesh.object_bind_group);
1432 render_pass.set_bind_group(1, bg, &[]);
1433 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1434 render_pass.set_index_buffer(
1435 mesh.edge_index_buffer.slice(..),
1436 wgpu::IndexFormat::Uint32,
1437 );
1438 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1439 wf_idx += 1;
1440 }
1441 }
1442 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
1443 &resources.hdr_solid_pipeline,
1444 &resources.hdr_solid_two_sided_pipeline,
1445 ) {
1446 for item in excluded_items
1447 .into_iter()
1448 .filter(|item| item.material.opacity >= 1.0)
1449 {
1450 let Some(mesh) = resources
1451 .mesh_store
1452 .get(item.mesh_id)
1453 else {
1454 continue;
1455 };
1456 let pipeline = if item.material.is_two_sided() {
1457 hdr_solid_two_sided
1458 } else {
1459 hdr_solid
1460 };
1461 render_pass.set_pipeline(pipeline);
1462 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1463 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1464 render_pass.set_index_buffer(
1465 mesh.index_buffer.slice(..),
1466 wgpu::IndexFormat::Uint32,
1467 );
1468 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1469 }
1470 }
1471 } else {
1472 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
1474 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
1475 let pos =
1476 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
1477 (pos - eye).length()
1478 };
1479
1480 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
1481 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
1482 for item in scene_items {
1483 if !item.visible
1484 || resources
1485 .mesh_store
1486 .get(item.mesh_id)
1487 .is_none()
1488 {
1489 continue;
1490 }
1491 if item.material.opacity < 1.0 {
1492 transparent.push(item);
1493 } else {
1494 opaque.push(item);
1495 }
1496 }
1497 opaque.sort_by(|a, b| {
1498 dist_from_eye(a)
1499 .partial_cmp(&dist_from_eye(b))
1500 .unwrap_or(std::cmp::Ordering::Equal)
1501 });
1502 transparent.sort_by(|a, b| {
1503 dist_from_eye(b)
1504 .partial_cmp(&dist_from_eye(a))
1505 .unwrap_or(std::cmp::Ordering::Equal)
1506 });
1507
1508 let draw_item_hdr =
1509 |render_pass: &mut wgpu::RenderPass<'_>,
1510 item: &SceneRenderItem,
1511 solid_pl: &wgpu::RenderPipeline,
1512 trans_pl: &wgpu::RenderPipeline,
1513 wf_pl: &wgpu::RenderPipeline| {
1514 let mesh = resources
1515 .mesh_store
1516 .get(item.mesh_id)
1517 .unwrap();
1518 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1521 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1522 matches!(
1523 a.kind,
1524 crate::resources::AttributeKind::Face
1525 | crate::resources::AttributeKind::FaceColor
1526 | crate::resources::AttributeKind::Halfedge
1527 | crate::resources::AttributeKind::Corner
1528 )
1529 });
1530 if frame.viewport.wireframe_mode {
1531 render_pass.set_pipeline(wf_pl);
1532 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1533 render_pass.set_index_buffer(
1534 mesh.edge_index_buffer.slice(..),
1535 wgpu::IndexFormat::Uint32,
1536 );
1537 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1538 } else if is_face_attr {
1539 if let Some(ref fvb) = mesh.face_vertex_buffer {
1540 let pl = if item.material.opacity < 1.0 {
1541 trans_pl
1542 } else {
1543 solid_pl
1544 };
1545 render_pass.set_pipeline(pl);
1546 render_pass.set_vertex_buffer(0, fvb.slice(..));
1547 render_pass.draw(0..mesh.index_count, 0..1);
1548 }
1549 } else if item.material.opacity < 1.0 {
1550 render_pass.set_pipeline(trans_pl);
1551 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1552 render_pass.set_index_buffer(
1553 mesh.index_buffer.slice(..),
1554 wgpu::IndexFormat::Uint32,
1555 );
1556 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1557 } else {
1558 render_pass.set_pipeline(solid_pl);
1559 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1560 render_pass.set_index_buffer(
1561 mesh.index_buffer.slice(..),
1562 wgpu::IndexFormat::Uint32,
1563 );
1564 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1565 }
1566 };
1567
1568 let _ = &transparent; if let (
1572 Some(hdr_solid),
1573 Some(hdr_solid_two_sided),
1574 Some(hdr_trans),
1575 Some(hdr_wf),
1576 ) = (
1577 &resources.hdr_solid_pipeline,
1578 &resources.hdr_solid_two_sided_pipeline,
1579 &resources.hdr_transparent_pipeline,
1580 &resources.hdr_wireframe_pipeline,
1581 ) {
1582 for item in &opaque {
1583 let solid_pl = if item.material.is_two_sided() {
1584 hdr_solid_two_sided
1585 } else {
1586 hdr_solid
1587 };
1588 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
1589 }
1590 }
1591 }
1592 }
1593
1594 if !slot.cap_buffers.is_empty() {
1596 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
1597 render_pass.set_pipeline(hdr_overlay);
1598 render_pass.set_bind_group(0, camera_bg, &[]);
1599 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
1600 render_pass.set_bind_group(1, bg, &[]);
1601 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1602 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1603 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
1604 }
1605 }
1606 }
1607
1608 emit_scivis_draw_calls!(
1610 &self.resources,
1611 &mut render_pass,
1612 &self.point_cloud_gpu_data,
1613 &self.glyph_gpu_data,
1614 &self.polyline_gpu_data,
1615 &self.volume_gpu_data,
1616 &self.streamtube_gpu_data,
1617 camera_bg,
1618 &self.tube_gpu_data,
1619 &self.image_slice_gpu_data,
1620 &self.tensor_glyph_gpu_data,
1621 &self.ribbon_gpu_data,
1622 &self.volume_surface_slice_gpu_data,
1623 &self.sprite_gpu_data,
1624 true
1625 );
1626
1627 if !self.implicit_gpu_data.is_empty() {
1629 if let Some(ref dual) = self.resources.implicit_pipeline {
1630 render_pass.set_pipeline(dual.for_format(true));
1631 render_pass.set_bind_group(0, camera_bg, &[]);
1632 for gpu in &self.implicit_gpu_data {
1633 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1634 render_pass.draw(0..6, 0..1);
1635 }
1636 }
1637 }
1638 if !self.mc_gpu_data.is_empty() {
1640 if let Some(ref dual) = self.resources.mc_surface_pipeline {
1641 render_pass.set_pipeline(dual.for_format(true));
1642 render_pass.set_bind_group(0, camera_bg, &[]);
1643 for mc in &self.mc_gpu_data {
1644 let vol = &self.resources.mc_volumes[mc.volume_idx];
1645 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1646 for slab in &vol.slabs {
1647 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1648 render_pass.draw_indirect(&slab.indirect_buf, 0);
1649 }
1650 }
1651 }
1652 }
1653
1654 if !self.gaussian_splat_draw_data.is_empty() {
1656 if let Some(ref dual) = self.resources.gaussian_splat_pipeline {
1657 render_pass.set_pipeline(dual.for_format(true));
1658 render_pass.set_bind_group(0, camera_bg, &[]);
1659 for dd in &self.gaussian_splat_draw_data {
1660 if let Some(set) = self.resources.gaussian_splat_store.get(dd.store_index) {
1661 if let Some(Some(vp_sort)) = set.viewport_sort.get(dd.viewport_index) {
1662 render_pass.set_bind_group(1, &vp_sort.render_bg, &[]);
1663 render_pass.draw(0..6, 0..dd.count);
1664 }
1665 }
1666 }
1667 }
1668 }
1669
1670 if show_skybox {
1672 render_pass.set_bind_group(0, camera_bg, &[]);
1673 render_pass.set_pipeline(&resources.skybox_pipeline);
1674 render_pass.draw(0..3, 0..1);
1675 }
1676 }
1677
1678 if ssaa_factor > 1 {
1683 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1684 if let (Some(pipeline), Some(bg)) = (
1685 &self.resources.ssaa_resolve_pipeline,
1686 &slot_hdr.ssaa_resolve_bind_group,
1687 ) {
1688 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1689 label: Some("ssaa_resolve_pass"),
1690 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1691 view: &slot_hdr.hdr_view,
1692 resolve_target: None,
1693 ops: wgpu::Operations {
1694 load: wgpu::LoadOp::Load,
1695 store: wgpu::StoreOp::Store,
1696 },
1697 depth_slice: None,
1698 })],
1699 depth_stencil_attachment: None,
1700 timestamp_writes: None,
1701 occlusion_query_set: None,
1702 });
1703 resolve_pass.set_pipeline(pipeline);
1704 resolve_pass.set_bind_group(0, bg, &[]);
1705 resolve_pass.draw(0..3, 0..1);
1706 }
1707 }
1708
1709 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
1715 let resources = &self.resources;
1716 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
1717 &resources.sub_highlight_fill_pipeline,
1718 &resources.sub_highlight_edge_pipeline,
1719 &resources.sub_highlight_sprite_pipeline,
1720 ) {
1721 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1722 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
1723 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1724 label: Some("sub_highlight_pass"),
1725 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1726 view: &slot_hdr.hdr_view,
1727 resolve_target: None,
1728 ops: wgpu::Operations {
1729 load: wgpu::LoadOp::Load,
1730 store: wgpu::StoreOp::Store,
1731 },
1732 depth_slice: None,
1733 })],
1734 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1735 view: &slot_hdr.hdr_depth_view,
1736 depth_ops: Some(wgpu::Operations {
1737 load: wgpu::LoadOp::Load,
1738 store: wgpu::StoreOp::Discard,
1739 }),
1740 stencil_ops: None,
1741 }),
1742 timestamp_writes: None,
1743 occlusion_query_set: None,
1744 });
1745
1746 if sub_hl.fill_vertex_count > 0 {
1747 pass.set_pipeline(fill_pl);
1748 pass.set_bind_group(0, camera_bg, &[]);
1749 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
1750 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
1751 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
1752 }
1753 if sub_hl.edge_segment_count > 0 {
1754 pass.set_pipeline(edge_pl);
1755 pass.set_bind_group(0, camera_bg, &[]);
1756 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
1757 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
1758 pass.draw(0..6, 0..sub_hl.edge_segment_count);
1759 }
1760 if sub_hl.sprite_point_count > 0 {
1761 pass.set_pipeline(sprite_pl);
1762 pass.set_bind_group(0, camera_bg, &[]);
1763 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
1764 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
1765 pass.draw(0..6, 0..sub_hl.sprite_point_count);
1766 }
1767 }
1768 }
1769
1770 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
1775 self.instanced_batches.iter().any(|b| b.is_transparent)
1776 } else {
1777 scene_items
1778 .iter()
1779 .any(|i| i.visible && i.material.opacity < 1.0)
1780 } || frame.scene.transparent_volume_meshes.iter().any(|i| i.visible);
1781
1782 if has_transparent {
1783 if let (Some(accum_view), Some(reveal_view)) = (
1785 slot_hdr.oit_accum_view.as_ref(),
1786 slot_hdr.oit_reveal_view.as_ref(),
1787 ) {
1788 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1789 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1791 label: Some("oit_pass"),
1792 color_attachments: &[
1793 Some(wgpu::RenderPassColorAttachment {
1794 view: accum_view,
1795 resolve_target: None,
1796 ops: wgpu::Operations {
1797 load: wgpu::LoadOp::Clear(wgpu::Color {
1798 r: 0.0,
1799 g: 0.0,
1800 b: 0.0,
1801 a: 0.0,
1802 }),
1803 store: wgpu::StoreOp::Store,
1804 },
1805 depth_slice: None,
1806 }),
1807 Some(wgpu::RenderPassColorAttachment {
1808 view: reveal_view,
1809 resolve_target: None,
1810 ops: wgpu::Operations {
1811 load: wgpu::LoadOp::Clear(wgpu::Color {
1812 r: 1.0,
1813 g: 1.0,
1814 b: 1.0,
1815 a: 1.0,
1816 }),
1817 store: wgpu::StoreOp::Store,
1818 },
1819 depth_slice: None,
1820 }),
1821 ],
1822 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1823 view: hdr_depth_view,
1824 depth_ops: Some(wgpu::Operations {
1825 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
1827 }),
1828 stencil_ops: None,
1829 }),
1830 timestamp_writes: None,
1831 occlusion_query_set: None,
1832 });
1833
1834 oit_pass.set_bind_group(0, camera_bg, &[]);
1835
1836 if self.use_instancing && !self.instanced_batches.is_empty() {
1837 let use_indirect_oit = self.gpu_culling_enabled
1838 && self.resources.oit_instanced_cull_pipeline.is_some()
1839 && self.resources.indirect_args_buf.is_some();
1840
1841 if use_indirect_oit {
1842 if let (
1843 Some(pipeline),
1844 Some(indirect_buf),
1845 ) = (
1846 &self.resources.oit_instanced_cull_pipeline,
1847 &self.resources.indirect_args_buf,
1848 ) {
1849 oit_pass.set_pipeline(pipeline);
1850 for (batch_global_idx, batch) in
1851 self.instanced_batches.iter().enumerate()
1852 {
1853 if !batch.is_transparent {
1854 continue;
1855 }
1856 let Some(mesh) =
1857 self.resources.mesh_store.get(batch.mesh_id)
1858 else {
1859 continue;
1860 };
1861 let mat_key = (
1862 batch.texture_id.unwrap_or(u64::MAX),
1863 batch.normal_map_id.unwrap_or(u64::MAX),
1864 batch.ao_map_id.unwrap_or(u64::MAX),
1865 );
1866 let Some(inst_tex_bg) =
1867 self.resources.instance_cull_bind_groups.get(&mat_key)
1868 else {
1869 continue;
1870 };
1871 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1872 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1873 oit_pass.set_index_buffer(
1874 mesh.index_buffer.slice(..),
1875 wgpu::IndexFormat::Uint32,
1876 );
1877 oit_pass.draw_indexed_indirect(
1878 indirect_buf,
1879 batch_global_idx as u64 * 20,
1880 );
1881 }
1882 }
1883 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
1884 oit_pass.set_pipeline(pipeline);
1885 for batch in &self.instanced_batches {
1886 if !batch.is_transparent {
1887 continue;
1888 }
1889 let Some(mesh) = self
1890 .resources
1891 .mesh_store
1892 .get(batch.mesh_id)
1893 else {
1894 continue;
1895 };
1896 let mat_key = (
1897 batch.texture_id.unwrap_or(u64::MAX),
1898 batch.normal_map_id.unwrap_or(u64::MAX),
1899 batch.ao_map_id.unwrap_or(u64::MAX),
1900 );
1901 let Some(inst_tex_bg) =
1902 self.resources.instance_bind_groups.get(&mat_key)
1903 else {
1904 continue;
1905 };
1906 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1907 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1908 oit_pass.set_index_buffer(
1909 mesh.index_buffer.slice(..),
1910 wgpu::IndexFormat::Uint32,
1911 );
1912 oit_pass.draw_indexed(
1913 0..mesh.index_count,
1914 0,
1915 batch.instance_offset..batch.instance_offset + batch.instance_count,
1916 );
1917 }
1918 }
1919 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
1920 oit_pass.set_pipeline(pipeline);
1921 for item in scene_items {
1922 if !item.visible || item.material.opacity >= 1.0 {
1923 continue;
1924 }
1925 let Some(mesh) = self
1926 .resources
1927 .mesh_store
1928 .get(item.mesh_id)
1929 else {
1930 continue;
1931 };
1932 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1933 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1934 oit_pass.set_index_buffer(
1935 mesh.index_buffer.slice(..),
1936 wgpu::IndexFormat::Uint32,
1937 );
1938 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1939 }
1940 }
1941
1942 if !frame.scene.transparent_volume_meshes.is_empty() {
1946 self.resources.ensure_pt_pipeline(device);
1947 if let Some(pipeline) = self.resources.pt_pipeline.as_ref() {
1948 oit_pass.set_pipeline(pipeline);
1949 oit_pass.set_bind_group(0, camera_bg, &[]);
1950 for item in &frame.scene.transparent_volume_meshes {
1951 if !item.visible {
1952 continue;
1953 }
1954 let Some(gpu) =
1955 self.resources.projected_tet_store.get(item.id.0)
1956 else {
1957 continue;
1958 };
1959 let (scalar_min, scalar_max) =
1960 item.scalar_range.unwrap_or(gpu.scalar_range);
1961 let uniform = crate::resources::ProjectedTetUniform {
1962 density: item.density,
1963 scalar_min,
1964 scalar_max,
1965 threshold_min: item.threshold_min,
1966 threshold_max: item.threshold_max,
1967 _pad: 0.0,
1968 };
1969 queue.write_buffer(
1970 &gpu.uniform_buffer,
1971 0,
1972 bytemuck::bytes_of(&uniform),
1973 );
1974 for chunk in &gpu.chunks {
1975 oit_pass.set_bind_group(1, &chunk.bind_group, &[]);
1976 oit_pass.draw(0..6, 0..chunk.tet_count);
1977 }
1978 }
1979 }
1980 }
1981 }
1982 }
1983
1984 if has_transparent {
1989 if let (Some(pipeline), Some(bg)) = (
1990 self.resources.oit_composite_pipeline.as_ref(),
1991 slot_hdr.oit_composite_bind_group.as_ref(),
1992 ) {
1993 let hdr_view = &slot_hdr.hdr_view;
1994 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1995 label: Some("oit_composite_pass"),
1996 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1997 view: hdr_view,
1998 resolve_target: None,
1999 ops: wgpu::Operations {
2000 load: wgpu::LoadOp::Load,
2001 store: wgpu::StoreOp::Store,
2002 },
2003 depth_slice: None,
2004 })],
2005 depth_stencil_attachment: None,
2006 timestamp_writes: None,
2007 occlusion_query_set: None,
2008 });
2009 composite_pass.set_pipeline(pipeline);
2010 composite_pass.set_bind_group(0, bg, &[]);
2011 composite_pass.draw(0..3, 0..1);
2012 }
2013 }
2014
2015 if !self.lic_gpu_data.is_empty() {
2021 if let (Some(surface_pipeline), Some(advect_pipeline)) = (
2022 self.resources.lic_surface_pipeline.as_ref(),
2023 self.resources.lic_advect_pipeline.as_ref(),
2024 ) {
2025 let camera_bg = &slot.camera_bind_group;
2026 {
2028 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2029 label: Some("lic_surface_pass"),
2030 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2031 view: &slot_hdr.lic_vector_view,
2032 resolve_target: None,
2033 ops: wgpu::Operations {
2034 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2035 store: wgpu::StoreOp::Store,
2036 },
2037 depth_slice: None,
2038 })],
2039 depth_stencil_attachment: None,
2040 timestamp_writes: None,
2041 occlusion_query_set: None,
2042 });
2043 pass.set_pipeline(surface_pipeline);
2044 pass.set_bind_group(0, camera_bg, &[]);
2045 for gpu in &self.lic_gpu_data {
2046 let Some(mesh) = self.resources.mesh_store.get(gpu.mesh_id) else {
2047 continue;
2048 };
2049 let Some(vec_buf) = mesh.vector_attribute_buffers.get(&gpu.vector_attribute) else {
2050 continue;
2051 };
2052 pass.set_bind_group(1, &gpu.bind_group, &[]);
2053 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2054 pass.set_vertex_buffer(1, vec_buf.slice(..));
2055 pass.set_index_buffer(
2056 mesh.index_buffer.slice(..),
2057 wgpu::IndexFormat::Uint32,
2058 );
2059 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2060 }
2061 }
2062 {
2064 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2065 label: Some("lic_advect_pass"),
2066 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2067 view: &slot_hdr.lic_output_view,
2068 resolve_target: None,
2069 ops: wgpu::Operations {
2070 load: wgpu::LoadOp::Clear(wgpu::Color {
2071 r: 0.5,
2072 g: 0.0,
2073 b: 0.0,
2074 a: 1.0,
2075 }),
2076 store: wgpu::StoreOp::Store,
2077 },
2078 depth_slice: None,
2079 })],
2080 depth_stencil_attachment: None,
2081 timestamp_writes: None,
2082 occlusion_query_set: None,
2083 });
2084 pass.set_pipeline(advect_pipeline);
2085 pass.set_bind_group(0, &slot_hdr.lic_advect_bind_group, &[]);
2086 pass.draw(0..3, 0..1);
2087 }
2088 }
2089 }
2090
2091 if !slot.outline_object_buffers.is_empty() || !slot.splat_outline_buffers.is_empty()
2097 || !slot.volume_outline_indices.is_empty()
2098 || !slot.glyph_outline_indices.is_empty()
2099 || !slot.tensor_glyph_outline_indices.is_empty()
2100 || !slot.sprite_outline_indices.is_empty()
2101 {
2102 let hdr_pipeline = self
2104 .resources
2105 .outline_composite_pipeline_hdr
2106 .as_ref()
2107 .or(self.resources.outline_composite_pipeline_single.as_ref());
2108 if let Some(pipeline) = hdr_pipeline {
2109 let bg = &slot_hdr.outline_composite_bind_group;
2110 let hdr_view = &slot_hdr.hdr_view;
2111 let hdr_depth_view = &slot_hdr.hdr_depth_view;
2112 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2113 label: Some("hdr_outline_composite_pass"),
2114 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2115 view: hdr_view,
2116 resolve_target: None,
2117 ops: wgpu::Operations {
2118 load: wgpu::LoadOp::Load,
2119 store: wgpu::StoreOp::Store,
2120 },
2121 depth_slice: None,
2122 })],
2123 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2124 view: hdr_depth_view,
2125 depth_ops: Some(wgpu::Operations {
2126 load: wgpu::LoadOp::Load,
2127 store: wgpu::StoreOp::Store,
2128 }),
2129 stencil_ops: None,
2130 }),
2131 timestamp_writes: None,
2132 occlusion_query_set: None,
2133 });
2134 outline_pass.set_pipeline(pipeline);
2135 outline_pass.set_bind_group(0, bg, &[]);
2136 outline_pass.draw(0..3, 0..1);
2137 }
2138 }
2139
2140 let throttle_effects = self.degradation_effects_throttled;
2143
2144 if pp.ssao && !throttle_effects {
2148 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
2149 {
2150 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2151 label: Some("ssao_pass"),
2152 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2153 view: &slot_hdr.ssao_view,
2154 resolve_target: None,
2155 ops: wgpu::Operations {
2156 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2157 store: wgpu::StoreOp::Store,
2158 },
2159 depth_slice: None,
2160 })],
2161 depth_stencil_attachment: None,
2162 timestamp_writes: None,
2163 occlusion_query_set: None,
2164 });
2165 ssao_pass.set_pipeline(ssao_pipeline);
2166 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
2167 ssao_pass.draw(0..3, 0..1);
2168 }
2169
2170 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
2172 let mut ssao_blur_pass =
2173 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2174 label: Some("ssao_blur_pass"),
2175 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2176 view: &slot_hdr.ssao_blur_view,
2177 resolve_target: None,
2178 ops: wgpu::Operations {
2179 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2180 store: wgpu::StoreOp::Store,
2181 },
2182 depth_slice: None,
2183 })],
2184 depth_stencil_attachment: None,
2185 timestamp_writes: None,
2186 occlusion_query_set: None,
2187 });
2188 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
2189 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
2190 ssao_blur_pass.draw(0..3, 0..1);
2191 }
2192 }
2193 }
2194
2195 if pp.contact_shadows && !throttle_effects {
2199 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
2200 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2201 label: Some("contact_shadow_pass"),
2202 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2203 view: &slot_hdr.contact_shadow_view,
2204 resolve_target: None,
2205 depth_slice: None,
2206 ops: wgpu::Operations {
2207 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
2208 store: wgpu::StoreOp::Store,
2209 },
2210 })],
2211 depth_stencil_attachment: None,
2212 timestamp_writes: None,
2213 occlusion_query_set: None,
2214 });
2215 cs_pass.set_pipeline(cs_pipeline);
2216 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
2217 cs_pass.draw(0..3, 0..1);
2218 }
2219 }
2220
2221 if pp.bloom && !throttle_effects {
2225 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
2227 {
2228 let mut threshold_pass =
2229 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2230 label: Some("bloom_threshold_pass"),
2231 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2232 view: &slot_hdr.bloom_threshold_view,
2233 resolve_target: None,
2234 ops: wgpu::Operations {
2235 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2236 store: wgpu::StoreOp::Store,
2237 },
2238 depth_slice: None,
2239 })],
2240 depth_stencil_attachment: None,
2241 timestamp_writes: None,
2242 occlusion_query_set: None,
2243 });
2244 threshold_pass.set_pipeline(bloom_threshold_pipeline);
2245 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
2246 threshold_pass.draw(0..3, 0..1);
2247 }
2248
2249 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
2252 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
2253 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
2254 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
2255 let bloom_ping_view = &slot_hdr.bloom_ping_view;
2256 let bloom_pong_view = &slot_hdr.bloom_pong_view;
2257 const BLUR_ITERATIONS: usize = 4;
2258 for i in 0..BLUR_ITERATIONS {
2259 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
2261 {
2262 let mut h_pass =
2263 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2264 label: Some("bloom_blur_h_pass"),
2265 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2266 view: bloom_ping_view,
2267 resolve_target: None,
2268 ops: wgpu::Operations {
2269 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2270 store: wgpu::StoreOp::Store,
2271 },
2272 depth_slice: None,
2273 })],
2274 depth_stencil_attachment: None,
2275 timestamp_writes: None,
2276 occlusion_query_set: None,
2277 });
2278 h_pass.set_pipeline(blur_pipeline);
2279 h_pass.set_bind_group(0, h_bg, &[]);
2280 h_pass.draw(0..3, 0..1);
2281 }
2282 {
2284 let mut v_pass =
2285 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2286 label: Some("bloom_blur_v_pass"),
2287 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2288 view: bloom_pong_view,
2289 resolve_target: None,
2290 ops: wgpu::Operations {
2291 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2292 store: wgpu::StoreOp::Store,
2293 },
2294 depth_slice: None,
2295 })],
2296 depth_stencil_attachment: None,
2297 timestamp_writes: None,
2298 occlusion_query_set: None,
2299 });
2300 v_pass.set_pipeline(blur_pipeline);
2301 v_pass.set_bind_group(0, blur_v_bg, &[]);
2302 v_pass.draw(0..3, 0..1);
2303 }
2304 }
2305 }
2306 }
2307 }
2308
2309 if pp.dof_enabled && !throttle_effects {
2313 if let Some(dof_pipeline) = &self.resources.dof_pipeline {
2314 let mut dof_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2315 label: Some("dof_pass"),
2316 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2317 view: &slot_hdr.dof_view,
2318 resolve_target: None,
2319 depth_slice: None,
2320 ops: wgpu::Operations {
2321 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2322 store: wgpu::StoreOp::Store,
2323 },
2324 })],
2325 depth_stencil_attachment: None,
2326 timestamp_writes: None,
2327 occlusion_query_set: None,
2328 });
2329 dof_pass.set_pipeline(dof_pipeline);
2330 dof_pass.set_bind_group(0, &slot_hdr.dof_bg, &[]);
2331 dof_pass.draw(0..3, 0..1);
2332 }
2333 }
2334
2335 let use_fxaa = pp.fxaa;
2339 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
2340 let tone_target: &wgpu::TextureView = if use_fxaa {
2341 &slot_hdr.fxaa_view
2342 } else {
2343 output_view
2344 };
2345 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2346 label: Some("tone_map_pass"),
2347 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2348 view: tone_target,
2349 resolve_target: None,
2350 ops: wgpu::Operations {
2351 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2352 store: wgpu::StoreOp::Store,
2353 },
2354 depth_slice: None,
2355 })],
2356 depth_stencil_attachment: None,
2357 timestamp_writes: None,
2358 occlusion_query_set: None,
2359 });
2360 tone_pass.set_pipeline(tone_map_pipeline);
2361 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
2362 tone_pass.draw(0..3, 0..1);
2363 }
2364
2365 if use_fxaa {
2369 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
2370 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2371 label: Some("fxaa_pass"),
2372 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2373 view: output_view,
2374 resolve_target: None,
2375 ops: wgpu::Operations {
2376 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
2377 store: wgpu::StoreOp::Store,
2378 },
2379 depth_slice: None,
2380 })],
2381 depth_stencil_attachment: None,
2382 timestamp_writes: None,
2383 occlusion_query_set: None,
2384 });
2385 fxaa_pass.set_pipeline(fxaa_pipeline);
2386 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
2387 fxaa_pass.draw(0..3, 0..1);
2388 }
2389 }
2390
2391 if frame.viewport.show_grid {
2395 let slot = &self.viewport_slots[vp_idx];
2396 let slot_hdr = slot.hdr.as_ref().unwrap();
2397 let grid_bg = &slot.grid_bind_group;
2398 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2399 label: Some("hdr_grid_pass"),
2400 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2401 view: output_view,
2402 resolve_target: None,
2403 ops: wgpu::Operations {
2404 load: wgpu::LoadOp::Load,
2405 store: wgpu::StoreOp::Store,
2406 },
2407 depth_slice: None,
2408 })],
2409 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2410 view: &slot_hdr.hdr_depth_view,
2411 depth_ops: Some(wgpu::Operations {
2412 load: wgpu::LoadOp::Load,
2413 store: wgpu::StoreOp::Store,
2414 }),
2415 stencil_ops: None,
2416 }),
2417 timestamp_writes: None,
2418 occlusion_query_set: None,
2419 });
2420 grid_pass.set_pipeline(&self.resources.grid_pipeline);
2421 grid_pass.set_bind_group(0, grid_bg, &[]);
2422 grid_pass.draw(0..3, 0..1);
2423 }
2424
2425 if !matches!(
2428 frame.effects.ground_plane.mode,
2429 crate::renderer::types::GroundPlaneMode::None
2430 ) {
2431 let slot = &self.viewport_slots[vp_idx];
2432 let slot_hdr = slot.hdr.as_ref().unwrap();
2433 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2434 label: Some("hdr_ground_plane_pass"),
2435 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2436 view: output_view,
2437 resolve_target: None,
2438 ops: wgpu::Operations {
2439 load: wgpu::LoadOp::Load,
2440 store: wgpu::StoreOp::Store,
2441 },
2442 depth_slice: None,
2443 })],
2444 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2445 view: &slot_hdr.hdr_depth_view,
2446 depth_ops: Some(wgpu::Operations {
2447 load: wgpu::LoadOp::Load,
2448 store: wgpu::StoreOp::Store,
2449 }),
2450 stencil_ops: None,
2451 }),
2452 timestamp_writes: None,
2453 occlusion_query_set: None,
2454 });
2455 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
2456 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
2457 gp_pass.draw(0..3, 0..1);
2458 }
2459
2460 {
2464 let slot = &self.viewport_slots[vp_idx];
2465 let slot_hdr = slot.hdr.as_ref().unwrap();
2466 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
2467 && slot.gizmo_index_count > 0)
2468 || !slot.constraint_line_buffers.is_empty()
2469 || !slot.clip_plane_fill_buffers.is_empty()
2470 || !slot.clip_plane_line_buffers.is_empty()
2471 || !slot.xray_object_buffers.is_empty();
2472 if has_editor_overlays {
2473 let camera_bg = &slot.camera_bind_group;
2474 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2475 label: Some("hdr_editor_overlay_pass"),
2476 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2477 view: output_view,
2478 resolve_target: None,
2479 ops: wgpu::Operations {
2480 load: wgpu::LoadOp::Load,
2481 store: wgpu::StoreOp::Store,
2482 },
2483 depth_slice: None,
2484 })],
2485 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2486 view: &slot_hdr.hdr_depth_view,
2487 depth_ops: Some(wgpu::Operations {
2488 load: wgpu::LoadOp::Load,
2489 store: wgpu::StoreOp::Discard,
2490 }),
2491 stencil_ops: None,
2492 }),
2493 timestamp_writes: None,
2494 occlusion_query_set: None,
2495 });
2496
2497 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
2498 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
2499 overlay_pass.set_bind_group(0, camera_bg, &[]);
2500 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
2501 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
2502 overlay_pass.set_index_buffer(
2503 slot.gizmo_index_buffer.slice(..),
2504 wgpu::IndexFormat::Uint32,
2505 );
2506 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
2507 }
2508
2509 if !slot.constraint_line_buffers.is_empty() {
2510 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
2511 overlay_pass.set_bind_group(0, camera_bg, &[]);
2512 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
2513 overlay_pass.set_bind_group(1, bg, &[]);
2514 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
2515 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2516 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
2517 }
2518 }
2519
2520 if !slot.clip_plane_fill_buffers.is_empty() {
2521 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
2522 overlay_pass.set_bind_group(0, camera_bg, &[]);
2523 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
2524 overlay_pass.set_bind_group(1, bg, &[]);
2525 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
2526 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2527 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
2528 }
2529 }
2530
2531 if !slot.clip_plane_line_buffers.is_empty() {
2532 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
2533 overlay_pass.set_bind_group(0, camera_bg, &[]);
2534 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
2535 overlay_pass.set_bind_group(1, bg, &[]);
2536 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
2537 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2538 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
2539 }
2540 }
2541
2542 if !slot.xray_object_buffers.is_empty() {
2543 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
2544 overlay_pass.set_bind_group(0, camera_bg, &[]);
2545 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
2546 let Some(mesh) = self
2547 .resources
2548 .mesh_store
2549 .get(*mesh_id)
2550 else {
2551 continue;
2552 };
2553 overlay_pass.set_bind_group(1, bg, &[]);
2554 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2555 overlay_pass.set_index_buffer(
2556 mesh.index_buffer.slice(..),
2557 wgpu::IndexFormat::Uint32,
2558 );
2559 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2560 }
2561 }
2562 }
2563 }
2564
2565 if frame.viewport.show_axes_indicator {
2568 let slot = &self.viewport_slots[vp_idx];
2569 if slot.axes_vertex_count > 0 {
2570 let slot_hdr = slot.hdr.as_ref().unwrap();
2571 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2572 label: Some("hdr_axes_pass"),
2573 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2574 view: output_view,
2575 resolve_target: None,
2576 ops: wgpu::Operations {
2577 load: wgpu::LoadOp::Load,
2578 store: wgpu::StoreOp::Store,
2579 },
2580 depth_slice: None,
2581 })],
2582 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2583 view: &slot_hdr.hdr_depth_view,
2584 depth_ops: Some(wgpu::Operations {
2585 load: wgpu::LoadOp::Load,
2586 store: wgpu::StoreOp::Discard,
2587 }),
2588 stencil_ops: None,
2589 }),
2590 timestamp_writes: None,
2591 occlusion_query_set: None,
2592 });
2593 axes_pass.set_pipeline(&self.resources.axes_pipeline);
2594 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
2595 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
2596 }
2597 }
2598
2599 if !self.screen_image_gpu_data.is_empty() {
2603 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
2604 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2605 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
2606 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2607 label: Some("screen_image_pass"),
2608 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2609 view: output_view,
2610 resolve_target: None,
2611 ops: wgpu::Operations {
2612 load: wgpu::LoadOp::Load,
2613 store: wgpu::StoreOp::Store,
2614 },
2615 depth_slice: None,
2616 })],
2617 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2618 view: &slot_hdr.hdr_depth_view,
2619 depth_ops: Some(wgpu::Operations {
2620 load: wgpu::LoadOp::Load,
2621 store: wgpu::StoreOp::Discard,
2622 }),
2623 stencil_ops: None,
2624 }),
2625 timestamp_writes: None,
2626 occlusion_query_set: None,
2627 });
2628 for gpu in &self.screen_image_gpu_data {
2629 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
2630 img_pass.set_pipeline(dc_pipe);
2631 img_pass.set_bind_group(0, dc_bg, &[]);
2632 } else {
2633 img_pass.set_pipeline(overlay_pipeline);
2634 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
2635 }
2636 img_pass.draw(0..6, 0..1);
2637 }
2638 }
2639 }
2640
2641 let has_overlay = self.label_gpu_data.is_some()
2643 || self.scalar_bar_gpu_data.is_some()
2644 || self.ruler_gpu_data.is_some()
2645 || self.loading_bar_gpu_data.is_some()
2646 || !self.overlay_image_gpu_data.is_empty();
2647 if has_overlay {
2648 let hdr_depth_view =
2649 &self.viewport_slots[vp_idx].hdr.as_ref().unwrap().hdr_depth_view;
2650 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2651 label: Some("overlay_pass"),
2652 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2653 view: output_view,
2654 resolve_target: None,
2655 ops: wgpu::Operations {
2656 load: wgpu::LoadOp::Load,
2657 store: wgpu::StoreOp::Store,
2658 },
2659 depth_slice: None,
2660 })],
2661 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2662 view: hdr_depth_view,
2663 depth_ops: Some(wgpu::Operations {
2664 load: wgpu::LoadOp::Load,
2665 store: wgpu::StoreOp::Discard,
2666 }),
2667 stencil_ops: None,
2668 }),
2669 timestamp_writes: None,
2670 occlusion_query_set: None,
2671 });
2672 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
2673 overlay_pass.set_pipeline(pipeline);
2674 if let Some(ref ld) = self.label_gpu_data {
2675 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
2676 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
2677 overlay_pass.draw(0..ld.vertex_count, 0..1);
2678 }
2679 if let Some(ref sb) = self.scalar_bar_gpu_data {
2680 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
2681 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
2682 overlay_pass.draw(0..sb.vertex_count, 0..1);
2683 }
2684 if let Some(ref rd) = self.ruler_gpu_data {
2685 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
2686 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
2687 overlay_pass.draw(0..rd.vertex_count, 0..1);
2688 }
2689 if let Some(ref lb) = self.loading_bar_gpu_data {
2690 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
2691 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
2692 overlay_pass.draw(0..lb.vertex_count, 0..1);
2693 }
2694 }
2695 if !self.overlay_image_gpu_data.is_empty() {
2697 if let Some(pipeline) = &self.resources.screen_image_pipeline {
2698 overlay_pass.set_pipeline(pipeline);
2699 for gpu in &self.overlay_image_gpu_data {
2700 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
2701 overlay_pass.draw(0..6, 0..1);
2702 }
2703 }
2704 }
2705 }
2706
2707 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
2709 self.ts_query_set.as_ref(),
2710 self.ts_resolve_buf.as_ref(),
2711 self.ts_staging_buf.as_ref(),
2712 ) {
2713 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
2714 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
2715 self.ts_needs_readback = true;
2716 }
2717
2718 encoder.finish()
2719 }
2720
2721 pub fn render_offscreen(
2735 &mut self,
2736 device: &wgpu::Device,
2737 queue: &wgpu::Queue,
2738 frame: &FrameData,
2739 width: u32,
2740 height: u32,
2741 ) -> Vec<u8> {
2742 let target_format = self.resources.target_format;
2744 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
2745 label: Some("offscreen_target"),
2746 size: wgpu::Extent3d {
2747 width: width.max(1),
2748 height: height.max(1),
2749 depth_or_array_layers: 1,
2750 },
2751 mip_level_count: 1,
2752 sample_count: 1,
2753 dimension: wgpu::TextureDimension::D2,
2754 format: target_format,
2755 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
2756 view_formats: &[],
2757 });
2758
2759 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
2761
2762 let cmd_buf = self.render(device, queue, &output_view, frame);
2770 queue.submit(std::iter::once(cmd_buf));
2771
2772 let bytes_per_pixel = 4u32;
2774 let unpadded_row = width * bytes_per_pixel;
2775 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
2776 let padded_row = (unpadded_row + align - 1) & !(align - 1);
2777 let buffer_size = (padded_row * height.max(1)) as u64;
2778
2779 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
2780 label: Some("offscreen_staging"),
2781 size: buffer_size,
2782 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
2783 mapped_at_creation: false,
2784 });
2785
2786 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
2787 label: Some("offscreen_copy_encoder"),
2788 });
2789 copy_encoder.copy_texture_to_buffer(
2790 wgpu::TexelCopyTextureInfo {
2791 texture: &offscreen_texture,
2792 mip_level: 0,
2793 origin: wgpu::Origin3d::ZERO,
2794 aspect: wgpu::TextureAspect::All,
2795 },
2796 wgpu::TexelCopyBufferInfo {
2797 buffer: &staging_buf,
2798 layout: wgpu::TexelCopyBufferLayout {
2799 offset: 0,
2800 bytes_per_row: Some(padded_row),
2801 rows_per_image: Some(height.max(1)),
2802 },
2803 },
2804 wgpu::Extent3d {
2805 width: width.max(1),
2806 height: height.max(1),
2807 depth_or_array_layers: 1,
2808 },
2809 );
2810 queue.submit(std::iter::once(copy_encoder.finish()));
2811
2812 let (tx, rx) = std::sync::mpsc::channel();
2814 staging_buf
2815 .slice(..)
2816 .map_async(wgpu::MapMode::Read, move |result| {
2817 let _ = tx.send(result);
2818 });
2819 device
2820 .poll(wgpu::PollType::Wait {
2821 submission_index: None,
2822 timeout: Some(std::time::Duration::from_secs(5)),
2823 })
2824 .unwrap();
2825 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
2826
2827 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
2828 {
2829 let mapped = staging_buf.slice(..).get_mapped_range();
2830 let data: &[u8] = &mapped;
2831 if padded_row == unpadded_row {
2832 pixels.extend_from_slice(data);
2834 } else {
2835 for row in 0..height as usize {
2837 let start = row * padded_row as usize;
2838 let end = start + unpadded_row as usize;
2839 pixels.extend_from_slice(&data[start..end]);
2840 }
2841 }
2842 }
2843 staging_buf.unmap();
2844
2845 let is_bgra = matches!(
2847 target_format,
2848 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
2849 );
2850 if is_bgra {
2851 for pixel in pixels.chunks_exact_mut(4) {
2852 pixel.swap(0, 2); }
2854 }
2855
2856 pixels
2857 }
2858}