bevy_core_pipeline/deferred/
node.rs

1use bevy_ecs::{prelude::*, query::QueryItem};
2use bevy_render::render_graph::ViewNode;
3
4use bevy_render::{
5    camera::ExtractedCamera,
6    render_graph::{NodeRunError, RenderGraphContext},
7    render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
8    render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
9    renderer::RenderContext,
10    view::ViewDepthTexture,
11};
12use bevy_utils::tracing::error;
13#[cfg(feature = "trace")]
14use bevy_utils::tracing::info_span;
15
16use crate::prepass::ViewPrepassTextures;
17
18use super::{AlphaMask3dDeferred, Opaque3dDeferred};
19
20/// Render node used by the prepass.
21///
22/// By default, inserted before the main pass in the render graph.
23#[derive(Default)]
24pub struct DeferredGBufferPrepassNode;
25
26impl ViewNode for DeferredGBufferPrepassNode {
27    type ViewQuery = (
28        Entity,
29        &'static ExtractedCamera,
30        &'static ViewDepthTexture,
31        &'static ViewPrepassTextures,
32    );
33
34    fn run<'w>(
35        &self,
36        graph: &mut RenderGraphContext,
37        render_context: &mut RenderContext<'w>,
38        (view, camera, view_depth_texture, view_prepass_textures): QueryItem<'w, Self::ViewQuery>,
39        world: &'w World,
40    ) -> Result<(), NodeRunError> {
41        let (Some(opaque_deferred_phases), Some(alpha_mask_deferred_phases)) = (
42            world.get_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>(),
43            world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>(),
44        ) else {
45            return Ok(());
46        };
47
48        let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
49            opaque_deferred_phases.get(&view),
50            alpha_mask_deferred_phases.get(&view),
51        ) else {
52            return Ok(());
53        };
54
55        let mut color_attachments = vec![];
56        color_attachments.push(
57            view_prepass_textures
58                .normal
59                .as_ref()
60                .map(|normals_texture| normals_texture.get_attachment()),
61        );
62        color_attachments.push(
63            view_prepass_textures
64                .motion_vectors
65                .as_ref()
66                .map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
67        );
68
69        // If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
70        // Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
71        // Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
72        // Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
73        // For webgl2 we fallback to manually clearing
74        #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
75        if let Some(deferred_texture) = &view_prepass_textures.deferred {
76            render_context.command_encoder().clear_texture(
77                &deferred_texture.texture.texture,
78                &bevy_render::render_resource::ImageSubresourceRange::default(),
79            );
80        }
81
82        color_attachments.push(
83            view_prepass_textures
84                .deferred
85                .as_ref()
86                .map(|deferred_texture| {
87                    #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
88                    {
89                        bevy_render::render_resource::RenderPassColorAttachment {
90                            view: &deferred_texture.texture.default_view,
91                            resolve_target: None,
92                            ops: bevy_render::render_resource::Operations {
93                                load: bevy_render::render_resource::LoadOp::Load,
94                                store: StoreOp::Store,
95                            },
96                        }
97                    }
98                    #[cfg(any(
99                        not(feature = "webgl"),
100                        not(target_arch = "wasm32"),
101                        feature = "webgpu"
102                    ))]
103                    deferred_texture.get_attachment()
104                }),
105        );
106
107        color_attachments.push(
108            view_prepass_textures
109                .deferred_lighting_pass_id
110                .as_ref()
111                .map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
112        );
113
114        // If all color attachments are none: clear the color attachment list so that no fragment shader is required
115        if color_attachments.iter().all(Option::is_none) {
116            color_attachments.clear();
117        }
118
119        let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
120
121        let view_entity = graph.view_entity();
122        render_context.add_command_buffer_generation_task(move |render_device| {
123            #[cfg(feature = "trace")]
124            let _deferred_span = info_span!("deferred_prepass").entered();
125
126            // Command encoder setup
127            let mut command_encoder =
128                render_device.create_command_encoder(&CommandEncoderDescriptor {
129                    label: Some("deferred_prepass_command_encoder"),
130                });
131
132            // Render pass setup
133            let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
134                label: Some("deferred_prepass"),
135                color_attachments: &color_attachments,
136                depth_stencil_attachment,
137                timestamp_writes: None,
138                occlusion_query_set: None,
139            });
140            let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
141            if let Some(viewport) = camera.viewport.as_ref() {
142                render_pass.set_camera_viewport(viewport);
143            }
144
145            // Opaque draws
146            if !opaque_deferred_phase.batchable_mesh_keys.is_empty()
147                || !opaque_deferred_phase.unbatchable_mesh_keys.is_empty()
148            {
149                #[cfg(feature = "trace")]
150                let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
151                if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity)
152                {
153                    error!("Error encountered while rendering the opaque deferred phase {err:?}");
154                }
155            }
156
157            // Alpha masked draws
158            if !alpha_mask_deferred_phase.is_empty() {
159                #[cfg(feature = "trace")]
160                let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
161                if let Err(err) =
162                    alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity)
163                {
164                    error!(
165                        "Error encountered while rendering the alpha mask deferred phase {err:?}"
166                    );
167                }
168            }
169
170            drop(render_pass);
171
172            // After rendering to the view depth texture, copy it to the prepass depth texture
173            if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
174                command_encoder.copy_texture_to_texture(
175                    view_depth_texture.texture.as_image_copy(),
176                    prepass_depth_texture.texture.texture.as_image_copy(),
177                    view_prepass_textures.size,
178                );
179            }
180
181            command_encoder.finish()
182        });
183
184        Ok(())
185    }
186}