bevy_core_pipeline/prepass/
node.rs1use 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#[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#[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 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
98fn 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 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 None,
158 None,
159 ];
160
161 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 let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
175 label: Some("prepass_command_encoder"),
176 });
177
178 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 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 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 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 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}