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 );
35 if !self.implicit_gpu_data.is_empty() {
37 if let Some(pipeline) = &self.resources.implicit_pipeline {
38 render_pass.set_pipeline(pipeline);
39 render_pass.set_bind_group(0, camera_bg, &[]);
40 for gpu in &self.implicit_gpu_data {
41 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
42 render_pass.draw(0..6, 0..1);
43 }
44 }
45 }
46 if !self.mc_gpu_data.is_empty() {
48 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
49 render_pass.set_pipeline(pipeline);
50 render_pass.set_bind_group(0, camera_bg, &[]);
51 for mc in &self.mc_gpu_data {
52 let vol = &self.resources.mc_volumes[mc.volume_idx];
53 render_pass.set_bind_group(1, &mc.render_bg, &[]);
54 for slab in &vol.slabs {
55 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
56 render_pass.draw_indirect(&slab.indirect_buf, 0);
57 }
58 }
59 }
60 }
61 if let Some(sub_hl) = self.viewport_slots.get(vp_idx).and_then(|s| s.sub_highlight.as_ref()) {
63 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
64 &self.resources.sub_highlight_fill_ldr_pipeline,
65 &self.resources.sub_highlight_edge_ldr_pipeline,
66 &self.resources.sub_highlight_sprite_ldr_pipeline,
67 ) {
68 if sub_hl.fill_vertex_count > 0 {
69 render_pass.set_pipeline(fill_pl);
70 render_pass.set_bind_group(0, camera_bg, &[]);
71 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
72 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
73 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
74 }
75 if sub_hl.edge_segment_count > 0 {
76 render_pass.set_pipeline(edge_pl);
77 render_pass.set_bind_group(0, camera_bg, &[]);
78 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
79 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
80 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
81 }
82 if sub_hl.sprite_point_count > 0 {
83 render_pass.set_pipeline(sprite_pl);
84 render_pass.set_bind_group(0, camera_bg, &[]);
85 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
86 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
87 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
88 }
89 }
90 }
91 if !self.screen_image_gpu_data.is_empty() {
93 if let Some(pipeline) = &self.resources.screen_image_pipeline {
94 render_pass.set_pipeline(pipeline);
95 for gpu in &self.screen_image_gpu_data {
96 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
97 render_pass.draw(0..6, 0..1);
98 }
99 }
100 }
101 if let Some(ref ld) = self.label_gpu_data {
103 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
104 render_pass.set_pipeline(pipeline);
105 render_pass.set_bind_group(0, &ld.bind_group, &[]);
106 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
107 render_pass.draw(0..ld.vertex_count, 0..1);
108 }
109 }
110 if let Some(ref sb) = self.scalar_bar_gpu_data {
112 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
113 render_pass.set_pipeline(pipeline);
114 render_pass.set_bind_group(0, &sb.bind_group, &[]);
115 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
116 render_pass.draw(0..sb.vertex_count, 0..1);
117 }
118 }
119 if let Some(ref rd) = self.ruler_gpu_data {
121 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
122 render_pass.set_pipeline(pipeline);
123 render_pass.set_bind_group(0, &rd.bind_group, &[]);
124 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
125 render_pass.draw(0..rd.vertex_count, 0..1);
126 }
127 }
128 if let Some(ref lb) = self.loading_bar_gpu_data {
130 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
131 render_pass.set_pipeline(pipeline);
132 render_pass.set_bind_group(0, &lb.bind_group, &[]);
133 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
134 render_pass.draw(0..lb.vertex_count, 0..1);
135 }
136 }
137 if !self.overlay_image_gpu_data.is_empty() {
139 if let Some(pipeline) = &self.resources.screen_image_pipeline {
140 render_pass.set_pipeline(pipeline);
141 for gpu in &self.overlay_image_gpu_data {
142 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
143 render_pass.draw(0..6, 0..1);
144 }
145 }
146 }
147 }
148
149 pub fn paint_to<'rp>(&'rp self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
155 let vp_idx = frame.camera.viewport_index;
156 let camera_bg = self.viewport_camera_bind_group(vp_idx);
157 let grid_bg = self.viewport_grid_bind_group(vp_idx);
158 let vp_slot = self.viewport_slots.get(vp_idx);
159 emit_draw_calls!(
160 &self.resources,
161 &mut *render_pass,
162 frame,
163 self.use_instancing,
164 &self.instanced_batches,
165 camera_bg,
166 grid_bg,
167 &self.compute_filter_results,
168 vp_slot
169 );
170 emit_scivis_draw_calls!(
171 &self.resources,
172 &mut *render_pass,
173 &self.point_cloud_gpu_data,
174 &self.glyph_gpu_data,
175 &self.polyline_gpu_data,
176 &self.volume_gpu_data,
177 &self.streamtube_gpu_data,
178 camera_bg
179 );
180 if !self.implicit_gpu_data.is_empty() {
182 if let Some(pipeline) = &self.resources.implicit_pipeline {
183 render_pass.set_pipeline(pipeline);
184 render_pass.set_bind_group(0, camera_bg, &[]);
185 for gpu in &self.implicit_gpu_data {
186 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
187 render_pass.draw(0..6, 0..1);
188 }
189 }
190 }
191 if !self.mc_gpu_data.is_empty() {
193 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
194 render_pass.set_pipeline(pipeline);
195 render_pass.set_bind_group(0, camera_bg, &[]);
196 for mc in &self.mc_gpu_data {
197 let vol = &self.resources.mc_volumes[mc.volume_idx];
198 render_pass.set_bind_group(1, &mc.render_bg, &[]);
199 for slab in &vol.slabs {
200 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
201 render_pass.draw_indirect(&slab.indirect_buf, 0);
202 }
203 }
204 }
205 }
206 if let Some(sub_hl) = self.viewport_slots.get(vp_idx).and_then(|s| s.sub_highlight.as_ref()) {
208 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
209 &self.resources.sub_highlight_fill_ldr_pipeline,
210 &self.resources.sub_highlight_edge_ldr_pipeline,
211 &self.resources.sub_highlight_sprite_ldr_pipeline,
212 ) {
213 if sub_hl.fill_vertex_count > 0 {
214 render_pass.set_pipeline(fill_pl);
215 render_pass.set_bind_group(0, camera_bg, &[]);
216 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
217 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
218 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
219 }
220 if sub_hl.edge_segment_count > 0 {
221 render_pass.set_pipeline(edge_pl);
222 render_pass.set_bind_group(0, camera_bg, &[]);
223 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
224 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
225 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
226 }
227 if sub_hl.sprite_point_count > 0 {
228 render_pass.set_pipeline(sprite_pl);
229 render_pass.set_bind_group(0, camera_bg, &[]);
230 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
231 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
232 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
233 }
234 }
235 }
236 if !self.screen_image_gpu_data.is_empty() {
238 if let Some(pipeline) = &self.resources.screen_image_pipeline {
239 render_pass.set_pipeline(pipeline);
240 for gpu in &self.screen_image_gpu_data {
241 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
242 render_pass.draw(0..6, 0..1);
243 }
244 }
245 }
246 if let Some(ref ld) = self.label_gpu_data {
248 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
249 render_pass.set_pipeline(pipeline);
250 render_pass.set_bind_group(0, &ld.bind_group, &[]);
251 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
252 render_pass.draw(0..ld.vertex_count, 0..1);
253 }
254 }
255 if let Some(ref sb) = self.scalar_bar_gpu_data {
257 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
258 render_pass.set_pipeline(pipeline);
259 render_pass.set_bind_group(0, &sb.bind_group, &[]);
260 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
261 render_pass.draw(0..sb.vertex_count, 0..1);
262 }
263 }
264 if let Some(ref rd) = self.ruler_gpu_data {
266 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
267 render_pass.set_pipeline(pipeline);
268 render_pass.set_bind_group(0, &rd.bind_group, &[]);
269 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
270 render_pass.draw(0..rd.vertex_count, 0..1);
271 }
272 }
273 if let Some(ref lb) = self.loading_bar_gpu_data {
275 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
276 render_pass.set_pipeline(pipeline);
277 render_pass.set_bind_group(0, &lb.bind_group, &[]);
278 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
279 render_pass.draw(0..lb.vertex_count, 0..1);
280 }
281 }
282 if !self.overlay_image_gpu_data.is_empty() {
284 if let Some(pipeline) = &self.resources.screen_image_pipeline {
285 render_pass.set_pipeline(pipeline);
286 for gpu in &self.overlay_image_gpu_data {
287 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
288 render_pass.draw(0..6, 0..1);
289 }
290 }
291 }
292 }
293
294 pub fn prepare_ldr_dyn_res(
309 &mut self,
310 encoder: &mut wgpu::CommandEncoder,
311 device: &wgpu::Device,
312 frame: &FrameData,
313 ) -> bool {
314 if self.current_render_scale >= 1.0 - 0.001 {
315 return false;
316 }
317
318 let vp_idx = frame.camera.viewport_index;
319 let w = (frame.camera.viewport_size[0] as u32).max(1);
320 let h = (frame.camera.viewport_size[1] as u32).max(1);
321 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
322 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
323
324 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w, h]);
325 self.resources.ensure_dyn_res_ds_pipeline(device);
326
327 let bg_color = frame.viewport.background_color.unwrap_or([
328 65.0 / 255.0,
329 65.0 / 255.0,
330 65.0 / 255.0,
331 1.0,
332 ]);
333
334 {
335 let slot = &self.viewport_slots[vp_idx];
336 let dr = slot.dyn_res.as_ref().unwrap();
337 let color_view = &dr.color_view;
338 let depth_view = &dr.depth_view;
339 let camera_bg = &slot.camera_bind_group;
340 let grid_bg = &slot.grid_bind_group;
341
342 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
343 label: Some("ldr_dyn_res_render_pass"),
344 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
345 view: color_view,
346 resolve_target: None,
347 ops: wgpu::Operations {
348 load: wgpu::LoadOp::Clear(wgpu::Color {
349 r: bg_color[0] as f64,
350 g: bg_color[1] as f64,
351 b: bg_color[2] as f64,
352 a: bg_color[3] as f64,
353 }),
354 store: wgpu::StoreOp::Store,
355 },
356 depth_slice: None,
357 })],
358 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
359 view: depth_view,
360 depth_ops: Some(wgpu::Operations {
361 load: wgpu::LoadOp::Clear(1.0),
362 store: wgpu::StoreOp::Discard,
363 }),
364 stencil_ops: None,
365 }),
366 timestamp_writes: None,
367 occlusion_query_set: None,
368 });
369 emit_draw_calls!(
370 &self.resources,
371 &mut render_pass,
372 frame,
373 self.use_instancing,
374 &self.instanced_batches,
375 camera_bg,
376 grid_bg,
377 &self.compute_filter_results,
378 Some(slot)
379 );
380 emit_scivis_draw_calls!(
381 &self.resources,
382 &mut render_pass,
383 &self.point_cloud_gpu_data,
384 &self.glyph_gpu_data,
385 &self.polyline_gpu_data,
386 &self.volume_gpu_data,
387 &self.streamtube_gpu_data,
388 camera_bg
389 );
390 if !self.implicit_gpu_data.is_empty() {
392 if let Some(pipeline) = &self.resources.implicit_pipeline {
393 render_pass.set_pipeline(pipeline);
394 render_pass.set_bind_group(0, camera_bg, &[]);
395 for gpu in &self.implicit_gpu_data {
396 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
397 render_pass.draw(0..6, 0..1);
398 }
399 }
400 }
401 if !self.mc_gpu_data.is_empty() {
403 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
404 render_pass.set_pipeline(pipeline);
405 render_pass.set_bind_group(0, camera_bg, &[]);
406 for mc in &self.mc_gpu_data {
407 let vol = &self.resources.mc_volumes[mc.volume_idx];
408 render_pass.set_bind_group(1, &mc.render_bg, &[]);
409 for slab in &vol.slabs {
410 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
411 render_pass.draw_indirect(&slab.indirect_buf, 0);
412 }
413 }
414 }
415 }
416 if let Some(sub_hl) = slot.sub_highlight.as_ref() {
418 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
419 &self.resources.sub_highlight_fill_ldr_pipeline,
420 &self.resources.sub_highlight_edge_ldr_pipeline,
421 &self.resources.sub_highlight_sprite_ldr_pipeline,
422 ) {
423 if sub_hl.fill_vertex_count > 0 {
424 render_pass.set_pipeline(fill_pl);
425 render_pass.set_bind_group(0, camera_bg, &[]);
426 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
427 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
428 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
429 }
430 if sub_hl.edge_segment_count > 0 {
431 render_pass.set_pipeline(edge_pl);
432 render_pass.set_bind_group(0, camera_bg, &[]);
433 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
434 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
435 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
436 }
437 if sub_hl.sprite_point_count > 0 {
438 render_pass.set_pipeline(sprite_pl);
439 render_pass.set_bind_group(0, camera_bg, &[]);
440 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
441 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
442 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
443 }
444 }
445 }
446 if !self.screen_image_gpu_data.is_empty() {
448 if let Some(pipeline) = &self.resources.screen_image_pipeline {
449 render_pass.set_pipeline(pipeline);
450 for gpu in &self.screen_image_gpu_data {
451 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
452 render_pass.draw(0..6, 0..1);
453 }
454 }
455 }
456 if let Some(ref ld) = self.label_gpu_data {
458 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
459 render_pass.set_pipeline(pipeline);
460 render_pass.set_bind_group(0, &ld.bind_group, &[]);
461 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
462 render_pass.draw(0..ld.vertex_count, 0..1);
463 }
464 }
465 if let Some(ref sb) = self.scalar_bar_gpu_data {
467 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
468 render_pass.set_pipeline(pipeline);
469 render_pass.set_bind_group(0, &sb.bind_group, &[]);
470 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
471 render_pass.draw(0..sb.vertex_count, 0..1);
472 }
473 }
474 if let Some(ref rd) = self.ruler_gpu_data {
476 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
477 render_pass.set_pipeline(pipeline);
478 render_pass.set_bind_group(0, &rd.bind_group, &[]);
479 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
480 render_pass.draw(0..rd.vertex_count, 0..1);
481 }
482 }
483 if let Some(ref lb) = self.loading_bar_gpu_data {
485 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
486 render_pass.set_pipeline(pipeline);
487 render_pass.set_bind_group(0, &lb.bind_group, &[]);
488 render_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
489 render_pass.draw(0..lb.vertex_count, 0..1);
490 }
491 }
492 if !self.overlay_image_gpu_data.is_empty() {
494 if let Some(pipeline) = &self.resources.screen_image_pipeline {
495 render_pass.set_pipeline(pipeline);
496 for gpu in &self.overlay_image_gpu_data {
497 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
498 render_pass.draw(0..6, 0..1);
499 }
500 }
501 }
502 }
503
504 true
505 }
506
507 pub fn paint_dyn_res_blit(
513 &self,
514 render_pass: &mut wgpu::RenderPass<'static>,
515 frame: &FrameData,
516 ) {
517 let vp_idx = frame.camera.viewport_index;
518 if let Some(dr) = self.viewport_slots.get(vp_idx).and_then(|s| s.dyn_res.as_ref()) {
519 if let Some(pipeline) = &self.resources.dyn_res_upscale_ds_pipeline {
520 render_pass.set_pipeline(pipeline);
521 render_pass.set_bind_group(0, &dr.upscale_bind_group, &[]);
522 render_pass.draw(0..3, 0..1);
523 }
524 }
525 }
526
527 pub fn render_viewport(
541 &mut self,
542 device: &wgpu::Device,
543 queue: &wgpu::Queue,
544 output_view: &wgpu::TextureView,
545 id: ViewportId,
546 frame: &FrameData,
547 ) -> wgpu::CommandBuffer {
548 self.render_frame_internal(device, queue, output_view, id.0, frame)
549 }
550
551 pub fn render(
559 &mut self,
560 device: &wgpu::Device,
561 queue: &wgpu::Queue,
562 output_view: &wgpu::TextureView,
563 frame: &FrameData,
564 ) -> wgpu::CommandBuffer {
565 self.prepare(device, queue, frame);
567 self.render_frame_internal(
568 device,
569 queue,
570 output_view,
571 frame.camera.viewport_index,
572 frame,
573 )
574 }
575
576 fn render_frame_internal(
581 &mut self,
582 device: &wgpu::Device,
583 queue: &wgpu::Queue,
584 output_view: &wgpu::TextureView,
585 vp_idx: usize,
586 frame: &FrameData,
587 ) -> wgpu::CommandBuffer {
588 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
590 SurfaceSubmission::Flat(items) => items.as_ref(),
591 };
592
593 let bg_color = frame.viewport.background_color.unwrap_or([
594 65.0 / 255.0,
595 65.0 / 255.0,
596 65.0 / 255.0,
597 1.0,
598 ]);
599 let w = frame.camera.viewport_size[0] as u32;
600 let h = frame.camera.viewport_size[1] as u32;
601
602 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
604 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor);
605
606 if self.ts_query_set.is_none()
608 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
609 {
610 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
611 label: Some("ts_query_set"),
612 ty: wgpu::QueryType::Timestamp,
613 count: 2,
614 }));
615 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
616 label: Some("ts_resolve_buf"),
617 size: 16,
618 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
619 mapped_at_creation: false,
620 }));
621 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
622 label: Some("ts_staging_buf"),
623 size: 16,
624 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
625 mapped_at_creation: false,
626 }));
627 self.ts_period = queue.get_timestamp_period();
628 }
629
630 if !frame.effects.post_process.enabled {
631 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
635 label: Some("ldr_encoder"),
636 });
637
638 let use_dyn_res = self.current_render_scale < 1.0 - 0.001;
639
640 if use_dyn_res {
641 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
642 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
643 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
644 }
645
646 {
647 let slot = &self.viewport_slots[vp_idx];
648 let slot_hdr = slot.hdr.as_ref().unwrap();
649 let camera_bg = &slot.camera_bind_group;
650 let grid_bg = &slot.grid_bind_group;
651 let (scene_color_view, scene_depth_view): (&wgpu::TextureView, &wgpu::TextureView) =
653 if use_dyn_res {
654 let dr = slot.dyn_res.as_ref().unwrap();
655 (&dr.color_view, &dr.depth_view)
656 } else {
657 (output_view, &slot_hdr.outline_depth_view)
658 };
659 let ts_writes = self.ts_query_set.as_ref().map(|qs| {
660 wgpu::RenderPassTimestampWrites {
661 query_set: qs,
662 beginning_of_pass_write_index: Some(0),
663 end_of_pass_write_index: Some(1),
664 }
665 });
666 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
667 label: Some("ldr_render_pass"),
668 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
669 view: scene_color_view,
670 resolve_target: None,
671 ops: wgpu::Operations {
672 load: wgpu::LoadOp::Clear(wgpu::Color {
673 r: bg_color[0] as f64,
674 g: bg_color[1] as f64,
675 b: bg_color[2] as f64,
676 a: bg_color[3] as f64,
677 }),
678 store: wgpu::StoreOp::Store,
679 },
680 depth_slice: None,
681 })],
682 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
683 view: scene_depth_view,
684 depth_ops: Some(wgpu::Operations {
685 load: wgpu::LoadOp::Clear(1.0),
686 store: wgpu::StoreOp::Discard,
687 }),
688 stencil_ops: None,
689 }),
690 timestamp_writes: ts_writes,
691 occlusion_query_set: None,
692 });
693 emit_draw_calls!(
694 &self.resources,
695 &mut render_pass,
696 frame,
697 self.use_instancing,
698 &self.instanced_batches,
699 camera_bg,
700 grid_bg,
701 &self.compute_filter_results,
702 Some(slot)
703 );
704 emit_scivis_draw_calls!(
705 &self.resources,
706 &mut render_pass,
707 &self.point_cloud_gpu_data,
708 &self.glyph_gpu_data,
709 &self.polyline_gpu_data,
710 &self.volume_gpu_data,
711 &self.streamtube_gpu_data,
712 camera_bg
713 );
714 if !self.implicit_gpu_data.is_empty() {
716 if let Some(pipeline) = &self.resources.implicit_pipeline {
717 render_pass.set_pipeline(pipeline);
718 render_pass.set_bind_group(0, camera_bg, &[]);
719 for gpu in &self.implicit_gpu_data {
720 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
721 render_pass.draw(0..6, 0..1);
722 }
723 }
724 }
725 if !self.mc_gpu_data.is_empty() {
727 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
728 render_pass.set_pipeline(pipeline);
729 render_pass.set_bind_group(0, camera_bg, &[]);
730 for mc in &self.mc_gpu_data {
731 let vol = &self.resources.mc_volumes[mc.volume_idx];
732 render_pass.set_bind_group(1, &mc.render_bg, &[]);
733 for slab in &vol.slabs {
734 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
735 render_pass.draw_indirect(&slab.indirect_buf, 0);
736 }
737 }
738 }
739 }
740 if !self.screen_image_gpu_data.is_empty() {
745 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
746 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
747 for gpu in &self.screen_image_gpu_data {
748 if let (Some(dc_bg), Some(dc_pipe)) =
749 (&gpu.depth_bind_group, dc_pipeline)
750 {
751 render_pass.set_pipeline(dc_pipe);
752 render_pass.set_bind_group(0, dc_bg, &[]);
753 } else {
754 render_pass.set_pipeline(overlay_pipeline);
755 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
756 }
757 render_pass.draw(0..6, 0..1);
758 }
759 }
760 }
761 if let Some(ref ld) = self.label_gpu_data {
763 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
764 render_pass.set_pipeline(pipeline);
765 render_pass.set_bind_group(0, &ld.bind_group, &[]);
766 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
767 render_pass.draw(0..ld.vertex_count, 0..1);
768 }
769 }
770 if let Some(ref sb) = self.scalar_bar_gpu_data {
772 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
773 render_pass.set_pipeline(pipeline);
774 render_pass.set_bind_group(0, &sb.bind_group, &[]);
775 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
776 render_pass.draw(0..sb.vertex_count, 0..1);
777 }
778 }
779 if let Some(ref rd) = self.ruler_gpu_data {
781 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
782 render_pass.set_pipeline(pipeline);
783 render_pass.set_bind_group(0, &rd.bind_group, &[]);
784 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
785 render_pass.draw(0..rd.vertex_count, 0..1);
786 }
787 }
788 if !self.overlay_image_gpu_data.is_empty() {
790 if let Some(pipeline) = &self.resources.screen_image_pipeline {
791 render_pass.set_pipeline(pipeline);
792 for gpu in &self.overlay_image_gpu_data {
793 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
794 render_pass.draw(0..6, 0..1);
795 }
796 }
797 }
798 }
799
800 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
802 self.ts_query_set.as_ref(),
803 self.ts_resolve_buf.as_ref(),
804 self.ts_staging_buf.as_ref(),
805 ) {
806 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
807 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
808 self.ts_needs_readback = true;
809 }
810
811 if use_dyn_res {
813 let upscale_bg =
814 &self.viewport_slots[vp_idx].dyn_res.as_ref().unwrap().upscale_bind_group;
815 let mut upscale_pass =
816 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
817 label: Some("dyn_res_upscale_pass"),
818 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
819 view: output_view,
820 resolve_target: None,
821 ops: wgpu::Operations {
822 load: wgpu::LoadOp::Load,
823 store: wgpu::StoreOp::Store,
824 },
825 depth_slice: None,
826 })],
827 depth_stencil_attachment: None,
828 timestamp_writes: None,
829 occlusion_query_set: None,
830 });
831 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
832 upscale_pass.set_pipeline(pipeline);
833 upscale_pass.set_bind_group(0, upscale_bg, &[]);
834 upscale_pass.draw(0..3, 0..1);
835 }
836 }
837
838 return encoder.finish();
839 }
840
841 let pp = &frame.effects.post_process;
843
844 let hdr_clear_rgb = [
845 bg_color[0].powf(2.2),
846 bg_color[1].powf(2.2),
847 bg_color[2].powf(2.2),
848 ];
849
850 let mode = match pp.tone_mapping {
852 crate::renderer::ToneMapping::Reinhard => 0u32,
853 crate::renderer::ToneMapping::Aces => 1u32,
854 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
855 };
856 let tm_uniform = crate::resources::ToneMapUniform {
857 exposure: pp.exposure,
858 mode,
859 bloom_enabled: if pp.bloom { 1 } else { 0 },
860 ssao_enabled: if pp.ssao { 1 } else { 0 },
861 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
862 _pad_tm: [0; 3],
863 background_color: bg_color,
864 };
865 {
866 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
867 queue.write_buffer(
868 &hdr.tone_map_uniform_buf,
869 0,
870 bytemuck::cast_slice(&[tm_uniform]),
871 );
872
873 if pp.ssao {
875 let proj = frame.camera.render_camera.projection;
876 let inv_proj = proj.inverse();
877 let ssao_uniform = crate::resources::SsaoUniform {
878 inv_proj: inv_proj.to_cols_array_2d(),
879 proj: proj.to_cols_array_2d(),
880 radius: 0.5,
881 bias: 0.025,
882 _pad: [0.0; 2],
883 };
884 queue.write_buffer(
885 &hdr.ssao_uniform_buf,
886 0,
887 bytemuck::cast_slice(&[ssao_uniform]),
888 );
889 }
890
891 if pp.contact_shadows {
893 let proj = frame.camera.render_camera.projection;
894 let inv_proj = proj.inverse();
895 let light_dir_world: glam::Vec3 =
896 if let Some(l) = frame.effects.lighting.lights.first() {
897 match l.kind {
898 LightKind::Directional { direction } => {
899 glam::Vec3::from(direction).normalize()
900 }
901 LightKind::Spot { direction, .. } => {
902 glam::Vec3::from(direction).normalize()
903 }
904 _ => glam::Vec3::new(0.0, -1.0, 0.0),
905 }
906 } else {
907 glam::Vec3::new(0.0, -1.0, 0.0)
908 };
909 let view = frame.camera.render_camera.view;
910 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
911 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
912 let cs_uniform = crate::resources::ContactShadowUniform {
913 inv_proj: inv_proj.to_cols_array_2d(),
914 proj: proj.to_cols_array_2d(),
915 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
916 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
917 params: [
918 pp.contact_shadow_max_distance,
919 pp.contact_shadow_steps as f32,
920 pp.contact_shadow_thickness,
921 0.0,
922 ],
923 };
924 queue.write_buffer(
925 &hdr.contact_shadow_uniform_buf,
926 0,
927 bytemuck::cast_slice(&[cs_uniform]),
928 );
929 }
930
931 if pp.bloom {
933 let bloom_u = crate::resources::BloomUniform {
934 threshold: pp.bloom_threshold,
935 intensity: pp.bloom_intensity,
936 horizontal: 0,
937 _pad: 0,
938 };
939 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
940 }
941 }
942
943 {
945 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
946 self.resources.rebuild_tone_map_bind_group(
947 device,
948 hdr,
949 pp.bloom,
950 pp.ssao,
951 pp.contact_shadows,
952 );
953 }
954
955 {
960 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
961 self.instanced_batches.iter().any(|b| b.is_transparent)
962 } else {
963 scene_items
964 .iter()
965 .any(|i| i.visible && i.material.opacity < 1.0)
966 };
967 if needs_oit {
968 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
969 self.resources
970 .ensure_viewport_oit(device, hdr, w.max(1), h.max(1));
971 }
972 }
973
974 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
978 label: Some("hdr_encoder"),
979 });
980
981 let slot = &self.viewport_slots[vp_idx];
983 let camera_bg = &slot.camera_bind_group;
984 let slot_hdr = slot.hdr.as_ref().unwrap();
985
986 {
990 let use_ssaa = ssaa_factor > 1
992 && slot_hdr.ssaa_color_view.is_some()
993 && slot_hdr.ssaa_depth_view.is_some();
994 let scene_color_view = if use_ssaa {
995 slot_hdr.ssaa_color_view.as_ref().unwrap()
996 } else {
997 &slot_hdr.hdr_view
998 };
999 let scene_depth_view = if use_ssaa {
1000 slot_hdr.ssaa_depth_view.as_ref().unwrap()
1001 } else {
1002 &slot_hdr.hdr_depth_view
1003 };
1004
1005 let clear_wgpu = wgpu::Color {
1006 r: hdr_clear_rgb[0] as f64,
1007 g: hdr_clear_rgb[1] as f64,
1008 b: hdr_clear_rgb[2] as f64,
1009 a: bg_color[3] as f64,
1010 };
1011
1012 let hdr_ts_writes = self.ts_query_set.as_ref().map(|qs| {
1013 wgpu::RenderPassTimestampWrites {
1014 query_set: qs,
1015 beginning_of_pass_write_index: Some(0),
1016 end_of_pass_write_index: Some(1),
1017 }
1018 });
1019 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1020 label: Some("hdr_scene_pass"),
1021 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1022 view: scene_color_view,
1023 resolve_target: None,
1024 ops: wgpu::Operations {
1025 load: wgpu::LoadOp::Clear(clear_wgpu),
1026 store: wgpu::StoreOp::Store,
1027 },
1028 depth_slice: None,
1029 })],
1030 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1031 view: scene_depth_view,
1032 depth_ops: Some(wgpu::Operations {
1033 load: wgpu::LoadOp::Clear(1.0),
1034 store: wgpu::StoreOp::Store,
1035 }),
1036 stencil_ops: Some(wgpu::Operations {
1037 load: wgpu::LoadOp::Clear(0),
1038 store: wgpu::StoreOp::Store,
1039 }),
1040 }),
1041 timestamp_writes: hdr_ts_writes,
1042 occlusion_query_set: None,
1043 });
1044
1045 let resources = &self.resources;
1046 render_pass.set_bind_group(0, camera_bg, &[]);
1047
1048 let show_skybox = frame
1050 .effects
1051 .environment
1052 .as_ref()
1053 .is_some_and(|e| e.show_skybox)
1054 && resources.ibl_skybox_view.is_some();
1055
1056 let use_instancing = self.use_instancing;
1057 let batches = &self.instanced_batches;
1058
1059 if !scene_items.is_empty() {
1060 if use_instancing && !batches.is_empty() {
1061 let excluded_items: Vec<&SceneRenderItem> = scene_items
1062 .iter()
1063 .filter(|item| {
1064 item.visible
1065 && (item.active_attribute.is_some()
1066 || item.material.is_two_sided()
1067 || item.material.matcap_id.is_some())
1068 && resources
1069 .mesh_store
1070 .get(item.mesh_id)
1071 .is_some()
1072 })
1073 .collect();
1074
1075 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1079 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
1080 for (batch_global_idx, batch) in batches.iter().enumerate() {
1081 if batch.is_transparent {
1082 transparent_batches.push((batch_global_idx, batch));
1083 } else {
1084 opaque_batches.push((batch_global_idx, batch));
1085 }
1086 }
1087
1088 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
1089 let use_indirect = self.gpu_culling_enabled
1090 && resources.hdr_solid_instanced_cull_pipeline.is_some()
1091 && resources.indirect_args_buf.is_some();
1092
1093 if use_indirect {
1094 if let (
1095 Some(pipeline),
1096 Some(indirect_buf),
1097 ) = (
1098 &resources.hdr_solid_instanced_cull_pipeline,
1099 &resources.indirect_args_buf,
1100 ) {
1101 render_pass.set_pipeline(pipeline);
1102 for (batch_global_idx, batch) in &opaque_batches {
1103 let Some(mesh) = resources.mesh_store.get(batch.mesh_id)
1104 else {
1105 continue;
1106 };
1107 let mat_key = (
1108 batch.texture_id.unwrap_or(u64::MAX),
1109 batch.normal_map_id.unwrap_or(u64::MAX),
1110 batch.ao_map_id.unwrap_or(u64::MAX),
1111 );
1112 let Some(inst_tex_bg) =
1113 resources.instance_cull_bind_groups.get(&mat_key)
1114 else {
1115 continue;
1116 };
1117 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1118 render_pass.set_vertex_buffer(
1119 0,
1120 mesh.vertex_buffer.slice(..),
1121 );
1122 render_pass.set_index_buffer(
1123 mesh.index_buffer.slice(..),
1124 wgpu::IndexFormat::Uint32,
1125 );
1126 render_pass.draw_indexed_indirect(
1129 indirect_buf,
1130 *batch_global_idx as u64 * 20,
1131 );
1132 }
1133 }
1134 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
1135 render_pass.set_pipeline(pipeline);
1136 for (_, batch) in &opaque_batches {
1137 let Some(mesh) = resources
1138 .mesh_store
1139 .get(batch.mesh_id)
1140 else {
1141 continue;
1142 };
1143 let mat_key = (
1144 batch.texture_id.unwrap_or(u64::MAX),
1145 batch.normal_map_id.unwrap_or(u64::MAX),
1146 batch.ao_map_id.unwrap_or(u64::MAX),
1147 );
1148 let Some(inst_tex_bg) =
1149 resources.instance_bind_groups.get(&mat_key)
1150 else {
1151 continue;
1152 };
1153 render_pass.set_bind_group(1, inst_tex_bg, &[]);
1154 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1155 render_pass.set_index_buffer(
1156 mesh.index_buffer.slice(..),
1157 wgpu::IndexFormat::Uint32,
1158 );
1159 render_pass.draw_indexed(
1160 0..mesh.index_count,
1161 0,
1162 batch.instance_offset
1163 ..batch.instance_offset + batch.instance_count,
1164 );
1165 }
1166 }
1167 }
1168
1169 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
1174 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
1175 render_pass.set_pipeline(hdr_wf);
1176 for item in scene_items {
1177 if !item.visible {
1178 continue;
1179 }
1180 let Some(mesh) = resources
1181 .mesh_store
1182 .get(item.mesh_id)
1183 else {
1184 continue;
1185 };
1186 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1187 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1188 render_pass.set_index_buffer(
1189 mesh.edge_index_buffer.slice(..),
1190 wgpu::IndexFormat::Uint32,
1191 );
1192 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1193 }
1194 }
1195 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
1196 &resources.hdr_solid_pipeline,
1197 &resources.hdr_solid_two_sided_pipeline,
1198 ) {
1199 for item in excluded_items
1200 .into_iter()
1201 .filter(|item| item.material.opacity >= 1.0)
1202 {
1203 let Some(mesh) = resources
1204 .mesh_store
1205 .get(item.mesh_id)
1206 else {
1207 continue;
1208 };
1209 let pipeline = if item.material.is_two_sided() {
1210 hdr_solid_two_sided
1211 } else {
1212 hdr_solid
1213 };
1214 render_pass.set_pipeline(pipeline);
1215 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1216 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1217 render_pass.set_index_buffer(
1218 mesh.index_buffer.slice(..),
1219 wgpu::IndexFormat::Uint32,
1220 );
1221 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1222 }
1223 }
1224 } else {
1225 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
1227 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
1228 let pos =
1229 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
1230 (pos - eye).length()
1231 };
1232
1233 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
1234 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
1235 for item in scene_items {
1236 if !item.visible
1237 || resources
1238 .mesh_store
1239 .get(item.mesh_id)
1240 .is_none()
1241 {
1242 continue;
1243 }
1244 if item.material.opacity < 1.0 {
1245 transparent.push(item);
1246 } else {
1247 opaque.push(item);
1248 }
1249 }
1250 opaque.sort_by(|a, b| {
1251 dist_from_eye(a)
1252 .partial_cmp(&dist_from_eye(b))
1253 .unwrap_or(std::cmp::Ordering::Equal)
1254 });
1255 transparent.sort_by(|a, b| {
1256 dist_from_eye(b)
1257 .partial_cmp(&dist_from_eye(a))
1258 .unwrap_or(std::cmp::Ordering::Equal)
1259 });
1260
1261 let draw_item_hdr =
1262 |render_pass: &mut wgpu::RenderPass<'_>,
1263 item: &SceneRenderItem,
1264 solid_pl: &wgpu::RenderPipeline,
1265 trans_pl: &wgpu::RenderPipeline,
1266 wf_pl: &wgpu::RenderPipeline| {
1267 let mesh = resources
1268 .mesh_store
1269 .get(item.mesh_id)
1270 .unwrap();
1271 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1274 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1275 matches!(
1276 a.kind,
1277 crate::resources::AttributeKind::Face
1278 | crate::resources::AttributeKind::FaceColor
1279 | crate::resources::AttributeKind::Halfedge
1280 | crate::resources::AttributeKind::Corner
1281 )
1282 });
1283 if frame.viewport.wireframe_mode {
1284 render_pass.set_pipeline(wf_pl);
1285 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1286 render_pass.set_index_buffer(
1287 mesh.edge_index_buffer.slice(..),
1288 wgpu::IndexFormat::Uint32,
1289 );
1290 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1291 } else if is_face_attr {
1292 if let Some(ref fvb) = mesh.face_vertex_buffer {
1293 let pl = if item.material.opacity < 1.0 {
1294 trans_pl
1295 } else {
1296 solid_pl
1297 };
1298 render_pass.set_pipeline(pl);
1299 render_pass.set_vertex_buffer(0, fvb.slice(..));
1300 render_pass.draw(0..mesh.index_count, 0..1);
1301 }
1302 } else if item.material.opacity < 1.0 {
1303 render_pass.set_pipeline(trans_pl);
1304 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1305 render_pass.set_index_buffer(
1306 mesh.index_buffer.slice(..),
1307 wgpu::IndexFormat::Uint32,
1308 );
1309 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1310 } else {
1311 render_pass.set_pipeline(solid_pl);
1312 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1313 render_pass.set_index_buffer(
1314 mesh.index_buffer.slice(..),
1315 wgpu::IndexFormat::Uint32,
1316 );
1317 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1318 }
1319 };
1320
1321 let _ = &transparent; if let (
1325 Some(hdr_solid),
1326 Some(hdr_solid_two_sided),
1327 Some(hdr_trans),
1328 Some(hdr_wf),
1329 ) = (
1330 &resources.hdr_solid_pipeline,
1331 &resources.hdr_solid_two_sided_pipeline,
1332 &resources.hdr_transparent_pipeline,
1333 &resources.hdr_wireframe_pipeline,
1334 ) {
1335 for item in &opaque {
1336 let solid_pl = if item.material.is_two_sided() {
1337 hdr_solid_two_sided
1338 } else {
1339 hdr_solid
1340 };
1341 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
1342 }
1343 }
1344 }
1345 }
1346
1347 if !slot.cap_buffers.is_empty() {
1349 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
1350 render_pass.set_pipeline(hdr_overlay);
1351 render_pass.set_bind_group(0, camera_bg, &[]);
1352 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
1353 render_pass.set_bind_group(1, bg, &[]);
1354 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1355 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1356 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
1357 }
1358 }
1359 }
1360
1361 emit_scivis_draw_calls!(
1363 &self.resources,
1364 &mut render_pass,
1365 &self.point_cloud_gpu_data,
1366 &self.glyph_gpu_data,
1367 &self.polyline_gpu_data,
1368 &self.volume_gpu_data,
1369 &self.streamtube_gpu_data,
1370 camera_bg
1371 );
1372
1373 if !self.implicit_gpu_data.is_empty() {
1375 if let Some(pipeline) = &self.resources.implicit_pipeline {
1376 render_pass.set_pipeline(pipeline);
1377 render_pass.set_bind_group(0, camera_bg, &[]);
1378 for gpu in &self.implicit_gpu_data {
1379 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1380 render_pass.draw(0..6, 0..1);
1381 }
1382 }
1383 }
1384 if !self.mc_gpu_data.is_empty() {
1386 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
1387 render_pass.set_pipeline(pipeline);
1388 render_pass.set_bind_group(0, camera_bg, &[]);
1389 for mc in &self.mc_gpu_data {
1390 let vol = &self.resources.mc_volumes[mc.volume_idx];
1391 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1392 for slab in &vol.slabs {
1393 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1394 render_pass.draw_indirect(&slab.indirect_buf, 0);
1395 }
1396 }
1397 }
1398 }
1399
1400 if show_skybox {
1402 render_pass.set_bind_group(0, camera_bg, &[]);
1403 render_pass.set_pipeline(&resources.skybox_pipeline);
1404 render_pass.draw(0..3, 0..1);
1405 }
1406 }
1407
1408 if ssaa_factor > 1 {
1413 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1414 if let (Some(pipeline), Some(bg)) = (
1415 &self.resources.ssaa_resolve_pipeline,
1416 &slot_hdr.ssaa_resolve_bind_group,
1417 ) {
1418 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1419 label: Some("ssaa_resolve_pass"),
1420 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1421 view: &slot_hdr.hdr_view,
1422 resolve_target: None,
1423 ops: wgpu::Operations {
1424 load: wgpu::LoadOp::Load,
1425 store: wgpu::StoreOp::Store,
1426 },
1427 depth_slice: None,
1428 })],
1429 depth_stencil_attachment: None,
1430 timestamp_writes: None,
1431 occlusion_query_set: None,
1432 });
1433 resolve_pass.set_pipeline(pipeline);
1434 resolve_pass.set_bind_group(0, bg, &[]);
1435 resolve_pass.draw(0..3, 0..1);
1436 }
1437 }
1438
1439 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
1445 let resources = &self.resources;
1446 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
1447 &resources.sub_highlight_fill_pipeline,
1448 &resources.sub_highlight_edge_pipeline,
1449 &resources.sub_highlight_sprite_pipeline,
1450 ) {
1451 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1452 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
1453 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1454 label: Some("sub_highlight_pass"),
1455 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1456 view: &slot_hdr.hdr_view,
1457 resolve_target: None,
1458 ops: wgpu::Operations {
1459 load: wgpu::LoadOp::Load,
1460 store: wgpu::StoreOp::Store,
1461 },
1462 depth_slice: None,
1463 })],
1464 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1465 view: &slot_hdr.hdr_depth_view,
1466 depth_ops: Some(wgpu::Operations {
1467 load: wgpu::LoadOp::Load,
1468 store: wgpu::StoreOp::Discard,
1469 }),
1470 stencil_ops: None,
1471 }),
1472 timestamp_writes: None,
1473 occlusion_query_set: None,
1474 });
1475
1476 if sub_hl.fill_vertex_count > 0 {
1477 pass.set_pipeline(fill_pl);
1478 pass.set_bind_group(0, camera_bg, &[]);
1479 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
1480 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
1481 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
1482 }
1483 if sub_hl.edge_segment_count > 0 {
1484 pass.set_pipeline(edge_pl);
1485 pass.set_bind_group(0, camera_bg, &[]);
1486 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
1487 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
1488 pass.draw(0..6, 0..sub_hl.edge_segment_count);
1489 }
1490 if sub_hl.sprite_point_count > 0 {
1491 pass.set_pipeline(sprite_pl);
1492 pass.set_bind_group(0, camera_bg, &[]);
1493 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
1494 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
1495 pass.draw(0..6, 0..sub_hl.sprite_point_count);
1496 }
1497 }
1498 }
1499
1500 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
1505 self.instanced_batches.iter().any(|b| b.is_transparent)
1506 } else {
1507 scene_items
1508 .iter()
1509 .any(|i| i.visible && i.material.opacity < 1.0)
1510 };
1511
1512 if has_transparent {
1513 if let (Some(accum_view), Some(reveal_view)) = (
1515 slot_hdr.oit_accum_view.as_ref(),
1516 slot_hdr.oit_reveal_view.as_ref(),
1517 ) {
1518 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1519 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1521 label: Some("oit_pass"),
1522 color_attachments: &[
1523 Some(wgpu::RenderPassColorAttachment {
1524 view: accum_view,
1525 resolve_target: None,
1526 ops: wgpu::Operations {
1527 load: wgpu::LoadOp::Clear(wgpu::Color {
1528 r: 0.0,
1529 g: 0.0,
1530 b: 0.0,
1531 a: 0.0,
1532 }),
1533 store: wgpu::StoreOp::Store,
1534 },
1535 depth_slice: None,
1536 }),
1537 Some(wgpu::RenderPassColorAttachment {
1538 view: reveal_view,
1539 resolve_target: None,
1540 ops: wgpu::Operations {
1541 load: wgpu::LoadOp::Clear(wgpu::Color {
1542 r: 1.0,
1543 g: 1.0,
1544 b: 1.0,
1545 a: 1.0,
1546 }),
1547 store: wgpu::StoreOp::Store,
1548 },
1549 depth_slice: None,
1550 }),
1551 ],
1552 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1553 view: hdr_depth_view,
1554 depth_ops: Some(wgpu::Operations {
1555 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
1557 }),
1558 stencil_ops: None,
1559 }),
1560 timestamp_writes: None,
1561 occlusion_query_set: None,
1562 });
1563
1564 oit_pass.set_bind_group(0, camera_bg, &[]);
1565
1566 if self.use_instancing && !self.instanced_batches.is_empty() {
1567 let use_indirect_oit = self.gpu_culling_enabled
1568 && self.resources.oit_instanced_cull_pipeline.is_some()
1569 && self.resources.indirect_args_buf.is_some();
1570
1571 if use_indirect_oit {
1572 if let (
1573 Some(pipeline),
1574 Some(indirect_buf),
1575 ) = (
1576 &self.resources.oit_instanced_cull_pipeline,
1577 &self.resources.indirect_args_buf,
1578 ) {
1579 oit_pass.set_pipeline(pipeline);
1580 for (batch_global_idx, batch) in
1581 self.instanced_batches.iter().enumerate()
1582 {
1583 if !batch.is_transparent {
1584 continue;
1585 }
1586 let Some(mesh) =
1587 self.resources.mesh_store.get(batch.mesh_id)
1588 else {
1589 continue;
1590 };
1591 let mat_key = (
1592 batch.texture_id.unwrap_or(u64::MAX),
1593 batch.normal_map_id.unwrap_or(u64::MAX),
1594 batch.ao_map_id.unwrap_or(u64::MAX),
1595 );
1596 let Some(inst_tex_bg) =
1597 self.resources.instance_cull_bind_groups.get(&mat_key)
1598 else {
1599 continue;
1600 };
1601 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1602 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1603 oit_pass.set_index_buffer(
1604 mesh.index_buffer.slice(..),
1605 wgpu::IndexFormat::Uint32,
1606 );
1607 oit_pass.draw_indexed_indirect(
1608 indirect_buf,
1609 batch_global_idx as u64 * 20,
1610 );
1611 }
1612 }
1613 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
1614 oit_pass.set_pipeline(pipeline);
1615 for batch in &self.instanced_batches {
1616 if !batch.is_transparent {
1617 continue;
1618 }
1619 let Some(mesh) = self
1620 .resources
1621 .mesh_store
1622 .get(batch.mesh_id)
1623 else {
1624 continue;
1625 };
1626 let mat_key = (
1627 batch.texture_id.unwrap_or(u64::MAX),
1628 batch.normal_map_id.unwrap_or(u64::MAX),
1629 batch.ao_map_id.unwrap_or(u64::MAX),
1630 );
1631 let Some(inst_tex_bg) =
1632 self.resources.instance_bind_groups.get(&mat_key)
1633 else {
1634 continue;
1635 };
1636 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1637 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1638 oit_pass.set_index_buffer(
1639 mesh.index_buffer.slice(..),
1640 wgpu::IndexFormat::Uint32,
1641 );
1642 oit_pass.draw_indexed(
1643 0..mesh.index_count,
1644 0,
1645 batch.instance_offset..batch.instance_offset + batch.instance_count,
1646 );
1647 }
1648 }
1649 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
1650 oit_pass.set_pipeline(pipeline);
1651 for item in scene_items {
1652 if !item.visible || item.material.opacity >= 1.0 {
1653 continue;
1654 }
1655 let Some(mesh) = self
1656 .resources
1657 .mesh_store
1658 .get(item.mesh_id)
1659 else {
1660 continue;
1661 };
1662 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1663 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1664 oit_pass.set_index_buffer(
1665 mesh.index_buffer.slice(..),
1666 wgpu::IndexFormat::Uint32,
1667 );
1668 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1669 }
1670 }
1671 }
1672 }
1673
1674 if has_transparent {
1679 if let (Some(pipeline), Some(bg)) = (
1680 self.resources.oit_composite_pipeline.as_ref(),
1681 slot_hdr.oit_composite_bind_group.as_ref(),
1682 ) {
1683 let hdr_view = &slot_hdr.hdr_view;
1684 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1685 label: Some("oit_composite_pass"),
1686 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1687 view: hdr_view,
1688 resolve_target: None,
1689 ops: wgpu::Operations {
1690 load: wgpu::LoadOp::Load,
1691 store: wgpu::StoreOp::Store,
1692 },
1693 depth_slice: None,
1694 })],
1695 depth_stencil_attachment: None,
1696 timestamp_writes: None,
1697 occlusion_query_set: None,
1698 });
1699 composite_pass.set_pipeline(pipeline);
1700 composite_pass.set_bind_group(0, bg, &[]);
1701 composite_pass.draw(0..3, 0..1);
1702 }
1703 }
1704
1705 if !slot.outline_object_buffers.is_empty() {
1711 let hdr_pipeline = self
1713 .resources
1714 .outline_composite_pipeline_hdr
1715 .as_ref()
1716 .or(self.resources.outline_composite_pipeline_single.as_ref());
1717 if let Some(pipeline) = hdr_pipeline {
1718 let bg = &slot_hdr.outline_composite_bind_group;
1719 let hdr_view = &slot_hdr.hdr_view;
1720 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1721 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1722 label: Some("hdr_outline_composite_pass"),
1723 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1724 view: hdr_view,
1725 resolve_target: None,
1726 ops: wgpu::Operations {
1727 load: wgpu::LoadOp::Load,
1728 store: wgpu::StoreOp::Store,
1729 },
1730 depth_slice: None,
1731 })],
1732 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1733 view: hdr_depth_view,
1734 depth_ops: Some(wgpu::Operations {
1735 load: wgpu::LoadOp::Load,
1736 store: wgpu::StoreOp::Store,
1737 }),
1738 stencil_ops: None,
1739 }),
1740 timestamp_writes: None,
1741 occlusion_query_set: None,
1742 });
1743 outline_pass.set_pipeline(pipeline);
1744 outline_pass.set_bind_group(0, bg, &[]);
1745 outline_pass.draw(0..3, 0..1);
1746 }
1747 }
1748
1749 let throttle_effects = self.degradation_effects_throttled;
1752
1753 if pp.ssao && !throttle_effects {
1757 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
1758 {
1759 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1760 label: Some("ssao_pass"),
1761 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1762 view: &slot_hdr.ssao_view,
1763 resolve_target: None,
1764 ops: wgpu::Operations {
1765 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1766 store: wgpu::StoreOp::Store,
1767 },
1768 depth_slice: None,
1769 })],
1770 depth_stencil_attachment: None,
1771 timestamp_writes: None,
1772 occlusion_query_set: None,
1773 });
1774 ssao_pass.set_pipeline(ssao_pipeline);
1775 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
1776 ssao_pass.draw(0..3, 0..1);
1777 }
1778
1779 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
1781 let mut ssao_blur_pass =
1782 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1783 label: Some("ssao_blur_pass"),
1784 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1785 view: &slot_hdr.ssao_blur_view,
1786 resolve_target: None,
1787 ops: wgpu::Operations {
1788 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1789 store: wgpu::StoreOp::Store,
1790 },
1791 depth_slice: None,
1792 })],
1793 depth_stencil_attachment: None,
1794 timestamp_writes: None,
1795 occlusion_query_set: None,
1796 });
1797 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
1798 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
1799 ssao_blur_pass.draw(0..3, 0..1);
1800 }
1801 }
1802 }
1803
1804 if pp.contact_shadows && !throttle_effects {
1808 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
1809 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1810 label: Some("contact_shadow_pass"),
1811 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1812 view: &slot_hdr.contact_shadow_view,
1813 resolve_target: None,
1814 depth_slice: None,
1815 ops: wgpu::Operations {
1816 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1817 store: wgpu::StoreOp::Store,
1818 },
1819 })],
1820 depth_stencil_attachment: None,
1821 timestamp_writes: None,
1822 occlusion_query_set: None,
1823 });
1824 cs_pass.set_pipeline(cs_pipeline);
1825 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
1826 cs_pass.draw(0..3, 0..1);
1827 }
1828 }
1829
1830 if pp.bloom && !throttle_effects {
1834 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
1836 {
1837 let mut threshold_pass =
1838 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1839 label: Some("bloom_threshold_pass"),
1840 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1841 view: &slot_hdr.bloom_threshold_view,
1842 resolve_target: None,
1843 ops: wgpu::Operations {
1844 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1845 store: wgpu::StoreOp::Store,
1846 },
1847 depth_slice: None,
1848 })],
1849 depth_stencil_attachment: None,
1850 timestamp_writes: None,
1851 occlusion_query_set: None,
1852 });
1853 threshold_pass.set_pipeline(bloom_threshold_pipeline);
1854 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
1855 threshold_pass.draw(0..3, 0..1);
1856 }
1857
1858 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
1861 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
1862 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
1863 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
1864 let bloom_ping_view = &slot_hdr.bloom_ping_view;
1865 let bloom_pong_view = &slot_hdr.bloom_pong_view;
1866 const BLUR_ITERATIONS: usize = 4;
1867 for i in 0..BLUR_ITERATIONS {
1868 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
1870 {
1871 let mut h_pass =
1872 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1873 label: Some("bloom_blur_h_pass"),
1874 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1875 view: bloom_ping_view,
1876 resolve_target: None,
1877 ops: wgpu::Operations {
1878 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1879 store: wgpu::StoreOp::Store,
1880 },
1881 depth_slice: None,
1882 })],
1883 depth_stencil_attachment: None,
1884 timestamp_writes: None,
1885 occlusion_query_set: None,
1886 });
1887 h_pass.set_pipeline(blur_pipeline);
1888 h_pass.set_bind_group(0, h_bg, &[]);
1889 h_pass.draw(0..3, 0..1);
1890 }
1891 {
1893 let mut v_pass =
1894 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1895 label: Some("bloom_blur_v_pass"),
1896 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1897 view: bloom_pong_view,
1898 resolve_target: None,
1899 ops: wgpu::Operations {
1900 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1901 store: wgpu::StoreOp::Store,
1902 },
1903 depth_slice: None,
1904 })],
1905 depth_stencil_attachment: None,
1906 timestamp_writes: None,
1907 occlusion_query_set: None,
1908 });
1909 v_pass.set_pipeline(blur_pipeline);
1910 v_pass.set_bind_group(0, blur_v_bg, &[]);
1911 v_pass.draw(0..3, 0..1);
1912 }
1913 }
1914 }
1915 }
1916 }
1917
1918 let use_fxaa = pp.fxaa;
1922 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
1923 let tone_target: &wgpu::TextureView = if use_fxaa {
1924 &slot_hdr.fxaa_view
1925 } else {
1926 output_view
1927 };
1928 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1929 label: Some("tone_map_pass"),
1930 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1931 view: tone_target,
1932 resolve_target: None,
1933 ops: wgpu::Operations {
1934 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1935 store: wgpu::StoreOp::Store,
1936 },
1937 depth_slice: None,
1938 })],
1939 depth_stencil_attachment: None,
1940 timestamp_writes: None,
1941 occlusion_query_set: None,
1942 });
1943 tone_pass.set_pipeline(tone_map_pipeline);
1944 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
1945 tone_pass.draw(0..3, 0..1);
1946 }
1947
1948 if use_fxaa {
1952 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
1953 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1954 label: Some("fxaa_pass"),
1955 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1956 view: output_view,
1957 resolve_target: None,
1958 ops: wgpu::Operations {
1959 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1960 store: wgpu::StoreOp::Store,
1961 },
1962 depth_slice: None,
1963 })],
1964 depth_stencil_attachment: None,
1965 timestamp_writes: None,
1966 occlusion_query_set: None,
1967 });
1968 fxaa_pass.set_pipeline(fxaa_pipeline);
1969 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
1970 fxaa_pass.draw(0..3, 0..1);
1971 }
1972 }
1973
1974 if frame.viewport.show_grid {
1978 let slot = &self.viewport_slots[vp_idx];
1979 let slot_hdr = slot.hdr.as_ref().unwrap();
1980 let grid_bg = &slot.grid_bind_group;
1981 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1982 label: Some("hdr_grid_pass"),
1983 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1984 view: output_view,
1985 resolve_target: None,
1986 ops: wgpu::Operations {
1987 load: wgpu::LoadOp::Load,
1988 store: wgpu::StoreOp::Store,
1989 },
1990 depth_slice: None,
1991 })],
1992 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1993 view: &slot_hdr.hdr_depth_view,
1994 depth_ops: Some(wgpu::Operations {
1995 load: wgpu::LoadOp::Load,
1996 store: wgpu::StoreOp::Store,
1997 }),
1998 stencil_ops: None,
1999 }),
2000 timestamp_writes: None,
2001 occlusion_query_set: None,
2002 });
2003 grid_pass.set_pipeline(&self.resources.grid_pipeline);
2004 grid_pass.set_bind_group(0, grid_bg, &[]);
2005 grid_pass.draw(0..3, 0..1);
2006 }
2007
2008 if !matches!(
2011 frame.effects.ground_plane.mode,
2012 crate::renderer::types::GroundPlaneMode::None
2013 ) {
2014 let slot = &self.viewport_slots[vp_idx];
2015 let slot_hdr = slot.hdr.as_ref().unwrap();
2016 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2017 label: Some("hdr_ground_plane_pass"),
2018 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2019 view: output_view,
2020 resolve_target: None,
2021 ops: wgpu::Operations {
2022 load: wgpu::LoadOp::Load,
2023 store: wgpu::StoreOp::Store,
2024 },
2025 depth_slice: None,
2026 })],
2027 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2028 view: &slot_hdr.hdr_depth_view,
2029 depth_ops: Some(wgpu::Operations {
2030 load: wgpu::LoadOp::Load,
2031 store: wgpu::StoreOp::Store,
2032 }),
2033 stencil_ops: None,
2034 }),
2035 timestamp_writes: None,
2036 occlusion_query_set: None,
2037 });
2038 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
2039 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
2040 gp_pass.draw(0..3, 0..1);
2041 }
2042
2043 {
2047 let slot = &self.viewport_slots[vp_idx];
2048 let slot_hdr = slot.hdr.as_ref().unwrap();
2049 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
2050 && slot.gizmo_index_count > 0)
2051 || !slot.constraint_line_buffers.is_empty()
2052 || !slot.clip_plane_fill_buffers.is_empty()
2053 || !slot.clip_plane_line_buffers.is_empty()
2054 || !slot.xray_object_buffers.is_empty();
2055 if has_editor_overlays {
2056 let camera_bg = &slot.camera_bind_group;
2057 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2058 label: Some("hdr_editor_overlay_pass"),
2059 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2060 view: output_view,
2061 resolve_target: None,
2062 ops: wgpu::Operations {
2063 load: wgpu::LoadOp::Load,
2064 store: wgpu::StoreOp::Store,
2065 },
2066 depth_slice: None,
2067 })],
2068 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2069 view: &slot_hdr.hdr_depth_view,
2070 depth_ops: Some(wgpu::Operations {
2071 load: wgpu::LoadOp::Load,
2072 store: wgpu::StoreOp::Discard,
2073 }),
2074 stencil_ops: None,
2075 }),
2076 timestamp_writes: None,
2077 occlusion_query_set: None,
2078 });
2079
2080 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
2081 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
2082 overlay_pass.set_bind_group(0, camera_bg, &[]);
2083 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
2084 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
2085 overlay_pass.set_index_buffer(
2086 slot.gizmo_index_buffer.slice(..),
2087 wgpu::IndexFormat::Uint32,
2088 );
2089 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
2090 }
2091
2092 if !slot.constraint_line_buffers.is_empty() {
2093 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
2094 overlay_pass.set_bind_group(0, camera_bg, &[]);
2095 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
2096 overlay_pass.set_bind_group(1, bg, &[]);
2097 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
2098 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2099 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
2100 }
2101 }
2102
2103 if !slot.clip_plane_fill_buffers.is_empty() {
2104 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
2105 overlay_pass.set_bind_group(0, camera_bg, &[]);
2106 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
2107 overlay_pass.set_bind_group(1, bg, &[]);
2108 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
2109 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2110 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
2111 }
2112 }
2113
2114 if !slot.clip_plane_line_buffers.is_empty() {
2115 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
2116 overlay_pass.set_bind_group(0, camera_bg, &[]);
2117 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
2118 overlay_pass.set_bind_group(1, bg, &[]);
2119 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
2120 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
2121 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
2122 }
2123 }
2124
2125 if !slot.xray_object_buffers.is_empty() {
2126 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
2127 overlay_pass.set_bind_group(0, camera_bg, &[]);
2128 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
2129 let Some(mesh) = self
2130 .resources
2131 .mesh_store
2132 .get(*mesh_id)
2133 else {
2134 continue;
2135 };
2136 overlay_pass.set_bind_group(1, bg, &[]);
2137 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
2138 overlay_pass.set_index_buffer(
2139 mesh.index_buffer.slice(..),
2140 wgpu::IndexFormat::Uint32,
2141 );
2142 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
2143 }
2144 }
2145 }
2146 }
2147
2148 if frame.viewport.show_axes_indicator {
2151 let slot = &self.viewport_slots[vp_idx];
2152 if slot.axes_vertex_count > 0 {
2153 let slot_hdr = slot.hdr.as_ref().unwrap();
2154 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2155 label: Some("hdr_axes_pass"),
2156 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2157 view: output_view,
2158 resolve_target: None,
2159 ops: wgpu::Operations {
2160 load: wgpu::LoadOp::Load,
2161 store: wgpu::StoreOp::Store,
2162 },
2163 depth_slice: None,
2164 })],
2165 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2166 view: &slot_hdr.hdr_depth_view,
2167 depth_ops: Some(wgpu::Operations {
2168 load: wgpu::LoadOp::Load,
2169 store: wgpu::StoreOp::Discard,
2170 }),
2171 stencil_ops: None,
2172 }),
2173 timestamp_writes: None,
2174 occlusion_query_set: None,
2175 });
2176 axes_pass.set_pipeline(&self.resources.axes_pipeline);
2177 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
2178 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
2179 }
2180 }
2181
2182 if !self.screen_image_gpu_data.is_empty() {
2186 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
2187 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
2188 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
2189 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2190 label: Some("screen_image_pass"),
2191 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2192 view: output_view,
2193 resolve_target: None,
2194 ops: wgpu::Operations {
2195 load: wgpu::LoadOp::Load,
2196 store: wgpu::StoreOp::Store,
2197 },
2198 depth_slice: None,
2199 })],
2200 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2201 view: &slot_hdr.hdr_depth_view,
2202 depth_ops: Some(wgpu::Operations {
2203 load: wgpu::LoadOp::Load,
2204 store: wgpu::StoreOp::Discard,
2205 }),
2206 stencil_ops: None,
2207 }),
2208 timestamp_writes: None,
2209 occlusion_query_set: None,
2210 });
2211 for gpu in &self.screen_image_gpu_data {
2212 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
2213 img_pass.set_pipeline(dc_pipe);
2214 img_pass.set_bind_group(0, dc_bg, &[]);
2215 } else {
2216 img_pass.set_pipeline(overlay_pipeline);
2217 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
2218 }
2219 img_pass.draw(0..6, 0..1);
2220 }
2221 }
2222 }
2223
2224 let has_overlay = self.label_gpu_data.is_some()
2226 || self.scalar_bar_gpu_data.is_some()
2227 || self.ruler_gpu_data.is_some()
2228 || self.loading_bar_gpu_data.is_some()
2229 || !self.overlay_image_gpu_data.is_empty();
2230 if has_overlay {
2231 let hdr_depth_view =
2232 &self.viewport_slots[vp_idx].hdr.as_ref().unwrap().hdr_depth_view;
2233 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2234 label: Some("overlay_pass"),
2235 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2236 view: output_view,
2237 resolve_target: None,
2238 ops: wgpu::Operations {
2239 load: wgpu::LoadOp::Load,
2240 store: wgpu::StoreOp::Store,
2241 },
2242 depth_slice: None,
2243 })],
2244 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2245 view: hdr_depth_view,
2246 depth_ops: Some(wgpu::Operations {
2247 load: wgpu::LoadOp::Load,
2248 store: wgpu::StoreOp::Discard,
2249 }),
2250 stencil_ops: None,
2251 }),
2252 timestamp_writes: None,
2253 occlusion_query_set: None,
2254 });
2255 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
2256 overlay_pass.set_pipeline(pipeline);
2257 if let Some(ref ld) = self.label_gpu_data {
2258 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
2259 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
2260 overlay_pass.draw(0..ld.vertex_count, 0..1);
2261 }
2262 if let Some(ref sb) = self.scalar_bar_gpu_data {
2263 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
2264 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
2265 overlay_pass.draw(0..sb.vertex_count, 0..1);
2266 }
2267 if let Some(ref rd) = self.ruler_gpu_data {
2268 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
2269 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
2270 overlay_pass.draw(0..rd.vertex_count, 0..1);
2271 }
2272 if let Some(ref lb) = self.loading_bar_gpu_data {
2273 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
2274 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
2275 overlay_pass.draw(0..lb.vertex_count, 0..1);
2276 }
2277 }
2278 if !self.overlay_image_gpu_data.is_empty() {
2280 if let Some(pipeline) = &self.resources.screen_image_pipeline {
2281 overlay_pass.set_pipeline(pipeline);
2282 for gpu in &self.overlay_image_gpu_data {
2283 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
2284 overlay_pass.draw(0..6, 0..1);
2285 }
2286 }
2287 }
2288 }
2289
2290 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
2292 self.ts_query_set.as_ref(),
2293 self.ts_resolve_buf.as_ref(),
2294 self.ts_staging_buf.as_ref(),
2295 ) {
2296 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
2297 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
2298 self.ts_needs_readback = true;
2299 }
2300
2301 encoder.finish()
2302 }
2303
2304 pub fn render_offscreen(
2318 &mut self,
2319 device: &wgpu::Device,
2320 queue: &wgpu::Queue,
2321 frame: &FrameData,
2322 width: u32,
2323 height: u32,
2324 ) -> Vec<u8> {
2325 let target_format = self.resources.target_format;
2327 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
2328 label: Some("offscreen_target"),
2329 size: wgpu::Extent3d {
2330 width: width.max(1),
2331 height: height.max(1),
2332 depth_or_array_layers: 1,
2333 },
2334 mip_level_count: 1,
2335 sample_count: 1,
2336 dimension: wgpu::TextureDimension::D2,
2337 format: target_format,
2338 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
2339 view_formats: &[],
2340 });
2341
2342 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
2344
2345 let cmd_buf = self.render(device, queue, &output_view, frame);
2353 queue.submit(std::iter::once(cmd_buf));
2354
2355 let bytes_per_pixel = 4u32;
2357 let unpadded_row = width * bytes_per_pixel;
2358 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
2359 let padded_row = (unpadded_row + align - 1) & !(align - 1);
2360 let buffer_size = (padded_row * height.max(1)) as u64;
2361
2362 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
2363 label: Some("offscreen_staging"),
2364 size: buffer_size,
2365 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
2366 mapped_at_creation: false,
2367 });
2368
2369 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
2370 label: Some("offscreen_copy_encoder"),
2371 });
2372 copy_encoder.copy_texture_to_buffer(
2373 wgpu::TexelCopyTextureInfo {
2374 texture: &offscreen_texture,
2375 mip_level: 0,
2376 origin: wgpu::Origin3d::ZERO,
2377 aspect: wgpu::TextureAspect::All,
2378 },
2379 wgpu::TexelCopyBufferInfo {
2380 buffer: &staging_buf,
2381 layout: wgpu::TexelCopyBufferLayout {
2382 offset: 0,
2383 bytes_per_row: Some(padded_row),
2384 rows_per_image: Some(height.max(1)),
2385 },
2386 },
2387 wgpu::Extent3d {
2388 width: width.max(1),
2389 height: height.max(1),
2390 depth_or_array_layers: 1,
2391 },
2392 );
2393 queue.submit(std::iter::once(copy_encoder.finish()));
2394
2395 let (tx, rx) = std::sync::mpsc::channel();
2397 staging_buf
2398 .slice(..)
2399 .map_async(wgpu::MapMode::Read, move |result| {
2400 let _ = tx.send(result);
2401 });
2402 device
2403 .poll(wgpu::PollType::Wait {
2404 submission_index: None,
2405 timeout: Some(std::time::Duration::from_secs(5)),
2406 })
2407 .unwrap();
2408 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
2409
2410 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
2411 {
2412 let mapped = staging_buf.slice(..).get_mapped_range();
2413 let data: &[u8] = &mapped;
2414 if padded_row == unpadded_row {
2415 pixels.extend_from_slice(data);
2417 } else {
2418 for row in 0..height as usize {
2420 let start = row * padded_row as usize;
2421 let end = start + unpadded_row as usize;
2422 pixels.extend_from_slice(&data[start..end]);
2423 }
2424 }
2425 }
2426 staging_buf.unmap();
2427
2428 let is_bgra = matches!(
2430 target_format,
2431 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
2432 );
2433 if is_bgra {
2434 for pixel in pixels.chunks_exact_mut(4) {
2435 pixel.swap(0, 2); }
2437 }
2438
2439 pixels
2440 }
2441}