bevy_core_pipeline/prepass/
node.rs

1use bevy_ecs::{prelude::*, query::QueryItem};
2use bevy_render::{
3    camera::ExtractedCamera,
4    diagnostic::RecordDiagnostics,
5    experimental::occlusion_culling::OcclusionCulling,
6    render_graph::{NodeRunError, RenderGraphContext, ViewNode},
7    render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
8    render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
9    renderer::RenderContext,
10    view::{ExtractedView, NoIndirectDrawing, ViewDepthTexture, ViewUniformOffset},
11};
12use tracing::error;
13#[cfg(feature = "trace")]
14use tracing::info_span;
15
16use crate::skybox::prepass::{RenderSkyboxPrepassPipeline, SkyboxPrepassBindGroup};
17
18use super::{
19    AlphaMask3dPrepass, DeferredPrepass, Opaque3dPrepass, PreviousViewUniformOffset,
20    ViewPrepassTextures,
21};
22
23/// The phase of the prepass that draws meshes that were visible last frame.
24///
25/// If occlusion culling isn't in use, this prepass simply draws all meshes.
26///
27/// Like all prepass nodes, this is inserted before the main pass in the render
28/// graph.
29#[derive(Default)]
30pub struct EarlyPrepassNode;
31
32impl ViewNode for EarlyPrepassNode {
33    type ViewQuery = <LatePrepassNode as ViewNode>::ViewQuery;
34
35    fn run<'w>(
36        &self,
37        graph: &mut RenderGraphContext,
38        render_context: &mut RenderContext<'w>,
39        view_query: QueryItem<'w, Self::ViewQuery>,
40        world: &'w World,
41    ) -> Result<(), NodeRunError> {
42        run_prepass(graph, render_context, view_query, world, "early prepass")
43    }
44}
45
46/// The phase of the prepass that runs after occlusion culling against the
47/// meshes that were visible last frame.
48///
49/// If occlusion culling isn't in use, this is a no-op.
50///
51/// Like all prepass nodes, this is inserted before the main pass in the render
52/// graph.
53#[derive(Default)]
54pub struct LatePrepassNode;
55
56impl ViewNode for LatePrepassNode {
57    type ViewQuery = (
58        &'static ExtractedCamera,
59        &'static ExtractedView,
60        &'static ViewDepthTexture,
61        &'static ViewPrepassTextures,
62        &'static ViewUniformOffset,
63        Option<&'static DeferredPrepass>,
64        Option<&'static RenderSkyboxPrepassPipeline>,
65        Option<&'static SkyboxPrepassBindGroup>,
66        Option<&'static PreviousViewUniformOffset>,
67        Has<OcclusionCulling>,
68        Has<NoIndirectDrawing>,
69        Has<DeferredPrepass>,
70    );
71
72    fn run<'w>(
73        &self,
74        graph: &mut RenderGraphContext,
75        render_context: &mut RenderContext<'w>,
76        query: QueryItem<'w, Self::ViewQuery>,
77        world: &'w World,
78    ) -> Result<(), NodeRunError> {
79        // We only need a late prepass if we have occlusion culling and indirect
80        // drawing.
81        let (_, _, _, _, _, _, _, _, _, occlusion_culling, no_indirect_drawing, _) = query;
82        if !occlusion_culling || no_indirect_drawing {
83            return Ok(());
84        }
85
86        run_prepass(graph, render_context, query, world, "late prepass")
87    }
88}
89
90/// Runs a prepass that draws all meshes to the depth buffer, and possibly
91/// normal and motion vector buffers as well.
92///
93/// If occlusion culling isn't in use, and a prepass is enabled, then there's
94/// only one prepass. If occlusion culling is in use, then any prepass is split
95/// into two: an *early* prepass and a *late* prepass. The early prepass draws
96/// what was visible last frame, and the last prepass performs occlusion culling
97/// against a conservative hierarchical Z buffer before drawing unoccluded
98/// meshes.
99fn run_prepass<'w>(
100    graph: &mut RenderGraphContext,
101    render_context: &mut RenderContext<'w>,
102    (
103        camera,
104        extracted_view,
105        view_depth_texture,
106        view_prepass_textures,
107        view_uniform_offset,
108        deferred_prepass,
109        skybox_prepass_pipeline,
110        skybox_prepass_bind_group,
111        view_prev_uniform_offset,
112        _,
113        _,
114        has_deferred,
115    ): QueryItem<'w, <LatePrepassNode as ViewNode>::ViewQuery>,
116    world: &'w World,
117    label: &'static str,
118) -> Result<(), NodeRunError> {
119    // If we're using deferred rendering, there will be a deferred prepass
120    // instead of this one. Just bail out so we don't have to bother looking at
121    // the empty bins.
122    if has_deferred {
123        return Ok(());
124    }
125
126    let (Some(opaque_prepass_phases), Some(alpha_mask_prepass_phases)) = (
127        world.get_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>(),
128        world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>(),
129    ) else {
130        return Ok(());
131    };
132
133    let (Some(opaque_prepass_phase), Some(alpha_mask_prepass_phase)) = (
134        opaque_prepass_phases.get(&extracted_view.retained_view_entity),
135        alpha_mask_prepass_phases.get(&extracted_view.retained_view_entity),
136    ) else {
137        return Ok(());
138    };
139
140    let diagnostics = render_context.diagnostic_recorder();
141
142    let mut color_attachments = vec![
143        view_prepass_textures
144            .normal
145            .as_ref()
146            .map(|normals_texture| normals_texture.get_attachment()),
147        view_prepass_textures
148            .motion_vectors
149            .as_ref()
150            .map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
151        // Use None in place of deferred attachments
152        None,
153        None,
154    ];
155
156    // If all color attachments are none: clear the color attachment list so that no fragment shader is required
157    if color_attachments.iter().all(Option::is_none) {
158        color_attachments.clear();
159    }
160
161    let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
162
163    let view_entity = graph.view_entity();
164    render_context.add_command_buffer_generation_task(move |render_device| {
165        #[cfg(feature = "trace")]
166        let _prepass_span = info_span!("prepass").entered();
167
168        // Command encoder setup
169        let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
170            label: Some("prepass_command_encoder"),
171        });
172
173        // Render pass setup
174        let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
175            label: Some(label),
176            color_attachments: &color_attachments,
177            depth_stencil_attachment,
178            timestamp_writes: None,
179            occlusion_query_set: None,
180        });
181
182        let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
183        let pass_span = diagnostics.pass_span(&mut render_pass, label);
184
185        if let Some(viewport) = camera.viewport.as_ref() {
186            render_pass.set_camera_viewport(viewport);
187        }
188
189        // Opaque draws
190        if !opaque_prepass_phase.is_empty() {
191            #[cfg(feature = "trace")]
192            let _opaque_prepass_span = info_span!("opaque_prepass").entered();
193            if let Err(err) = opaque_prepass_phase.render(&mut render_pass, world, view_entity) {
194                error!("Error encountered while rendering the opaque prepass phase {err:?}");
195            }
196        }
197
198        // Alpha masked draws
199        if !alpha_mask_prepass_phase.is_empty() {
200            #[cfg(feature = "trace")]
201            let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
202            if let Err(err) = alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity)
203            {
204                error!("Error encountered while rendering the alpha mask prepass phase {err:?}");
205            }
206        }
207
208        // Skybox draw using a fullscreen triangle
209        if let (
210            Some(skybox_prepass_pipeline),
211            Some(skybox_prepass_bind_group),
212            Some(view_prev_uniform_offset),
213        ) = (
214            skybox_prepass_pipeline,
215            skybox_prepass_bind_group,
216            view_prev_uniform_offset,
217        ) {
218            let pipeline_cache = world.resource::<PipelineCache>();
219            if let Some(pipeline) = pipeline_cache.get_render_pipeline(skybox_prepass_pipeline.0) {
220                render_pass.set_render_pipeline(pipeline);
221                render_pass.set_bind_group(
222                    0,
223                    &skybox_prepass_bind_group.0,
224                    &[view_uniform_offset.offset, view_prev_uniform_offset.offset],
225                );
226                render_pass.draw(0..3, 0..1);
227            }
228        }
229
230        pass_span.end(&mut render_pass);
231        drop(render_pass);
232
233        // After rendering to the view depth texture, copy it to the prepass depth texture if deferred isn't going to
234        if deferred_prepass.is_none() {
235            if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
236                command_encoder.copy_texture_to_texture(
237                    view_depth_texture.texture.as_image_copy(),
238                    prepass_depth_texture.texture.texture.as_image_copy(),
239                    view_prepass_textures.size,
240                );
241            }
242        }
243
244        command_encoder.finish()
245    });
246
247    Ok(())
248}