bevy_core_pipeline/prepass/
node.rs

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