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 render_viewport(
308 &mut self,
309 device: &wgpu::Device,
310 queue: &wgpu::Queue,
311 output_view: &wgpu::TextureView,
312 id: ViewportId,
313 frame: &FrameData,
314 ) -> wgpu::CommandBuffer {
315 self.render_frame_internal(device, queue, output_view, id.0, frame)
316 }
317
318 pub fn render(
326 &mut self,
327 device: &wgpu::Device,
328 queue: &wgpu::Queue,
329 output_view: &wgpu::TextureView,
330 frame: &FrameData,
331 ) -> wgpu::CommandBuffer {
332 self.prepare(device, queue, frame);
334 self.render_frame_internal(
335 device,
336 queue,
337 output_view,
338 frame.camera.viewport_index,
339 frame,
340 )
341 }
342
343 fn render_frame_internal(
348 &mut self,
349 device: &wgpu::Device,
350 queue: &wgpu::Queue,
351 output_view: &wgpu::TextureView,
352 vp_idx: usize,
353 frame: &FrameData,
354 ) -> wgpu::CommandBuffer {
355 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
357 SurfaceSubmission::Flat(items) => items.as_ref(),
358 };
359
360 let bg_color = frame.viewport.background_color.unwrap_or([
361 65.0 / 255.0,
362 65.0 / 255.0,
363 65.0 / 255.0,
364 1.0,
365 ]);
366 let w = frame.camera.viewport_size[0] as u32;
367 let h = frame.camera.viewport_size[1] as u32;
368
369 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
371 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor);
372
373 if self.ts_query_set.is_none()
375 && device.features().contains(wgpu::Features::TIMESTAMP_QUERY)
376 {
377 self.ts_query_set = Some(device.create_query_set(&wgpu::QuerySetDescriptor {
378 label: Some("ts_query_set"),
379 ty: wgpu::QueryType::Timestamp,
380 count: 2,
381 }));
382 self.ts_resolve_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
383 label: Some("ts_resolve_buf"),
384 size: 16,
385 usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
386 mapped_at_creation: false,
387 }));
388 self.ts_staging_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
389 label: Some("ts_staging_buf"),
390 size: 16,
391 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
392 mapped_at_creation: false,
393 }));
394 self.ts_period = queue.get_timestamp_period();
395 }
396
397 if !frame.effects.post_process.enabled {
398 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
402 label: Some("ldr_encoder"),
403 });
404
405 let use_dyn_res = self.performance_policy.allow_dynamic_resolution
406 && self.current_render_scale < 1.0;
407
408 if use_dyn_res {
409 let sw = ((w as f32 * self.current_render_scale) as u32).max(1);
410 let sh = ((h as f32 * self.current_render_scale) as u32).max(1);
411 self.ensure_dyn_res_target(device, vp_idx, [sw, sh], [w.max(1), h.max(1)]);
412 }
413
414 {
415 let slot = &self.viewport_slots[vp_idx];
416 let slot_hdr = slot.hdr.as_ref().unwrap();
417 let camera_bg = &slot.camera_bind_group;
418 let grid_bg = &slot.grid_bind_group;
419 let (scene_color_view, scene_depth_view): (&wgpu::TextureView, &wgpu::TextureView) =
421 if use_dyn_res {
422 let dr = slot.dyn_res.as_ref().unwrap();
423 (&dr.color_view, &dr.depth_view)
424 } else {
425 (output_view, &slot_hdr.outline_depth_view)
426 };
427 let ts_writes = self.ts_query_set.as_ref().map(|qs| {
428 wgpu::RenderPassTimestampWrites {
429 query_set: qs,
430 beginning_of_pass_write_index: Some(0),
431 end_of_pass_write_index: Some(1),
432 }
433 });
434 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
435 label: Some("ldr_render_pass"),
436 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
437 view: scene_color_view,
438 resolve_target: None,
439 ops: wgpu::Operations {
440 load: wgpu::LoadOp::Clear(wgpu::Color {
441 r: bg_color[0] as f64,
442 g: bg_color[1] as f64,
443 b: bg_color[2] as f64,
444 a: bg_color[3] as f64,
445 }),
446 store: wgpu::StoreOp::Store,
447 },
448 depth_slice: None,
449 })],
450 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
451 view: scene_depth_view,
452 depth_ops: Some(wgpu::Operations {
453 load: wgpu::LoadOp::Clear(1.0),
454 store: wgpu::StoreOp::Discard,
455 }),
456 stencil_ops: None,
457 }),
458 timestamp_writes: ts_writes,
459 occlusion_query_set: None,
460 });
461 emit_draw_calls!(
462 &self.resources,
463 &mut render_pass,
464 frame,
465 self.use_instancing,
466 &self.instanced_batches,
467 camera_bg,
468 grid_bg,
469 &self.compute_filter_results,
470 Some(slot)
471 );
472 emit_scivis_draw_calls!(
473 &self.resources,
474 &mut render_pass,
475 &self.point_cloud_gpu_data,
476 &self.glyph_gpu_data,
477 &self.polyline_gpu_data,
478 &self.volume_gpu_data,
479 &self.streamtube_gpu_data,
480 camera_bg
481 );
482 if !self.implicit_gpu_data.is_empty() {
484 if let Some(pipeline) = &self.resources.implicit_pipeline {
485 render_pass.set_pipeline(pipeline);
486 render_pass.set_bind_group(0, camera_bg, &[]);
487 for gpu in &self.implicit_gpu_data {
488 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
489 render_pass.draw(0..6, 0..1);
490 }
491 }
492 }
493 if !self.mc_gpu_data.is_empty() {
495 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
496 render_pass.set_pipeline(pipeline);
497 render_pass.set_bind_group(0, camera_bg, &[]);
498 for mc in &self.mc_gpu_data {
499 let vol = &self.resources.mc_volumes[mc.volume_idx];
500 render_pass.set_bind_group(1, &mc.render_bg, &[]);
501 for slab in &vol.slabs {
502 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
503 render_pass.draw_indirect(&slab.indirect_buf, 0);
504 }
505 }
506 }
507 }
508 if !self.screen_image_gpu_data.is_empty() {
513 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
514 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
515 for gpu in &self.screen_image_gpu_data {
516 if let (Some(dc_bg), Some(dc_pipe)) =
517 (&gpu.depth_bind_group, dc_pipeline)
518 {
519 render_pass.set_pipeline(dc_pipe);
520 render_pass.set_bind_group(0, dc_bg, &[]);
521 } else {
522 render_pass.set_pipeline(overlay_pipeline);
523 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
524 }
525 render_pass.draw(0..6, 0..1);
526 }
527 }
528 }
529 if let Some(ref ld) = self.label_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, &ld.bind_group, &[]);
534 render_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
535 render_pass.draw(0..ld.vertex_count, 0..1);
536 }
537 }
538 if let Some(ref sb) = self.scalar_bar_gpu_data {
540 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
541 render_pass.set_pipeline(pipeline);
542 render_pass.set_bind_group(0, &sb.bind_group, &[]);
543 render_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
544 render_pass.draw(0..sb.vertex_count, 0..1);
545 }
546 }
547 if let Some(ref rd) = self.ruler_gpu_data {
549 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
550 render_pass.set_pipeline(pipeline);
551 render_pass.set_bind_group(0, &rd.bind_group, &[]);
552 render_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
553 render_pass.draw(0..rd.vertex_count, 0..1);
554 }
555 }
556 if !self.overlay_image_gpu_data.is_empty() {
558 if let Some(pipeline) = &self.resources.screen_image_pipeline {
559 render_pass.set_pipeline(pipeline);
560 for gpu in &self.overlay_image_gpu_data {
561 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
562 render_pass.draw(0..6, 0..1);
563 }
564 }
565 }
566 }
567
568 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
570 self.ts_query_set.as_ref(),
571 self.ts_resolve_buf.as_ref(),
572 self.ts_staging_buf.as_ref(),
573 ) {
574 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
575 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
576 self.ts_needs_readback = true;
577 }
578
579 if use_dyn_res {
581 let upscale_bg =
582 &self.viewport_slots[vp_idx].dyn_res.as_ref().unwrap().upscale_bind_group;
583 let mut upscale_pass =
584 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
585 label: Some("dyn_res_upscale_pass"),
586 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
587 view: output_view,
588 resolve_target: None,
589 ops: wgpu::Operations {
590 load: wgpu::LoadOp::Load,
591 store: wgpu::StoreOp::Store,
592 },
593 depth_slice: None,
594 })],
595 depth_stencil_attachment: None,
596 timestamp_writes: None,
597 occlusion_query_set: None,
598 });
599 if let Some(pipeline) = &self.resources.dyn_res_upscale_pipeline {
600 upscale_pass.set_pipeline(pipeline);
601 upscale_pass.set_bind_group(0, upscale_bg, &[]);
602 upscale_pass.draw(0..3, 0..1);
603 }
604 }
605
606 return encoder.finish();
607 }
608
609 let pp = &frame.effects.post_process;
611
612 let hdr_clear_rgb = [
613 bg_color[0].powf(2.2),
614 bg_color[1].powf(2.2),
615 bg_color[2].powf(2.2),
616 ];
617
618 let mode = match pp.tone_mapping {
620 crate::renderer::ToneMapping::Reinhard => 0u32,
621 crate::renderer::ToneMapping::Aces => 1u32,
622 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
623 };
624 let tm_uniform = crate::resources::ToneMapUniform {
625 exposure: pp.exposure,
626 mode,
627 bloom_enabled: if pp.bloom { 1 } else { 0 },
628 ssao_enabled: if pp.ssao { 1 } else { 0 },
629 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
630 _pad_tm: [0; 3],
631 background_color: bg_color,
632 };
633 {
634 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
635 queue.write_buffer(
636 &hdr.tone_map_uniform_buf,
637 0,
638 bytemuck::cast_slice(&[tm_uniform]),
639 );
640
641 if pp.ssao {
643 let proj = frame.camera.render_camera.projection;
644 let inv_proj = proj.inverse();
645 let ssao_uniform = crate::resources::SsaoUniform {
646 inv_proj: inv_proj.to_cols_array_2d(),
647 proj: proj.to_cols_array_2d(),
648 radius: 0.5,
649 bias: 0.025,
650 _pad: [0.0; 2],
651 };
652 queue.write_buffer(
653 &hdr.ssao_uniform_buf,
654 0,
655 bytemuck::cast_slice(&[ssao_uniform]),
656 );
657 }
658
659 if pp.contact_shadows {
661 let proj = frame.camera.render_camera.projection;
662 let inv_proj = proj.inverse();
663 let light_dir_world: glam::Vec3 =
664 if let Some(l) = frame.effects.lighting.lights.first() {
665 match l.kind {
666 LightKind::Directional { direction } => {
667 glam::Vec3::from(direction).normalize()
668 }
669 LightKind::Spot { direction, .. } => {
670 glam::Vec3::from(direction).normalize()
671 }
672 _ => glam::Vec3::new(0.0, -1.0, 0.0),
673 }
674 } else {
675 glam::Vec3::new(0.0, -1.0, 0.0)
676 };
677 let view = frame.camera.render_camera.view;
678 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
679 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
680 let cs_uniform = crate::resources::ContactShadowUniform {
681 inv_proj: inv_proj.to_cols_array_2d(),
682 proj: proj.to_cols_array_2d(),
683 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
684 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
685 params: [
686 pp.contact_shadow_max_distance,
687 pp.contact_shadow_steps as f32,
688 pp.contact_shadow_thickness,
689 0.0,
690 ],
691 };
692 queue.write_buffer(
693 &hdr.contact_shadow_uniform_buf,
694 0,
695 bytemuck::cast_slice(&[cs_uniform]),
696 );
697 }
698
699 if pp.bloom {
701 let bloom_u = crate::resources::BloomUniform {
702 threshold: pp.bloom_threshold,
703 intensity: pp.bloom_intensity,
704 horizontal: 0,
705 _pad: 0,
706 };
707 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
708 }
709 }
710
711 {
713 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
714 self.resources.rebuild_tone_map_bind_group(
715 device,
716 hdr,
717 pp.bloom,
718 pp.ssao,
719 pp.contact_shadows,
720 );
721 }
722
723 {
728 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
729 self.instanced_batches.iter().any(|b| b.is_transparent)
730 } else {
731 scene_items
732 .iter()
733 .any(|i| i.visible && i.material.opacity < 1.0)
734 };
735 if needs_oit {
736 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
737 self.resources
738 .ensure_viewport_oit(device, hdr, w.max(1), h.max(1));
739 }
740 }
741
742 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
746 label: Some("hdr_encoder"),
747 });
748
749 let slot = &self.viewport_slots[vp_idx];
751 let camera_bg = &slot.camera_bind_group;
752 let slot_hdr = slot.hdr.as_ref().unwrap();
753
754 {
758 let use_ssaa = ssaa_factor > 1
760 && slot_hdr.ssaa_color_view.is_some()
761 && slot_hdr.ssaa_depth_view.is_some();
762 let scene_color_view = if use_ssaa {
763 slot_hdr.ssaa_color_view.as_ref().unwrap()
764 } else {
765 &slot_hdr.hdr_view
766 };
767 let scene_depth_view = if use_ssaa {
768 slot_hdr.ssaa_depth_view.as_ref().unwrap()
769 } else {
770 &slot_hdr.hdr_depth_view
771 };
772
773 let clear_wgpu = wgpu::Color {
774 r: hdr_clear_rgb[0] as f64,
775 g: hdr_clear_rgb[1] as f64,
776 b: hdr_clear_rgb[2] as f64,
777 a: bg_color[3] as f64,
778 };
779
780 let hdr_ts_writes = self.ts_query_set.as_ref().map(|qs| {
781 wgpu::RenderPassTimestampWrites {
782 query_set: qs,
783 beginning_of_pass_write_index: Some(0),
784 end_of_pass_write_index: Some(1),
785 }
786 });
787 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
788 label: Some("hdr_scene_pass"),
789 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
790 view: scene_color_view,
791 resolve_target: None,
792 ops: wgpu::Operations {
793 load: wgpu::LoadOp::Clear(clear_wgpu),
794 store: wgpu::StoreOp::Store,
795 },
796 depth_slice: None,
797 })],
798 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
799 view: scene_depth_view,
800 depth_ops: Some(wgpu::Operations {
801 load: wgpu::LoadOp::Clear(1.0),
802 store: wgpu::StoreOp::Store,
803 }),
804 stencil_ops: Some(wgpu::Operations {
805 load: wgpu::LoadOp::Clear(0),
806 store: wgpu::StoreOp::Store,
807 }),
808 }),
809 timestamp_writes: hdr_ts_writes,
810 occlusion_query_set: None,
811 });
812
813 let resources = &self.resources;
814 render_pass.set_bind_group(0, camera_bg, &[]);
815
816 let show_skybox = frame
818 .effects
819 .environment
820 .as_ref()
821 .is_some_and(|e| e.show_skybox)
822 && resources.ibl_skybox_view.is_some();
823
824 let use_instancing = self.use_instancing;
825 let batches = &self.instanced_batches;
826
827 if !scene_items.is_empty() {
828 if use_instancing && !batches.is_empty() {
829 let excluded_items: Vec<&SceneRenderItem> = scene_items
830 .iter()
831 .filter(|item| {
832 item.visible
833 && (item.active_attribute.is_some()
834 || item.material.is_two_sided()
835 || item.material.matcap_id.is_some())
836 && resources
837 .mesh_store
838 .get(item.mesh_id)
839 .is_some()
840 })
841 .collect();
842
843 let mut opaque_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
847 let mut transparent_batches: Vec<(usize, &InstancedBatch)> = Vec::new();
848 for (batch_global_idx, batch) in batches.iter().enumerate() {
849 if batch.is_transparent {
850 transparent_batches.push((batch_global_idx, batch));
851 } else {
852 opaque_batches.push((batch_global_idx, batch));
853 }
854 }
855
856 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
857 let use_indirect = self.gpu_culling_enabled
858 && resources.hdr_solid_instanced_cull_pipeline.is_some()
859 && resources.indirect_args_buf.is_some();
860
861 if use_indirect {
862 if let (
863 Some(pipeline),
864 Some(indirect_buf),
865 ) = (
866 &resources.hdr_solid_instanced_cull_pipeline,
867 &resources.indirect_args_buf,
868 ) {
869 render_pass.set_pipeline(pipeline);
870 for (batch_global_idx, batch) in &opaque_batches {
871 let Some(mesh) = resources.mesh_store.get(batch.mesh_id)
872 else {
873 continue;
874 };
875 let mat_key = (
876 batch.texture_id.unwrap_or(u64::MAX),
877 batch.normal_map_id.unwrap_or(u64::MAX),
878 batch.ao_map_id.unwrap_or(u64::MAX),
879 );
880 let Some(inst_tex_bg) =
881 resources.instance_cull_bind_groups.get(&mat_key)
882 else {
883 continue;
884 };
885 render_pass.set_bind_group(1, inst_tex_bg, &[]);
886 render_pass.set_vertex_buffer(
887 0,
888 mesh.vertex_buffer.slice(..),
889 );
890 render_pass.set_index_buffer(
891 mesh.index_buffer.slice(..),
892 wgpu::IndexFormat::Uint32,
893 );
894 render_pass.draw_indexed_indirect(
897 indirect_buf,
898 *batch_global_idx as u64 * 20,
899 );
900 }
901 }
902 } else if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
903 render_pass.set_pipeline(pipeline);
904 for (_, batch) in &opaque_batches {
905 let Some(mesh) = resources
906 .mesh_store
907 .get(batch.mesh_id)
908 else {
909 continue;
910 };
911 let mat_key = (
912 batch.texture_id.unwrap_or(u64::MAX),
913 batch.normal_map_id.unwrap_or(u64::MAX),
914 batch.ao_map_id.unwrap_or(u64::MAX),
915 );
916 let Some(inst_tex_bg) =
917 resources.instance_bind_groups.get(&mat_key)
918 else {
919 continue;
920 };
921 render_pass.set_bind_group(1, inst_tex_bg, &[]);
922 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
923 render_pass.set_index_buffer(
924 mesh.index_buffer.slice(..),
925 wgpu::IndexFormat::Uint32,
926 );
927 render_pass.draw_indexed(
928 0..mesh.index_count,
929 0,
930 batch.instance_offset
931 ..batch.instance_offset + batch.instance_count,
932 );
933 }
934 }
935 }
936
937 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
942 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
943 render_pass.set_pipeline(hdr_wf);
944 for item in scene_items {
945 if !item.visible {
946 continue;
947 }
948 let Some(mesh) = resources
949 .mesh_store
950 .get(item.mesh_id)
951 else {
952 continue;
953 };
954 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
955 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
956 render_pass.set_index_buffer(
957 mesh.edge_index_buffer.slice(..),
958 wgpu::IndexFormat::Uint32,
959 );
960 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
961 }
962 }
963 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
964 &resources.hdr_solid_pipeline,
965 &resources.hdr_solid_two_sided_pipeline,
966 ) {
967 for item in excluded_items
968 .into_iter()
969 .filter(|item| item.material.opacity >= 1.0)
970 {
971 let Some(mesh) = resources
972 .mesh_store
973 .get(item.mesh_id)
974 else {
975 continue;
976 };
977 let pipeline = if item.material.is_two_sided() {
978 hdr_solid_two_sided
979 } else {
980 hdr_solid
981 };
982 render_pass.set_pipeline(pipeline);
983 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
984 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
985 render_pass.set_index_buffer(
986 mesh.index_buffer.slice(..),
987 wgpu::IndexFormat::Uint32,
988 );
989 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
990 }
991 }
992 } else {
993 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
995 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
996 let pos =
997 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
998 (pos - eye).length()
999 };
1000
1001 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
1002 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
1003 for item in scene_items {
1004 if !item.visible
1005 || resources
1006 .mesh_store
1007 .get(item.mesh_id)
1008 .is_none()
1009 {
1010 continue;
1011 }
1012 if item.material.opacity < 1.0 {
1013 transparent.push(item);
1014 } else {
1015 opaque.push(item);
1016 }
1017 }
1018 opaque.sort_by(|a, b| {
1019 dist_from_eye(a)
1020 .partial_cmp(&dist_from_eye(b))
1021 .unwrap_or(std::cmp::Ordering::Equal)
1022 });
1023 transparent.sort_by(|a, b| {
1024 dist_from_eye(b)
1025 .partial_cmp(&dist_from_eye(a))
1026 .unwrap_or(std::cmp::Ordering::Equal)
1027 });
1028
1029 let draw_item_hdr =
1030 |render_pass: &mut wgpu::RenderPass<'_>,
1031 item: &SceneRenderItem,
1032 solid_pl: &wgpu::RenderPipeline,
1033 trans_pl: &wgpu::RenderPipeline,
1034 wf_pl: &wgpu::RenderPipeline| {
1035 let mesh = resources
1036 .mesh_store
1037 .get(item.mesh_id)
1038 .unwrap();
1039 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1042 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
1043 matches!(
1044 a.kind,
1045 crate::resources::AttributeKind::Face
1046 | crate::resources::AttributeKind::FaceColor
1047 | crate::resources::AttributeKind::Halfedge
1048 | crate::resources::AttributeKind::Corner
1049 )
1050 });
1051 if frame.viewport.wireframe_mode {
1052 render_pass.set_pipeline(wf_pl);
1053 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1054 render_pass.set_index_buffer(
1055 mesh.edge_index_buffer.slice(..),
1056 wgpu::IndexFormat::Uint32,
1057 );
1058 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
1059 } else if is_face_attr {
1060 if let Some(ref fvb) = mesh.face_vertex_buffer {
1061 let pl = if item.material.opacity < 1.0 {
1062 trans_pl
1063 } else {
1064 solid_pl
1065 };
1066 render_pass.set_pipeline(pl);
1067 render_pass.set_vertex_buffer(0, fvb.slice(..));
1068 render_pass.draw(0..mesh.index_count, 0..1);
1069 }
1070 } else if item.material.opacity < 1.0 {
1071 render_pass.set_pipeline(trans_pl);
1072 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1073 render_pass.set_index_buffer(
1074 mesh.index_buffer.slice(..),
1075 wgpu::IndexFormat::Uint32,
1076 );
1077 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1078 } else {
1079 render_pass.set_pipeline(solid_pl);
1080 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1081 render_pass.set_index_buffer(
1082 mesh.index_buffer.slice(..),
1083 wgpu::IndexFormat::Uint32,
1084 );
1085 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1086 }
1087 };
1088
1089 let _ = &transparent; if let (
1093 Some(hdr_solid),
1094 Some(hdr_solid_two_sided),
1095 Some(hdr_trans),
1096 Some(hdr_wf),
1097 ) = (
1098 &resources.hdr_solid_pipeline,
1099 &resources.hdr_solid_two_sided_pipeline,
1100 &resources.hdr_transparent_pipeline,
1101 &resources.hdr_wireframe_pipeline,
1102 ) {
1103 for item in &opaque {
1104 let solid_pl = if item.material.is_two_sided() {
1105 hdr_solid_two_sided
1106 } else {
1107 hdr_solid
1108 };
1109 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
1110 }
1111 }
1112 }
1113 }
1114
1115 if !slot.cap_buffers.is_empty() {
1117 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
1118 render_pass.set_pipeline(hdr_overlay);
1119 render_pass.set_bind_group(0, camera_bg, &[]);
1120 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
1121 render_pass.set_bind_group(1, bg, &[]);
1122 render_pass.set_vertex_buffer(0, vbuf.slice(..));
1123 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1124 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
1125 }
1126 }
1127 }
1128
1129 emit_scivis_draw_calls!(
1131 &self.resources,
1132 &mut render_pass,
1133 &self.point_cloud_gpu_data,
1134 &self.glyph_gpu_data,
1135 &self.polyline_gpu_data,
1136 &self.volume_gpu_data,
1137 &self.streamtube_gpu_data,
1138 camera_bg
1139 );
1140
1141 if !self.implicit_gpu_data.is_empty() {
1143 if let Some(pipeline) = &self.resources.implicit_pipeline {
1144 render_pass.set_pipeline(pipeline);
1145 render_pass.set_bind_group(0, camera_bg, &[]);
1146 for gpu in &self.implicit_gpu_data {
1147 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
1148 render_pass.draw(0..6, 0..1);
1149 }
1150 }
1151 }
1152 if !self.mc_gpu_data.is_empty() {
1154 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
1155 render_pass.set_pipeline(pipeline);
1156 render_pass.set_bind_group(0, camera_bg, &[]);
1157 for mc in &self.mc_gpu_data {
1158 let vol = &self.resources.mc_volumes[mc.volume_idx];
1159 render_pass.set_bind_group(1, &mc.render_bg, &[]);
1160 for slab in &vol.slabs {
1161 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
1162 render_pass.draw_indirect(&slab.indirect_buf, 0);
1163 }
1164 }
1165 }
1166 }
1167
1168 if show_skybox {
1170 render_pass.set_bind_group(0, camera_bg, &[]);
1171 render_pass.set_pipeline(&resources.skybox_pipeline);
1172 render_pass.draw(0..3, 0..1);
1173 }
1174 }
1175
1176 if ssaa_factor > 1 {
1181 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1182 if let (Some(pipeline), Some(bg)) = (
1183 &self.resources.ssaa_resolve_pipeline,
1184 &slot_hdr.ssaa_resolve_bind_group,
1185 ) {
1186 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1187 label: Some("ssaa_resolve_pass"),
1188 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1189 view: &slot_hdr.hdr_view,
1190 resolve_target: None,
1191 ops: wgpu::Operations {
1192 load: wgpu::LoadOp::Load,
1193 store: wgpu::StoreOp::Store,
1194 },
1195 depth_slice: None,
1196 })],
1197 depth_stencil_attachment: None,
1198 timestamp_writes: None,
1199 occlusion_query_set: None,
1200 });
1201 resolve_pass.set_pipeline(pipeline);
1202 resolve_pass.set_bind_group(0, bg, &[]);
1203 resolve_pass.draw(0..3, 0..1);
1204 }
1205 }
1206
1207 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
1213 let resources = &self.resources;
1214 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
1215 &resources.sub_highlight_fill_pipeline,
1216 &resources.sub_highlight_edge_pipeline,
1217 &resources.sub_highlight_sprite_pipeline,
1218 ) {
1219 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1220 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
1221 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1222 label: Some("sub_highlight_pass"),
1223 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1224 view: &slot_hdr.hdr_view,
1225 resolve_target: None,
1226 ops: wgpu::Operations {
1227 load: wgpu::LoadOp::Load,
1228 store: wgpu::StoreOp::Store,
1229 },
1230 depth_slice: None,
1231 })],
1232 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1233 view: &slot_hdr.hdr_depth_view,
1234 depth_ops: Some(wgpu::Operations {
1235 load: wgpu::LoadOp::Load,
1236 store: wgpu::StoreOp::Discard,
1237 }),
1238 stencil_ops: None,
1239 }),
1240 timestamp_writes: None,
1241 occlusion_query_set: None,
1242 });
1243
1244 if sub_hl.fill_vertex_count > 0 {
1245 pass.set_pipeline(fill_pl);
1246 pass.set_bind_group(0, camera_bg, &[]);
1247 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
1248 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
1249 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
1250 }
1251 if sub_hl.edge_segment_count > 0 {
1252 pass.set_pipeline(edge_pl);
1253 pass.set_bind_group(0, camera_bg, &[]);
1254 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
1255 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
1256 pass.draw(0..6, 0..sub_hl.edge_segment_count);
1257 }
1258 if sub_hl.sprite_point_count > 0 {
1259 pass.set_pipeline(sprite_pl);
1260 pass.set_bind_group(0, camera_bg, &[]);
1261 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
1262 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
1263 pass.draw(0..6, 0..sub_hl.sprite_point_count);
1264 }
1265 }
1266 }
1267
1268 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
1273 self.instanced_batches.iter().any(|b| b.is_transparent)
1274 } else {
1275 scene_items
1276 .iter()
1277 .any(|i| i.visible && i.material.opacity < 1.0)
1278 };
1279
1280 if has_transparent {
1281 if let (Some(accum_view), Some(reveal_view)) = (
1283 slot_hdr.oit_accum_view.as_ref(),
1284 slot_hdr.oit_reveal_view.as_ref(),
1285 ) {
1286 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1287 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1289 label: Some("oit_pass"),
1290 color_attachments: &[
1291 Some(wgpu::RenderPassColorAttachment {
1292 view: accum_view,
1293 resolve_target: None,
1294 ops: wgpu::Operations {
1295 load: wgpu::LoadOp::Clear(wgpu::Color {
1296 r: 0.0,
1297 g: 0.0,
1298 b: 0.0,
1299 a: 0.0,
1300 }),
1301 store: wgpu::StoreOp::Store,
1302 },
1303 depth_slice: None,
1304 }),
1305 Some(wgpu::RenderPassColorAttachment {
1306 view: reveal_view,
1307 resolve_target: None,
1308 ops: wgpu::Operations {
1309 load: wgpu::LoadOp::Clear(wgpu::Color {
1310 r: 1.0,
1311 g: 1.0,
1312 b: 1.0,
1313 a: 1.0,
1314 }),
1315 store: wgpu::StoreOp::Store,
1316 },
1317 depth_slice: None,
1318 }),
1319 ],
1320 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1321 view: hdr_depth_view,
1322 depth_ops: Some(wgpu::Operations {
1323 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
1325 }),
1326 stencil_ops: None,
1327 }),
1328 timestamp_writes: None,
1329 occlusion_query_set: None,
1330 });
1331
1332 oit_pass.set_bind_group(0, camera_bg, &[]);
1333
1334 if self.use_instancing && !self.instanced_batches.is_empty() {
1335 let use_indirect_oit = self.gpu_culling_enabled
1336 && self.resources.oit_instanced_cull_pipeline.is_some()
1337 && self.resources.indirect_args_buf.is_some();
1338
1339 if use_indirect_oit {
1340 if let (
1341 Some(pipeline),
1342 Some(indirect_buf),
1343 ) = (
1344 &self.resources.oit_instanced_cull_pipeline,
1345 &self.resources.indirect_args_buf,
1346 ) {
1347 oit_pass.set_pipeline(pipeline);
1348 for (batch_global_idx, batch) in
1349 self.instanced_batches.iter().enumerate()
1350 {
1351 if !batch.is_transparent {
1352 continue;
1353 }
1354 let Some(mesh) =
1355 self.resources.mesh_store.get(batch.mesh_id)
1356 else {
1357 continue;
1358 };
1359 let mat_key = (
1360 batch.texture_id.unwrap_or(u64::MAX),
1361 batch.normal_map_id.unwrap_or(u64::MAX),
1362 batch.ao_map_id.unwrap_or(u64::MAX),
1363 );
1364 let Some(inst_tex_bg) =
1365 self.resources.instance_cull_bind_groups.get(&mat_key)
1366 else {
1367 continue;
1368 };
1369 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1370 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1371 oit_pass.set_index_buffer(
1372 mesh.index_buffer.slice(..),
1373 wgpu::IndexFormat::Uint32,
1374 );
1375 oit_pass.draw_indexed_indirect(
1376 indirect_buf,
1377 batch_global_idx as u64 * 20,
1378 );
1379 }
1380 }
1381 } else if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
1382 oit_pass.set_pipeline(pipeline);
1383 for batch in &self.instanced_batches {
1384 if !batch.is_transparent {
1385 continue;
1386 }
1387 let Some(mesh) = self
1388 .resources
1389 .mesh_store
1390 .get(batch.mesh_id)
1391 else {
1392 continue;
1393 };
1394 let mat_key = (
1395 batch.texture_id.unwrap_or(u64::MAX),
1396 batch.normal_map_id.unwrap_or(u64::MAX),
1397 batch.ao_map_id.unwrap_or(u64::MAX),
1398 );
1399 let Some(inst_tex_bg) =
1400 self.resources.instance_bind_groups.get(&mat_key)
1401 else {
1402 continue;
1403 };
1404 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1405 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1406 oit_pass.set_index_buffer(
1407 mesh.index_buffer.slice(..),
1408 wgpu::IndexFormat::Uint32,
1409 );
1410 oit_pass.draw_indexed(
1411 0..mesh.index_count,
1412 0,
1413 batch.instance_offset..batch.instance_offset + batch.instance_count,
1414 );
1415 }
1416 }
1417 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
1418 oit_pass.set_pipeline(pipeline);
1419 for item in scene_items {
1420 if !item.visible || item.material.opacity >= 1.0 {
1421 continue;
1422 }
1423 let Some(mesh) = self
1424 .resources
1425 .mesh_store
1426 .get(item.mesh_id)
1427 else {
1428 continue;
1429 };
1430 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1431 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1432 oit_pass.set_index_buffer(
1433 mesh.index_buffer.slice(..),
1434 wgpu::IndexFormat::Uint32,
1435 );
1436 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1437 }
1438 }
1439 }
1440 }
1441
1442 if has_transparent {
1447 if let (Some(pipeline), Some(bg)) = (
1448 self.resources.oit_composite_pipeline.as_ref(),
1449 slot_hdr.oit_composite_bind_group.as_ref(),
1450 ) {
1451 let hdr_view = &slot_hdr.hdr_view;
1452 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1453 label: Some("oit_composite_pass"),
1454 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1455 view: hdr_view,
1456 resolve_target: None,
1457 ops: wgpu::Operations {
1458 load: wgpu::LoadOp::Load,
1459 store: wgpu::StoreOp::Store,
1460 },
1461 depth_slice: None,
1462 })],
1463 depth_stencil_attachment: None,
1464 timestamp_writes: None,
1465 occlusion_query_set: None,
1466 });
1467 composite_pass.set_pipeline(pipeline);
1468 composite_pass.set_bind_group(0, bg, &[]);
1469 composite_pass.draw(0..3, 0..1);
1470 }
1471 }
1472
1473 if !slot.outline_object_buffers.is_empty() {
1479 let hdr_pipeline = self
1481 .resources
1482 .outline_composite_pipeline_hdr
1483 .as_ref()
1484 .or(self.resources.outline_composite_pipeline_single.as_ref());
1485 if let Some(pipeline) = hdr_pipeline {
1486 let bg = &slot_hdr.outline_composite_bind_group;
1487 let hdr_view = &slot_hdr.hdr_view;
1488 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1489 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1490 label: Some("hdr_outline_composite_pass"),
1491 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1492 view: hdr_view,
1493 resolve_target: None,
1494 ops: wgpu::Operations {
1495 load: wgpu::LoadOp::Load,
1496 store: wgpu::StoreOp::Store,
1497 },
1498 depth_slice: None,
1499 })],
1500 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1501 view: hdr_depth_view,
1502 depth_ops: Some(wgpu::Operations {
1503 load: wgpu::LoadOp::Load,
1504 store: wgpu::StoreOp::Store,
1505 }),
1506 stencil_ops: None,
1507 }),
1508 timestamp_writes: None,
1509 occlusion_query_set: None,
1510 });
1511 outline_pass.set_pipeline(pipeline);
1512 outline_pass.set_bind_group(0, bg, &[]);
1513 outline_pass.draw(0..3, 0..1);
1514 }
1515 }
1516
1517 let throttle_effects = self.last_stats.missed_budget
1520 && self.performance_policy.allow_effect_throttling;
1521
1522 if pp.ssao && !throttle_effects {
1526 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
1527 {
1528 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1529 label: Some("ssao_pass"),
1530 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1531 view: &slot_hdr.ssao_view,
1532 resolve_target: None,
1533 ops: wgpu::Operations {
1534 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1535 store: wgpu::StoreOp::Store,
1536 },
1537 depth_slice: None,
1538 })],
1539 depth_stencil_attachment: None,
1540 timestamp_writes: None,
1541 occlusion_query_set: None,
1542 });
1543 ssao_pass.set_pipeline(ssao_pipeline);
1544 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
1545 ssao_pass.draw(0..3, 0..1);
1546 }
1547
1548 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
1550 let mut ssao_blur_pass =
1551 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1552 label: Some("ssao_blur_pass"),
1553 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1554 view: &slot_hdr.ssao_blur_view,
1555 resolve_target: None,
1556 ops: wgpu::Operations {
1557 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1558 store: wgpu::StoreOp::Store,
1559 },
1560 depth_slice: None,
1561 })],
1562 depth_stencil_attachment: None,
1563 timestamp_writes: None,
1564 occlusion_query_set: None,
1565 });
1566 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
1567 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
1568 ssao_blur_pass.draw(0..3, 0..1);
1569 }
1570 }
1571 }
1572
1573 if pp.contact_shadows && !throttle_effects {
1577 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
1578 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1579 label: Some("contact_shadow_pass"),
1580 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1581 view: &slot_hdr.contact_shadow_view,
1582 resolve_target: None,
1583 depth_slice: None,
1584 ops: wgpu::Operations {
1585 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1586 store: wgpu::StoreOp::Store,
1587 },
1588 })],
1589 depth_stencil_attachment: None,
1590 timestamp_writes: None,
1591 occlusion_query_set: None,
1592 });
1593 cs_pass.set_pipeline(cs_pipeline);
1594 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
1595 cs_pass.draw(0..3, 0..1);
1596 }
1597 }
1598
1599 if pp.bloom && !throttle_effects {
1603 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
1605 {
1606 let mut threshold_pass =
1607 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1608 label: Some("bloom_threshold_pass"),
1609 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1610 view: &slot_hdr.bloom_threshold_view,
1611 resolve_target: None,
1612 ops: wgpu::Operations {
1613 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1614 store: wgpu::StoreOp::Store,
1615 },
1616 depth_slice: None,
1617 })],
1618 depth_stencil_attachment: None,
1619 timestamp_writes: None,
1620 occlusion_query_set: None,
1621 });
1622 threshold_pass.set_pipeline(bloom_threshold_pipeline);
1623 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
1624 threshold_pass.draw(0..3, 0..1);
1625 }
1626
1627 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
1630 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
1631 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
1632 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
1633 let bloom_ping_view = &slot_hdr.bloom_ping_view;
1634 let bloom_pong_view = &slot_hdr.bloom_pong_view;
1635 const BLUR_ITERATIONS: usize = 4;
1636 for i in 0..BLUR_ITERATIONS {
1637 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
1639 {
1640 let mut h_pass =
1641 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1642 label: Some("bloom_blur_h_pass"),
1643 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1644 view: bloom_ping_view,
1645 resolve_target: None,
1646 ops: wgpu::Operations {
1647 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1648 store: wgpu::StoreOp::Store,
1649 },
1650 depth_slice: None,
1651 })],
1652 depth_stencil_attachment: None,
1653 timestamp_writes: None,
1654 occlusion_query_set: None,
1655 });
1656 h_pass.set_pipeline(blur_pipeline);
1657 h_pass.set_bind_group(0, h_bg, &[]);
1658 h_pass.draw(0..3, 0..1);
1659 }
1660 {
1662 let mut v_pass =
1663 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1664 label: Some("bloom_blur_v_pass"),
1665 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1666 view: bloom_pong_view,
1667 resolve_target: None,
1668 ops: wgpu::Operations {
1669 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1670 store: wgpu::StoreOp::Store,
1671 },
1672 depth_slice: None,
1673 })],
1674 depth_stencil_attachment: None,
1675 timestamp_writes: None,
1676 occlusion_query_set: None,
1677 });
1678 v_pass.set_pipeline(blur_pipeline);
1679 v_pass.set_bind_group(0, blur_v_bg, &[]);
1680 v_pass.draw(0..3, 0..1);
1681 }
1682 }
1683 }
1684 }
1685 }
1686
1687 let use_fxaa = pp.fxaa;
1691 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
1692 let tone_target: &wgpu::TextureView = if use_fxaa {
1693 &slot_hdr.fxaa_view
1694 } else {
1695 output_view
1696 };
1697 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1698 label: Some("tone_map_pass"),
1699 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1700 view: tone_target,
1701 resolve_target: None,
1702 ops: wgpu::Operations {
1703 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1704 store: wgpu::StoreOp::Store,
1705 },
1706 depth_slice: None,
1707 })],
1708 depth_stencil_attachment: None,
1709 timestamp_writes: None,
1710 occlusion_query_set: None,
1711 });
1712 tone_pass.set_pipeline(tone_map_pipeline);
1713 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
1714 tone_pass.draw(0..3, 0..1);
1715 }
1716
1717 if use_fxaa {
1721 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
1722 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1723 label: Some("fxaa_pass"),
1724 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1725 view: output_view,
1726 resolve_target: None,
1727 ops: wgpu::Operations {
1728 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1729 store: wgpu::StoreOp::Store,
1730 },
1731 depth_slice: None,
1732 })],
1733 depth_stencil_attachment: None,
1734 timestamp_writes: None,
1735 occlusion_query_set: None,
1736 });
1737 fxaa_pass.set_pipeline(fxaa_pipeline);
1738 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
1739 fxaa_pass.draw(0..3, 0..1);
1740 }
1741 }
1742
1743 if frame.viewport.show_grid {
1747 let slot = &self.viewport_slots[vp_idx];
1748 let slot_hdr = slot.hdr.as_ref().unwrap();
1749 let grid_bg = &slot.grid_bind_group;
1750 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1751 label: Some("hdr_grid_pass"),
1752 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1753 view: output_view,
1754 resolve_target: None,
1755 ops: wgpu::Operations {
1756 load: wgpu::LoadOp::Load,
1757 store: wgpu::StoreOp::Store,
1758 },
1759 depth_slice: None,
1760 })],
1761 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1762 view: &slot_hdr.hdr_depth_view,
1763 depth_ops: Some(wgpu::Operations {
1764 load: wgpu::LoadOp::Load,
1765 store: wgpu::StoreOp::Store,
1766 }),
1767 stencil_ops: None,
1768 }),
1769 timestamp_writes: None,
1770 occlusion_query_set: None,
1771 });
1772 grid_pass.set_pipeline(&self.resources.grid_pipeline);
1773 grid_pass.set_bind_group(0, grid_bg, &[]);
1774 grid_pass.draw(0..3, 0..1);
1775 }
1776
1777 if !matches!(
1780 frame.effects.ground_plane.mode,
1781 crate::renderer::types::GroundPlaneMode::None
1782 ) {
1783 let slot = &self.viewport_slots[vp_idx];
1784 let slot_hdr = slot.hdr.as_ref().unwrap();
1785 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1786 label: Some("hdr_ground_plane_pass"),
1787 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1788 view: output_view,
1789 resolve_target: None,
1790 ops: wgpu::Operations {
1791 load: wgpu::LoadOp::Load,
1792 store: wgpu::StoreOp::Store,
1793 },
1794 depth_slice: None,
1795 })],
1796 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1797 view: &slot_hdr.hdr_depth_view,
1798 depth_ops: Some(wgpu::Operations {
1799 load: wgpu::LoadOp::Load,
1800 store: wgpu::StoreOp::Store,
1801 }),
1802 stencil_ops: None,
1803 }),
1804 timestamp_writes: None,
1805 occlusion_query_set: None,
1806 });
1807 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
1808 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
1809 gp_pass.draw(0..3, 0..1);
1810 }
1811
1812 {
1816 let slot = &self.viewport_slots[vp_idx];
1817 let slot_hdr = slot.hdr.as_ref().unwrap();
1818 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
1819 && slot.gizmo_index_count > 0)
1820 || !slot.constraint_line_buffers.is_empty()
1821 || !slot.clip_plane_fill_buffers.is_empty()
1822 || !slot.clip_plane_line_buffers.is_empty()
1823 || !slot.xray_object_buffers.is_empty();
1824 if has_editor_overlays {
1825 let camera_bg = &slot.camera_bind_group;
1826 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1827 label: Some("hdr_editor_overlay_pass"),
1828 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1829 view: output_view,
1830 resolve_target: None,
1831 ops: wgpu::Operations {
1832 load: wgpu::LoadOp::Load,
1833 store: wgpu::StoreOp::Store,
1834 },
1835 depth_slice: None,
1836 })],
1837 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1838 view: &slot_hdr.hdr_depth_view,
1839 depth_ops: Some(wgpu::Operations {
1840 load: wgpu::LoadOp::Load,
1841 store: wgpu::StoreOp::Discard,
1842 }),
1843 stencil_ops: None,
1844 }),
1845 timestamp_writes: None,
1846 occlusion_query_set: None,
1847 });
1848
1849 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
1850 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
1851 overlay_pass.set_bind_group(0, camera_bg, &[]);
1852 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
1853 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
1854 overlay_pass.set_index_buffer(
1855 slot.gizmo_index_buffer.slice(..),
1856 wgpu::IndexFormat::Uint32,
1857 );
1858 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
1859 }
1860
1861 if !slot.constraint_line_buffers.is_empty() {
1862 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
1863 overlay_pass.set_bind_group(0, camera_bg, &[]);
1864 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
1865 overlay_pass.set_bind_group(1, bg, &[]);
1866 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1867 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1868 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
1869 }
1870 }
1871
1872 if !slot.clip_plane_fill_buffers.is_empty() {
1873 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
1874 overlay_pass.set_bind_group(0, camera_bg, &[]);
1875 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
1876 overlay_pass.set_bind_group(1, bg, &[]);
1877 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1878 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1879 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
1880 }
1881 }
1882
1883 if !slot.clip_plane_line_buffers.is_empty() {
1884 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
1885 overlay_pass.set_bind_group(0, camera_bg, &[]);
1886 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
1887 overlay_pass.set_bind_group(1, bg, &[]);
1888 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1889 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1890 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
1891 }
1892 }
1893
1894 if !slot.xray_object_buffers.is_empty() {
1895 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
1896 overlay_pass.set_bind_group(0, camera_bg, &[]);
1897 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
1898 let Some(mesh) = self
1899 .resources
1900 .mesh_store
1901 .get(*mesh_id)
1902 else {
1903 continue;
1904 };
1905 overlay_pass.set_bind_group(1, bg, &[]);
1906 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1907 overlay_pass.set_index_buffer(
1908 mesh.index_buffer.slice(..),
1909 wgpu::IndexFormat::Uint32,
1910 );
1911 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1912 }
1913 }
1914 }
1915 }
1916
1917 if frame.viewport.show_axes_indicator {
1920 let slot = &self.viewport_slots[vp_idx];
1921 if slot.axes_vertex_count > 0 {
1922 let slot_hdr = slot.hdr.as_ref().unwrap();
1923 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1924 label: Some("hdr_axes_pass"),
1925 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1926 view: output_view,
1927 resolve_target: None,
1928 ops: wgpu::Operations {
1929 load: wgpu::LoadOp::Load,
1930 store: wgpu::StoreOp::Store,
1931 },
1932 depth_slice: None,
1933 })],
1934 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1935 view: &slot_hdr.hdr_depth_view,
1936 depth_ops: Some(wgpu::Operations {
1937 load: wgpu::LoadOp::Load,
1938 store: wgpu::StoreOp::Discard,
1939 }),
1940 stencil_ops: None,
1941 }),
1942 timestamp_writes: None,
1943 occlusion_query_set: None,
1944 });
1945 axes_pass.set_pipeline(&self.resources.axes_pipeline);
1946 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
1947 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
1948 }
1949 }
1950
1951 if !self.screen_image_gpu_data.is_empty() {
1955 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
1956 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1957 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
1958 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1959 label: Some("screen_image_pass"),
1960 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1961 view: output_view,
1962 resolve_target: None,
1963 ops: wgpu::Operations {
1964 load: wgpu::LoadOp::Load,
1965 store: wgpu::StoreOp::Store,
1966 },
1967 depth_slice: None,
1968 })],
1969 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1970 view: &slot_hdr.hdr_depth_view,
1971 depth_ops: Some(wgpu::Operations {
1972 load: wgpu::LoadOp::Load,
1973 store: wgpu::StoreOp::Discard,
1974 }),
1975 stencil_ops: None,
1976 }),
1977 timestamp_writes: None,
1978 occlusion_query_set: None,
1979 });
1980 for gpu in &self.screen_image_gpu_data {
1981 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
1982 img_pass.set_pipeline(dc_pipe);
1983 img_pass.set_bind_group(0, dc_bg, &[]);
1984 } else {
1985 img_pass.set_pipeline(overlay_pipeline);
1986 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
1987 }
1988 img_pass.draw(0..6, 0..1);
1989 }
1990 }
1991 }
1992
1993 let has_overlay = self.label_gpu_data.is_some()
1995 || self.scalar_bar_gpu_data.is_some()
1996 || self.ruler_gpu_data.is_some()
1997 || self.loading_bar_gpu_data.is_some()
1998 || !self.overlay_image_gpu_data.is_empty();
1999 if has_overlay {
2000 let hdr_depth_view =
2001 &self.viewport_slots[vp_idx].hdr.as_ref().unwrap().hdr_depth_view;
2002 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2003 label: Some("overlay_pass"),
2004 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2005 view: output_view,
2006 resolve_target: None,
2007 ops: wgpu::Operations {
2008 load: wgpu::LoadOp::Load,
2009 store: wgpu::StoreOp::Store,
2010 },
2011 depth_slice: None,
2012 })],
2013 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2014 view: hdr_depth_view,
2015 depth_ops: Some(wgpu::Operations {
2016 load: wgpu::LoadOp::Load,
2017 store: wgpu::StoreOp::Discard,
2018 }),
2019 stencil_ops: None,
2020 }),
2021 timestamp_writes: None,
2022 occlusion_query_set: None,
2023 });
2024 if let Some(pipeline) = &self.resources.overlay_text_pipeline {
2025 overlay_pass.set_pipeline(pipeline);
2026 if let Some(ref ld) = self.label_gpu_data {
2027 overlay_pass.set_bind_group(0, &ld.bind_group, &[]);
2028 overlay_pass.set_vertex_buffer(0, ld.vertex_buf.slice(..));
2029 overlay_pass.draw(0..ld.vertex_count, 0..1);
2030 }
2031 if let Some(ref sb) = self.scalar_bar_gpu_data {
2032 overlay_pass.set_bind_group(0, &sb.bind_group, &[]);
2033 overlay_pass.set_vertex_buffer(0, sb.vertex_buf.slice(..));
2034 overlay_pass.draw(0..sb.vertex_count, 0..1);
2035 }
2036 if let Some(ref rd) = self.ruler_gpu_data {
2037 overlay_pass.set_bind_group(0, &rd.bind_group, &[]);
2038 overlay_pass.set_vertex_buffer(0, rd.vertex_buf.slice(..));
2039 overlay_pass.draw(0..rd.vertex_count, 0..1);
2040 }
2041 if let Some(ref lb) = self.loading_bar_gpu_data {
2042 overlay_pass.set_bind_group(0, &lb.bind_group, &[]);
2043 overlay_pass.set_vertex_buffer(0, lb.vertex_buf.slice(..));
2044 overlay_pass.draw(0..lb.vertex_count, 0..1);
2045 }
2046 }
2047 if !self.overlay_image_gpu_data.is_empty() {
2049 if let Some(pipeline) = &self.resources.screen_image_pipeline {
2050 overlay_pass.set_pipeline(pipeline);
2051 for gpu in &self.overlay_image_gpu_data {
2052 overlay_pass.set_bind_group(0, &gpu.bind_group, &[]);
2053 overlay_pass.draw(0..6, 0..1);
2054 }
2055 }
2056 }
2057 }
2058
2059 if let (Some(qs), Some(res_buf), Some(stg_buf)) = (
2061 self.ts_query_set.as_ref(),
2062 self.ts_resolve_buf.as_ref(),
2063 self.ts_staging_buf.as_ref(),
2064 ) {
2065 encoder.resolve_query_set(qs, 0..2, res_buf, 0);
2066 encoder.copy_buffer_to_buffer(res_buf, 0, stg_buf, 0, 16);
2067 self.ts_needs_readback = true;
2068 }
2069
2070 encoder.finish()
2071 }
2072
2073 pub fn render_offscreen(
2087 &mut self,
2088 device: &wgpu::Device,
2089 queue: &wgpu::Queue,
2090 frame: &FrameData,
2091 width: u32,
2092 height: u32,
2093 ) -> Vec<u8> {
2094 let target_format = self.resources.target_format;
2096 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
2097 label: Some("offscreen_target"),
2098 size: wgpu::Extent3d {
2099 width: width.max(1),
2100 height: height.max(1),
2101 depth_or_array_layers: 1,
2102 },
2103 mip_level_count: 1,
2104 sample_count: 1,
2105 dimension: wgpu::TextureDimension::D2,
2106 format: target_format,
2107 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
2108 view_formats: &[],
2109 });
2110
2111 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
2113
2114 let cmd_buf = self.render(device, queue, &output_view, frame);
2122 queue.submit(std::iter::once(cmd_buf));
2123
2124 let bytes_per_pixel = 4u32;
2126 let unpadded_row = width * bytes_per_pixel;
2127 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
2128 let padded_row = (unpadded_row + align - 1) & !(align - 1);
2129 let buffer_size = (padded_row * height.max(1)) as u64;
2130
2131 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
2132 label: Some("offscreen_staging"),
2133 size: buffer_size,
2134 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
2135 mapped_at_creation: false,
2136 });
2137
2138 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
2139 label: Some("offscreen_copy_encoder"),
2140 });
2141 copy_encoder.copy_texture_to_buffer(
2142 wgpu::TexelCopyTextureInfo {
2143 texture: &offscreen_texture,
2144 mip_level: 0,
2145 origin: wgpu::Origin3d::ZERO,
2146 aspect: wgpu::TextureAspect::All,
2147 },
2148 wgpu::TexelCopyBufferInfo {
2149 buffer: &staging_buf,
2150 layout: wgpu::TexelCopyBufferLayout {
2151 offset: 0,
2152 bytes_per_row: Some(padded_row),
2153 rows_per_image: Some(height.max(1)),
2154 },
2155 },
2156 wgpu::Extent3d {
2157 width: width.max(1),
2158 height: height.max(1),
2159 depth_or_array_layers: 1,
2160 },
2161 );
2162 queue.submit(std::iter::once(copy_encoder.finish()));
2163
2164 let (tx, rx) = std::sync::mpsc::channel();
2166 staging_buf
2167 .slice(..)
2168 .map_async(wgpu::MapMode::Read, move |result| {
2169 let _ = tx.send(result);
2170 });
2171 device
2172 .poll(wgpu::PollType::Wait {
2173 submission_index: None,
2174 timeout: Some(std::time::Duration::from_secs(5)),
2175 })
2176 .unwrap();
2177 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
2178
2179 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
2180 {
2181 let mapped = staging_buf.slice(..).get_mapped_range();
2182 let data: &[u8] = &mapped;
2183 if padded_row == unpadded_row {
2184 pixels.extend_from_slice(data);
2186 } else {
2187 for row in 0..height as usize {
2189 let start = row * padded_row as usize;
2190 let end = start + unpadded_row as usize;
2191 pixels.extend_from_slice(&data[start..end]);
2192 }
2193 }
2194 }
2195 staging_buf.unmap();
2196
2197 let is_bgra = matches!(
2199 target_format,
2200 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
2201 );
2202 if is_bgra {
2203 for pixel in pixels.chunks_exact_mut(4) {
2204 pixel.swap(0, 2); }
2206 }
2207
2208 pixels
2209 }
2210}