allow DeferredPrepass to work without other prepass markers (#10223)

# Objective

fix crash / misbehaviour when `DeferredPrepass` is used without
`DepthPrepass`.

- Deferred lighting requires the depth prepass texture to be present, so
that the depth texture is available for binding. without it the deferred
lighting pass will use 0 for depth of all meshes.
- When `DeferredPrepass` is used without other prepass markers, and with
any materials that use `OpaqueRenderMode::Forward`, those entities will
try to queue to the `Opaque3dPrepass` render phase, which doesn't exist,
causing a crash.

## Solution

- check if the prepass phases exist before queueing
- generate prepass textures if `Opaque3dDeferred` is present
- add a note to the DeferredPrepass marker to note that DepthPrepass is
also required by the default deferred lighting pass
- also changed some `With<T>.is_some()`s to `Has<T>`s
This commit is contained in:
robtfm 2023-11-03 01:09:14 +00:00 committed by GitHub
parent 74b5073f75
commit 5cc3352f5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 33 deletions

View file

@ -450,10 +450,10 @@ pub fn extract_camera_prepass_phase(
( (
Entity, Entity,
&Camera, &Camera,
Option<&DepthPrepass>, Has<DepthPrepass>,
Option<&NormalPrepass>, Has<NormalPrepass>,
Option<&MotionVectorPrepass>, Has<MotionVectorPrepass>,
Option<&DeferredPrepass>, Has<DeferredPrepass>,
), ),
With<Camera3d>, With<Camera3d>,
>, >,
@ -465,33 +465,30 @@ pub fn extract_camera_prepass_phase(
if camera.is_active { if camera.is_active {
let mut entity = commands.get_or_spawn(entity); let mut entity = commands.get_or_spawn(entity);
if depth_prepass.is_some() if depth_prepass || normal_prepass || motion_vector_prepass {
|| normal_prepass.is_some()
|| motion_vector_prepass.is_some()
{
entity.insert(( entity.insert((
RenderPhase::<Opaque3dPrepass>::default(), RenderPhase::<Opaque3dPrepass>::default(),
RenderPhase::<AlphaMask3dPrepass>::default(), RenderPhase::<AlphaMask3dPrepass>::default(),
)); ));
} }
if deferred_prepass.is_some() { if deferred_prepass {
entity.insert(( entity.insert((
RenderPhase::<Opaque3dDeferred>::default(), RenderPhase::<Opaque3dDeferred>::default(),
RenderPhase::<AlphaMask3dDeferred>::default(), RenderPhase::<AlphaMask3dDeferred>::default(),
)); ));
} }
if depth_prepass.is_some() { if depth_prepass {
entity.insert(DepthPrepass); entity.insert(DepthPrepass);
} }
if normal_prepass.is_some() { if normal_prepass {
entity.insert(NormalPrepass); entity.insert(NormalPrepass);
} }
if motion_vector_prepass.is_some() { if motion_vector_prepass {
entity.insert(MotionVectorPrepass); entity.insert(MotionVectorPrepass);
} }
if deferred_prepass.is_some() { if deferred_prepass {
entity.insert(DeferredPrepass); entity.insert(DeferredPrepass);
} }
} }
@ -685,15 +682,17 @@ pub fn prepare_prepass_textures(
( (
Entity, Entity,
&ExtractedCamera, &ExtractedCamera,
Option<&DepthPrepass>, Has<DepthPrepass>,
Option<&NormalPrepass>, Has<NormalPrepass>,
Option<&MotionVectorPrepass>, Has<MotionVectorPrepass>,
Option<&DeferredPrepass>, Has<DeferredPrepass>,
), ),
( Or<(
With<RenderPhase<Opaque3dPrepass>>, With<RenderPhase<Opaque3dPrepass>>,
With<RenderPhase<AlphaMask3dPrepass>>, With<RenderPhase<AlphaMask3dPrepass>>,
), With<RenderPhase<Opaque3dDeferred>>,
With<RenderPhase<AlphaMask3dDeferred>>,
)>,
>, >,
) { ) {
let mut depth_textures = HashMap::default(); let mut depth_textures = HashMap::default();
@ -714,7 +713,7 @@ pub fn prepare_prepass_textures(
height: physical_target_size.y, height: physical_target_size.y,
}; };
let cached_depth_texture = depth_prepass.is_some().then(|| { let cached_depth_texture = depth_prepass.then(|| {
depth_textures depth_textures
.entry(camera.target.clone()) .entry(camera.target.clone())
.or_insert_with(|| { .or_insert_with(|| {
@ -735,7 +734,7 @@ pub fn prepare_prepass_textures(
.clone() .clone()
}); });
let cached_normals_texture = normal_prepass.is_some().then(|| { let cached_normals_texture = normal_prepass.then(|| {
normal_textures normal_textures
.entry(camera.target.clone()) .entry(camera.target.clone())
.or_insert_with(|| { .or_insert_with(|| {
@ -757,7 +756,7 @@ pub fn prepare_prepass_textures(
.clone() .clone()
}); });
let cached_motion_vectors_texture = motion_vector_prepass.is_some().then(|| { let cached_motion_vectors_texture = motion_vector_prepass.then(|| {
motion_vectors_textures motion_vectors_textures
.entry(camera.target.clone()) .entry(camera.target.clone())
.or_insert_with(|| { .or_insert_with(|| {
@ -779,7 +778,7 @@ pub fn prepare_prepass_textures(
.clone() .clone()
}); });
let cached_deferred_texture = deferred_prepass.is_some().then(|| { let cached_deferred_texture = deferred_prepass.then(|| {
deferred_textures deferred_textures
.entry(camera.target.clone()) .entry(camera.target.clone())
.or_insert_with(|| { .or_insert_with(|| {
@ -801,7 +800,7 @@ pub fn prepare_prepass_textures(
.clone() .clone()
}); });
let deferred_lighting_pass_id_texture = deferred_prepass.is_some().then(|| { let deferred_lighting_pass_id_texture = deferred_prepass.then(|| {
deferred_lighting_id_textures deferred_lighting_id_textures
.entry(camera.target.clone()) .entry(camera.target.clone())
.or_insert_with(|| { .or_insert_with(|| {

View file

@ -55,6 +55,7 @@ pub struct NormalPrepass;
pub struct MotionVectorPrepass; pub struct MotionVectorPrepass;
/// If added to a [`crate::prelude::Camera3d`] then deferred materials will be rendered to the deferred gbuffer texture and will be available to subsequent passes. /// If added to a [`crate::prelude::Camera3d`] then deferred materials will be rendered to the deferred gbuffer texture and will be available to subsequent passes.
/// Note the default deferred lighting plugin also requires `DepthPrepass` to work correctly.
#[derive(Component, Default, Reflect)] #[derive(Component, Default, Reflect)]
pub struct DeferredPrepass; pub struct DeferredPrepass;

View file

@ -784,9 +784,6 @@ pub fn queue_prepass_material_meshes<M: Material>(
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
} }
let mut opaque_phase_deferred = opaque_deferred_phase.as_mut();
let mut alpha_mask_phase_deferred = alpha_mask_deferred_phase.as_mut();
let rangefinder = view.rangefinder3d(); let rangefinder = view.rangefinder3d();
for visible_entity in &visible_entities.entities { for visible_entity in &visible_entities.entities {
@ -859,7 +856,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
match alpha_mode { match alpha_mode {
AlphaMode::Opaque => { AlphaMode::Opaque => {
if deferred { if deferred {
opaque_phase_deferred opaque_deferred_phase
.as_mut() .as_mut()
.unwrap() .unwrap()
.add(Opaque3dDeferred { .add(Opaque3dDeferred {
@ -870,8 +867,8 @@ pub fn queue_prepass_material_meshes<M: Material>(
batch_range: 0..1, batch_range: 0..1,
dynamic_offset: None, dynamic_offset: None,
}); });
} else { } else if let Some(opaque_phase) = opaque_phase.as_mut() {
opaque_phase.as_mut().unwrap().add(Opaque3dPrepass { opaque_phase.add(Opaque3dPrepass {
entity: *visible_entity, entity: *visible_entity,
draw_function: opaque_draw_prepass, draw_function: opaque_draw_prepass,
pipeline_id, pipeline_id,
@ -883,7 +880,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
} }
AlphaMode::Mask(_) => { AlphaMode::Mask(_) => {
if deferred { if deferred {
alpha_mask_phase_deferred alpha_mask_deferred_phase
.as_mut() .as_mut()
.unwrap() .unwrap()
.add(AlphaMask3dDeferred { .add(AlphaMask3dDeferred {
@ -894,8 +891,8 @@ pub fn queue_prepass_material_meshes<M: Material>(
batch_range: 0..1, batch_range: 0..1,
dynamic_offset: None, dynamic_offset: None,
}); });
} else { } else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
alpha_mask_phase.as_mut().unwrap().add(AlphaMask3dPrepass { alpha_mask_phase.add(AlphaMask3dPrepass {
entity: *visible_entity, entity: *visible_entity,
draw_function: alpha_mask_draw_prepass, draw_function: alpha_mask_draw_prepass,
pipeline_id, pipeline_id,