*_PREPASS Shader Def Cleanup (#10136)

# Objective

- This PR aims to make the various `*_PREPASS` shader defs we have
(`NORMAL_PREPASS`, `DEPTH_PREPASS`, `MOTION_VECTORS_PREPASS` AND
`DEFERRED_PREPASS`) easier to use and understand:
- So that their meaning is now consistent across all contexts; (“prepass
X is enabled for the current view”)
  - So that they're also consistently set across all contexts.
- It also aims to enable us to (with a follow up PR) to conditionally
gate the `BindGroupEntry` and `BindGroupLayoutEntry` items associated
with these prepasses, saving us up to 4 texture slots in WebGL
(currently globally limited to 16 per shader, regardless of bind groups)

## Solution

- We now consistently set these from `PrepassPipeline`, the
`MeshPipeline` and the `DeferredLightingPipeline`, we also set their
`MeshPipelineKey`s;
- We introduce `PREPASS_PIPELINE`, `MESH_PIPELINE` and
`DEFERRED_LIGHTING_PIPELINE` that can be used to detect where the code
is running, without overloading the meanings of the prepass shader defs;
- We also gate the WGSL functions in `bevy_pbr::prepass_utils` with
`#ifdef`s for their respective shader defs, so that shader code can
provide a fallback whenever they're not available.
- This allows us to conditionally include the bindings for these prepass
textures (My next PR, which will hopefully unblock #8015)
- @robtfm mentioned [these were being used to prevent accessing the same
binding as read/write in the
prepass](https://discord.com/channels/691052431525675048/743663924229963868/1163270458393759814),
however even after reversing the `#ifndef`s I had no issues running the
code, so perhaps the compiler is already smart enough even without tree
shaking to know they're not being used, thanks to `#ifdef
PREPASS_PIPELINE`?

## Comparison

### Before

| Shader Def | `PrepassPipeline` | `MeshPipeline` |
`DeferredLightingPipeline` |
| ------------------------ | ----------------- | -------------- |
-------------------------- |
| `NORMAL_PREPASS` | Yes | No | No |
| `DEPTH_PREPASS` | Yes | No | No |
| `MOTION_VECTORS_PREPASS` | Yes | No | No |
| `DEFERRED_PREPASS` | Yes | No | No |

| View Key | `PrepassPipeline` | `MeshPipeline` |
`DeferredLightingPipeline` |
| ------------------------ | ----------------- | -------------- |
-------------------------- |
| `NORMAL_PREPASS` | Yes | Yes | No |
| `DEPTH_PREPASS` | Yes | No | No |
| `MOTION_VECTORS_PREPASS` | Yes | No | No |
| `DEFERRED_PREPASS` | Yes | Yes\* | No |

\* Accidentally was being set twice, once with only
`deferred_prepass.is_some()` as a condition,
and once with `deferred_p repass.is_some() && !forward` as a condition.

### After

| Shader Def | `PrepassPipeline` | `MeshPipeline` |
`DeferredLightingPipeline` |
| ---------------------------- | ----------------- | --------------- |
-------------------------- |
| `NORMAL_PREPASS` | Yes | Yes | Yes |
| `DEPTH_PREPASS` | Yes | Yes | Yes |
| `MOTION_VECTORS_PREPASS` | Yes | Yes | Yes |
| `DEFERRED_PREPASS` | Yes | Yes | Unconditionally |
| `PREPASS_PIPELINE` | Unconditionally | No | No |
| `MESH_PIPELINE` | No | Unconditionally | No |
| `DEFERRED_LIGHTING_PIPELINE` | No | No | Unconditionally |

| View Key | `PrepassPipeline` | `MeshPipeline` |
`DeferredLightingPipeline` |
| ------------------------ | ----------------- | -------------- |
-------------------------- |
| `NORMAL_PREPASS` | Yes | Yes | Yes |
| `DEPTH_PREPASS` | Yes | Yes | Yes |
| `MOTION_VECTORS_PREPASS` | Yes | Yes | Yes |
| `DEFERRED_PREPASS` | Yes | Yes | Unconditionally |

---

## Changelog

- Cleaned up WGSL `*_PREPASS` shader defs so they're now consistently
used everywhere;
- Introduced `PREPASS_PIPELINE`, `MESH_PIPELINE` and
`DEFERRED_LIGHTING_PIPELINE` WGSL shader defs for conditionally
compiling logic based the current pipeline;
- WGSL functions from `bevy_pbr::prepass_utils` are now guarded with
`#ifdef` based on the currently enabled prepasses;

## Migration Guide

- When using functions from `bevy_pbr::prepass_utils`
(`prepass_depth()`, `prepass_normal()`, `prepass_motion_vector()`) in
contexts where these prepasses might be disabled, you should now wrap
your calls with the appropriate `#ifdef` guards, (`#ifdef
DEPTH_PREPASS`, `#ifdef NORMAL_PREPASS`, `#ifdef MOTION_VECTOR_PREPASS`)
providing fallback logic where applicable.

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
This commit is contained in:
Marco Buono 2023-10-16 21:16:21 -03:00 committed by GitHub
parent 01b910a148
commit 5733d2403e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 23 deletions

View File

@ -50,7 +50,9 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
#ifdef WEBGL2 #ifdef WEBGL2
frag_coord.z = deferred_types::unpack_unorm3x4_plus_unorm_20_(deferred_data.b).w; frag_coord.z = deferred_types::unpack_unorm3x4_plus_unorm_20_(deferred_data.b).w;
#else #else
#ifdef DEPTH_PREPASS
frag_coord.z = bevy_pbr::prepass_utils::prepass_depth(in.position, 0u); frag_coord.z = bevy_pbr::prepass_utils::prepass_depth(in.position, 0u);
#endif
#endif #endif
var pbr_input = pbr_input_from_deferred_gbuffer(frag_coord, deferred_data); var pbr_input = pbr_input_from_deferred_gbuffer(frag_coord, deferred_data);

View File

@ -8,7 +8,7 @@ use bevy_core_pipeline::{
copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT, copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
}, },
prelude::{Camera3d, ClearColor}, prelude::{Camera3d, ClearColor},
prepass::DeferredPrepass, prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
tonemapping::{DebandDither, Tonemapping}, tonemapping::{DebandDither, Tonemapping},
}; };
use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_ecs::{prelude::*, query::QueryItem};
@ -258,6 +258,9 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mut shader_defs = Vec::new(); let mut shader_defs = Vec::new();
// Let the shader code know that it's running in a deferred pipeline.
shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into());
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32"))]
shader_defs.push("WEBGL2".into()); shader_defs.push("WEBGL2".into());
@ -298,6 +301,21 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
shader_defs.push("ENVIRONMENT_MAP".into()); shader_defs.push("ENVIRONMENT_MAP".into());
} }
if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
shader_defs.push("NORMAL_PREPASS".into());
}
if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
shader_defs.push("DEPTH_PREPASS".into());
}
if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
shader_defs.push("MOTION_VECTOR_PREPASS".into());
}
// Always true, since we're in the deferred lighting pipeline
shader_defs.push("DEFERRED_PREPASS".into());
let shadow_filter_method = let shadow_filter_method =
key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS); key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 { if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
@ -408,14 +426,44 @@ pub fn prepare_deferred_lighting_pipelines(
Option<&EnvironmentMapLight>, Option<&EnvironmentMapLight>,
Option<&ShadowFilteringMethod>, Option<&ShadowFilteringMethod>,
Option<&ScreenSpaceAmbientOcclusionSettings>, Option<&ScreenSpaceAmbientOcclusionSettings>,
(
Has<NormalPrepass>,
Has<DepthPrepass>,
Has<MotionVectorPrepass>,
),
), ),
With<DeferredPrepass>, With<DeferredPrepass>,
>, >,
images: Res<RenderAssets<Image>>, images: Res<RenderAssets<Image>>,
) { ) {
for (entity, view, tonemapping, dither, environment_map, shadow_filter_method, ssao) in &views { for (
entity,
view,
tonemapping,
dither,
environment_map,
shadow_filter_method,
ssao,
(normal_prepass, depth_prepass, motion_vector_prepass),
) in &views
{
let mut view_key = MeshPipelineKey::from_hdr(view.hdr); let mut view_key = MeshPipelineKey::from_hdr(view.hdr);
if normal_prepass {
view_key |= MeshPipelineKey::NORMAL_PREPASS;
}
if depth_prepass {
view_key |= MeshPipelineKey::DEPTH_PREPASS;
}
if motion_vector_prepass {
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}
// Always true, since we're in the deferred lighting pipeline
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
if !view.hdr { if !view.hdr {
if let Some(tonemapping) = tonemapping { if let Some(tonemapping) = tonemapping {
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER; view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;

View File

@ -8,7 +8,7 @@ use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Hand
use bevy_core_pipeline::{ use bevy_core_pipeline::{
core_3d::{AlphaMask3d, Opaque3d, Transparent3d}, core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
experimental::taa::TemporalAntiAliasSettings, experimental::taa::TemporalAntiAliasSettings,
prepass::{DeferredPrepass, NormalPrepass}, prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
tonemapping::{DebandDither, Tonemapping}, tonemapping::{DebandDither, Tonemapping},
}; };
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
@ -450,8 +450,12 @@ pub fn queue_material_meshes<M: Material>(
Option<&EnvironmentMapLight>, Option<&EnvironmentMapLight>,
Option<&ShadowFilteringMethod>, Option<&ShadowFilteringMethod>,
Option<&ScreenSpaceAmbientOcclusionSettings>, Option<&ScreenSpaceAmbientOcclusionSettings>,
Option<&NormalPrepass>, (
Option<&DeferredPrepass>, Has<NormalPrepass>,
Has<DepthPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
),
Option<&TemporalAntiAliasSettings>, Option<&TemporalAntiAliasSettings>,
&mut RenderPhase<Opaque3d>, &mut RenderPhase<Opaque3d>,
&mut RenderPhase<AlphaMask3d>, &mut RenderPhase<AlphaMask3d>,
@ -468,8 +472,7 @@ pub fn queue_material_meshes<M: Material>(
environment_map, environment_map,
shadow_filter_method, shadow_filter_method,
ssao, ssao,
normal_prepass, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
deferred_prepass,
taa_settings, taa_settings,
mut opaque_phase, mut opaque_phase,
mut alpha_mask_phase, mut alpha_mask_phase,
@ -483,11 +486,19 @@ pub fn queue_material_meshes<M: Material>(
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()) let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr); | MeshPipelineKey::from_hdr(view.hdr);
if normal_prepass.is_some() { if normal_prepass {
view_key |= MeshPipelineKey::NORMAL_PREPASS; view_key |= MeshPipelineKey::NORMAL_PREPASS;
} }
if deferred_prepass.is_some() { if depth_prepass {
view_key |= MeshPipelineKey::DEPTH_PREPASS;
}
if motion_vector_prepass {
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}
if deferred_prepass {
view_key |= MeshPipelineKey::DEFERRED_PREPASS; view_key |= MeshPipelineKey::DEFERRED_PREPASS;
} }
@ -554,10 +565,6 @@ pub fn queue_material_meshes<M: Material>(
} }
mesh_key |= alpha_mode_pipeline_key(material.properties.alpha_mode); mesh_key |= alpha_mode_pipeline_key(material.properties.alpha_mode);
if deferred_prepass.is_some() && !forward {
mesh_key |= MeshPipelineKey::DEFERRED_PREPASS;
}
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&pipeline_cache, &pipeline_cache,
&material_pipeline, &material_pipeline,

View File

@ -378,6 +378,11 @@ where
let mut shader_defs = Vec::new(); let mut shader_defs = Vec::new();
let mut vertex_attributes = Vec::new(); let mut vertex_attributes = Vec::new();
// Let the shader code know that it's running in a prepass pipeline.
// (PBR code will use this to detect that it's running in deferred mode,
// since that's the only time it gets called from a prepass pipeline.)
shader_defs.push("PREPASS_PIPELINE".into());
// NOTE: Eventually, it would be nice to only add this when the shaders are overloaded by the Material. // NOTE: Eventually, it would be nice to only add this when the shaders are overloaded by the Material.
// The main limitation right now is that bind group order is hardcoded in shaders. // The main limitation right now is that bind group order is hardcoded in shaders.
bind_group_layouts.insert(1, self.material_layout.clone()); bind_group_layouts.insert(1, self.material_layout.clone());

View File

@ -2,7 +2,7 @@
#import bevy_pbr::mesh_view_bindings as view_bindings #import bevy_pbr::mesh_view_bindings as view_bindings
#ifndef DEPTH_PREPASS #ifdef DEPTH_PREPASS
fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 { fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
let depth_sample = textureLoad(view_bindings::depth_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index)); let depth_sample = textureLoad(view_bindings::depth_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
@ -13,7 +13,7 @@ fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
} }
#endif // DEPTH_PREPASS #endif // DEPTH_PREPASS
#ifndef NORMAL_PREPASS #ifdef NORMAL_PREPASS
fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> { fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index)); let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
@ -24,7 +24,7 @@ fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
} }
#endif // NORMAL_PREPASS #endif // NORMAL_PREPASS
#ifndef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> { fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> {
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index)); let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));

View File

@ -772,6 +772,9 @@ impl SpecializedMeshPipeline for MeshPipeline {
let mut shader_defs = Vec::new(); let mut shader_defs = Vec::new();
let mut vertex_attributes = Vec::new(); let mut vertex_attributes = Vec::new();
// Let the shader code know that it's running in a mesh pipeline.
shader_defs.push("MESH_PIPELINE".into());
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into()); shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
if layout.contains(Mesh::ATTRIBUTE_POSITION) { if layout.contains(Mesh::ATTRIBUTE_POSITION) {
@ -870,6 +873,22 @@ impl SpecializedMeshPipeline for MeshPipeline {
is_opaque = true; is_opaque = true;
} }
if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
shader_defs.push("NORMAL_PREPASS".into());
}
if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
shader_defs.push("DEPTH_PREPASS".into());
}
if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
shader_defs.push("MOTION_VECTOR_PREPASS".into());
}
if key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
shader_defs.push("DEFERRED_PREPASS".into());
}
if key.contains(MeshPipelineKey::NORMAL_PREPASS) && key.msaa_samples() == 1 && is_opaque { if key.contains(MeshPipelineKey::NORMAL_PREPASS) && key.msaa_samples() == 1 && is_opaque {
shader_defs.push("LOAD_PREPASS_NORMALS".into()); shader_defs.push("LOAD_PREPASS_NORMALS".into());
} }

View File

@ -16,13 +16,13 @@
#import bevy_pbr::gtao_utils gtao_multibounce #import bevy_pbr::gtao_utils gtao_multibounce
#endif #endif
#ifdef DEFERRED_PREPASS #ifdef PREPASS_PIPELINE
#import bevy_pbr::pbr_deferred_functions deferred_gbuffer_from_pbr_input #import bevy_pbr::pbr_deferred_functions deferred_gbuffer_from_pbr_input
#import bevy_pbr::pbr_prepass_functions calculate_motion_vector #import bevy_pbr::pbr_prepass_functions calculate_motion_vector
#import bevy_pbr::prepass_io VertexOutput, FragmentOutput #import bevy_pbr::prepass_io VertexOutput, FragmentOutput
#else // DEFERRED_PREPASS #else // PREPASS_PIPELINE
#import bevy_pbr::forward_io VertexOutput, FragmentOutput #import bevy_pbr::forward_io VertexOutput, FragmentOutput
#endif // DEFERRED_PREPASS #endif // PREPASS_PIPELINE
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
@group(0) @binding(2) @group(0) @binding(2)
@ -159,7 +159,7 @@ fn fragment(
pbr_input.V = V; pbr_input.V = V;
} else { // if UNLIT_BIT != 0 } else { // if UNLIT_BIT != 0
#ifdef DEFERRED_PREPASS #ifdef PREPASS_PIPELINE
// in deferred mode, we need to fill some of the pbr input data even for unlit materials // in deferred mode, we need to fill some of the pbr input data even for unlit materials
// to pass through the gbuffer to the deferred lighting shader // to pass through the gbuffer to the deferred lighting shader
pbr_input = pbr_types::pbr_input_new(); pbr_input = pbr_types::pbr_input_new();
@ -179,7 +179,7 @@ fn fragment(
// generate output // generate output
// --------------- // ---------------
#ifdef DEFERRED_PREPASS #ifdef PREPASS_PIPELINE
// write the gbuffer // write the gbuffer
out.deferred = deferred_gbuffer_from_pbr_input(pbr_input); out.deferred = deferred_gbuffer_from_pbr_input(pbr_input);
out.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; out.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id;
@ -190,7 +190,7 @@ fn fragment(
out.motion_vector = calculate_motion_vector(in.world_position, in.previous_world_position); out.motion_vector = calculate_motion_vector(in.world_position, in.previous_world_position);
#endif // MOTION_VECTOR_PREPASS #endif // MOTION_VECTOR_PREPASS
#else // DEFERRED_PREPASS #else // PREPASS_PIPELINE
// in forward mode, we calculate the lit color immediately, and then apply some post-lighting effects here. // in forward mode, we calculate the lit color immediately, and then apply some post-lighting effects here.
// in deferred mode the lit color and these effects will be calculated in the deferred lighting shader // in deferred mode the lit color and these effects will be calculated in the deferred lighting shader
@ -222,7 +222,7 @@ fn fragment(
// write the final pixel color // write the final pixel color
out.color = output_color; out.color = output_color;
#endif //DEFERRED_PREPASS #endif // PREPASS_PIPELINE
return out; return out;
} }