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 !self.overlay_image_gpu_data.is_empty() {
130 if let Some(pipeline) = &self.resources.screen_image_pipeline {
131 render_pass.set_pipeline(pipeline);
132 for gpu in &self.overlay_image_gpu_data {
133 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
134 render_pass.draw(0..6, 0..1);
135 }
136 }
137 }
138 }
139
140 pub fn paint_to<'rp>(&'rp self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
146 let vp_idx = frame.camera.viewport_index;
147 let camera_bg = self.viewport_camera_bind_group(vp_idx);
148 let grid_bg = self.viewport_grid_bind_group(vp_idx);
149 let vp_slot = self.viewport_slots.get(vp_idx);
150 emit_draw_calls!(
151 &self.resources,
152 &mut *render_pass,
153 frame,
154 self.use_instancing,
155 &self.instanced_batches,
156 camera_bg,
157 grid_bg,
158 &self.compute_filter_results,
159 vp_slot
160 );
161 emit_scivis_draw_calls!(
162 &self.resources,
163 &mut *render_pass,
164 &self.point_cloud_gpu_data,
165 &self.glyph_gpu_data,
166 &self.polyline_gpu_data,
167 &self.volume_gpu_data,
168 &self.streamtube_gpu_data,
169 camera_bg
170 );
171 if !self.implicit_gpu_data.is_empty() {
173 if let Some(pipeline) = &self.resources.implicit_pipeline {
174 render_pass.set_pipeline(pipeline);
175 render_pass.set_bind_group(0, camera_bg, &[]);
176 for gpu in &self.implicit_gpu_data {
177 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
178 render_pass.draw(0..6, 0..1);
179 }
180 }
181 }
182 if !self.mc_gpu_data.is_empty() {
184 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
185 render_pass.set_pipeline(pipeline);
186 render_pass.set_bind_group(0, camera_bg, &[]);
187 for mc in &self.mc_gpu_data {
188 let vol = &self.resources.mc_volumes[mc.volume_idx];
189 render_pass.set_bind_group(1, &mc.render_bg, &[]);
190 for slab in &vol.slabs {
191 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
192 render_pass.draw_indirect(&slab.indirect_buf, 0);
193 }
194 }
195 }
196 }
197 if let Some(sub_hl) = self.viewport_slots.get(vp_idx).and_then(|s| s.sub_highlight.as_ref()) {
199 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
200 &self.resources.sub_highlight_fill_ldr_pipeline,
201 &self.resources.sub_highlight_edge_ldr_pipeline,
202 &self.resources.sub_highlight_sprite_ldr_pipeline,
203 ) {
204 if sub_hl.fill_vertex_count > 0 {
205 render_pass.set_pipeline(fill_pl);
206 render_pass.set_bind_group(0, camera_bg, &[]);
207 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
208 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
209 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
210 }
211 if sub_hl.edge_segment_count > 0 {
212 render_pass.set_pipeline(edge_pl);
213 render_pass.set_bind_group(0, camera_bg, &[]);
214 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
215 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
216 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
217 }
218 if sub_hl.sprite_point_count > 0 {
219 render_pass.set_pipeline(sprite_pl);
220 render_pass.set_bind_group(0, camera_bg, &[]);
221 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
222 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
223 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
224 }
225 }
226 }
227 if !self.screen_image_gpu_data.is_empty() {
229 if let Some(pipeline) = &self.resources.screen_image_pipeline {
230 render_pass.set_pipeline(pipeline);
231 for gpu in &self.screen_image_gpu_data {
232 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
233 render_pass.draw(0..6, 0..1);
234 }
235 }
236 }
237 if let Some(ref ld) = self.label_gpu_data {
239 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
240 render_pass.set_pipeline(pipeline);
241 render_pass.set_bind_group(0, &ld.bind_group, &[]);
242 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
243 render_pass.draw(0..ld.vertex_count, 0..1);
244 }
245 }
246 if let Some(ref sb) = self.scalar_bar_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, &sb.bind_group, &[]);
251 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
252 render_pass.draw(0..sb.vertex_count, 0..1);
253 }
254 }
255 if let Some(ref rd) = self.ruler_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, &rd.bind_group, &[]);
260 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
261 render_pass.draw(0..rd.vertex_count, 0..1);
262 }
263 }
264 if !self.overlay_image_gpu_data.is_empty() {
266 if let Some(pipeline) = &self.resources.screen_image_pipeline {
267 render_pass.set_pipeline(pipeline);
268 for gpu in &self.overlay_image_gpu_data {
269 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
270 render_pass.draw(0..6, 0..1);
271 }
272 }
273 }
274 }
275
276 pub fn render_viewport(
290 &mut self,
291 device: &wgpu::Device,
292 queue: &wgpu::Queue,
293 output_view: &wgpu::TextureView,
294 id: ViewportId,
295 frame: &FrameData,
296 ) -> wgpu::CommandBuffer {
297 self.render_frame_internal(device, queue, output_view, id.0, frame)
298 }
299
300 pub fn render(
308 &mut self,
309 device: &wgpu::Device,
310 queue: &wgpu::Queue,
311 output_view: &wgpu::TextureView,
312 frame: &FrameData,
313 ) -> wgpu::CommandBuffer {
314 self.prepare(device, queue, frame);
316 self.render_frame_internal(
317 device,
318 queue,
319 output_view,
320 frame.camera.viewport_index,
321 frame,
322 )
323 }
324
325 fn render_frame_internal(
330 &mut self,
331 device: &wgpu::Device,
332 queue: &wgpu::Queue,
333 output_view: &wgpu::TextureView,
334 vp_idx: usize,
335 frame: &FrameData,
336 ) -> wgpu::CommandBuffer {
337 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
339 SurfaceSubmission::Flat(items) => items,
340 };
341
342 let bg_color = frame.viewport.background_color.unwrap_or([
343 65.0 / 255.0,
344 65.0 / 255.0,
345 65.0 / 255.0,
346 1.0,
347 ]);
348 let w = frame.camera.viewport_size[0] as u32;
349 let h = frame.camera.viewport_size[1] as u32;
350
351 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
353 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor);
354
355 if self.ts_query_set.is_none()
357 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
358 {
359 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
360 label: Some("ts_query_set"),
361 ty: wgpu::QueryType::Timestamp,
362 count: 2,
363 }));
364 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
365 label: Some("ts_resolve_buf"),
366 size: 16,
367 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
368 mapped_at_creation: false,
369 }));
370 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
371 label: Some("ts_staging_buf"),
372 size: 16,
373 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
374 mapped_at_creation: false,
375 }));
376 self.ts_period = queue.get_timestamp_period();
377 }
378
379 if !frame.effects.post_process.enabled {
380 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
384 label: Some("ldr_encoder"),
385 });
386
387 let use_dyn_res = self.performance_policy.allow_dynamic_resolution
388 && self.current_render_scale < 1.0;
389
390 if use_dyn_res {
391 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
392 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
393 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
394 }
395
396 {
397 let slot = &self.viewport_slots[vp_idx];
398 let slot_hdr = slot.hdr.as_ref().unwrap();
399 let camera_bg = &slot.camera_bind_group;
400 let grid_bg = &slot.grid_bind_group;
401 let (scene_color_view, scene_depth_view): (&wgpu::TextureView, &wgpu::TextureView) =
403 if use_dyn_res {
404 let dr = slot.dyn_res.as_ref().unwrap();
405 (&dr.color_view, &dr.depth_view)
406 } else {
407 (output_view, &slot_hdr.outline_depth_view)
408 };
409 let ts_writes = self.ts_query_set.as_ref().map(|qs| {
410 wgpu::RenderPassTimestampWrites {
411 query_set: qs,
412 beginning_of_pass_write_index: Some(0),
413 end_of_pass_write_index: Some(1),
414 }
415 });
416 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
417 label: Some("ldr_render_pass"),
418 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
419 view: scene_color_view,
420 resolve_target: None,
421 ops: wgpu::Operations {
422 load: wgpu::LoadOp::Clear(wgpu::Color {
423 r: bg_color[0] as f64,
424 g: bg_color[1] as f64,
425 b: bg_color[2] as f64,
426 a: bg_color[3] as f64,
427 }),
428 store: wgpu::StoreOp::Store,
429 },
430 depth_slice: None,
431 })],
432 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
433 view: scene_depth_view,
434 depth_ops: Some(wgpu::Operations {
435 load: wgpu::LoadOp::Clear(1.0),
436 store: wgpu::StoreOp::Discard,
437 }),
438 stencil_ops: None,
439 }),
440 timestamp_writes: ts_writes,
441 occlusion_query_set: None,
442 });
443 emit_draw_calls!(
444 &self.resources,
445 &mut render_pass,
446 frame,
447 self.use_instancing,
448 &self.instanced_batches,
449 camera_bg,
450 grid_bg,
451 &self.compute_filter_results,
452 Some(slot)
453 );
454 emit_scivis_draw_calls!(
455 &self.resources,
456 &mut render_pass,
457 &self.point_cloud_gpu_data,
458 &self.glyph_gpu_data,
459 &self.polyline_gpu_data,
460 &self.volume_gpu_data,
461 &self.streamtube_gpu_data,
462 camera_bg
463 );
464 if !self.implicit_gpu_data.is_empty() {
466 if let Some(pipeline) = &self.resources.implicit_pipeline {
467 render_pass.set_pipeline(pipeline);
468 render_pass.set_bind_group(0, camera_bg, &[]);
469 for gpu in &self.implicit_gpu_data {
470 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
471 render_pass.draw(0..6, 0..1);
472 }
473 }
474 }
475 if !self.mc_gpu_data.is_empty() {
477 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
478 render_pass.set_pipeline(pipeline);
479 render_pass.set_bind_group(0, camera_bg, &[]);
480 for mc in &self.mc_gpu_data {
481 let vol = &self.resources.mc_volumes[mc.volume_idx];
482 render_pass.set_bind_group(1, &mc.render_bg, &[]);
483 for slab in &vol.slabs {
484 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
485 render_pass.draw_indirect(&slab.indirect_buf, 0);
486 }
487 }
488 }
489 }
490 if !self.screen_image_gpu_data.is_empty() {
495 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
496 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
497 for gpu in &self.screen_image_gpu_data {
498 if let (Some(dc_bg), Some(dc_pipe)) =
499 (&gpu.depth_bind_group, dc_pipeline)
500 {
501 render_pass.set_pipeline(dc_pipe);
502 render_pass.set_bind_group(0, dc_bg, &[]);
503 } else {
504 render_pass.set_pipeline(overlay_pipeline);
505 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
506 }
507 render_pass.draw(0..6, 0..1);
508 }
509 }
510 }
511 if let Some(ref ld) = self.label_gpu_data {
513 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
514 render_pass.set_pipeline(pipeline);
515 render_pass.set_bind_group(0, &ld.bind_group, &[]);
516 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
517 render_pass.draw(0..ld.vertex_count, 0..1);
518 }
519 }
520 if let Some(ref sb) = self.scalar_bar_gpu_data {
522 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
523 render_pass.set_pipeline(pipeline);
524 render_pass.set_bind_group(0, &sb.bind_group, &[]);
525 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
526 render_pass.draw(0..sb.vertex_count, 0..1);
527 }
528 }
529 if let Some(ref rd) = self.ruler_gpu_data {
531 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
532 render_pass.set_pipeline(pipeline);
533 render_pass.set_bind_group(0, &rd.bind_group, &[]);
534 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
535 render_pass.draw(0..rd.vertex_count, 0..1);
536 }
537 }
538 if !self.overlay_image_gpu_data.is_empty() {
540 if let Some(pipeline) = &self.resources.screen_image_pipeline {
541 render_pass.set_pipeline(pipeline);
542 for gpu in &self.overlay_image_gpu_data {
543 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
544 render_pass.draw(0..6, 0..1);
545 }
546 }
547 }
548 }
549
550 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
552 self.ts_query_set.as_ref(),
553 self.ts_resolve_buf.as_ref(),
554 self.ts_staging_buf.as_ref(),
555 ) {
556 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
557 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
558 self.ts_needs_readback = true;
559 }
560
561 if use_dyn_res {
563 let upscale_bg =
564 &self.viewport_slots[vp_idx].dyn_res.as_ref().unwrap().upscale_bind_group;
565 let mut upscale_pass =
566 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
567 label: Some("dyn_res_upscale_pass"),
568 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
569 view: output_view,
570 resolve_target: None,
571 ops: wgpu::Operations {
572 load: wgpu::LoadOp::Load,
573 store: wgpu::StoreOp::Store,
574 },
575 depth_slice: None,
576 })],
577 depth_stencil_attachment: None,
578 timestamp_writes: None,
579 occlusion_query_set: None,
580 });
581 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
582 upscale_pass.set_pipeline(pipeline);
583 upscale_pass.set_bind_group(0, upscale_bg, &[]);
584 upscale_pass.draw(0..3, 0..1);
585 }
586 }
587
588 return encoder.finish();
589 }
590
591 let pp = &frame.effects.post_process;
593
594 let hdr_clear_rgb = [
595 bg_color[0].powf(2.2),
596 bg_color[1].powf(2.2),
597 bg_color[2].powf(2.2),
598 ];
599
600 let mode = match pp.tone_mapping {
602 crate::renderer::ToneMapping::Reinhard => 0u32,
603 crate::renderer::ToneMapping::Aces => 1u32,
604 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
605 };
606 let tm_uniform = crate::resources::ToneMapUniform {
607 exposure: pp.exposure,
608 mode,
609 bloom_enabled: if pp.bloom { 1 } else { 0 },
610 ssao_enabled: if pp.ssao { 1 } else { 0 },
611 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
612 _pad_tm: [0; 3],
613 background_color: bg_color,
614 };
615 {
616 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
617 queue.write_buffer(
618 &hdr.tone_map_uniform_buf,
619 0,
620 bytemuck::cast_slice(&[tm_uniform]),
621 );
622
623 if pp.ssao {
625 let proj = frame.camera.render_camera.projection;
626 let inv_proj = proj.inverse();
627 let ssao_uniform = crate::resources::SsaoUniform {
628 inv_proj: inv_proj.to_cols_array_2d(),
629 proj: proj.to_cols_array_2d(),
630 radius: 0.5,
631 bias: 0.025,
632 _pad: [0.0; 2],
633 };
634 queue.write_buffer(
635 &hdr.ssao_uniform_buf,
636 0,
637 bytemuck::cast_slice(&[ssao_uniform]),
638 );
639 }
640
641 if pp.contact_shadows {
643 let proj = frame.camera.render_camera.projection;
644 let inv_proj = proj.inverse();
645 let light_dir_world: glam::Vec3 =
646 if let Some(l) = frame.effects.lighting.lights.first() {
647 match l.kind {
648 LightKind::Directional { direction } => {
649 glam::Vec3::from(direction).normalize()
650 }
651 LightKind::Spot { direction, .. } => {
652 glam::Vec3::from(direction).normalize()
653 }
654 _ => glam::Vec3::new(0.0, -1.0, 0.0),
655 }
656 } else {
657 glam::Vec3::new(0.0, -1.0, 0.0)
658 };
659 let view = frame.camera.render_camera.view;
660 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
661 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
662 let cs_uniform = crate::resources::ContactShadowUniform {
663 inv_proj: inv_proj.to_cols_array_2d(),
664 proj: proj.to_cols_array_2d(),
665 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
666 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
667 params: [
668 pp.contact_shadow_max_distance,
669 pp.contact_shadow_steps as f32,
670 pp.contact_shadow_thickness,
671 0.0,
672 ],
673 };
674 queue.write_buffer(
675 &hdr.contact_shadow_uniform_buf,
676 0,
677 bytemuck::cast_slice(&[cs_uniform]),
678 );
679 }
680
681 if pp.bloom {
683 let bloom_u = crate::resources::BloomUniform {
684 threshold: pp.bloom_threshold,
685 intensity: pp.bloom_intensity,
686 horizontal: 0,
687 _pad: 0,
688 };
689 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
690 }
691 }
692
693 {
695 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
696 self.resources.rebuild_tone_map_bind_group(
697 device,
698 hdr,
699 pp.bloom,
700 pp.ssao,
701 pp.contact_shadows,
702 );
703 }
704
705 {
710 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
711 self.instanced_batches.iter().any(|b| b.is_transparent)
712 } else {
713 scene_items
714 .iter()
715 .any(|i| i.visible && i.material.opacity < 1.0)
716 };
717 if needs_oit {
718 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
719 self.resources
720 .ensure_viewport_oit(device, hdr, w.max(1), h.max(1));
721 }
722 }
723
724 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
728 label: Some("hdr_encoder"),
729 });
730
731 let slot = &self.viewport_slots[vp_idx];
733 let camera_bg = &slot.camera_bind_group;
734 let slot_hdr = slot.hdr.as_ref().unwrap();
735
736 {
740 let use_ssaa = ssaa_factor > 1
742 && slot_hdr.ssaa_color_view.is_some()
743 && slot_hdr.ssaa_depth_view.is_some();
744 let scene_color_view = if use_ssaa {
745 slot_hdr.ssaa_color_view.as_ref().unwrap()
746 } else {
747 &slot_hdr.hdr_view
748 };
749 let scene_depth_view = if use_ssaa {
750 slot_hdr.ssaa_depth_view.as_ref().unwrap()
751 } else {
752 &slot_hdr.hdr_depth_view
753 };
754
755 let clear_wgpu = wgpu::Color {
756 r: hdr_clear_rgb[0] as f64,
757 g: hdr_clear_rgb[1] as f64,
758 b: hdr_clear_rgb[2] as f64,
759 a: bg_color[3] as f64,
760 };
761
762 let hdr_ts_writes = self.ts_query_set.as_ref().map(|qs| {
763 wgpu::RenderPassTimestampWrites {
764 query_set: qs,
765 beginning_of_pass_write_index: Some(0),
766 end_of_pass_write_index: Some(1),
767 }
768 });
769 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
770 label: Some("hdr_scene_pass"),
771 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
772 view: scene_color_view,
773 resolve_target: None,
774 ops: wgpu::Operations {
775 load: wgpu::LoadOp::Clear(clear_wgpu),
776 store: wgpu::StoreOp::Store,
777 },
778 depth_slice: None,
779 })],
780 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
781 view: scene_depth_view,
782 depth_ops: Some(wgpu::Operations {
783 load: wgpu::LoadOp::Clear(1.0),
784 store: wgpu::StoreOp::Store,
785 }),
786 stencil_ops: Some(wgpu::Operations {
787 load: wgpu::LoadOp::Clear(0),
788 store: wgpu::StoreOp::Store,
789 }),
790 }),
791 timestamp_writes: hdr_ts_writes,
792 occlusion_query_set: None,
793 });
794
795 let resources = &self.resources;
796 render_pass.set_bind_group(0, camera_bg, &[]);
797
798 let show_skybox = frame
800 .effects
801 .environment
802 .as_ref()
803 .is_some_and(|e| e.show_skybox)
804 && resources.ibl_skybox_view.is_some();
805
806 let use_instancing = self.use_instancing;
807 let batches = &self.instanced_batches;
808
809 if !scene_items.is_empty() {
810 if use_instancing && !batches.is_empty() {
811 let excluded_items: Vec<&SceneRenderItem> = scene_items
812 .iter()
813 .filter(|item| {
814 item.visible
815 && (item.active_attribute.is_some()
816 || item.material.is_two_sided()
817 || item.material.matcap_id.is_some())
818 && resources
819 .mesh_store
820 .get(item.mesh_id)
821 .is_some()
822 })
823 .collect();
824
825 let mut opaque_batches: Vec<&InstancedBatch> = Vec::new();
827 let mut transparent_batches: Vec<&InstancedBatch> = Vec::new();
828 for batch in batches {
829 if batch.is_transparent {
830 transparent_batches.push(batch);
831 } else {
832 opaque_batches.push(batch);
833 }
834 }
835
836 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
837 if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
838 render_pass.set_pipeline(pipeline);
839 for batch in &opaque_batches {
840 let Some(mesh) = resources
841 .mesh_store
842 .get(batch.mesh_id)
843 else {
844 continue;
845 };
846 let mat_key = (
847 batch.texture_id.unwrap_or(u64::MAX),
848 batch.normal_map_id.unwrap_or(u64::MAX),
849 batch.ao_map_id.unwrap_or(u64::MAX),
850 );
851 let Some(inst_tex_bg) =
852 resources.instance_bind_groups.get(&mat_key)
853 else {
854 continue;
855 };
856 render_pass.set_bind_group(1, inst_tex_bg, &[]);
857 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
858 render_pass.set_index_buffer(
859 mesh.index_buffer.slice(..),
860 wgpu::IndexFormat::Uint32,
861 );
862 render_pass.draw_indexed(
863 0..mesh.index_count,
864 0,
865 batch.instance_offset
866 ..batch.instance_offset + batch.instance_count,
867 );
868 }
869 }
870 }
871
872 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
877 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
878 render_pass.set_pipeline(hdr_wf);
879 for item in scene_items {
880 if !item.visible {
881 continue;
882 }
883 let Some(mesh) = resources
884 .mesh_store
885 .get(item.mesh_id)
886 else {
887 continue;
888 };
889 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
890 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
891 render_pass.set_index_buffer(
892 mesh.edge_index_buffer.slice(..),
893 wgpu::IndexFormat::Uint32,
894 );
895 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
896 }
897 }
898 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
899 &resources.hdr_solid_pipeline,
900 &resources.hdr_solid_two_sided_pipeline,
901 ) {
902 for item in excluded_items
903 .into_iter()
904 .filter(|item| item.material.opacity >= 1.0)
905 {
906 let Some(mesh) = resources
907 .mesh_store
908 .get(item.mesh_id)
909 else {
910 continue;
911 };
912 let pipeline = if item.material.is_two_sided() {
913 hdr_solid_two_sided
914 } else {
915 hdr_solid
916 };
917 render_pass.set_pipeline(pipeline);
918 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
919 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
920 render_pass.set_index_buffer(
921 mesh.index_buffer.slice(..),
922 wgpu::IndexFormat::Uint32,
923 );
924 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
925 }
926 }
927 } else {
928 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
930 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
931 let pos =
932 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
933 (pos - eye).length()
934 };
935
936 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
937 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
938 for item in scene_items {
939 if !item.visible
940 || resources
941 .mesh_store
942 .get(item.mesh_id)
943 .is_none()
944 {
945 continue;
946 }
947 if item.material.opacity < 1.0 {
948 transparent.push(item);
949 } else {
950 opaque.push(item);
951 }
952 }
953 opaque.sort_by(|a, b| {
954 dist_from_eye(a)
955 .partial_cmp(&dist_from_eye(b))
956 .unwrap_or(std::cmp::Ordering::Equal)
957 });
958 transparent.sort_by(|a, b| {
959 dist_from_eye(b)
960 .partial_cmp(&dist_from_eye(a))
961 .unwrap_or(std::cmp::Ordering::Equal)
962 });
963
964 let draw_item_hdr =
965 |render_pass: &mut wgpu::RenderPass<'_>,
966 item: &SceneRenderItem,
967 solid_pl: &wgpu::RenderPipeline,
968 trans_pl: &wgpu::RenderPipeline,
969 wf_pl: &wgpu::RenderPipeline| {
970 let mesh = resources
971 .mesh_store
972 .get(item.mesh_id)
973 .unwrap();
974 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
977 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
978 matches!(
979 a.kind,
980 crate::resources::AttributeKind::Face
981 | crate::resources::AttributeKind::FaceColor
982 | crate::resources::AttributeKind::Halfedge
983 | crate::resources::AttributeKind::Corner
984 )
985 });
986 if frame.viewport.wireframe_mode {
987 render_pass.set_pipeline(wf_pl);
988 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
989 render_pass.set_index_buffer(
990 mesh.edge_index_buffer.slice(..),
991 wgpu::IndexFormat::Uint32,
992 );
993 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
994 } else if is_face_attr {
995 if let Some(ref fvb) = mesh.face_vertex_buffer {
996 let pl = if item.material.opacity < 1.0 {
997 trans_pl
998 } else {
999 solid_pl
1000 };
1001 render_pass.set_pipeline(pl);
1002 render_pass.set_vertex_buffer(0, fvb.slice(..));
1003 render_pass.draw(0..mesh.index_count, 0..1);
1004 }
1005 } else if item.material.opacity < 1.0 {
1006 render_pass.set_pipeline(trans_pl);
1007 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1008 render_pass.set_index_buffer(
1009 mesh.index_buffer.slice(..),
1010 wgpu::IndexFormat::Uint32,
1011 );
1012 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1013 } else {
1014 render_pass.set_pipeline(solid_pl);
1015 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1016 render_pass.set_index_buffer(
1017 mesh.index_buffer.slice(..),
1018 wgpu::IndexFormat::Uint32,
1019 );
1020 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1021 }
1022 };
1023
1024 let _ = &transparent; if let (
1028 Some(hdr_solid),
1029 Some(hdr_solid_two_sided),
1030 Some(hdr_trans),
1031 Some(hdr_wf),
1032 ) = (
1033 &resources.hdr_solid_pipeline,
1034 &resources.hdr_solid_two_sided_pipeline,
1035 &resources.hdr_transparent_pipeline,
1036 &resources.hdr_wireframe_pipeline,
1037 ) {
1038 for item in &opaque {
1039 let solid_pl = if item.material.is_two_sided() {
1040 hdr_solid_two_sided
1041 } else {
1042 hdr_solid
1043 };
1044 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
1045 }
1046 }
1047 }
1048 }
1049
1050 if !slot.cap_buffers.is_empty() {
1052 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
1053 render_pass.set_pipeline(hdr_overlay);
1054 render_pass.set_bind_group(0, camera_bg, &[]);
1055 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
1056 render_pass.set_bind_group(1, bg, &[]);
1057 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1058 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1059 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
1060 }
1061 }
1062 }
1063
1064 emit_scivis_draw_calls!(
1066 &self.resources,
1067 &mut render_pass,
1068 &self.point_cloud_gpu_data,
1069 &self.glyph_gpu_data,
1070 &self.polyline_gpu_data,
1071 &self.volume_gpu_data,
1072 &self.streamtube_gpu_data,
1073 camera_bg
1074 );
1075
1076 if !self.implicit_gpu_data.is_empty() {
1078 if let Some(pipeline) = &self.resources.implicit_pipeline {
1079 render_pass.set_pipeline(pipeline);
1080 render_pass.set_bind_group(0, camera_bg, &[]);
1081 for gpu in &self.implicit_gpu_data {
1082 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1083 render_pass.draw(0..6, 0..1);
1084 }
1085 }
1086 }
1087 if !self.mc_gpu_data.is_empty() {
1089 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
1090 render_pass.set_pipeline(pipeline);
1091 render_pass.set_bind_group(0, camera_bg, &[]);
1092 for mc in &self.mc_gpu_data {
1093 let vol = &self.resources.mc_volumes[mc.volume_idx];
1094 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1095 for slab in &vol.slabs {
1096 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1097 render_pass.draw_indirect(&slab.indirect_buf, 0);
1098 }
1099 }
1100 }
1101 }
1102
1103 if show_skybox {
1105 render_pass.set_bind_group(0, camera_bg, &[]);
1106 render_pass.set_pipeline(&resources.skybox_pipeline);
1107 render_pass.draw(0..3, 0..1);
1108 }
1109 }
1110
1111 if ssaa_factor > 1 {
1116 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1117 if let (Some(pipeline), Some(bg)) = (
1118 &self.resources.ssaa_resolve_pipeline,
1119 &slot_hdr.ssaa_resolve_bind_group,
1120 ) {
1121 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1122 label: Some("ssaa_resolve_pass"),
1123 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1124 view: &slot_hdr.hdr_view,
1125 resolve_target: None,
1126 ops: wgpu::Operations {
1127 load: wgpu::LoadOp::Load,
1128 store: wgpu::StoreOp::Store,
1129 },
1130 depth_slice: None,
1131 })],
1132 depth_stencil_attachment: None,
1133 timestamp_writes: None,
1134 occlusion_query_set: None,
1135 });
1136 resolve_pass.set_pipeline(pipeline);
1137 resolve_pass.set_bind_group(0, bg, &[]);
1138 resolve_pass.draw(0..3, 0..1);
1139 }
1140 }
1141
1142 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
1148 let resources = &self.resources;
1149 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
1150 &resources.sub_highlight_fill_pipeline,
1151 &resources.sub_highlight_edge_pipeline,
1152 &resources.sub_highlight_sprite_pipeline,
1153 ) {
1154 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1155 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
1156 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1157 label: Some("sub_highlight_pass"),
1158 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1159 view: &slot_hdr.hdr_view,
1160 resolve_target: None,
1161 ops: wgpu::Operations {
1162 load: wgpu::LoadOp::Load,
1163 store: wgpu::StoreOp::Store,
1164 },
1165 depth_slice: None,
1166 })],
1167 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1168 view: &slot_hdr.hdr_depth_view,
1169 depth_ops: Some(wgpu::Operations {
1170 load: wgpu::LoadOp::Load,
1171 store: wgpu::StoreOp::Discard,
1172 }),
1173 stencil_ops: None,
1174 }),
1175 timestamp_writes: None,
1176 occlusion_query_set: None,
1177 });
1178
1179 if sub_hl.fill_vertex_count > 0 {
1180 pass.set_pipeline(fill_pl);
1181 pass.set_bind_group(0, camera_bg, &[]);
1182 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
1183 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
1184 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
1185 }
1186 if sub_hl.edge_segment_count > 0 {
1187 pass.set_pipeline(edge_pl);
1188 pass.set_bind_group(0, camera_bg, &[]);
1189 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
1190 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
1191 pass.draw(0..6, 0..sub_hl.edge_segment_count);
1192 }
1193 if sub_hl.sprite_point_count > 0 {
1194 pass.set_pipeline(sprite_pl);
1195 pass.set_bind_group(0, camera_bg, &[]);
1196 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
1197 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
1198 pass.draw(0..6, 0..sub_hl.sprite_point_count);
1199 }
1200 }
1201 }
1202
1203 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
1208 self.instanced_batches.iter().any(|b| b.is_transparent)
1209 } else {
1210 scene_items
1211 .iter()
1212 .any(|i| i.visible && i.material.opacity < 1.0)
1213 };
1214
1215 if has_transparent {
1216 if let (Some(accum_view), Some(reveal_view)) = (
1218 slot_hdr.oit_accum_view.as_ref(),
1219 slot_hdr.oit_reveal_view.as_ref(),
1220 ) {
1221 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1222 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1224 label: Some("oit_pass"),
1225 color_attachments: &[
1226 Some(wgpu::RenderPassColorAttachment {
1227 view: accum_view,
1228 resolve_target: None,
1229 ops: wgpu::Operations {
1230 load: wgpu::LoadOp::Clear(wgpu::Color {
1231 r: 0.0,
1232 g: 0.0,
1233 b: 0.0,
1234 a: 0.0,
1235 }),
1236 store: wgpu::StoreOp::Store,
1237 },
1238 depth_slice: None,
1239 }),
1240 Some(wgpu::RenderPassColorAttachment {
1241 view: reveal_view,
1242 resolve_target: None,
1243 ops: wgpu::Operations {
1244 load: wgpu::LoadOp::Clear(wgpu::Color {
1245 r: 1.0,
1246 g: 1.0,
1247 b: 1.0,
1248 a: 1.0,
1249 }),
1250 store: wgpu::StoreOp::Store,
1251 },
1252 depth_slice: None,
1253 }),
1254 ],
1255 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1256 view: hdr_depth_view,
1257 depth_ops: Some(wgpu::Operations {
1258 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
1260 }),
1261 stencil_ops: None,
1262 }),
1263 timestamp_writes: None,
1264 occlusion_query_set: None,
1265 });
1266
1267 oit_pass.set_bind_group(0, camera_bg, &[]);
1268
1269 if self.use_instancing && !self.instanced_batches.is_empty() {
1270 if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
1271 oit_pass.set_pipeline(pipeline);
1272 for batch in &self.instanced_batches {
1273 if !batch.is_transparent {
1274 continue;
1275 }
1276 let Some(mesh) = self
1277 .resources
1278 .mesh_store
1279 .get(batch.mesh_id)
1280 else {
1281 continue;
1282 };
1283 let mat_key = (
1284 batch.texture_id.unwrap_or(u64::MAX),
1285 batch.normal_map_id.unwrap_or(u64::MAX),
1286 batch.ao_map_id.unwrap_or(u64::MAX),
1287 );
1288 let Some(inst_tex_bg) =
1289 self.resources.instance_bind_groups.get(&mat_key)
1290 else {
1291 continue;
1292 };
1293 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1294 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1295 oit_pass.set_index_buffer(
1296 mesh.index_buffer.slice(..),
1297 wgpu::IndexFormat::Uint32,
1298 );
1299 oit_pass.draw_indexed(
1300 0..mesh.index_count,
1301 0,
1302 batch.instance_offset..batch.instance_offset + batch.instance_count,
1303 );
1304 }
1305 }
1306 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
1307 oit_pass.set_pipeline(pipeline);
1308 for item in scene_items {
1309 if !item.visible || item.material.opacity >= 1.0 {
1310 continue;
1311 }
1312 let Some(mesh) = self
1313 .resources
1314 .mesh_store
1315 .get(item.mesh_id)
1316 else {
1317 continue;
1318 };
1319 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1320 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1321 oit_pass.set_index_buffer(
1322 mesh.index_buffer.slice(..),
1323 wgpu::IndexFormat::Uint32,
1324 );
1325 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1326 }
1327 }
1328 }
1329 }
1330
1331 if has_transparent {
1336 if let (Some(pipeline), Some(bg)) = (
1337 self.resources.oit_composite_pipeline.as_ref(),
1338 slot_hdr.oit_composite_bind_group.as_ref(),
1339 ) {
1340 let hdr_view = &slot_hdr.hdr_view;
1341 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1342 label: Some("oit_composite_pass"),
1343 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1344 view: hdr_view,
1345 resolve_target: None,
1346 ops: wgpu::Operations {
1347 load: wgpu::LoadOp::Load,
1348 store: wgpu::StoreOp::Store,
1349 },
1350 depth_slice: None,
1351 })],
1352 depth_stencil_attachment: None,
1353 timestamp_writes: None,
1354 occlusion_query_set: None,
1355 });
1356 composite_pass.set_pipeline(pipeline);
1357 composite_pass.set_bind_group(0, bg, &[]);
1358 composite_pass.draw(0..3, 0..1);
1359 }
1360 }
1361
1362 if !slot.outline_object_buffers.is_empty() {
1368 let hdr_pipeline = self
1370 .resources
1371 .outline_composite_pipeline_hdr
1372 .as_ref()
1373 .or(self.resources.outline_composite_pipeline_single.as_ref());
1374 if let Some(pipeline) = hdr_pipeline {
1375 let bg = &slot_hdr.outline_composite_bind_group;
1376 let hdr_view = &slot_hdr.hdr_view;
1377 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1378 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1379 label: Some("hdr_outline_composite_pass"),
1380 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1381 view: hdr_view,
1382 resolve_target: None,
1383 ops: wgpu::Operations {
1384 load: wgpu::LoadOp::Load,
1385 store: wgpu::StoreOp::Store,
1386 },
1387 depth_slice: None,
1388 })],
1389 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1390 view: hdr_depth_view,
1391 depth_ops: Some(wgpu::Operations {
1392 load: wgpu::LoadOp::Load,
1393 store: wgpu::StoreOp::Store,
1394 }),
1395 stencil_ops: None,
1396 }),
1397 timestamp_writes: None,
1398 occlusion_query_set: None,
1399 });
1400 outline_pass.set_pipeline(pipeline);
1401 outline_pass.set_bind_group(0, bg, &[]);
1402 outline_pass.draw(0..3, 0..1);
1403 }
1404 }
1405
1406 let throttle_effects = self.last_stats.missed_budget
1409 && self.performance_policy.allow_effect_throttling;
1410
1411 if pp.ssao && !throttle_effects {
1415 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
1416 {
1417 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1418 label: Some("ssao_pass"),
1419 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1420 view: &slot_hdr.ssao_view,
1421 resolve_target: None,
1422 ops: wgpu::Operations {
1423 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1424 store: wgpu::StoreOp::Store,
1425 },
1426 depth_slice: None,
1427 })],
1428 depth_stencil_attachment: None,
1429 timestamp_writes: None,
1430 occlusion_query_set: None,
1431 });
1432 ssao_pass.set_pipeline(ssao_pipeline);
1433 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
1434 ssao_pass.draw(0..3, 0..1);
1435 }
1436
1437 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
1439 let mut ssao_blur_pass =
1440 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1441 label: Some("ssao_blur_pass"),
1442 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1443 view: &slot_hdr.ssao_blur_view,
1444 resolve_target: None,
1445 ops: wgpu::Operations {
1446 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1447 store: wgpu::StoreOp::Store,
1448 },
1449 depth_slice: None,
1450 })],
1451 depth_stencil_attachment: None,
1452 timestamp_writes: None,
1453 occlusion_query_set: None,
1454 });
1455 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
1456 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
1457 ssao_blur_pass.draw(0..3, 0..1);
1458 }
1459 }
1460 }
1461
1462 if pp.contact_shadows && !throttle_effects {
1466 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
1467 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1468 label: Some("contact_shadow_pass"),
1469 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1470 view: &slot_hdr.contact_shadow_view,
1471 resolve_target: None,
1472 depth_slice: None,
1473 ops: wgpu::Operations {
1474 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1475 store: wgpu::StoreOp::Store,
1476 },
1477 })],
1478 depth_stencil_attachment: None,
1479 timestamp_writes: None,
1480 occlusion_query_set: None,
1481 });
1482 cs_pass.set_pipeline(cs_pipeline);
1483 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
1484 cs_pass.draw(0..3, 0..1);
1485 }
1486 }
1487
1488 if pp.bloom && !throttle_effects {
1492 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
1494 {
1495 let mut threshold_pass =
1496 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1497 label: Some("bloom_threshold_pass"),
1498 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1499 view: &slot_hdr.bloom_threshold_view,
1500 resolve_target: None,
1501 ops: wgpu::Operations {
1502 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1503 store: wgpu::StoreOp::Store,
1504 },
1505 depth_slice: None,
1506 })],
1507 depth_stencil_attachment: None,
1508 timestamp_writes: None,
1509 occlusion_query_set: None,
1510 });
1511 threshold_pass.set_pipeline(bloom_threshold_pipeline);
1512 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
1513 threshold_pass.draw(0..3, 0..1);
1514 }
1515
1516 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
1519 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
1520 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
1521 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
1522 let bloom_ping_view = &slot_hdr.bloom_ping_view;
1523 let bloom_pong_view = &slot_hdr.bloom_pong_view;
1524 const BLUR_ITERATIONS: usize = 4;
1525 for i in 0..BLUR_ITERATIONS {
1526 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
1528 {
1529 let mut h_pass =
1530 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1531 label: Some("bloom_blur_h_pass"),
1532 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1533 view: bloom_ping_view,
1534 resolve_target: None,
1535 ops: wgpu::Operations {
1536 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1537 store: wgpu::StoreOp::Store,
1538 },
1539 depth_slice: None,
1540 })],
1541 depth_stencil_attachment: None,
1542 timestamp_writes: None,
1543 occlusion_query_set: None,
1544 });
1545 h_pass.set_pipeline(blur_pipeline);
1546 h_pass.set_bind_group(0, h_bg, &[]);
1547 h_pass.draw(0..3, 0..1);
1548 }
1549 {
1551 let mut v_pass =
1552 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1553 label: Some("bloom_blur_v_pass"),
1554 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1555 view: bloom_pong_view,
1556 resolve_target: None,
1557 ops: wgpu::Operations {
1558 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1559 store: wgpu::StoreOp::Store,
1560 },
1561 depth_slice: None,
1562 })],
1563 depth_stencil_attachment: None,
1564 timestamp_writes: None,
1565 occlusion_query_set: None,
1566 });
1567 v_pass.set_pipeline(blur_pipeline);
1568 v_pass.set_bind_group(0, blur_v_bg, &[]);
1569 v_pass.draw(0..3, 0..1);
1570 }
1571 }
1572 }
1573 }
1574 }
1575
1576 let use_fxaa = pp.fxaa;
1580 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
1581 let tone_target: &wgpu::TextureView = if use_fxaa {
1582 &slot_hdr.fxaa_view
1583 } else {
1584 output_view
1585 };
1586 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1587 label: Some("tone_map_pass"),
1588 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1589 view: tone_target,
1590 resolve_target: None,
1591 ops: wgpu::Operations {
1592 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1593 store: wgpu::StoreOp::Store,
1594 },
1595 depth_slice: None,
1596 })],
1597 depth_stencil_attachment: None,
1598 timestamp_writes: None,
1599 occlusion_query_set: None,
1600 });
1601 tone_pass.set_pipeline(tone_map_pipeline);
1602 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
1603 tone_pass.draw(0..3, 0..1);
1604 }
1605
1606 if use_fxaa {
1610 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
1611 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1612 label: Some("fxaa_pass"),
1613 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1614 view: output_view,
1615 resolve_target: None,
1616 ops: wgpu::Operations {
1617 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1618 store: wgpu::StoreOp::Store,
1619 },
1620 depth_slice: None,
1621 })],
1622 depth_stencil_attachment: None,
1623 timestamp_writes: None,
1624 occlusion_query_set: None,
1625 });
1626 fxaa_pass.set_pipeline(fxaa_pipeline);
1627 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
1628 fxaa_pass.draw(0..3, 0..1);
1629 }
1630 }
1631
1632 if frame.viewport.show_grid {
1636 let slot = &self.viewport_slots[vp_idx];
1637 let slot_hdr = slot.hdr.as_ref().unwrap();
1638 let grid_bg = &slot.grid_bind_group;
1639 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1640 label: Some("hdr_grid_pass"),
1641 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1642 view: output_view,
1643 resolve_target: None,
1644 ops: wgpu::Operations {
1645 load: wgpu::LoadOp::Load,
1646 store: wgpu::StoreOp::Store,
1647 },
1648 depth_slice: None,
1649 })],
1650 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1651 view: &slot_hdr.hdr_depth_view,
1652 depth_ops: Some(wgpu::Operations {
1653 load: wgpu::LoadOp::Load,
1654 store: wgpu::StoreOp::Store,
1655 }),
1656 stencil_ops: None,
1657 }),
1658 timestamp_writes: None,
1659 occlusion_query_set: None,
1660 });
1661 grid_pass.set_pipeline(&self.resources.grid_pipeline);
1662 grid_pass.set_bind_group(0, grid_bg, &[]);
1663 grid_pass.draw(0..3, 0..1);
1664 }
1665
1666 if !matches!(
1669 frame.effects.ground_plane.mode,
1670 crate::renderer::types::GroundPlaneMode::None
1671 ) {
1672 let slot = &self.viewport_slots[vp_idx];
1673 let slot_hdr = slot.hdr.as_ref().unwrap();
1674 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1675 label: Some("hdr_ground_plane_pass"),
1676 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1677 view: output_view,
1678 resolve_target: None,
1679 ops: wgpu::Operations {
1680 load: wgpu::LoadOp::Load,
1681 store: wgpu::StoreOp::Store,
1682 },
1683 depth_slice: None,
1684 })],
1685 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1686 view: &slot_hdr.hdr_depth_view,
1687 depth_ops: Some(wgpu::Operations {
1688 load: wgpu::LoadOp::Load,
1689 store: wgpu::StoreOp::Store,
1690 }),
1691 stencil_ops: None,
1692 }),
1693 timestamp_writes: None,
1694 occlusion_query_set: None,
1695 });
1696 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
1697 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
1698 gp_pass.draw(0..3, 0..1);
1699 }
1700
1701 {
1705 let slot = &self.viewport_slots[vp_idx];
1706 let slot_hdr = slot.hdr.as_ref().unwrap();
1707 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
1708 && slot.gizmo_index_count > 0)
1709 || !slot.constraint_line_buffers.is_empty()
1710 || !slot.clip_plane_fill_buffers.is_empty()
1711 || !slot.clip_plane_line_buffers.is_empty()
1712 || !slot.xray_object_buffers.is_empty();
1713 if has_editor_overlays {
1714 let camera_bg = &slot.camera_bind_group;
1715 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1716 label: Some("hdr_editor_overlay_pass"),
1717 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1718 view: output_view,
1719 resolve_target: None,
1720 ops: wgpu::Operations {
1721 load: wgpu::LoadOp::Load,
1722 store: wgpu::StoreOp::Store,
1723 },
1724 depth_slice: None,
1725 })],
1726 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1727 view: &slot_hdr.hdr_depth_view,
1728 depth_ops: Some(wgpu::Operations {
1729 load: wgpu::LoadOp::Load,
1730 store: wgpu::StoreOp::Discard,
1731 }),
1732 stencil_ops: None,
1733 }),
1734 timestamp_writes: None,
1735 occlusion_query_set: None,
1736 });
1737
1738 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
1739 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
1740 overlay_pass.set_bind_group(0, camera_bg, &[]);
1741 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
1742 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
1743 overlay_pass.set_index_buffer(
1744 slot.gizmo_index_buffer.slice(..),
1745 wgpu::IndexFormat::Uint32,
1746 );
1747 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
1748 }
1749
1750 if !slot.constraint_line_buffers.is_empty() {
1751 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
1752 overlay_pass.set_bind_group(0, camera_bg, &[]);
1753 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
1754 overlay_pass.set_bind_group(1, bg, &[]);
1755 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1756 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1757 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
1758 }
1759 }
1760
1761 if !slot.clip_plane_fill_buffers.is_empty() {
1762 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
1763 overlay_pass.set_bind_group(0, camera_bg, &[]);
1764 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
1765 overlay_pass.set_bind_group(1, bg, &[]);
1766 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1767 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1768 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
1769 }
1770 }
1771
1772 if !slot.clip_plane_line_buffers.is_empty() {
1773 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
1774 overlay_pass.set_bind_group(0, camera_bg, &[]);
1775 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
1776 overlay_pass.set_bind_group(1, bg, &[]);
1777 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1778 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1779 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
1780 }
1781 }
1782
1783 if !slot.xray_object_buffers.is_empty() {
1784 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
1785 overlay_pass.set_bind_group(0, camera_bg, &[]);
1786 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
1787 let Some(mesh) = self
1788 .resources
1789 .mesh_store
1790 .get(*mesh_id)
1791 else {
1792 continue;
1793 };
1794 overlay_pass.set_bind_group(1, bg, &[]);
1795 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1796 overlay_pass.set_index_buffer(
1797 mesh.index_buffer.slice(..),
1798 wgpu::IndexFormat::Uint32,
1799 );
1800 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1801 }
1802 }
1803 }
1804 }
1805
1806 if frame.viewport.show_axes_indicator {
1809 let slot = &self.viewport_slots[vp_idx];
1810 if slot.axes_vertex_count > 0 {
1811 let slot_hdr = slot.hdr.as_ref().unwrap();
1812 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1813 label: Some("hdr_axes_pass"),
1814 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1815 view: output_view,
1816 resolve_target: None,
1817 ops: wgpu::Operations {
1818 load: wgpu::LoadOp::Load,
1819 store: wgpu::StoreOp::Store,
1820 },
1821 depth_slice: None,
1822 })],
1823 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1824 view: &slot_hdr.hdr_depth_view,
1825 depth_ops: Some(wgpu::Operations {
1826 load: wgpu::LoadOp::Load,
1827 store: wgpu::StoreOp::Discard,
1828 }),
1829 stencil_ops: None,
1830 }),
1831 timestamp_writes: None,
1832 occlusion_query_set: None,
1833 });
1834 axes_pass.set_pipeline(&self.resources.axes_pipeline);
1835 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
1836 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
1837 }
1838 }
1839
1840 if !self.screen_image_gpu_data.is_empty() {
1844 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
1845 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1846 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
1847 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1848 label: Some("screen_image_pass"),
1849 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1850 view: output_view,
1851 resolve_target: None,
1852 ops: wgpu::Operations {
1853 load: wgpu::LoadOp::Load,
1854 store: wgpu::StoreOp::Store,
1855 },
1856 depth_slice: None,
1857 })],
1858 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1859 view: &slot_hdr.hdr_depth_view,
1860 depth_ops: Some(wgpu::Operations {
1861 load: wgpu::LoadOp::Load,
1862 store: wgpu::StoreOp::Discard,
1863 }),
1864 stencil_ops: None,
1865 }),
1866 timestamp_writes: None,
1867 occlusion_query_set: None,
1868 });
1869 for gpu in &self.screen_image_gpu_data {
1870 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
1871 img_pass.set_pipeline(dc_pipe);
1872 img_pass.set_bind_group(0, dc_bg, &[]);
1873 } else {
1874 img_pass.set_pipeline(overlay_pipeline);
1875 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
1876 }
1877 img_pass.draw(0..6, 0..1);
1878 }
1879 }
1880 }
1881
1882 let has_overlay = self.label_gpu_data.is_some()
1884 || self.scalar_bar_gpu_data.is_some()
1885 || self.ruler_gpu_data.is_some()
1886 || !self.overlay_image_gpu_data.is_empty();
1887 if has_overlay {
1888 let hdr_depth_view =
1889 &self.viewport_slots[vp_idx].hdr.as_ref().unwrap().hdr_depth_view;
1890 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1891 label: Some("overlay_pass"),
1892 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1893 view: output_view,
1894 resolve_target: None,
1895 ops: wgpu::Operations {
1896 load: wgpu::LoadOp::Load,
1897 store: wgpu::StoreOp::Store,
1898 },
1899 depth_slice: None,
1900 })],
1901 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1902 view: hdr_depth_view,
1903 depth_ops: Some(wgpu::Operations {
1904 load: wgpu::LoadOp::Load,
1905 store: wgpu::StoreOp::Discard,
1906 }),
1907 stencil_ops: None,
1908 }),
1909 timestamp_writes: None,
1910 occlusion_query_set: None,
1911 });
1912 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
1913 overlay_pass.set_pipeline(pipeline);
1914 if let Some(ref ld) = self.label_gpu_data {
1915 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
1916 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
1917 overlay_pass.draw(0..ld.vertex_count, 0..1);
1918 }
1919 if let Some(ref sb) = self.scalar_bar_gpu_data {
1920 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
1921 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
1922 overlay_pass.draw(0..sb.vertex_count, 0..1);
1923 }
1924 if let Some(ref rd) = self.ruler_gpu_data {
1925 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
1926 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
1927 overlay_pass.draw(0..rd.vertex_count, 0..1);
1928 }
1929 }
1930 if !self.overlay_image_gpu_data.is_empty() {
1932 if let Some(pipeline) = &self.resources.screen_image_pipeline {
1933 overlay_pass.set_pipeline(pipeline);
1934 for gpu in &self.overlay_image_gpu_data {
1935 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
1936 overlay_pass.draw(0..6, 0..1);
1937 }
1938 }
1939 }
1940 }
1941
1942 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
1944 self.ts_query_set.as_ref(),
1945 self.ts_resolve_buf.as_ref(),
1946 self.ts_staging_buf.as_ref(),
1947 ) {
1948 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
1949 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
1950 self.ts_needs_readback = true;
1951 }
1952
1953 encoder.finish()
1954 }
1955
1956 pub fn render_offscreen(
1970 &mut self,
1971 device: &wgpu::Device,
1972 queue: &wgpu::Queue,
1973 frame: &FrameData,
1974 width: u32,
1975 height: u32,
1976 ) -> Vec<u8> {
1977 let target_format = self.resources.target_format;
1979 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
1980 label: Some("offscreen_target"),
1981 size: wgpu::Extent3d {
1982 width: width.max(1),
1983 height: height.max(1),
1984 depth_or_array_layers: 1,
1985 },
1986 mip_level_count: 1,
1987 sample_count: 1,
1988 dimension: wgpu::TextureDimension::D2,
1989 format: target_format,
1990 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
1991 view_formats: &[],
1992 });
1993
1994 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
1996
1997 let cmd_buf = self.render(device, queue, &output_view, frame);
2005 queue.submit(std::iter::once(cmd_buf));
2006
2007 let bytes_per_pixel = 4u32;
2009 let unpadded_row = width * bytes_per_pixel;
2010 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
2011 let padded_row = (unpadded_row + align - 1) & !(align - 1);
2012 let buffer_size = (padded_row * height.max(1)) as u64;
2013
2014 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
2015 label: Some("offscreen_staging"),
2016 size: buffer_size,
2017 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
2018 mapped_at_creation: false,
2019 });
2020
2021 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
2022 label: Some("offscreen_copy_encoder"),
2023 });
2024 copy_encoder.copy_texture_to_buffer(
2025 wgpu::TexelCopyTextureInfo {
2026 texture: &offscreen_texture,
2027 mip_level: 0,
2028 origin: wgpu::Origin3d::ZERO,
2029 aspect: wgpu::TextureAspect::All,
2030 },
2031 wgpu::TexelCopyBufferInfo {
2032 buffer: &staging_buf,
2033 layout: wgpu::TexelCopyBufferLayout {
2034 offset: 0,
2035 bytes_per_row: Some(padded_row),
2036 rows_per_image: Some(height.max(1)),
2037 },
2038 },
2039 wgpu::Extent3d {
2040 width: width.max(1),
2041 height: height.max(1),
2042 depth_or_array_layers: 1,
2043 },
2044 );
2045 queue.submit(std::iter::once(copy_encoder.finish()));
2046
2047 let (tx, rx) = std::sync::mpsc::channel();
2049 staging_buf
2050 .slice(..)
2051 .map_async(wgpu::MapMode::Read, move |result| {
2052 let _ = tx.send(result);
2053 });
2054 device
2055 .poll(wgpu::PollType::Wait {
2056 submission_index: None,
2057 timeout: Some(std::time::Duration::from_secs(5)),
2058 })
2059 .unwrap();
2060 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
2061
2062 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
2063 {
2064 let mapped = staging_buf.slice(..).get_mapped_range();
2065 let data: &[u8] = &mapped;
2066 if padded_row == unpadded_row {
2067 pixels.extend_from_slice(data);
2069 } else {
2070 for row in 0..height as usize {
2072 let start = row * padded_row as usize;
2073 let end = start + unpadded_row as usize;
2074 pixels.extend_from_slice(&data[start..end]);
2075 }
2076 }
2077 }
2078 staging_buf.unmap();
2079
2080 let is_bgra = matches!(
2082 target_format,
2083 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
2084 );
2085 if is_bgra {
2086 for pixel in pixels.chunks_exact_mut(4) {
2087 pixel.swap(0, 2); }
2089 }
2090
2091 pixels
2092 }
2093}