bevy_core_pipeline/prepass/
node.rs

1use bevy_ecs::{prelude::*, query::QueryItem};
2use bevy_render::{
3    camera::ExtractedCamera,
4    diagnostic::RecordDiagnostics,
5    render_graph::{NodeRunError, RenderGraphContext, ViewNode},
6    render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
7    render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
8    renderer::RenderContext,
9    view::{ViewDepthTexture, ViewUniformOffset},
10};
11use bevy_utils::tracing::error;
12#[cfg(feature = "trace")]
13use bevy_utils::tracing::info_span;
14
15use crate::skybox::prepass::{RenderSkyboxPrepassPipeline, SkyboxPrepassBindGroup};
16
17use super::{
18    AlphaMask3dPrepass, DeferredPrepass, Opaque3dPrepass, PreviousViewUniformOffset,
19    ViewPrepassTextures,
20};
21
22/// Render node used by the prepass.
23///
24/// By default, inserted before the main pass in the render graph.
25#[derive(Default)]
26pub struct PrepassNode;
27
28impl ViewNode for PrepassNode {
29    type ViewQuery = (
30        Entity,
31        &'static ExtractedCamera,
32        &'static ViewDepthTexture,
33        &'static ViewPrepassTextures,
34        &'static ViewUniformOffset,
35        Option<&'static DeferredPrepass>,
36        Option<&'static RenderSkyboxPrepassPipeline>,
37        Option<&'static SkyboxPrepassBindGroup>,
38        Option<&'static PreviousViewUniformOffset>,
39    );
40
41    fn run<'w>(
42        &self,
43        graph: &mut RenderGraphContext,
44        render_context: &mut RenderContext<'w>,
45        (
46            view,
47            camera,
48            view_depth_texture,
49            view_prepass_textures,
50            view_uniform_offset,
51            deferred_prepass,
52            skybox_prepass_pipeline,
53            skybox_prepass_bind_group,
54            view_prev_uniform_offset,
55        ): QueryItem<'w, Self::ViewQuery>,
56        world: &'w World,
57    ) -> Result<(), NodeRunError> {
58        let (Some(opaque_prepass_phases), Some(alpha_mask_prepass_phases)) = (
59            world.get_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>(),
60            world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>(),
61        ) else {
62            return Ok(());
63        };
64
65        let (Some(opaque_prepass_phase), Some(alpha_mask_prepass_phase)) = (
66            opaque_prepass_phases.get(&view),
67            alpha_mask_prepass_phases.get(&view),
68        ) else {
69            return Ok(());
70        };
71
72        let diagnostics = render_context.diagnostic_recorder();
73
74        let mut color_attachments = vec![
75            view_prepass_textures
76                .normal
77                .as_ref()
78                .map(|normals_texture| normals_texture.get_attachment()),
79            view_prepass_textures
80                .motion_vectors
81                .as_ref()
82                .map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
83            // Use None in place of deferred attachments
84            None,
85            None,
86        ];
87
88        // If all color attachments are none: clear the color attachment list so that no fragment shader is required
89        if color_attachments.iter().all(Option::is_none) {
90            color_attachments.clear();
91        }
92
93        let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
94
95        let view_entity = graph.view_entity();
96        render_context.add_command_buffer_generation_task(move |render_device| {
97            #[cfg(feature = "trace")]
98            let _prepass_span = info_span!("prepass").entered();
99
100            // Command encoder setup
101            let mut command_encoder =
102                render_device.create_command_encoder(&CommandEncoderDescriptor {
103                    label: Some("prepass_command_encoder"),
104                });
105
106            // Render pass setup
107            let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
108                label: Some("prepass"),
109                color_attachments: &color_attachments,
110                depth_stencil_attachment,
111                timestamp_writes: None,
112                occlusion_query_set: None,
113            });
114
115            let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
116            let pass_span = diagnostics.pass_span(&mut render_pass, "prepass");
117
118            if let Some(viewport) = camera.viewport.as_ref() {
119                render_pass.set_camera_viewport(viewport);
120            }
121
122            // Opaque draws
123            if !opaque_prepass_phase.batchable_mesh_keys.is_empty()
124                || !opaque_prepass_phase.unbatchable_mesh_keys.is_empty()
125            {
126                #[cfg(feature = "trace")]
127                let _opaque_prepass_span = info_span!("opaque_prepass").entered();
128                if let Err(err) = opaque_prepass_phase.render(&mut render_pass, world, view_entity)
129                {
130                    error!("Error encountered while rendering the opaque prepass phase {err:?}");
131                }
132            }
133
134            // Alpha masked draws
135            if !alpha_mask_prepass_phase.is_empty() {
136                #[cfg(feature = "trace")]
137                let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
138                if let Err(err) =
139                    alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity)
140                {
141                    error!(
142                        "Error encountered while rendering the alpha mask prepass phase {err:?}"
143                    );
144                }
145            }
146
147            // Skybox draw using a fullscreen triangle
148            if let (
149                Some(skybox_prepass_pipeline),
150                Some(skybox_prepass_bind_group),
151                Some(view_prev_uniform_offset),
152            ) = (
153                skybox_prepass_pipeline,
154                skybox_prepass_bind_group,
155                view_prev_uniform_offset,
156            ) {
157                let pipeline_cache = world.resource::<PipelineCache>();
158                if let Some(pipeline) =
159                    pipeline_cache.get_render_pipeline(skybox_prepass_pipeline.0)
160                {
161                    render_pass.set_render_pipeline(pipeline);
162                    render_pass.set_bind_group(
163                        0,
164                        &skybox_prepass_bind_group.0,
165                        &[view_uniform_offset.offset, view_prev_uniform_offset.offset],
166                    );
167                    render_pass.draw(0..3, 0..1);
168                }
169            }
170
171            pass_span.end(&mut render_pass);
172            drop(render_pass);
173
174            // After rendering to the view depth texture, copy it to the prepass depth texture if deferred isn't going to
175            if deferred_prepass.is_none() {
176                if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
177                    command_encoder.copy_texture_to_texture(
178                        view_depth_texture.texture.as_image_copy(),
179                        prepass_depth_texture.texture.texture.as_image_copy(),
180                        view_prepass_textures.size,
181                    );
182                }
183            }
184
185            command_encoder.finish()
186        });
187
188        Ok(())
189    }
190}