mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
*_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:
parent
01b910a148
commit
5733d2403e
7 changed files with 104 additions and 23 deletions
|
@ -50,7 +50,9 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
|||
#ifdef WEBGL2
|
||||
frag_coord.z = deferred_types::unpack_unorm3x4_plus_unorm_20_(deferred_data.b).w;
|
||||
#else
|
||||
#ifdef DEPTH_PREPASS
|
||||
frag_coord.z = bevy_pbr::prepass_utils::prepass_depth(in.position, 0u);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
var pbr_input = pbr_input_from_deferred_gbuffer(frag_coord, deferred_data);
|
||||
|
|
|
@ -8,7 +8,7 @@ use bevy_core_pipeline::{
|
|||
copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
|
||||
},
|
||||
prelude::{Camera3d, ClearColor},
|
||||
prepass::DeferredPrepass,
|
||||
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
||||
tonemapping::{DebandDither, Tonemapping},
|
||||
};
|
||||
use bevy_ecs::{prelude::*, query::QueryItem};
|
||||
|
@ -258,6 +258,9 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
|
|||
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
||||
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"))]
|
||||
shader_defs.push("WEBGL2".into());
|
||||
|
||||
|
@ -298,6 +301,21 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
|
|||
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 =
|
||||
key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
|
||||
if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
|
||||
|
@ -408,14 +426,44 @@ pub fn prepare_deferred_lighting_pipelines(
|
|||
Option<&EnvironmentMapLight>,
|
||||
Option<&ShadowFilteringMethod>,
|
||||
Option<&ScreenSpaceAmbientOcclusionSettings>,
|
||||
(
|
||||
Has<NormalPrepass>,
|
||||
Has<DepthPrepass>,
|
||||
Has<MotionVectorPrepass>,
|
||||
),
|
||||
),
|
||||
With<DeferredPrepass>,
|
||||
>,
|
||||
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);
|
||||
|
||||
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 let Some(tonemapping) = tonemapping {
|
||||
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
|
||||
|
|
|
@ -8,7 +8,7 @@ use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Hand
|
|||
use bevy_core_pipeline::{
|
||||
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
|
||||
experimental::taa::TemporalAntiAliasSettings,
|
||||
prepass::{DeferredPrepass, NormalPrepass},
|
||||
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
||||
tonemapping::{DebandDither, Tonemapping},
|
||||
};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
|
@ -450,8 +450,12 @@ pub fn queue_material_meshes<M: Material>(
|
|||
Option<&EnvironmentMapLight>,
|
||||
Option<&ShadowFilteringMethod>,
|
||||
Option<&ScreenSpaceAmbientOcclusionSettings>,
|
||||
Option<&NormalPrepass>,
|
||||
Option<&DeferredPrepass>,
|
||||
(
|
||||
Has<NormalPrepass>,
|
||||
Has<DepthPrepass>,
|
||||
Has<MotionVectorPrepass>,
|
||||
Has<DeferredPrepass>,
|
||||
),
|
||||
Option<&TemporalAntiAliasSettings>,
|
||||
&mut RenderPhase<Opaque3d>,
|
||||
&mut RenderPhase<AlphaMask3d>,
|
||||
|
@ -468,8 +472,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||
environment_map,
|
||||
shadow_filter_method,
|
||||
ssao,
|
||||
normal_prepass,
|
||||
deferred_prepass,
|
||||
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
|
||||
taa_settings,
|
||||
mut opaque_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())
|
||||
| MeshPipelineKey::from_hdr(view.hdr);
|
||||
|
||||
if normal_prepass.is_some() {
|
||||
if 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;
|
||||
}
|
||||
|
||||
|
@ -554,10 +565,6 @@ pub fn queue_material_meshes<M: Material>(
|
|||
}
|
||||
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(
|
||||
&pipeline_cache,
|
||||
&material_pipeline,
|
||||
|
|
|
@ -378,6 +378,11 @@ where
|
|||
let mut shader_defs = 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.
|
||||
// The main limitation right now is that bind group order is hardcoded in shaders.
|
||||
bind_group_layouts.insert(1, self.material_layout.clone());
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#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 {
|
||||
#ifdef MULTISAMPLED
|
||||
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
|
||||
|
||||
#ifndef NORMAL_PREPASS
|
||||
#ifdef NORMAL_PREPASS
|
||||
fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
|
||||
#ifdef MULTISAMPLED
|
||||
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
|
||||
|
||||
#ifndef MOTION_VECTOR_PREPASS
|
||||
#ifdef MOTION_VECTOR_PREPASS
|
||||
fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> {
|
||||
#ifdef MULTISAMPLED
|
||||
let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
||||
|
|
|
@ -772,6 +772,9 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
let mut shader_defs = 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());
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
|
@ -870,6 +873,22 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
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 {
|
||||
shader_defs.push("LOAD_PREPASS_NORMALS".into());
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
#import bevy_pbr::gtao_utils gtao_multibounce
|
||||
#endif
|
||||
|
||||
#ifdef DEFERRED_PREPASS
|
||||
#ifdef PREPASS_PIPELINE
|
||||
#import bevy_pbr::pbr_deferred_functions deferred_gbuffer_from_pbr_input
|
||||
#import bevy_pbr::pbr_prepass_functions calculate_motion_vector
|
||||
#import bevy_pbr::prepass_io VertexOutput, FragmentOutput
|
||||
#else // DEFERRED_PREPASS
|
||||
#else // PREPASS_PIPELINE
|
||||
#import bevy_pbr::forward_io VertexOutput, FragmentOutput
|
||||
#endif // DEFERRED_PREPASS
|
||||
#endif // PREPASS_PIPELINE
|
||||
|
||||
#ifdef MOTION_VECTOR_PREPASS
|
||||
@group(0) @binding(2)
|
||||
|
@ -159,7 +159,7 @@ fn fragment(
|
|||
pbr_input.V = V;
|
||||
|
||||
} 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
|
||||
// to pass through the gbuffer to the deferred lighting shader
|
||||
pbr_input = pbr_types::pbr_input_new();
|
||||
|
@ -179,7 +179,7 @@ fn fragment(
|
|||
|
||||
// generate output
|
||||
// ---------------
|
||||
#ifdef DEFERRED_PREPASS
|
||||
#ifdef PREPASS_PIPELINE
|
||||
// write the gbuffer
|
||||
out.deferred = deferred_gbuffer_from_pbr_input(pbr_input);
|
||||
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);
|
||||
#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 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
|
||||
out.color = output_color;
|
||||
|
||||
#endif //DEFERRED_PREPASS
|
||||
#endif // PREPASS_PIPELINE
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue