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