Skip to content

Add MAY_DISCARD shader def, enabling early depth tests for most cases #6697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ pub fn queue_material_meshes<M: Material>(
AlphaMode::Multiply => {
mesh_key |= MeshPipelineKey::BLEND_MULTIPLY;
}
AlphaMode::Mask(_) => {
mesh_key |= MeshPipelineKey::MAY_DISCARD;
}
_ => (),
}

Expand Down
10 changes: 4 additions & 6 deletions crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,8 @@ where
shader_defs.push("DEPTH_PREPASS".into());
}

if key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK) {
shader_defs.push("ALPHA_MASK".into());
if key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
shader_defs.push("MAY_DISCARD".into());
}

let blend_key = key
Expand Down Expand Up @@ -467,9 +467,7 @@ where
// is enabled or the material uses alpha cutoff values and doesn't rely on the standard
// prepass shader
let fragment_required = !targets.is_empty()
|| ((key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK)
|| blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA
|| blend_key == MeshPipelineKey::BLEND_ALPHA)
|| (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
&& self.material_fragment_shader.is_some());

let fragment = fragment_required.then(|| {
Expand Down Expand Up @@ -967,7 +965,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
let alpha_mode = material.properties.alpha_mode;
match alpha_mode {
AlphaMode::Opaque => {}
AlphaMode::Mask(_) => mesh_key |= MeshPipelineKey::ALPHA_MASK,
AlphaMode::Mask(_) => mesh_key |= MeshPipelineKey::MAY_DISCARD,
AlphaMode::Blend
| AlphaMode::Premultiplied
| AlphaMode::Add
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1608,11 +1608,11 @@ pub fn queue_shadows<M: Material>(
}
let alpha_mode = material.properties.alpha_mode;
match alpha_mode {
AlphaMode::Mask(_) => {
mesh_key |= MeshPipelineKey::ALPHA_MASK;
}
AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add => {
mesh_key |= MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA;
AlphaMode::Mask(_)
| AlphaMode::Blend
| AlphaMode::Premultiplied
| AlphaMode::Add => {
mesh_key |= MeshPipelineKey::MAY_DISCARD;
}
_ => {}
}
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,8 @@ bitflags::bitflags! {
const DEPTH_PREPASS = (1 << 3);
const NORMAL_PREPASS = (1 << 4);
const MOTION_VECTOR_PREPASS = (1 << 5);
const ALPHA_MASK = (1 << 6);
const MAY_DISCARD = (1 << 6); // Guards shader codepaths that may discard, allowing early depth tests in most cases
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
const ENVIRONMENT_MAP = (1 << 7);
const DEPTH_CLAMP_ORTHO = (1 << 8);
const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
Expand Down Expand Up @@ -795,6 +796,10 @@ impl SpecializedMeshPipeline for MeshPipeline {
}
}

if key.contains(MeshPipelineKey::MAY_DISCARD) {
shader_defs.push("MAY_DISCARD".into());
}

if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
shader_defs.push("ENVIRONMENT_MAP".into());
}
Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ fn alpha_discard(material: StandardMaterial, output_color: vec4<f32>) -> vec4<f3
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
// NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
color.a = 1.0;
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
}

#ifdef MAY_DISCARD
else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
if color.a >= material.alpha_cutoff {
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
color.a = 1.0;
} else {
// NOTE: output_color.a < in.material.alpha_cutoff should not is not rendered
// NOTE: This and any other discards mean that early-z testing cannot be done!
// NOTE: output_color.a < in.material.alpha_cutoff should not be rendered
discard;
}
}
#endif

return color;
}

Expand Down
40 changes: 14 additions & 26 deletions crates/bevy_pbr/src/render/pbr_prepass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,7 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
// We can use a simplified version of alpha_discard() here since we only need to handle the alpha_cutoff
fn prepass_alpha_discard(in: FragmentInput) {

// This is a workaround since the preprocessor does not support
// #if defined(ALPHA_MASK) || defined(BLEND_PREMULTIPLIED_ALPHA)
#ifndef ALPHA_MASK
#ifndef BLEND_PREMULTIPLIED_ALPHA
#ifndef BLEND_ALPHA

#define EMPTY_PREPASS_ALPHA_DISCARD

#endif // BLEND_ALPHA
#endif // BLEND_PREMULTIPLIED_ALPHA not defined
#endif // ALPHA_MASK not defined

#ifndef EMPTY_PREPASS_ALPHA_DISCARD
#ifdef MAY_DISCARD
var output_color: vec4<f32> = material.base_color;

#ifdef VERTEX_UVS
Expand All @@ -51,22 +39,22 @@ fn prepass_alpha_discard(in: FragmentInput) {
}
#endif // VERTEX_UVS

#ifdef ALPHA_MASK
if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) && output_color.a < material.alpha_cutoff {
discard;
}
#else // BLEND_PREMULTIPLIED_ALPHA || BLEND_ALPHA
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD)
&& output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
discard;
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED
&& all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
discard;
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
if output_color.a < material.alpha_cutoff {
discard;
}
} else if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) {
if output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
discard;
}
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
discard;
}
}
#endif // !ALPHA_MASK

#endif // EMPTY_PREPASS_ALPHA_DISCARD not defined
#endif // MAY_DISCARD
}

#ifdef PREPASS_FRAGMENT
Expand Down