From 8e0bdd6516813be332821488c2e2dfbe65bd64fe Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Sat, 19 Nov 2022 23:00:38 -0300 Subject: [PATCH 1/4] Add `MAY_DISCARD` shader def, enabling early depth tests for most cases --- crates/bevy_pbr/src/material.rs | 10 ++++++++-- crates/bevy_pbr/src/render/mesh.rs | 6 ++++++ crates/bevy_pbr/src/render/pbr.wgsl | 5 +++++ crates/bevy_pbr/src/render/pbr_functions.wgsl | 10 +++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 2dc0e0f3ed59e..11c96fd5f2d0a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -384,8 +384,14 @@ pub fn queue_material_meshes( MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) | view_key; let alpha_mode = material.properties.alpha_mode; - if let AlphaMode::Blend = alpha_mode { - mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS; + match alpha_mode { + AlphaMode::Blend => { + mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS; + } + AlphaMode::Mask(_) => { + mesh_key |= MeshPipelineKey::MAY_DISCARD; + } + _ => {} } let pipeline_id = pipelines.specialize( diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 73e7eea7b0c5f..14786386af643 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -519,6 +519,8 @@ bitflags::bitflags! { const HDR = (1 << 1); const TONEMAP_IN_SHADER = (1 << 2); const DEBAND_DITHER = (1 << 3); + const MAY_DISCARD = (1 << 4); // Guards shader codepaths that may discard, allowing early depth tests in most cases + // See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS; const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS; } @@ -644,6 +646,10 @@ impl SpecializedMeshPipeline for MeshPipeline { } } + if key.contains(MeshPipelineKey::MAY_DISCARD) { + shader_defs.push("MAY_DISCARD".to_string()); + } + let format = match key.contains(MeshPipelineKey::HDR) { true => ViewTarget::TEXTURE_FORMAT_HDR, false => TextureFormat::bevy_default(), diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 6f5d94edfbaf3..3a62985e61d38 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -15,6 +15,11 @@ struct FragmentInput { }; @fragment +// NOTE: in the future, if we do depth writes, we'll also need to disable +// `@early_depth_test` for them, via a similar def (e.g. `MAY_DEPTH_WRITE`) +#ifndef MAY_DISCARD +@early_depth_test +#endif fn fragment(in: FragmentInput) -> @location(0) vec4 { var output_color: vec4 = material.base_color; #ifdef VERTEX_COLORS diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 4f4c5495dbaa3..29fb98ba25899 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -10,16 +10,20 @@ fn alpha_discard(material: StandardMaterial, output_color: vec4) -> vec4= 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; } From a8865437dbfd2ae0ac0d1b8e5788b78092d62c86 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Wed, 10 May 2023 21:42:46 -0300 Subject: [PATCH 2/4] Adopt `MAY_DISCARD` in prepass and shadow maps, remove now-redundant `ALPHA_MASK` flag --- crates/bevy_pbr/src/prepass/mod.rs | 10 ++--- crates/bevy_pbr/src/render/light.rs | 10 ++--- crates/bevy_pbr/src/render/mesh.rs | 5 +-- crates/bevy_pbr/src/render/pbr_prepass.wgsl | 50 ++++++++++----------- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 7fd87d4716f6c..9307bc0d75aa9 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -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 @@ -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(|| { @@ -967,7 +965,7 @@ pub fn queue_prepass_material_meshes( 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 diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 410ec24471b7a..85dacdad9be35 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1608,11 +1608,11 @@ pub fn queue_shadows( } 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; } _ => {} } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 65f85a2c635a5..3a3834a8133a9 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -582,11 +582,10 @@ 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 MAY_DISCARD = (1 << 4); // Guards shader codepaths that may discard, allowing early depth tests in most cases - // See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state const BLEND_OPAQUE = (0 << Self::BLEND_SHIFT_BITS); // ← Values are just sequential within the mask, and can range from 0 to 3 const BLEND_PREMULTIPLIED_ALPHA = (1 << Self::BLEND_SHIFT_BITS); // diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index d96a23b845d4b..8d46e9b802444 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -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 = material.base_color; #ifdef VERTEX_UVS @@ -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 @@ -81,6 +69,11 @@ struct FragmentOutput { } @fragment +// NOTE: in the future, if we do depth writes, we'll also need to disable +// `@early_depth_test` for them, via a similar def (e.g. `MAY_DEPTH_WRITE`) +#ifndef MAY_DISCARD +@early_depth_test +#endif fn fragment(in: FragmentInput) -> FragmentOutput { prepass_alpha_discard(in); @@ -132,6 +125,11 @@ fn fragment(in: FragmentInput) -> FragmentOutput { } #else @fragment +// NOTE: in the future, if we do depth writes, we'll also need to disable +// `@early_depth_test` for them, via a similar def (e.g. `MAY_DEPTH_WRITE`) +#ifndef MAY_DISCARD +@early_depth_test +#endif fn fragment(in: FragmentInput) { prepass_alpha_discard(in); } From e9674be73c204b356c5ca83759ee3f265467e5b8 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Wed, 10 May 2023 21:54:33 -0300 Subject: [PATCH 3/4] Remove `@early_depth_test` - It is only currently supported by the GLES3.1+ wgpu backend; - Enabling it would require special backend-specific code; - Using it in the shaders would require adding a `#define` specifically for it; - Only benefit is that it prevents adding accidental unguarded `discard;` statements that may harm performance. --- crates/bevy_pbr/src/render/pbr.wgsl | 5 ----- crates/bevy_pbr/src/render/pbr_prepass.wgsl | 9 --------- 2 files changed, 14 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 662b845ecab33..f058a83ff8b53 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -20,11 +20,6 @@ struct FragmentInput { }; @fragment -// NOTE: in the future, if we do depth writes, we'll also need to disable -// `@early_depth_test` for them, via a similar def (e.g. `MAY_DEPTH_WRITE`) -#ifndef MAY_DISCARD -@early_depth_test -#endif fn fragment(in: FragmentInput) -> @location(0) vec4 { let is_orthographic = view.projection[3].w == 1.0; let V = calculate_view(in.world_position, is_orthographic); diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index 8d46e9b802444..31d25e18e3b77 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -69,11 +69,6 @@ struct FragmentOutput { } @fragment -// NOTE: in the future, if we do depth writes, we'll also need to disable -// `@early_depth_test` for them, via a similar def (e.g. `MAY_DEPTH_WRITE`) -#ifndef MAY_DISCARD -@early_depth_test -#endif fn fragment(in: FragmentInput) -> FragmentOutput { prepass_alpha_discard(in); @@ -125,10 +120,6 @@ fn fragment(in: FragmentInput) -> FragmentOutput { } #else @fragment -// NOTE: in the future, if we do depth writes, we'll also need to disable -// `@early_depth_test` for them, via a similar def (e.g. `MAY_DEPTH_WRITE`) -#ifndef MAY_DISCARD -@early_depth_test #endif fn fragment(in: FragmentInput) { prepass_alpha_discard(in); From 4b5216964b2de01b34579a44eb8d72138dfa055a Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Wed, 10 May 2023 22:09:42 -0300 Subject: [PATCH 4/4] Remove spurious `#endif` left behind accidentally in last commit --- crates/bevy_pbr/src/render/pbr_prepass.wgsl | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index 31d25e18e3b77..1a90c4570934f 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -120,7 +120,6 @@ fn fragment(in: FragmentInput) -> FragmentOutput { } #else @fragment -#endif fn fragment(in: FragmentInput) { prepass_alpha_discard(in); }