bevy_core_pipeline/deferred/
node.rs1use bevy_camera::{MainPassResolutionOverride, Viewport};
2use bevy_ecs::prelude::*;
3use bevy_render::occlusion_culling::OcclusionCulling;
4
5use bevy_log::error;
6#[cfg(feature = "trace")]
7use bevy_log::info_span;
8use bevy_render::view::{ExtractedView, NoIndirectDrawing};
9use bevy_render::{
10 camera::ExtractedCamera,
11 diagnostic::RecordDiagnostics,
12 render_phase::ViewBinnedRenderPhases,
13 render_resource::{RenderPassDescriptor, StoreOp},
14 renderer::{RenderContext, ViewQuery},
15 view::ViewDepthTexture,
16};
17
18use crate::prepass::ViewPrepassTextures;
19
20use super::{AlphaMask3dDeferred, Opaque3dDeferred};
21
22type DeferredPrepassViewQueryData = (
24 &'static ExtractedCamera,
25 &'static ExtractedView,
26 &'static ViewDepthTexture,
27 &'static ViewPrepassTextures,
28 Option<&'static MainPassResolutionOverride>,
29 Has<OcclusionCulling>,
30 Has<NoIndirectDrawing>,
31);
32
33pub(crate) fn early_deferred_prepass(
34 world: &World,
35 view: ViewQuery<DeferredPrepassViewQueryData>,
36 opaque_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
37 alpha_mask_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
38 mut ctx: RenderContext,
39) {
40 let view_entity = view.entity();
41 let (
42 camera,
43 extracted_view,
44 view_depth_texture,
45 view_prepass_textures,
46 resolution_override,
47 _,
48 _,
49 ) = view.into_inner();
50
51 run_deferred_prepass_system(
52 world,
53 view_entity,
54 camera,
55 extracted_view,
56 view_depth_texture,
57 view_prepass_textures,
58 resolution_override,
59 false,
60 &opaque_deferred_phases,
61 &alpha_mask_deferred_phases,
62 &mut ctx,
63 "early deferred prepass",
64 );
65}
66
67pub fn late_deferred_prepass(
68 world: &World,
69 view: ViewQuery<DeferredPrepassViewQueryData>,
70 opaque_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
71 alpha_mask_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
72 mut ctx: RenderContext,
73) {
74 let view_entity = view.entity();
75 let (
76 camera,
77 extracted_view,
78 view_depth_texture,
79 view_prepass_textures,
80 resolution_override,
81 occlusion_culling,
82 no_indirect_drawing,
83 ) = view.into_inner();
84
85 if !occlusion_culling || no_indirect_drawing {
86 return;
87 }
88
89 run_deferred_prepass_system(
90 world,
91 view_entity,
92 camera,
93 extracted_view,
94 view_depth_texture,
95 view_prepass_textures,
96 resolution_override,
97 true,
98 &opaque_deferred_phases,
99 &alpha_mask_deferred_phases,
100 &mut ctx,
101 "late deferred prepass",
102 );
103}
104
105#[expect(
106 clippy::too_many_arguments,
107 reason = "render system with many view components"
108)]
109fn run_deferred_prepass_system(
110 world: &World,
111 view_entity: Entity,
112 camera: &ExtractedCamera,
113 extracted_view: &ExtractedView,
114 view_depth_texture: &ViewDepthTexture,
115 view_prepass_textures: &ViewPrepassTextures,
116 resolution_override: Option<&MainPassResolutionOverride>,
117 is_late: bool,
118 opaque_deferred_phases: &ViewBinnedRenderPhases<Opaque3dDeferred>,
119 alpha_mask_deferred_phases: &ViewBinnedRenderPhases<AlphaMask3dDeferred>,
120 ctx: &mut RenderContext,
121 label: &'static str,
122) {
123 let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
124 opaque_deferred_phases.get(&extracted_view.retained_view_entity),
125 alpha_mask_deferred_phases.get(&extracted_view.retained_view_entity),
126 ) else {
127 return;
128 };
129
130 #[cfg(feature = "trace")]
131 let _deferred_span = info_span!("deferred_prepass").entered();
132
133 let diagnostics = ctx.diagnostic_recorder();
134 let diagnostics = diagnostics.as_deref();
135
136 let mut color_attachments = vec![];
137 color_attachments.push(
138 view_prepass_textures
139 .normal
140 .as_ref()
141 .map(|normals_texture| normals_texture.get_attachment()),
142 );
143 color_attachments.push(
144 view_prepass_textures
145 .motion_vectors
146 .as_ref()
147 .map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
148 );
149
150 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
156 if !is_late {
157 if let Some(deferred_texture) = &view_prepass_textures.deferred {
158 ctx.command_encoder().clear_texture(
159 &deferred_texture.texture.texture,
160 &bevy_render::render_resource::ImageSubresourceRange::default(),
161 );
162 }
163 }
164
165 color_attachments.push(
166 view_prepass_textures
167 .deferred
168 .as_ref()
169 .map(|deferred_texture| {
170 if is_late {
171 deferred_texture.get_attachment()
172 } else {
173 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
174 {
175 bevy_render::render_resource::RenderPassColorAttachment {
176 view: &deferred_texture.texture.default_view,
177 resolve_target: None,
178 ops: bevy_render::render_resource::Operations {
179 load: bevy_render::render_resource::LoadOp::Load,
180 store: StoreOp::Store,
181 },
182 depth_slice: None,
183 }
184 }
185 #[cfg(any(
186 not(feature = "webgl"),
187 not(target_arch = "wasm32"),
188 feature = "webgpu"
189 ))]
190 deferred_texture.get_attachment()
191 }
192 }),
193 );
194
195 color_attachments.push(
196 view_prepass_textures
197 .deferred_lighting_pass_id
198 .as_ref()
199 .map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
200 );
201
202 if color_attachments.iter().all(Option::is_none) {
204 color_attachments.clear();
205 }
206
207 let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
208
209 let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
210 label: Some(label),
211 color_attachments: &color_attachments,
212 depth_stencil_attachment,
213 timestamp_writes: None,
214 occlusion_query_set: None,
215 multiview_mask: None,
216 });
217 let pass_span = diagnostics.pass_span(&mut render_pass, label);
218
219 if let Some(viewport) =
220 Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
221 {
222 render_pass.set_camera_viewport(&viewport);
223 }
224
225 if !opaque_deferred_phase.multidrawable_meshes.is_empty()
226 || !opaque_deferred_phase.batchable_meshes.is_empty()
227 || !opaque_deferred_phase.unbatchable_meshes.is_empty()
228 {
229 #[cfg(feature = "trace")]
230 let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
231 if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity) {
232 error!("Error encountered while rendering the opaque deferred phase {err:?}");
233 }
234 }
235
236 if !alpha_mask_deferred_phase.is_empty() {
237 #[cfg(feature = "trace")]
238 let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
239 if let Err(err) = alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity) {
240 error!("Error encountered while rendering the alpha mask deferred phase {err:?}");
241 }
242 }
243
244 pass_span.end(&mut render_pass);
245 drop(render_pass);
246
247 if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
249 ctx.command_encoder().copy_texture_to_texture(
250 view_depth_texture.texture.as_image_copy(),
251 prepass_depth_texture.texture.texture.as_image_copy(),
252 view_prepass_textures.size,
253 );
254 }
255}