bevy_pbr/transmission/
node.rs1use crate::{ScreenSpaceTransmission, Transmissive3d, ViewTransmissionTexture};
2
3use bevy_camera::{MainPassResolutionOverride, Viewport};
4use bevy_ecs::prelude::*;
5use bevy_image::ToExtents;
6use bevy_render::{
7 camera::ExtractedCamera,
8 diagnostic::RecordDiagnostics,
9 render_phase::ViewSortedRenderPhases,
10 render_resource::{RenderPassDescriptor, StoreOp},
11 renderer::{RenderContext, ViewQuery},
12 view::{ExtractedView, ViewDepthTexture, ViewTarget},
13};
14use core::ops::Range;
15use tracing::error;
16#[cfg(feature = "trace")]
17use tracing::info_span;
18
19pub fn main_transmissive_pass_3d(
20 world: &World,
21 view: ViewQuery<(
22 &ExtractedCamera,
23 &ExtractedView,
24 &ScreenSpaceTransmission,
25 &ViewTarget,
26 Option<&ViewTransmissionTexture>,
27 &ViewDepthTexture,
28 Option<&MainPassResolutionOverride>,
29 )>,
30 transmissive_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
31 mut ctx: RenderContext,
32) {
33 let view_entity = view.entity();
34
35 let (
36 camera,
37 extracted_view,
38 transmission_settings,
39 target,
40 transmission,
41 depth,
42 resolution_override,
43 ) = view.into_inner();
44
45 let Some(transmissive_phase) = transmissive_phases.get(&extracted_view.retained_view_entity)
46 else {
47 return;
48 };
49
50 let Some(physical_target_size) = camera.physical_target_size else {
51 return;
52 };
53
54 #[cfg(feature = "trace")]
55 let _main_transmissive_pass_3d_span = info_span!("main_transmissive_pass_3d").entered();
56
57 let diagnostics = ctx.diagnostic_recorder();
58 let diagnostics = diagnostics.as_deref();
59
60 let render_pass_descriptor = RenderPassDescriptor {
61 label: Some("main_transmissive_pass_3d"),
62 color_attachments: &[Some(target.get_color_attachment())],
63 depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
64 timestamp_writes: None,
65 occlusion_query_set: None,
66 multiview_mask: None,
67 };
68
69 if !transmissive_phase.items.is_empty() {
70 let steps = transmission_settings.steps;
71 if steps > 0 {
72 let transmission =
73 transmission.expect("`ViewTransmissionTexture` should exist at this point");
74
75 for range in split_range(0..transmissive_phase.items.len(), steps) {
78 ctx.command_encoder().copy_texture_to_texture(
80 target.main_texture().as_image_copy(),
81 transmission.texture.as_image_copy(),
82 physical_target_size.to_extents(),
83 );
84
85 let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor.clone());
86 let pass_span =
87 diagnostics.pass_span(&mut render_pass, "main_transmissive_pass_3d");
88
89 if let Some(viewport) = camera.viewport.as_ref() {
90 render_pass.set_camera_viewport(viewport);
91 }
92
93 if let Err(err) =
94 transmissive_phase.render_range(&mut render_pass, world, view_entity, range)
95 {
96 error!("Error encountered while rendering the transmissive phase {err:?}");
97 }
98
99 pass_span.end(&mut render_pass);
100 }
101 } else {
102 let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor);
103 let pass_span = diagnostics.pass_span(&mut render_pass, "main_transmissive_pass_3d");
104
105 if let Some(viewport) =
106 Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
107 {
108 render_pass.set_camera_viewport(&viewport);
109 }
110
111 if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {
112 error!("Error encountered while rendering the transmissive phase {err:?}");
113 }
114
115 pass_span.end(&mut render_pass);
116 }
117 }
118}
119
120fn split_range(range: Range<usize>, max_num_splits: usize) -> impl Iterator<Item = Range<usize>> {
125 let len = range.end - range.start;
126 assert!(len > 0, "to be split, a range must not be empty");
127 assert!(max_num_splits > 0, "max_num_splits must be at least 1");
128 let num_splits = max_num_splits.min(len);
129 let step = len / num_splits;
130 let mut rem = len % num_splits;
131 let mut start = range.start;
132
133 (0..num_splits).map(move |_| {
134 let extra = if rem > 0 {
135 rem -= 1;
136 1
137 } else {
138 0
139 };
140 let end = (start + step + extra).min(range.end);
141 let result = start..end;
142 start = end;
143 result
144 })
145}