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