bevy_core_pipeline/prepass/
node.rs1use bevy_camera::{MainPassResolutionOverride, Viewport};
2use bevy_ecs::prelude::*;
3use bevy_log::error;
4#[cfg(feature = "trace")]
5use bevy_log::info_span;
6use bevy_render::{
7 camera::ExtractedCamera,
8 diagnostic::RecordDiagnostics,
9 occlusion_culling::OcclusionCulling,
10 render_phase::ViewBinnedRenderPhases,
11 render_resource::{PipelineCache, RenderPassDescriptor, StoreOp},
12 renderer::{RenderContext, ViewQuery},
13 view::{ExtractedView, NoIndirectDrawing, ViewDepthTexture, ViewUniformOffset},
14};
15
16use crate::prepass::background_motion_vectors::{
17 BackgroundMotionVectorsBindGroup, BackgroundMotionVectorsPipelineId,
18};
19
20use super::{
21 AlphaMask3dPrepass, DeferredPrepass, Opaque3dPrepass, PreviousViewUniformOffset,
22 ViewPrepassTextures,
23};
24
25type PrepassViewQueryData = (
27 (
28 &'static ExtractedCamera,
29 &'static ExtractedView,
30 &'static ViewDepthTexture,
31 &'static ViewPrepassTextures,
32 &'static ViewUniformOffset,
33 ),
34 (
35 Option<&'static DeferredPrepass>,
36 Option<&'static BackgroundMotionVectorsPipelineId>,
37 Option<&'static BackgroundMotionVectorsBindGroup>,
38 Option<&'static PreviousViewUniformOffset>,
39 Option<&'static MainPassResolutionOverride>,
40 ),
41 (Has<OcclusionCulling>, Has<NoIndirectDrawing>),
42);
43
44pub fn early_prepass(
45 world: &World,
46 view: ViewQuery<PrepassViewQueryData>,
47 opaque_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
48 alpha_mask_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
49 pipeline_cache: Res<PipelineCache>,
50 mut ctx: RenderContext,
51) {
52 let view_entity = view.entity();
53 let (
54 (camera, extracted_view, view_depth_texture, view_prepass_textures, view_uniform_offset),
55 (
56 deferred_prepass,
57 background_motion_vectors_pipeline,
58 background_motion_vectors_bind_group,
59 view_prev_uniform_offset,
60 resolution_override,
61 ),
62 (_, _),
63 ) = view.into_inner();
64
65 run_prepass_system(
66 world,
67 view_entity,
68 camera,
69 extracted_view,
70 view_depth_texture,
71 view_prepass_textures,
72 view_uniform_offset,
73 deferred_prepass,
74 background_motion_vectors_pipeline,
75 background_motion_vectors_bind_group,
76 view_prev_uniform_offset,
77 resolution_override,
78 &opaque_prepass_phases,
79 &alpha_mask_prepass_phases,
80 &pipeline_cache,
81 &mut ctx,
82 "early prepass",
83 );
84}
85
86pub fn late_prepass(
87 world: &World,
88 view: ViewQuery<PrepassViewQueryData>,
89 opaque_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
90 alpha_mask_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
91 pipeline_cache: Res<PipelineCache>,
92 mut ctx: RenderContext,
93) {
94 let view_entity = view.entity();
95 let (
96 (camera, extracted_view, view_depth_texture, view_prepass_textures, view_uniform_offset),
97 (
98 deferred_prepass,
99 background_motion_vectors_pipeline,
100 background_motion_vectors_bind_group,
101 view_prev_uniform_offset,
102 resolution_override,
103 ),
104 (occlusion_culling, no_indirect_drawing),
105 ) = view.into_inner();
106
107 if !occlusion_culling || no_indirect_drawing {
108 return;
109 }
110
111 run_prepass_system(
112 world,
113 view_entity,
114 camera,
115 extracted_view,
116 view_depth_texture,
117 view_prepass_textures,
118 view_uniform_offset,
119 deferred_prepass,
120 background_motion_vectors_pipeline,
121 background_motion_vectors_bind_group,
122 view_prev_uniform_offset,
123 resolution_override,
124 &opaque_prepass_phases,
125 &alpha_mask_prepass_phases,
126 &pipeline_cache,
127 &mut ctx,
128 "late prepass",
129 );
130}
131
132#[expect(
134 clippy::too_many_arguments,
135 reason = "render system with many view components"
136)]
137fn run_prepass_system(
138 world: &World,
139 view_entity: Entity,
140 camera: &ExtractedCamera,
141 extracted_view: &ExtractedView,
142 view_depth_texture: &ViewDepthTexture,
143 view_prepass_textures: &ViewPrepassTextures,
144 view_uniform_offset: &ViewUniformOffset,
145 deferred_prepass: Option<&DeferredPrepass>,
146 background_motion_vectors_pipeline: Option<&BackgroundMotionVectorsPipelineId>,
147 background_motion_vectors_bind_group: Option<&BackgroundMotionVectorsBindGroup>,
148 view_prev_uniform_offset: Option<&PreviousViewUniformOffset>,
149 resolution_override: Option<&MainPassResolutionOverride>,
150 opaque_prepass_phases: &ViewBinnedRenderPhases<Opaque3dPrepass>,
151 alpha_mask_prepass_phases: &ViewBinnedRenderPhases<AlphaMask3dPrepass>,
152 pipeline_cache: &PipelineCache,
153 ctx: &mut RenderContext,
154 label: &'static str,
155) {
156 let (Some(opaque_prepass_phase), Some(alpha_mask_prepass_phase)) = (
157 opaque_prepass_phases.get(&extracted_view.retained_view_entity),
158 alpha_mask_prepass_phases.get(&extracted_view.retained_view_entity),
159 ) else {
160 return;
161 };
162
163 #[cfg(feature = "trace")]
164 let _prepass_span = info_span!("prepass").entered();
165
166 let diagnostics = ctx.diagnostic_recorder();
167 let diagnostics = diagnostics.as_deref();
168
169 let mut color_attachments = vec![
170 view_prepass_textures
171 .normal
172 .as_ref()
173 .map(|normals_texture| normals_texture.get_attachment()),
174 view_prepass_textures
175 .motion_vectors
176 .as_ref()
177 .map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
178 None,
180 None,
181 ];
182
183 if color_attachments.iter().all(Option::is_none) {
185 color_attachments.clear();
186 }
187
188 let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
189
190 let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
191 label: Some(label),
192 color_attachments: &color_attachments,
193 depth_stencil_attachment,
194 timestamp_writes: None,
195 occlusion_query_set: None,
196 multiview_mask: None,
197 });
198 let pass_span = diagnostics.pass_span(&mut render_pass, label);
199
200 if let Some(viewport) =
201 Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
202 {
203 render_pass.set_camera_viewport(&viewport);
204 }
205
206 if !opaque_prepass_phase.is_empty() {
207 #[cfg(feature = "trace")]
208 let _opaque_prepass_span = info_span!("opaque_prepass").entered();
209 if let Err(err) = opaque_prepass_phase.render(&mut render_pass, world, view_entity) {
210 error!("Error encountered while rendering the opaque prepass phase {err:?}");
211 }
212 }
213
214 if !alpha_mask_prepass_phase.is_empty() {
215 #[cfg(feature = "trace")]
216 let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
217 if let Err(err) = alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity) {
218 error!("Error encountered while rendering the alpha mask prepass phase {err:?}");
219 }
220 }
221
222 if let (
223 Some(background_motion_vectors_pipeline),
224 Some(background_motion_vectors_bind_group),
225 Some(view_prev_uniform_offset),
226 ) = (
227 background_motion_vectors_pipeline,
228 background_motion_vectors_bind_group,
229 view_prev_uniform_offset,
230 ) && let Some(pipeline) =
231 pipeline_cache.get_render_pipeline(background_motion_vectors_pipeline.0)
232 {
233 render_pass.set_render_pipeline(pipeline);
234 render_pass.set_bind_group(
235 0,
236 &background_motion_vectors_bind_group.0,
237 &[view_uniform_offset.offset, view_prev_uniform_offset.offset],
238 );
239 render_pass.draw(0..3, 0..1);
240 }
241
242 pass_span.end(&mut render_pass);
243 drop(render_pass);
244
245 if deferred_prepass.is_none()
246 && let Some(prepass_depth_texture) = &view_prepass_textures.depth
247 {
248 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}