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 }
102
103 pub fn paint_to<'rp>(&'rp self, render_pass: &mut wgpu::RenderPass<'rp>, frame: &FrameData) {
109 let vp_idx = frame.camera.viewport_index;
110 let camera_bg = self.viewport_camera_bind_group(vp_idx);
111 let grid_bg = self.viewport_grid_bind_group(vp_idx);
112 let vp_slot = self.viewport_slots.get(vp_idx);
113 emit_draw_calls!(
114 &self.resources,
115 &mut *render_pass,
116 frame,
117 self.use_instancing,
118 &self.instanced_batches,
119 camera_bg,
120 grid_bg,
121 &self.compute_filter_results,
122 vp_slot
123 );
124 emit_scivis_draw_calls!(
125 &self.resources,
126 &mut *render_pass,
127 &self.point_cloud_gpu_data,
128 &self.glyph_gpu_data,
129 &self.polyline_gpu_data,
130 &self.volume_gpu_data,
131 &self.streamtube_gpu_data,
132 camera_bg
133 );
134 if !self.implicit_gpu_data.is_empty() {
136 if let Some(pipeline) = &self.resources.implicit_pipeline {
137 render_pass.set_pipeline(pipeline);
138 render_pass.set_bind_group(0, camera_bg, &[]);
139 for gpu in &self.implicit_gpu_data {
140 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
141 render_pass.draw(0..6, 0..1);
142 }
143 }
144 }
145 if !self.mc_gpu_data.is_empty() {
147 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
148 render_pass.set_pipeline(pipeline);
149 render_pass.set_bind_group(0, camera_bg, &[]);
150 for mc in &self.mc_gpu_data {
151 let vol = &self.resources.mc_volumes[mc.volume_idx];
152 render_pass.set_bind_group(1, &mc.render_bg, &[]);
153 for slab in &vol.slabs {
154 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
155 render_pass.draw_indirect(&slab.indirect_buf, 0);
156 }
157 }
158 }
159 }
160 if let Some(sub_hl) = self.viewport_slots.get(vp_idx).and_then(|s| s.sub_highlight.as_ref()) {
162 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
163 &self.resources.sub_highlight_fill_ldr_pipeline,
164 &self.resources.sub_highlight_edge_ldr_pipeline,
165 &self.resources.sub_highlight_sprite_ldr_pipeline,
166 ) {
167 if sub_hl.fill_vertex_count > 0 {
168 render_pass.set_pipeline(fill_pl);
169 render_pass.set_bind_group(0, camera_bg, &[]);
170 render_pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
171 render_pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
172 render_pass.draw(0..sub_hl.fill_vertex_count, 0..1);
173 }
174 if sub_hl.edge_segment_count > 0 {
175 render_pass.set_pipeline(edge_pl);
176 render_pass.set_bind_group(0, camera_bg, &[]);
177 render_pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
178 render_pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
179 render_pass.draw(0..6, 0..sub_hl.edge_segment_count);
180 }
181 if sub_hl.sprite_point_count > 0 {
182 render_pass.set_pipeline(sprite_pl);
183 render_pass.set_bind_group(0, camera_bg, &[]);
184 render_pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
185 render_pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
186 render_pass.draw(0..6, 0..sub_hl.sprite_point_count);
187 }
188 }
189 }
190 if !self.screen_image_gpu_data.is_empty() {
192 if let Some(pipeline) = &self.resources.screen_image_pipeline {
193 render_pass.set_pipeline(pipeline);
194 for gpu in &self.screen_image_gpu_data {
195 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
196 render_pass.draw(0..6, 0..1);
197 }
198 }
199 }
200 }
201
202 pub fn render_viewport(
216 &mut self,
217 device: &wgpu::Device,
218 queue: &wgpu::Queue,
219 output_view: &wgpu::TextureView,
220 id: ViewportId,
221 frame: &FrameData,
222 ) -> wgpu::CommandBuffer {
223 self.render_frame_internal(device, queue, output_view, id.0, frame)
224 }
225
226 pub fn render(
234 &mut self,
235 device: &wgpu::Device,
236 queue: &wgpu::Queue,
237 output_view: &wgpu::TextureView,
238 frame: &FrameData,
239 ) -> wgpu::CommandBuffer {
240 self.prepare(device, queue, frame);
242 self.render_frame_internal(
243 device,
244 queue,
245 output_view,
246 frame.camera.viewport_index,
247 frame,
248 )
249 }
250
251 fn render_frame_internal(
256 &mut self,
257 device: &wgpu::Device,
258 queue: &wgpu::Queue,
259 output_view: &wgpu::TextureView,
260 vp_idx: usize,
261 frame: &FrameData,
262 ) -> wgpu::CommandBuffer {
263 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
265 SurfaceSubmission::Flat(items) => items,
266 };
267
268 let bg_color = frame.viewport.background_color.unwrap_or([
269 65.0 / 255.0,
270 65.0 / 255.0,
271 65.0 / 255.0,
272 1.0,
273 ]);
274 let w = frame.camera.viewport_size[0] as u32;
275 let h = frame.camera.viewport_size[1] as u32;
276
277 let ssaa_factor = frame.effects.post_process.ssaa_factor.max(1);
279 self.ensure_viewport_hdr(device, queue, vp_idx, w.max(1), h.max(1), ssaa_factor);
280
281 if !frame.effects.post_process.enabled {
282 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
284 label: Some("ldr_encoder"),
285 });
286 {
287 let slot = &self.viewport_slots[vp_idx];
288 let slot_hdr = slot.hdr.as_ref().unwrap();
289 let camera_bg = &slot.camera_bind_group;
290 let grid_bg = &slot.grid_bind_group;
291 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
292 label: Some("ldr_render_pass"),
293 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
294 view: output_view,
295 resolve_target: None,
296 ops: wgpu::Operations {
297 load: wgpu::LoadOp::Clear(wgpu::Color {
298 r: bg_color[0] as f64,
299 g: bg_color[1] as f64,
300 b: bg_color[2] as f64,
301 a: bg_color[3] as f64,
302 }),
303 store: wgpu::StoreOp::Store,
304 },
305 depth_slice: None,
306 })],
307 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
308 view: &slot_hdr.outline_depth_view,
309 depth_ops: Some(wgpu::Operations {
310 load: wgpu::LoadOp::Clear(1.0),
311 store: wgpu::StoreOp::Discard,
312 }),
313 stencil_ops: None,
314 }),
315 timestamp_writes: None,
316 occlusion_query_set: None,
317 });
318 emit_draw_calls!(
319 &self.resources,
320 &mut render_pass,
321 frame,
322 self.use_instancing,
323 &self.instanced_batches,
324 camera_bg,
325 grid_bg,
326 &self.compute_filter_results,
327 Some(slot)
328 );
329 emit_scivis_draw_calls!(
330 &self.resources,
331 &mut render_pass,
332 &self.point_cloud_gpu_data,
333 &self.glyph_gpu_data,
334 &self.polyline_gpu_data,
335 &self.volume_gpu_data,
336 &self.streamtube_gpu_data,
337 camera_bg
338 );
339 if !self.implicit_gpu_data.is_empty() {
341 if let Some(pipeline) = &self.resources.implicit_pipeline {
342 render_pass.set_pipeline(pipeline);
343 render_pass.set_bind_group(0, camera_bg, &[]);
344 for gpu in &self.implicit_gpu_data {
345 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
346 render_pass.draw(0..6, 0..1);
347 }
348 }
349 }
350 if !self.mc_gpu_data.is_empty() {
352 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
353 render_pass.set_pipeline(pipeline);
354 render_pass.set_bind_group(0, camera_bg, &[]);
355 for mc in &self.mc_gpu_data {
356 let vol = &self.resources.mc_volumes[mc.volume_idx];
357 render_pass.set_bind_group(1, &mc.render_bg, &[]);
358 for slab in &vol.slabs {
359 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
360 render_pass.draw_indirect(&slab.indirect_buf, 0);
361 }
362 }
363 }
364 }
365 if !self.screen_image_gpu_data.is_empty() {
370 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
371 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
372 for gpu in &self.screen_image_gpu_data {
373 if let (Some(dc_bg), Some(dc_pipe)) =
374 (&gpu.depth_bind_group, dc_pipeline)
375 {
376 render_pass.set_pipeline(dc_pipe);
377 render_pass.set_bind_group(0, dc_bg, &[]);
378 } else {
379 render_pass.set_pipeline(overlay_pipeline);
380 render_pass.set_bind_group(0, &gpu.bind_group, &[]);
381 }
382 render_pass.draw(0..6, 0..1);
383 }
384 }
385 }
386 }
387 return encoder.finish();
388 }
389
390 let pp = &frame.effects.post_process;
392
393 let hdr_clear_rgb = [
394 bg_color[0].powf(2.2),
395 bg_color[1].powf(2.2),
396 bg_color[2].powf(2.2),
397 ];
398
399 let mode = match pp.tone_mapping {
401 crate::renderer::ToneMapping::Reinhard => 0u32,
402 crate::renderer::ToneMapping::Aces => 1u32,
403 crate::renderer::ToneMapping::KhronosNeutral => 2u32,
404 };
405 let tm_uniform = crate::resources::ToneMapUniform {
406 exposure: pp.exposure,
407 mode,
408 bloom_enabled: if pp.bloom { 1 } else { 0 },
409 ssao_enabled: if pp.ssao { 1 } else { 0 },
410 contact_shadows_enabled: if pp.contact_shadows { 1 } else { 0 },
411 _pad_tm: [0; 3],
412 background_color: bg_color,
413 };
414 {
415 let hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
416 queue.write_buffer(
417 &hdr.tone_map_uniform_buf,
418 0,
419 bytemuck::cast_slice(&[tm_uniform]),
420 );
421
422 if pp.ssao {
424 let proj = frame.camera.render_camera.projection;
425 let inv_proj = proj.inverse();
426 let ssao_uniform = crate::resources::SsaoUniform {
427 inv_proj: inv_proj.to_cols_array_2d(),
428 proj: proj.to_cols_array_2d(),
429 radius: 0.5,
430 bias: 0.025,
431 _pad: [0.0; 2],
432 };
433 queue.write_buffer(
434 &hdr.ssao_uniform_buf,
435 0,
436 bytemuck::cast_slice(&[ssao_uniform]),
437 );
438 }
439
440 if pp.contact_shadows {
442 let proj = frame.camera.render_camera.projection;
443 let inv_proj = proj.inverse();
444 let light_dir_world: glam::Vec3 =
445 if let Some(l) = frame.effects.lighting.lights.first() {
446 match l.kind {
447 LightKind::Directional { direction } => {
448 glam::Vec3::from(direction).normalize()
449 }
450 LightKind::Spot { direction, .. } => {
451 glam::Vec3::from(direction).normalize()
452 }
453 _ => glam::Vec3::new(0.0, -1.0, 0.0),
454 }
455 } else {
456 glam::Vec3::new(0.0, -1.0, 0.0)
457 };
458 let view = frame.camera.render_camera.view;
459 let light_dir_view = view.transform_vector3(light_dir_world).normalize();
460 let world_up_view = view.transform_vector3(glam::Vec3::Z).normalize();
461 let cs_uniform = crate::resources::ContactShadowUniform {
462 inv_proj: inv_proj.to_cols_array_2d(),
463 proj: proj.to_cols_array_2d(),
464 light_dir_view: [light_dir_view.x, light_dir_view.y, light_dir_view.z, 0.0],
465 world_up_view: [world_up_view.x, world_up_view.y, world_up_view.z, 0.0],
466 params: [
467 pp.contact_shadow_max_distance,
468 pp.contact_shadow_steps as f32,
469 pp.contact_shadow_thickness,
470 0.0,
471 ],
472 };
473 queue.write_buffer(
474 &hdr.contact_shadow_uniform_buf,
475 0,
476 bytemuck::cast_slice(&[cs_uniform]),
477 );
478 }
479
480 if pp.bloom {
482 let bloom_u = crate::resources::BloomUniform {
483 threshold: pp.bloom_threshold,
484 intensity: pp.bloom_intensity,
485 horizontal: 0,
486 _pad: 0,
487 };
488 queue.write_buffer(&hdr.bloom_uniform_buf, 0, bytemuck::cast_slice(&[bloom_u]));
489 }
490 }
491
492 {
494 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
495 self.resources.rebuild_tone_map_bind_group(
496 device,
497 hdr,
498 pp.bloom,
499 pp.ssao,
500 pp.contact_shadows,
501 );
502 }
503
504 {
509 let needs_oit = if self.use_instancing && !self.instanced_batches.is_empty() {
510 self.instanced_batches.iter().any(|b| b.is_transparent)
511 } else {
512 scene_items
513 .iter()
514 .any(|i| i.visible && i.material.opacity < 1.0)
515 };
516 if needs_oit {
517 let hdr = self.viewport_slots[vp_idx].hdr.as_mut().unwrap();
518 self.resources
519 .ensure_viewport_oit(device, hdr, w.max(1), h.max(1));
520 }
521 }
522
523 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
527 label: Some("hdr_encoder"),
528 });
529
530 let slot = &self.viewport_slots[vp_idx];
532 let camera_bg = &slot.camera_bind_group;
533 let slot_hdr = slot.hdr.as_ref().unwrap();
534
535 {
539 let use_ssaa = ssaa_factor > 1
541 && slot_hdr.ssaa_color_view.is_some()
542 && slot_hdr.ssaa_depth_view.is_some();
543 let scene_color_view = if use_ssaa {
544 slot_hdr.ssaa_color_view.as_ref().unwrap()
545 } else {
546 &slot_hdr.hdr_view
547 };
548 let scene_depth_view = if use_ssaa {
549 slot_hdr.ssaa_depth_view.as_ref().unwrap()
550 } else {
551 &slot_hdr.hdr_depth_view
552 };
553
554 let clear_wgpu = wgpu::Color {
555 r: hdr_clear_rgb[0] as f64,
556 g: hdr_clear_rgb[1] as f64,
557 b: hdr_clear_rgb[2] as f64,
558 a: bg_color[3] as f64,
559 };
560
561 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
562 label: Some("hdr_scene_pass"),
563 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
564 view: scene_color_view,
565 resolve_target: None,
566 ops: wgpu::Operations {
567 load: wgpu::LoadOp::Clear(clear_wgpu),
568 store: wgpu::StoreOp::Store,
569 },
570 depth_slice: None,
571 })],
572 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
573 view: scene_depth_view,
574 depth_ops: Some(wgpu::Operations {
575 load: wgpu::LoadOp::Clear(1.0),
576 store: wgpu::StoreOp::Store,
577 }),
578 stencil_ops: Some(wgpu::Operations {
579 load: wgpu::LoadOp::Clear(0),
580 store: wgpu::StoreOp::Store,
581 }),
582 }),
583 timestamp_writes: None,
584 occlusion_query_set: None,
585 });
586
587 let resources = &self.resources;
588 render_pass.set_bind_group(0, camera_bg, &[]);
589
590 let show_skybox = frame
592 .effects
593 .environment
594 .as_ref()
595 .is_some_and(|e| e.show_skybox)
596 && resources.ibl_skybox_view.is_some();
597
598 let use_instancing = self.use_instancing;
599 let batches = &self.instanced_batches;
600
601 if !scene_items.is_empty() {
602 if use_instancing && !batches.is_empty() {
603 let excluded_items: Vec<&SceneRenderItem> = scene_items
604 .iter()
605 .filter(|item| {
606 item.visible
607 && (item.active_attribute.is_some()
608 || item.material.is_two_sided()
609 || item.material.matcap_id.is_some())
610 && resources
611 .mesh_store
612 .get(item.mesh_id)
613 .is_some()
614 })
615 .collect();
616
617 let mut opaque_batches: Vec<&InstancedBatch> = Vec::new();
619 let mut transparent_batches: Vec<&InstancedBatch> = Vec::new();
620 for batch in batches {
621 if batch.is_transparent {
622 transparent_batches.push(batch);
623 } else {
624 opaque_batches.push(batch);
625 }
626 }
627
628 if !opaque_batches.is_empty() && !frame.viewport.wireframe_mode {
629 if let Some(ref pipeline) = resources.hdr_solid_instanced_pipeline {
630 render_pass.set_pipeline(pipeline);
631 for batch in &opaque_batches {
632 let Some(mesh) = resources
633 .mesh_store
634 .get(batch.mesh_id)
635 else {
636 continue;
637 };
638 let mat_key = (
639 batch.texture_id.unwrap_or(u64::MAX),
640 batch.normal_map_id.unwrap_or(u64::MAX),
641 batch.ao_map_id.unwrap_or(u64::MAX),
642 );
643 let Some(inst_tex_bg) =
644 resources.instance_bind_groups.get(&mat_key)
645 else {
646 continue;
647 };
648 render_pass.set_bind_group(1, inst_tex_bg, &[]);
649 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
650 render_pass.set_index_buffer(
651 mesh.index_buffer.slice(..),
652 wgpu::IndexFormat::Uint32,
653 );
654 render_pass.draw_indexed(
655 0..mesh.index_count,
656 0,
657 batch.instance_offset
658 ..batch.instance_offset + batch.instance_count,
659 );
660 }
661 }
662 }
663
664 let _ = &transparent_batches; if frame.viewport.wireframe_mode {
669 if let Some(ref hdr_wf) = resources.hdr_wireframe_pipeline {
670 render_pass.set_pipeline(hdr_wf);
671 for item in scene_items {
672 if !item.visible {
673 continue;
674 }
675 let Some(mesh) = resources
676 .mesh_store
677 .get(item.mesh_id)
678 else {
679 continue;
680 };
681 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
682 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
683 render_pass.set_index_buffer(
684 mesh.edge_index_buffer.slice(..),
685 wgpu::IndexFormat::Uint32,
686 );
687 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
688 }
689 }
690 } else if let (Some(hdr_solid), Some(hdr_solid_two_sided)) = (
691 &resources.hdr_solid_pipeline,
692 &resources.hdr_solid_two_sided_pipeline,
693 ) {
694 for item in excluded_items
695 .into_iter()
696 .filter(|item| item.material.opacity >= 1.0)
697 {
698 let Some(mesh) = resources
699 .mesh_store
700 .get(item.mesh_id)
701 else {
702 continue;
703 };
704 let pipeline = if item.material.is_two_sided() {
705 hdr_solid_two_sided
706 } else {
707 hdr_solid
708 };
709 render_pass.set_pipeline(pipeline);
710 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
711 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
712 render_pass.set_index_buffer(
713 mesh.index_buffer.slice(..),
714 wgpu::IndexFormat::Uint32,
715 );
716 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
717 }
718 }
719 } else {
720 let eye = glam::Vec3::from(frame.camera.render_camera.eye_position);
722 let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
723 let pos =
724 glam::Vec3::new(item.model[3][0], item.model[3][1], item.model[3][2]);
725 (pos - eye).length()
726 };
727
728 let mut opaque: Vec<&SceneRenderItem> = Vec::new();
729 let mut transparent: Vec<&SceneRenderItem> = Vec::new();
730 for item in scene_items {
731 if !item.visible
732 || resources
733 .mesh_store
734 .get(item.mesh_id)
735 .is_none()
736 {
737 continue;
738 }
739 if item.material.opacity < 1.0 {
740 transparent.push(item);
741 } else {
742 opaque.push(item);
743 }
744 }
745 opaque.sort_by(|a, b| {
746 dist_from_eye(a)
747 .partial_cmp(&dist_from_eye(b))
748 .unwrap_or(std::cmp::Ordering::Equal)
749 });
750 transparent.sort_by(|a, b| {
751 dist_from_eye(b)
752 .partial_cmp(&dist_from_eye(a))
753 .unwrap_or(std::cmp::Ordering::Equal)
754 });
755
756 let draw_item_hdr =
757 |render_pass: &mut wgpu::RenderPass<'_>,
758 item: &SceneRenderItem,
759 solid_pl: &wgpu::RenderPipeline,
760 trans_pl: &wgpu::RenderPipeline,
761 wf_pl: &wgpu::RenderPipeline| {
762 let mesh = resources
763 .mesh_store
764 .get(item.mesh_id)
765 .unwrap();
766 render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
769 let is_face_attr = item.active_attribute.as_ref().map_or(false, |a| {
770 matches!(
771 a.kind,
772 crate::resources::AttributeKind::Face
773 | crate::resources::AttributeKind::FaceColor
774 | crate::resources::AttributeKind::Halfedge
775 | crate::resources::AttributeKind::Corner
776 )
777 });
778 if frame.viewport.wireframe_mode {
779 render_pass.set_pipeline(wf_pl);
780 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
781 render_pass.set_index_buffer(
782 mesh.edge_index_buffer.slice(..),
783 wgpu::IndexFormat::Uint32,
784 );
785 render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
786 } else if is_face_attr {
787 if let Some(ref fvb) = mesh.face_vertex_buffer {
788 let pl = if item.material.opacity < 1.0 {
789 trans_pl
790 } else {
791 solid_pl
792 };
793 render_pass.set_pipeline(pl);
794 render_pass.set_vertex_buffer(0, fvb.slice(..));
795 render_pass.draw(0..mesh.index_count, 0..1);
796 }
797 } else if item.material.opacity < 1.0 {
798 render_pass.set_pipeline(trans_pl);
799 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
800 render_pass.set_index_buffer(
801 mesh.index_buffer.slice(..),
802 wgpu::IndexFormat::Uint32,
803 );
804 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
805 } else {
806 render_pass.set_pipeline(solid_pl);
807 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
808 render_pass.set_index_buffer(
809 mesh.index_buffer.slice(..),
810 wgpu::IndexFormat::Uint32,
811 );
812 render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
813 }
814 };
815
816 let _ = &transparent; if let (
820 Some(hdr_solid),
821 Some(hdr_solid_two_sided),
822 Some(hdr_trans),
823 Some(hdr_wf),
824 ) = (
825 &resources.hdr_solid_pipeline,
826 &resources.hdr_solid_two_sided_pipeline,
827 &resources.hdr_transparent_pipeline,
828 &resources.hdr_wireframe_pipeline,
829 ) {
830 for item in &opaque {
831 let solid_pl = if item.material.is_two_sided() {
832 hdr_solid_two_sided
833 } else {
834 hdr_solid
835 };
836 draw_item_hdr(&mut render_pass, item, solid_pl, hdr_trans, hdr_wf);
837 }
838 }
839 }
840 }
841
842 if !slot.cap_buffers.is_empty() {
844 if let Some(ref hdr_overlay) = resources.hdr_overlay_pipeline {
845 render_pass.set_pipeline(hdr_overlay);
846 render_pass.set_bind_group(0, camera_bg, &[]);
847 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.cap_buffers {
848 render_pass.set_bind_group(1, bg, &[]);
849 render_pass.set_vertex_buffer(0, vbuf.slice(..));
850 render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
851 render_pass.draw_indexed(0..*idx_count, 0, 0..1);
852 }
853 }
854 }
855
856 emit_scivis_draw_calls!(
858 &self.resources,
859 &mut render_pass,
860 &self.point_cloud_gpu_data,
861 &self.glyph_gpu_data,
862 &self.polyline_gpu_data,
863 &self.volume_gpu_data,
864 &self.streamtube_gpu_data,
865 camera_bg
866 );
867
868 if !self.implicit_gpu_data.is_empty() {
870 if let Some(pipeline) = &self.resources.implicit_pipeline {
871 render_pass.set_pipeline(pipeline);
872 render_pass.set_bind_group(0, camera_bg, &[]);
873 for gpu in &self.implicit_gpu_data {
874 render_pass.set_bind_group(1, &gpu.bind_group, &[]);
875 render_pass.draw(0..6, 0..1);
876 }
877 }
878 }
879 if !self.mc_gpu_data.is_empty() {
881 if let Some(pipeline) = &self.resources.mc_surface_pipeline {
882 render_pass.set_pipeline(pipeline);
883 render_pass.set_bind_group(0, camera_bg, &[]);
884 for mc in &self.mc_gpu_data {
885 let vol = &self.resources.mc_volumes[mc.volume_idx];
886 render_pass.set_bind_group(1, &mc.render_bg, &[]);
887 for slab in &vol.slabs {
888 render_pass.set_vertex_buffer(0, slab.vertex_buf.slice(..));
889 render_pass.draw_indirect(&slab.indirect_buf, 0);
890 }
891 }
892 }
893 }
894
895 if show_skybox {
897 render_pass.set_bind_group(0, camera_bg, &[]);
898 render_pass.set_pipeline(&resources.skybox_pipeline);
899 render_pass.draw(0..3, 0..1);
900 }
901 }
902
903 if ssaa_factor > 1 {
908 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
909 if let (Some(pipeline), Some(bg)) = (
910 &self.resources.ssaa_resolve_pipeline,
911 &slot_hdr.ssaa_resolve_bind_group,
912 ) {
913 let mut resolve_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
914 label: Some("ssaa_resolve_pass"),
915 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
916 view: &slot_hdr.hdr_view,
917 resolve_target: None,
918 ops: wgpu::Operations {
919 load: wgpu::LoadOp::Load,
920 store: wgpu::StoreOp::Store,
921 },
922 depth_slice: None,
923 })],
924 depth_stencil_attachment: None,
925 timestamp_writes: None,
926 occlusion_query_set: None,
927 });
928 resolve_pass.set_pipeline(pipeline);
929 resolve_pass.set_bind_group(0, bg, &[]);
930 resolve_pass.draw(0..3, 0..1);
931 }
932 }
933
934 if let Some(sub_hl) = self.viewport_slots[vp_idx].sub_highlight.as_ref() {
940 let resources = &self.resources;
941 if let (Some(fill_pl), Some(edge_pl), Some(sprite_pl)) = (
942 &resources.sub_highlight_fill_pipeline,
943 &resources.sub_highlight_edge_pipeline,
944 &resources.sub_highlight_sprite_pipeline,
945 ) {
946 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
947 let camera_bg = &self.viewport_slots[vp_idx].camera_bind_group;
948 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
949 label: Some("sub_highlight_pass"),
950 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
951 view: &slot_hdr.hdr_view,
952 resolve_target: None,
953 ops: wgpu::Operations {
954 load: wgpu::LoadOp::Load,
955 store: wgpu::StoreOp::Store,
956 },
957 depth_slice: None,
958 })],
959 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
960 view: &slot_hdr.hdr_depth_view,
961 depth_ops: Some(wgpu::Operations {
962 load: wgpu::LoadOp::Load,
963 store: wgpu::StoreOp::Discard,
964 }),
965 stencil_ops: None,
966 }),
967 timestamp_writes: None,
968 occlusion_query_set: None,
969 });
970
971 if sub_hl.fill_vertex_count > 0 {
972 pass.set_pipeline(fill_pl);
973 pass.set_bind_group(0, camera_bg, &[]);
974 pass.set_bind_group(1, &sub_hl.fill_bind_group, &[]);
975 pass.set_vertex_buffer(0, sub_hl.fill_vertex_buf.slice(..));
976 pass.draw(0..sub_hl.fill_vertex_count, 0..1);
977 }
978 if sub_hl.edge_segment_count > 0 {
979 pass.set_pipeline(edge_pl);
980 pass.set_bind_group(0, camera_bg, &[]);
981 pass.set_bind_group(1, &sub_hl.edge_bind_group, &[]);
982 pass.set_vertex_buffer(0, sub_hl.edge_vertex_buf.slice(..));
983 pass.draw(0..6, 0..sub_hl.edge_segment_count);
984 }
985 if sub_hl.sprite_point_count > 0 {
986 pass.set_pipeline(sprite_pl);
987 pass.set_bind_group(0, camera_bg, &[]);
988 pass.set_bind_group(1, &sub_hl.sprite_bind_group, &[]);
989 pass.set_vertex_buffer(0, sub_hl.sprite_vertex_buf.slice(..));
990 pass.draw(0..6, 0..sub_hl.sprite_point_count);
991 }
992 }
993 }
994
995 let has_transparent = if self.use_instancing && !self.instanced_batches.is_empty() {
1000 self.instanced_batches.iter().any(|b| b.is_transparent)
1001 } else {
1002 scene_items
1003 .iter()
1004 .any(|i| i.visible && i.material.opacity < 1.0)
1005 };
1006
1007 if has_transparent {
1008 if let (Some(accum_view), Some(reveal_view)) = (
1010 slot_hdr.oit_accum_view.as_ref(),
1011 slot_hdr.oit_reveal_view.as_ref(),
1012 ) {
1013 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1014 let mut oit_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1016 label: Some("oit_pass"),
1017 color_attachments: &[
1018 Some(wgpu::RenderPassColorAttachment {
1019 view: accum_view,
1020 resolve_target: None,
1021 ops: wgpu::Operations {
1022 load: wgpu::LoadOp::Clear(wgpu::Color {
1023 r: 0.0,
1024 g: 0.0,
1025 b: 0.0,
1026 a: 0.0,
1027 }),
1028 store: wgpu::StoreOp::Store,
1029 },
1030 depth_slice: None,
1031 }),
1032 Some(wgpu::RenderPassColorAttachment {
1033 view: reveal_view,
1034 resolve_target: None,
1035 ops: wgpu::Operations {
1036 load: wgpu::LoadOp::Clear(wgpu::Color {
1037 r: 1.0,
1038 g: 1.0,
1039 b: 1.0,
1040 a: 1.0,
1041 }),
1042 store: wgpu::StoreOp::Store,
1043 },
1044 depth_slice: None,
1045 }),
1046 ],
1047 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1048 view: hdr_depth_view,
1049 depth_ops: Some(wgpu::Operations {
1050 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
1052 }),
1053 stencil_ops: None,
1054 }),
1055 timestamp_writes: None,
1056 occlusion_query_set: None,
1057 });
1058
1059 oit_pass.set_bind_group(0, camera_bg, &[]);
1060
1061 if self.use_instancing && !self.instanced_batches.is_empty() {
1062 if let Some(ref pipeline) = self.resources.oit_instanced_pipeline {
1063 oit_pass.set_pipeline(pipeline);
1064 for batch in &self.instanced_batches {
1065 if !batch.is_transparent {
1066 continue;
1067 }
1068 let Some(mesh) = self
1069 .resources
1070 .mesh_store
1071 .get(batch.mesh_id)
1072 else {
1073 continue;
1074 };
1075 let mat_key = (
1076 batch.texture_id.unwrap_or(u64::MAX),
1077 batch.normal_map_id.unwrap_or(u64::MAX),
1078 batch.ao_map_id.unwrap_or(u64::MAX),
1079 );
1080 let Some(inst_tex_bg) =
1081 self.resources.instance_bind_groups.get(&mat_key)
1082 else {
1083 continue;
1084 };
1085 oit_pass.set_bind_group(1, inst_tex_bg, &[]);
1086 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1087 oit_pass.set_index_buffer(
1088 mesh.index_buffer.slice(..),
1089 wgpu::IndexFormat::Uint32,
1090 );
1091 oit_pass.draw_indexed(
1092 0..mesh.index_count,
1093 0,
1094 batch.instance_offset..batch.instance_offset + batch.instance_count,
1095 );
1096 }
1097 }
1098 } else if let Some(ref pipeline) = self.resources.oit_pipeline {
1099 oit_pass.set_pipeline(pipeline);
1100 for item in scene_items {
1101 if !item.visible || item.material.opacity >= 1.0 {
1102 continue;
1103 }
1104 let Some(mesh) = self
1105 .resources
1106 .mesh_store
1107 .get(item.mesh_id)
1108 else {
1109 continue;
1110 };
1111 oit_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
1112 oit_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1113 oit_pass.set_index_buffer(
1114 mesh.index_buffer.slice(..),
1115 wgpu::IndexFormat::Uint32,
1116 );
1117 oit_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1118 }
1119 }
1120 }
1121 }
1122
1123 if has_transparent {
1128 if let (Some(pipeline), Some(bg)) = (
1129 self.resources.oit_composite_pipeline.as_ref(),
1130 slot_hdr.oit_composite_bind_group.as_ref(),
1131 ) {
1132 let hdr_view = &slot_hdr.hdr_view;
1133 let mut composite_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1134 label: Some("oit_composite_pass"),
1135 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1136 view: hdr_view,
1137 resolve_target: None,
1138 ops: wgpu::Operations {
1139 load: wgpu::LoadOp::Load,
1140 store: wgpu::StoreOp::Store,
1141 },
1142 depth_slice: None,
1143 })],
1144 depth_stencil_attachment: None,
1145 timestamp_writes: None,
1146 occlusion_query_set: None,
1147 });
1148 composite_pass.set_pipeline(pipeline);
1149 composite_pass.set_bind_group(0, bg, &[]);
1150 composite_pass.draw(0..3, 0..1);
1151 }
1152 }
1153
1154 if !slot.outline_object_buffers.is_empty() {
1160 let hdr_pipeline = self
1162 .resources
1163 .outline_composite_pipeline_hdr
1164 .as_ref()
1165 .or(self.resources.outline_composite_pipeline_single.as_ref());
1166 if let Some(pipeline) = hdr_pipeline {
1167 let bg = &slot_hdr.outline_composite_bind_group;
1168 let hdr_view = &slot_hdr.hdr_view;
1169 let hdr_depth_view = &slot_hdr.hdr_depth_view;
1170 let mut outline_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1171 label: Some("hdr_outline_composite_pass"),
1172 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1173 view: hdr_view,
1174 resolve_target: None,
1175 ops: wgpu::Operations {
1176 load: wgpu::LoadOp::Load,
1177 store: wgpu::StoreOp::Store,
1178 },
1179 depth_slice: None,
1180 })],
1181 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1182 view: hdr_depth_view,
1183 depth_ops: Some(wgpu::Operations {
1184 load: wgpu::LoadOp::Load,
1185 store: wgpu::StoreOp::Store,
1186 }),
1187 stencil_ops: None,
1188 }),
1189 timestamp_writes: None,
1190 occlusion_query_set: None,
1191 });
1192 outline_pass.set_pipeline(pipeline);
1193 outline_pass.set_bind_group(0, bg, &[]);
1194 outline_pass.draw(0..3, 0..1);
1195 }
1196 }
1197
1198 if pp.ssao {
1202 if let Some(ssao_pipeline) = &self.resources.ssao_pipeline {
1203 {
1204 let mut ssao_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1205 label: Some("ssao_pass"),
1206 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1207 view: &slot_hdr.ssao_view,
1208 resolve_target: None,
1209 ops: wgpu::Operations {
1210 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1211 store: wgpu::StoreOp::Store,
1212 },
1213 depth_slice: None,
1214 })],
1215 depth_stencil_attachment: None,
1216 timestamp_writes: None,
1217 occlusion_query_set: None,
1218 });
1219 ssao_pass.set_pipeline(ssao_pipeline);
1220 ssao_pass.set_bind_group(0, &slot_hdr.ssao_bg, &[]);
1221 ssao_pass.draw(0..3, 0..1);
1222 }
1223
1224 if let Some(ssao_blur_pipeline) = &self.resources.ssao_blur_pipeline {
1226 let mut ssao_blur_pass =
1227 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1228 label: Some("ssao_blur_pass"),
1229 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1230 view: &slot_hdr.ssao_blur_view,
1231 resolve_target: None,
1232 ops: wgpu::Operations {
1233 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1234 store: wgpu::StoreOp::Store,
1235 },
1236 depth_slice: None,
1237 })],
1238 depth_stencil_attachment: None,
1239 timestamp_writes: None,
1240 occlusion_query_set: None,
1241 });
1242 ssao_blur_pass.set_pipeline(ssao_blur_pipeline);
1243 ssao_blur_pass.set_bind_group(0, &slot_hdr.ssao_blur_bg, &[]);
1244 ssao_blur_pass.draw(0..3, 0..1);
1245 }
1246 }
1247 }
1248
1249 if pp.contact_shadows {
1253 if let Some(cs_pipeline) = &self.resources.contact_shadow_pipeline {
1254 let mut cs_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1255 label: Some("contact_shadow_pass"),
1256 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1257 view: &slot_hdr.contact_shadow_view,
1258 resolve_target: None,
1259 depth_slice: None,
1260 ops: wgpu::Operations {
1261 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
1262 store: wgpu::StoreOp::Store,
1263 },
1264 })],
1265 depth_stencil_attachment: None,
1266 timestamp_writes: None,
1267 occlusion_query_set: None,
1268 });
1269 cs_pass.set_pipeline(cs_pipeline);
1270 cs_pass.set_bind_group(0, &slot_hdr.contact_shadow_bg, &[]);
1271 cs_pass.draw(0..3, 0..1);
1272 }
1273 }
1274
1275 if pp.bloom {
1279 if let Some(bloom_threshold_pipeline) = &self.resources.bloom_threshold_pipeline {
1281 {
1282 let mut threshold_pass =
1283 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1284 label: Some("bloom_threshold_pass"),
1285 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1286 view: &slot_hdr.bloom_threshold_view,
1287 resolve_target: None,
1288 ops: wgpu::Operations {
1289 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1290 store: wgpu::StoreOp::Store,
1291 },
1292 depth_slice: None,
1293 })],
1294 depth_stencil_attachment: None,
1295 timestamp_writes: None,
1296 occlusion_query_set: None,
1297 });
1298 threshold_pass.set_pipeline(bloom_threshold_pipeline);
1299 threshold_pass.set_bind_group(0, &slot_hdr.bloom_threshold_bg, &[]);
1300 threshold_pass.draw(0..3, 0..1);
1301 }
1302
1303 if let Some(blur_pipeline) = &self.resources.bloom_blur_pipeline {
1306 let blur_h_bg = &slot_hdr.bloom_blur_h_bg;
1307 let blur_h_pong_bg = &slot_hdr.bloom_blur_h_pong_bg;
1308 let blur_v_bg = &slot_hdr.bloom_blur_v_bg;
1309 let bloom_ping_view = &slot_hdr.bloom_ping_view;
1310 let bloom_pong_view = &slot_hdr.bloom_pong_view;
1311 const BLUR_ITERATIONS: usize = 4;
1312 for i in 0..BLUR_ITERATIONS {
1313 let h_bg = if i == 0 { blur_h_bg } else { blur_h_pong_bg };
1315 {
1316 let mut h_pass =
1317 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1318 label: Some("bloom_blur_h_pass"),
1319 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1320 view: bloom_ping_view,
1321 resolve_target: None,
1322 ops: wgpu::Operations {
1323 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1324 store: wgpu::StoreOp::Store,
1325 },
1326 depth_slice: None,
1327 })],
1328 depth_stencil_attachment: None,
1329 timestamp_writes: None,
1330 occlusion_query_set: None,
1331 });
1332 h_pass.set_pipeline(blur_pipeline);
1333 h_pass.set_bind_group(0, h_bg, &[]);
1334 h_pass.draw(0..3, 0..1);
1335 }
1336 {
1338 let mut v_pass =
1339 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1340 label: Some("bloom_blur_v_pass"),
1341 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1342 view: bloom_pong_view,
1343 resolve_target: None,
1344 ops: wgpu::Operations {
1345 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1346 store: wgpu::StoreOp::Store,
1347 },
1348 depth_slice: None,
1349 })],
1350 depth_stencil_attachment: None,
1351 timestamp_writes: None,
1352 occlusion_query_set: None,
1353 });
1354 v_pass.set_pipeline(blur_pipeline);
1355 v_pass.set_bind_group(0, blur_v_bg, &[]);
1356 v_pass.draw(0..3, 0..1);
1357 }
1358 }
1359 }
1360 }
1361 }
1362
1363 let use_fxaa = pp.fxaa;
1367 if let Some(tone_map_pipeline) = &self.resources.tone_map_pipeline {
1368 let tone_target: &wgpu::TextureView = if use_fxaa {
1369 &slot_hdr.fxaa_view
1370 } else {
1371 output_view
1372 };
1373 let mut tone_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1374 label: Some("tone_map_pass"),
1375 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1376 view: tone_target,
1377 resolve_target: None,
1378 ops: wgpu::Operations {
1379 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1380 store: wgpu::StoreOp::Store,
1381 },
1382 depth_slice: None,
1383 })],
1384 depth_stencil_attachment: None,
1385 timestamp_writes: None,
1386 occlusion_query_set: None,
1387 });
1388 tone_pass.set_pipeline(tone_map_pipeline);
1389 tone_pass.set_bind_group(0, &slot_hdr.tone_map_bind_group, &[]);
1390 tone_pass.draw(0..3, 0..1);
1391 }
1392
1393 if use_fxaa {
1397 if let Some(fxaa_pipeline) = &self.resources.fxaa_pipeline {
1398 let mut fxaa_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1399 label: Some("fxaa_pass"),
1400 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1401 view: output_view,
1402 resolve_target: None,
1403 ops: wgpu::Operations {
1404 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1405 store: wgpu::StoreOp::Store,
1406 },
1407 depth_slice: None,
1408 })],
1409 depth_stencil_attachment: None,
1410 timestamp_writes: None,
1411 occlusion_query_set: None,
1412 });
1413 fxaa_pass.set_pipeline(fxaa_pipeline);
1414 fxaa_pass.set_bind_group(0, &slot_hdr.fxaa_bind_group, &[]);
1415 fxaa_pass.draw(0..3, 0..1);
1416 }
1417 }
1418
1419 if frame.viewport.show_grid {
1423 let slot = &self.viewport_slots[vp_idx];
1424 let slot_hdr = slot.hdr.as_ref().unwrap();
1425 let grid_bg = &slot.grid_bind_group;
1426 let mut grid_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1427 label: Some("hdr_grid_pass"),
1428 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1429 view: output_view,
1430 resolve_target: None,
1431 ops: wgpu::Operations {
1432 load: wgpu::LoadOp::Load,
1433 store: wgpu::StoreOp::Store,
1434 },
1435 depth_slice: None,
1436 })],
1437 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1438 view: &slot_hdr.hdr_depth_view,
1439 depth_ops: Some(wgpu::Operations {
1440 load: wgpu::LoadOp::Load,
1441 store: wgpu::StoreOp::Store,
1442 }),
1443 stencil_ops: None,
1444 }),
1445 timestamp_writes: None,
1446 occlusion_query_set: None,
1447 });
1448 grid_pass.set_pipeline(&self.resources.grid_pipeline);
1449 grid_pass.set_bind_group(0, grid_bg, &[]);
1450 grid_pass.draw(0..3, 0..1);
1451 }
1452
1453 if !matches!(
1456 frame.effects.ground_plane.mode,
1457 crate::renderer::types::GroundPlaneMode::None
1458 ) {
1459 let slot = &self.viewport_slots[vp_idx];
1460 let slot_hdr = slot.hdr.as_ref().unwrap();
1461 let mut gp_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1462 label: Some("hdr_ground_plane_pass"),
1463 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1464 view: output_view,
1465 resolve_target: None,
1466 ops: wgpu::Operations {
1467 load: wgpu::LoadOp::Load,
1468 store: wgpu::StoreOp::Store,
1469 },
1470 depth_slice: None,
1471 })],
1472 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1473 view: &slot_hdr.hdr_depth_view,
1474 depth_ops: Some(wgpu::Operations {
1475 load: wgpu::LoadOp::Load,
1476 store: wgpu::StoreOp::Store,
1477 }),
1478 stencil_ops: None,
1479 }),
1480 timestamp_writes: None,
1481 occlusion_query_set: None,
1482 });
1483 gp_pass.set_pipeline(&self.resources.ground_plane_pipeline);
1484 gp_pass.set_bind_group(0, &self.resources.ground_plane_bind_group, &[]);
1485 gp_pass.draw(0..3, 0..1);
1486 }
1487
1488 {
1492 let slot = &self.viewport_slots[vp_idx];
1493 let slot_hdr = slot.hdr.as_ref().unwrap();
1494 let has_editor_overlays = (frame.interaction.gizmo_model.is_some()
1495 && slot.gizmo_index_count > 0)
1496 || !slot.constraint_line_buffers.is_empty()
1497 || !slot.clip_plane_fill_buffers.is_empty()
1498 || !slot.clip_plane_line_buffers.is_empty()
1499 || !slot.xray_object_buffers.is_empty();
1500 if has_editor_overlays {
1501 let camera_bg = &slot.camera_bind_group;
1502 let mut overlay_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1503 label: Some("hdr_editor_overlay_pass"),
1504 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1505 view: output_view,
1506 resolve_target: None,
1507 ops: wgpu::Operations {
1508 load: wgpu::LoadOp::Load,
1509 store: wgpu::StoreOp::Store,
1510 },
1511 depth_slice: None,
1512 })],
1513 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1514 view: &slot_hdr.hdr_depth_view,
1515 depth_ops: Some(wgpu::Operations {
1516 load: wgpu::LoadOp::Load,
1517 store: wgpu::StoreOp::Discard,
1518 }),
1519 stencil_ops: None,
1520 }),
1521 timestamp_writes: None,
1522 occlusion_query_set: None,
1523 });
1524
1525 if frame.interaction.gizmo_model.is_some() && slot.gizmo_index_count > 0 {
1526 overlay_pass.set_pipeline(&self.resources.gizmo_pipeline);
1527 overlay_pass.set_bind_group(0, camera_bg, &[]);
1528 overlay_pass.set_bind_group(1, &slot.gizmo_bind_group, &[]);
1529 overlay_pass.set_vertex_buffer(0, slot.gizmo_vertex_buffer.slice(..));
1530 overlay_pass.set_index_buffer(
1531 slot.gizmo_index_buffer.slice(..),
1532 wgpu::IndexFormat::Uint32,
1533 );
1534 overlay_pass.draw_indexed(0..slot.gizmo_index_count, 0, 0..1);
1535 }
1536
1537 if !slot.constraint_line_buffers.is_empty() {
1538 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
1539 overlay_pass.set_bind_group(0, camera_bg, &[]);
1540 for (vbuf, ibuf, index_count, _ubuf, bg) in &slot.constraint_line_buffers {
1541 overlay_pass.set_bind_group(1, bg, &[]);
1542 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1543 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1544 overlay_pass.draw_indexed(0..*index_count, 0, 0..1);
1545 }
1546 }
1547
1548 if !slot.clip_plane_fill_buffers.is_empty() {
1549 overlay_pass.set_pipeline(&self.resources.overlay_pipeline);
1550 overlay_pass.set_bind_group(0, camera_bg, &[]);
1551 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_fill_buffers {
1552 overlay_pass.set_bind_group(1, bg, &[]);
1553 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1554 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1555 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
1556 }
1557 }
1558
1559 if !slot.clip_plane_line_buffers.is_empty() {
1560 overlay_pass.set_pipeline(&self.resources.overlay_line_pipeline);
1561 overlay_pass.set_bind_group(0, camera_bg, &[]);
1562 for (vbuf, ibuf, idx_count, _ubuf, bg) in &slot.clip_plane_line_buffers {
1563 overlay_pass.set_bind_group(1, bg, &[]);
1564 overlay_pass.set_vertex_buffer(0, vbuf.slice(..));
1565 overlay_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
1566 overlay_pass.draw_indexed(0..*idx_count, 0, 0..1);
1567 }
1568 }
1569
1570 if !slot.xray_object_buffers.is_empty() {
1571 overlay_pass.set_pipeline(&self.resources.xray_pipeline);
1572 overlay_pass.set_bind_group(0, camera_bg, &[]);
1573 for (mesh_id, _buf, bg) in &slot.xray_object_buffers {
1574 let Some(mesh) = self
1575 .resources
1576 .mesh_store
1577 .get(*mesh_id)
1578 else {
1579 continue;
1580 };
1581 overlay_pass.set_bind_group(1, bg, &[]);
1582 overlay_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1583 overlay_pass.set_index_buffer(
1584 mesh.index_buffer.slice(..),
1585 wgpu::IndexFormat::Uint32,
1586 );
1587 overlay_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
1588 }
1589 }
1590 }
1591 }
1592
1593 if frame.viewport.show_axes_indicator {
1596 let slot = &self.viewport_slots[vp_idx];
1597 if slot.axes_vertex_count > 0 {
1598 let slot_hdr = slot.hdr.as_ref().unwrap();
1599 let mut axes_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1600 label: Some("hdr_axes_pass"),
1601 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1602 view: output_view,
1603 resolve_target: None,
1604 ops: wgpu::Operations {
1605 load: wgpu::LoadOp::Load,
1606 store: wgpu::StoreOp::Store,
1607 },
1608 depth_slice: None,
1609 })],
1610 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1611 view: &slot_hdr.hdr_depth_view,
1612 depth_ops: Some(wgpu::Operations {
1613 load: wgpu::LoadOp::Load,
1614 store: wgpu::StoreOp::Discard,
1615 }),
1616 stencil_ops: None,
1617 }),
1618 timestamp_writes: None,
1619 occlusion_query_set: None,
1620 });
1621 axes_pass.set_pipeline(&self.resources.axes_pipeline);
1622 axes_pass.set_vertex_buffer(0, slot.axes_vertex_buffer.slice(..));
1623 axes_pass.draw(0..slot.axes_vertex_count, 0..1);
1624 }
1625 }
1626
1627 if !self.screen_image_gpu_data.is_empty() {
1631 if let Some(overlay_pipeline) = &self.resources.screen_image_pipeline {
1632 let slot_hdr = self.viewport_slots[vp_idx].hdr.as_ref().unwrap();
1633 let dc_pipeline = self.resources.screen_image_dc_pipeline.as_ref();
1634 let mut img_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1635 label: Some("screen_image_pass"),
1636 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1637 view: output_view,
1638 resolve_target: None,
1639 ops: wgpu::Operations {
1640 load: wgpu::LoadOp::Load,
1641 store: wgpu::StoreOp::Store,
1642 },
1643 depth_slice: None,
1644 })],
1645 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1646 view: &slot_hdr.hdr_depth_view,
1647 depth_ops: Some(wgpu::Operations {
1648 load: wgpu::LoadOp::Load,
1649 store: wgpu::StoreOp::Discard,
1650 }),
1651 stencil_ops: None,
1652 }),
1653 timestamp_writes: None,
1654 occlusion_query_set: None,
1655 });
1656 for gpu in &self.screen_image_gpu_data {
1657 if let (Some(dc_bg), Some(dc_pipe)) = (&gpu.depth_bind_group, dc_pipeline) {
1658 img_pass.set_pipeline(dc_pipe);
1659 img_pass.set_bind_group(0, dc_bg, &[]);
1660 } else {
1661 img_pass.set_pipeline(overlay_pipeline);
1662 img_pass.set_bind_group(0, &gpu.bind_group, &[]);
1663 }
1664 img_pass.draw(0..6, 0..1);
1665 }
1666 }
1667 }
1668
1669 encoder.finish()
1670 }
1671
1672 pub fn render_offscreen(
1686 &mut self,
1687 device: &wgpu::Device,
1688 queue: &wgpu::Queue,
1689 frame: &FrameData,
1690 width: u32,
1691 height: u32,
1692 ) -> Vec<u8> {
1693 let target_format = self.resources.target_format;
1695 let offscreen_texture = device.create_texture(&wgpu::TextureDescriptor {
1696 label: Some("offscreen_target"),
1697 size: wgpu::Extent3d {
1698 width: width.max(1),
1699 height: height.max(1),
1700 depth_or_array_layers: 1,
1701 },
1702 mip_level_count: 1,
1703 sample_count: 1,
1704 dimension: wgpu::TextureDimension::D2,
1705 format: target_format,
1706 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
1707 view_formats: &[],
1708 });
1709
1710 let output_view = offscreen_texture.create_view(&wgpu::TextureViewDescriptor::default());
1712
1713 let cmd_buf = self.render(device, queue, &output_view, frame);
1721 queue.submit(std::iter::once(cmd_buf));
1722
1723 let bytes_per_pixel = 4u32;
1725 let unpadded_row = width * bytes_per_pixel;
1726 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
1727 let padded_row = (unpadded_row + align - 1) & !(align - 1);
1728 let buffer_size = (padded_row * height.max(1)) as u64;
1729
1730 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
1731 label: Some("offscreen_staging"),
1732 size: buffer_size,
1733 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
1734 mapped_at_creation: false,
1735 });
1736
1737 let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1738 label: Some("offscreen_copy_encoder"),
1739 });
1740 copy_encoder.copy_texture_to_buffer(
1741 wgpu::TexelCopyTextureInfo {
1742 texture: &offscreen_texture,
1743 mip_level: 0,
1744 origin: wgpu::Origin3d::ZERO,
1745 aspect: wgpu::TextureAspect::All,
1746 },
1747 wgpu::TexelCopyBufferInfo {
1748 buffer: &staging_buf,
1749 layout: wgpu::TexelCopyBufferLayout {
1750 offset: 0,
1751 bytes_per_row: Some(padded_row),
1752 rows_per_image: Some(height.max(1)),
1753 },
1754 },
1755 wgpu::Extent3d {
1756 width: width.max(1),
1757 height: height.max(1),
1758 depth_or_array_layers: 1,
1759 },
1760 );
1761 queue.submit(std::iter::once(copy_encoder.finish()));
1762
1763 let (tx, rx) = std::sync::mpsc::channel();
1765 staging_buf
1766 .slice(..)
1767 .map_async(wgpu::MapMode::Read, move |result| {
1768 let _ = tx.send(result);
1769 });
1770 device
1771 .poll(wgpu::PollType::Wait {
1772 submission_index: None,
1773 timeout: Some(std::time::Duration::from_secs(5)),
1774 })
1775 .unwrap();
1776 let _ = rx.recv().unwrap_or(Err(wgpu::BufferAsyncError));
1777
1778 let mut pixels: Vec<u8> = Vec::with_capacity((width * height * 4) as usize);
1779 {
1780 let mapped = staging_buf.slice(..).get_mapped_range();
1781 let data: &[u8] = &mapped;
1782 if padded_row == unpadded_row {
1783 pixels.extend_from_slice(data);
1785 } else {
1786 for row in 0..height as usize {
1788 let start = row * padded_row as usize;
1789 let end = start + unpadded_row as usize;
1790 pixels.extend_from_slice(&data[start..end]);
1791 }
1792 }
1793 }
1794 staging_buf.unmap();
1795
1796 let is_bgra = matches!(
1798 target_format,
1799 wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb
1800 );
1801 if is_bgra {
1802 for pixel in pixels.chunks_exact_mut(4) {
1803 pixel.swap(0, 2); }
1805 }
1806
1807 pixels
1808 }
1809}