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