Skip to main content

bevy_core_pipeline/prepass/
node.rs

1use 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
25/// Type alias for the prepass view query.
26type 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/// Shared implementation for prepass systems.
133#[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        // Use None in place of deferred attachments
179        None,
180        None,
181    ];
182
183    // If all color attachments are none: clear the color attachment list so that no fragment shader is required
184    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        // TODO: Copy depth texture fails for WebGL2, https://github.com/bevyengine/bevy/issues/9710
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}