bevy_core_pipeline/deferred/
node.rs

1use bevy_camera::{MainPassResolutionOverride, Viewport};
2use bevy_ecs::{prelude::*, query::QueryItem};
3use bevy_render::experimental::occlusion_culling::OcclusionCulling;
4use bevy_render::render_graph::ViewNode;
5
6use bevy_render::view::{ExtractedView, NoIndirectDrawing};
7use bevy_render::{
8    camera::ExtractedCamera,
9    diagnostic::RecordDiagnostics,
10    render_graph::{NodeRunError, RenderGraphContext},
11    render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
12    render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
13    renderer::RenderContext,
14    view::ViewDepthTexture,
15};
16use tracing::error;
17#[cfg(feature = "trace")]
18use tracing::info_span;
19
20use crate::prepass::ViewPrepassTextures;
21
22use super::{AlphaMask3dDeferred, Opaque3dDeferred};
23
24/// The phase of the deferred prepass that draws meshes that were visible last
25/// frame.
26///
27/// If occlusion culling isn't in use, this prepass simply draws all meshes.
28///
29/// Like all prepass nodes, this is inserted before the main pass in the render
30/// graph.
31#[derive(Default)]
32pub struct EarlyDeferredGBufferPrepassNode;
33
34impl ViewNode for EarlyDeferredGBufferPrepassNode {
35    type ViewQuery = <LateDeferredGBufferPrepassNode as ViewNode>::ViewQuery;
36
37    fn run<'w>(
38        &self,
39        graph: &mut RenderGraphContext,
40        render_context: &mut RenderContext<'w>,
41        view_query: QueryItem<'w, '_, Self::ViewQuery>,
42        world: &'w World,
43    ) -> Result<(), NodeRunError> {
44        run_deferred_prepass(
45            graph,
46            render_context,
47            view_query,
48            false,
49            world,
50            "early deferred prepass",
51        )
52    }
53}
54
55/// The phase of the prepass that runs after occlusion culling against the
56/// meshes that were visible last frame.
57///
58/// If occlusion culling isn't in use, this is a no-op.
59///
60/// Like all prepass nodes, this is inserted before the main pass in the render
61/// graph.
62#[derive(Default)]
63pub struct LateDeferredGBufferPrepassNode;
64
65impl ViewNode for LateDeferredGBufferPrepassNode {
66    type ViewQuery = (
67        &'static ExtractedCamera,
68        &'static ExtractedView,
69        &'static ViewDepthTexture,
70        &'static ViewPrepassTextures,
71        Option<&'static MainPassResolutionOverride>,
72        Has<OcclusionCulling>,
73        Has<NoIndirectDrawing>,
74    );
75
76    fn run<'w>(
77        &self,
78        graph: &mut RenderGraphContext,
79        render_context: &mut RenderContext<'w>,
80        view_query: QueryItem<'w, '_, Self::ViewQuery>,
81        world: &'w World,
82    ) -> Result<(), NodeRunError> {
83        let (.., occlusion_culling, no_indirect_drawing) = view_query;
84        if !occlusion_culling || no_indirect_drawing {
85            return Ok(());
86        }
87
88        run_deferred_prepass(
89            graph,
90            render_context,
91            view_query,
92            true,
93            world,
94            "late deferred prepass",
95        )
96    }
97}
98
99/// Runs the deferred prepass that draws all meshes to the depth buffer and
100/// G-buffers.
101///
102/// If occlusion culling isn't in use, and a prepass is enabled, then there's
103/// only one prepass. If occlusion culling is in use, then any prepass is split
104/// into two: an *early* prepass and a *late* prepass. The early prepass draws
105/// what was visible last frame, and the last prepass performs occlusion culling
106/// against a conservative hierarchical Z buffer before drawing unoccluded
107/// meshes.
108fn run_deferred_prepass<'w>(
109    graph: &mut RenderGraphContext,
110    render_context: &mut RenderContext<'w>,
111    (camera, extracted_view, view_depth_texture, view_prepass_textures, resolution_override, _, _): QueryItem<
112        'w,
113        '_,
114        <LateDeferredGBufferPrepassNode as ViewNode>::ViewQuery,
115    >,
116    is_late: bool,
117    world: &'w World,
118    label: &'static str,
119) -> Result<(), NodeRunError> {
120    let (Some(opaque_deferred_phases), Some(alpha_mask_deferred_phases)) = (
121        world.get_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>(),
122        world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>(),
123    ) else {
124        return Ok(());
125    };
126
127    let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
128        opaque_deferred_phases.get(&extracted_view.retained_view_entity),
129        alpha_mask_deferred_phases.get(&extracted_view.retained_view_entity),
130    ) else {
131        return Ok(());
132    };
133
134    let diagnostic = render_context.diagnostic_recorder();
135
136    let mut color_attachments = vec![];
137    color_attachments.push(
138        view_prepass_textures
139            .normal
140            .as_ref()
141            .map(|normals_texture| normals_texture.get_attachment()),
142    );
143    color_attachments.push(
144        view_prepass_textures
145            .motion_vectors
146            .as_ref()
147            .map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
148    );
149
150    // If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
151    // Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
152    // Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
153    // Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
154    // For webgl2 we fallback to manually clearing
155    #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
156    if !is_late {
157        if let Some(deferred_texture) = &view_prepass_textures.deferred {
158            render_context.command_encoder().clear_texture(
159                &deferred_texture.texture.texture,
160                &bevy_render::render_resource::ImageSubresourceRange::default(),
161            );
162        }
163    }
164
165    color_attachments.push(
166        view_prepass_textures
167            .deferred
168            .as_ref()
169            .map(|deferred_texture| {
170                if is_late {
171                    deferred_texture.get_attachment()
172                } else {
173                    #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
174                    {
175                        bevy_render::render_resource::RenderPassColorAttachment {
176                            view: &deferred_texture.texture.default_view,
177                            resolve_target: None,
178                            ops: bevy_render::render_resource::Operations {
179                                load: bevy_render::render_resource::LoadOp::Load,
180                                store: StoreOp::Store,
181                            },
182                            depth_slice: None,
183                        }
184                    }
185                    #[cfg(any(
186                        not(feature = "webgl"),
187                        not(target_arch = "wasm32"),
188                        feature = "webgpu"
189                    ))]
190                    deferred_texture.get_attachment()
191                }
192            }),
193    );
194
195    color_attachments.push(
196        view_prepass_textures
197            .deferred_lighting_pass_id
198            .as_ref()
199            .map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
200    );
201
202    // If all color attachments are none: clear the color attachment list so that no fragment shader is required
203    if color_attachments.iter().all(Option::is_none) {
204        color_attachments.clear();
205    }
206
207    let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
208
209    let view_entity = graph.view_entity();
210    render_context.add_command_buffer_generation_task(move |render_device| {
211        #[cfg(feature = "trace")]
212        let _deferred_span = info_span!("deferred_prepass").entered();
213
214        // Command encoder setup
215        let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
216            label: Some("deferred_prepass_command_encoder"),
217        });
218
219        // Render pass setup
220        let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
221            label: Some(label),
222            color_attachments: &color_attachments,
223            depth_stencil_attachment,
224            timestamp_writes: None,
225            occlusion_query_set: None,
226        });
227        let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
228        let pass_span = diagnostic.pass_span(&mut render_pass, label);
229        if let Some(viewport) =
230            Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
231        {
232            render_pass.set_camera_viewport(&viewport);
233        }
234
235        // Opaque draws
236        if !opaque_deferred_phase.multidrawable_meshes.is_empty()
237            || !opaque_deferred_phase.batchable_meshes.is_empty()
238            || !opaque_deferred_phase.unbatchable_meshes.is_empty()
239        {
240            #[cfg(feature = "trace")]
241            let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
242            if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity) {
243                error!("Error encountered while rendering the opaque deferred phase {err:?}");
244            }
245        }
246
247        // Alpha masked draws
248        if !alpha_mask_deferred_phase.is_empty() {
249            #[cfg(feature = "trace")]
250            let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
251            if let Err(err) = alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity)
252            {
253                error!("Error encountered while rendering the alpha mask deferred phase {err:?}");
254            }
255        }
256
257        pass_span.end(&mut render_pass);
258        drop(render_pass);
259
260        // After rendering to the view depth texture, copy it to the prepass depth texture
261        if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
262            command_encoder.copy_texture_to_texture(
263                view_depth_texture.texture.as_image_copy(),
264                prepass_depth_texture.texture.texture.as_image_copy(),
265                view_prepass_textures.size,
266            );
267        }
268
269        command_encoder.finish()
270    });
271
272    Ok(())
273}