
# Objective Fixes a part of #14274. Bevy has an incredibly inconsistent naming convention for its system sets, both internally and across the ecosystem. <img alt="System sets in Bevy" src="https://github.com/user-attachments/assets/d16e2027-793f-4ba4-9cc9-e780b14a5a1b" width="450" /> *Names of public system set types in Bevy* Most Bevy types use a naming of `FooSystem` or just `Foo`, but there are also a few `FooSystems` and `FooSet` types. In ecosystem crates on the other hand, `FooSet` is perhaps the most commonly used name in general. Conventions being so wildly inconsistent can make it harder for users to pick names for their own types, to search for system sets on docs.rs, or to even discern which types *are* system sets. To reign in the inconsistency a bit and help unify the ecosystem, it would be good to establish a common recommended naming convention for system sets in Bevy itself, similar to how plugins are commonly suffixed with `Plugin` (ex: `TimePlugin`). By adopting a consistent naming convention in first-party Bevy, we can softly nudge ecosystem crates to follow suit (for types where it makes sense to do so). Choosing a naming convention is also relevant now, as the [`bevy_cli` recently adopted lints](https://github.com/TheBevyFlock/bevy_cli/pull/345) to enforce naming for plugins and system sets, and the recommended naming used for system sets is still a bit open. ## Which Name To Use? Now the contentious part: what naming convention should we actually adopt? This was discussed on the Bevy Discord at the end of last year, starting [here](<https://discord.com/channels/691052431525675048/692572690833473578/1310659954683936789>). `FooSet` and `FooSystems` were the clear favorites, with `FooSet` very narrowly winning an unofficial poll. However, it seems to me like the consensus was broadly moving towards `FooSystems` at the end and after the poll, with Cart ([source](https://discord.com/channels/691052431525675048/692572690833473578/1311140204974706708)) and later Alice ([source](https://discord.com/channels/691052431525675048/692572690833473578/1311092530732859533)) and also me being in favor of it. Let's do a quick pros and cons list! Of course these are just what I thought of, so take it with a grain of salt. `FooSet`: - Pro: Nice and short! - Pro: Used by many ecosystem crates. - Pro: The `Set` suffix comes directly from the trait name `SystemSet`. - Pro: Pairs nicely with existing APIs like `in_set` and `configure_sets`. - Con: `Set` by itself doesn't actually indicate that it's related to systems *at all*, apart from the implemented trait. A set of what? - Con: Is `FooSet` a set of `Foo`s or a system set related to `Foo`? Ex: `ContactSet`, `MeshSet`, `EnemySet`... `FooSystems`: - Pro: Very clearly indicates that the type represents a collection of systems. The actual core concept, system(s), is in the name. - Pro: Parallels nicely with `FooPlugins` for plugin groups. - Pro: Low risk of conflicts with other names or misunderstandings about what the type is. - Pro: In most cases, reads *very* nicely and clearly. Ex: `PhysicsSystems` and `AnimationSystems` as opposed to `PhysicsSet` and `AnimationSet`. - Pro: Easy to search for on docs.rs. - Con: Usually results in longer names. - Con: Not yet as widely used. Really the big problem with `FooSet` is that it doesn't actually describe what it is. It describes what *kind of thing* it is (a set of something), but not *what it is a set of*, unless you know the type or check its docs or implemented traits. `FooSystems` on the other hand is much more self-descriptive in this regard, at the cost of being a bit longer to type. Ultimately, in some ways it comes down to preference and how you think of system sets. Personally, I was originally in favor of `FooSet`, but have been increasingly on the side of `FooSystems`, especially after seeing what the new names would actually look like in Avian and now Bevy. I prefer it because it usually reads better, is much more clearly related to groups of systems than `FooSet`, and overall *feels* more correct and natural to me in the long term. For these reasons, and because Alice and Cart also seemed to share a preference for it when it was previously being discussed, I propose that we adopt a `FooSystems` naming convention where applicable. ## Solution Rename Bevy's system set types to use a consistent `FooSet` naming where applicable. - `AccessibilitySystem` → `AccessibilitySystems` - `GizmoRenderSystem` → `GizmoRenderSystems` - `PickSet` → `PickingSystems` - `RunFixedMainLoopSystem` → `RunFixedMainLoopSystems` - `TransformSystem` → `TransformSystems` - `RemoteSet` → `RemoteSystems` - `RenderSet` → `RenderSystems` - `SpriteSystem` → `SpriteSystems` - `StateTransitionSteps` → `StateTransitionSystems` - `RenderUiSystem` → `RenderUiSystems` - `UiSystem` → `UiSystems` - `Animation` → `AnimationSystems` - `AssetEvents` → `AssetEventSystems` - `TrackAssets` → `AssetTrackingSystems` - `UpdateGizmoMeshes` → `GizmoMeshSystems` - `InputSystem` → `InputSystems` - `InputFocusSet` → `InputFocusSystems` - `ExtractMaterialsSet` → `MaterialExtractionSystems` - `ExtractMeshesSet` → `MeshExtractionSystems` - `RumbleSystem` → `RumbleSystems` - `CameraUpdateSystem` → `CameraUpdateSystems` - `ExtractAssetsSet` → `AssetExtractionSystems` - `Update2dText` → `Text2dUpdateSystems` - `TimeSystem` → `TimeSystems` - `AudioPlaySet` → `AudioPlaybackSystems` - `SendEvents` → `EventSenderSystems` - `EventUpdates` → `EventUpdateSystems` A lot of the names got slightly longer, but they are also a lot more consistent, and in my opinion the majority of them read much better. For a few of the names I took the liberty of rewording things a bit; definitely open to any further naming improvements. There are still also cases where the `FooSystems` naming doesn't really make sense, and those I left alone. This primarily includes system sets like `Interned<dyn SystemSet>`, `EnterSchedules<S>`, `ExitSchedules<S>`, or `TransitionSchedules<S>`, where the type has some special purpose and semantics. ## Todo - [x] Should I keep all the old names as deprecated type aliases? I can do this, but to avoid wasting work I'd prefer to first reach consensus on whether these renames are even desired. - [x] Migration guide - [x] Release notes
561 lines
21 KiB
Rust
561 lines
21 KiB
Rust
use crate::{
|
|
graph::NodePbr, irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight,
|
|
MeshPipeline, MeshViewBindGroup, RenderViewLightProbes, ScreenSpaceAmbientOcclusion,
|
|
ScreenSpaceReflectionsUniform, ViewEnvironmentMapUniformOffset, ViewLightProbesUniformOffset,
|
|
ViewScreenSpaceReflectionsUniformOffset, TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
|
|
TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
|
|
};
|
|
use crate::{
|
|
DistanceFog, MeshPipelineKey, ShadowFilteringMethod, ViewFogUniformOffset,
|
|
ViewLightsUniformOffset,
|
|
};
|
|
use bevy_app::prelude::*;
|
|
use bevy_asset::{load_internal_asset, weak_handle, Handle};
|
|
use bevy_core_pipeline::{
|
|
core_3d::graph::{Core3d, Node3d},
|
|
deferred::{
|
|
copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
|
|
},
|
|
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
|
tonemapping::{DebandDither, Tonemapping},
|
|
};
|
|
use bevy_ecs::{prelude::*, query::QueryItem};
|
|
use bevy_image::BevyDefault as _;
|
|
use bevy_render::{
|
|
extract_component::{
|
|
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
|
},
|
|
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
|
|
render_resource::{binding_types::uniform_buffer, *},
|
|
renderer::{RenderContext, RenderDevice},
|
|
view::{ExtractedView, ViewTarget, ViewUniformOffset},
|
|
Render, RenderApp, RenderSystems,
|
|
};
|
|
|
|
pub struct DeferredPbrLightingPlugin;
|
|
|
|
pub const DEFERRED_LIGHTING_SHADER_HANDLE: Handle<Shader> =
|
|
weak_handle!("f4295279-8890-4748-b654-ca4d2183df1c");
|
|
|
|
pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1;
|
|
|
|
/// Component with a `depth_id` for specifying which corresponding materials should be rendered by this specific PBR deferred lighting pass.
|
|
///
|
|
/// Will be automatically added to entities with the [`DeferredPrepass`] component that don't already have a [`PbrDeferredLightingDepthId`].
|
|
#[derive(Component, Clone, Copy, ExtractComponent, ShaderType)]
|
|
pub struct PbrDeferredLightingDepthId {
|
|
depth_id: u32,
|
|
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_0: f32,
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_1: f32,
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_2: f32,
|
|
}
|
|
|
|
impl PbrDeferredLightingDepthId {
|
|
pub fn new(value: u8) -> PbrDeferredLightingDepthId {
|
|
PbrDeferredLightingDepthId {
|
|
depth_id: value as u32,
|
|
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_0: 0.0,
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_1: 0.0,
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_2: 0.0,
|
|
}
|
|
}
|
|
|
|
pub fn set(&mut self, value: u8) {
|
|
self.depth_id = value as u32;
|
|
}
|
|
|
|
pub fn get(&self) -> u8 {
|
|
self.depth_id as u8
|
|
}
|
|
}
|
|
|
|
impl Default for PbrDeferredLightingDepthId {
|
|
fn default() -> Self {
|
|
PbrDeferredLightingDepthId {
|
|
depth_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID as u32,
|
|
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_0: 0.0,
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_1: 0.0,
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
_webgl2_padding_2: 0.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Plugin for DeferredPbrLightingPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.add_plugins((
|
|
ExtractComponentPlugin::<PbrDeferredLightingDepthId>::default(),
|
|
UniformComponentPlugin::<PbrDeferredLightingDepthId>::default(),
|
|
))
|
|
.add_systems(PostUpdate, insert_deferred_lighting_pass_id_component);
|
|
|
|
load_internal_asset!(
|
|
app,
|
|
DEFERRED_LIGHTING_SHADER_HANDLE,
|
|
"deferred_lighting.wgsl",
|
|
Shader::from_wgsl
|
|
);
|
|
|
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
|
return;
|
|
};
|
|
|
|
render_app
|
|
.init_resource::<SpecializedRenderPipelines<DeferredLightingLayout>>()
|
|
.add_systems(
|
|
Render,
|
|
(prepare_deferred_lighting_pipelines.in_set(RenderSystems::Prepare),),
|
|
)
|
|
.add_render_graph_node::<ViewNodeRunner<DeferredOpaquePass3dPbrLightingNode>>(
|
|
Core3d,
|
|
NodePbr::DeferredLightingPass,
|
|
)
|
|
.add_render_graph_edges(
|
|
Core3d,
|
|
(
|
|
Node3d::StartMainPass,
|
|
NodePbr::DeferredLightingPass,
|
|
Node3d::MainOpaquePass,
|
|
),
|
|
);
|
|
}
|
|
|
|
fn finish(&self, app: &mut App) {
|
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
|
return;
|
|
};
|
|
|
|
render_app.init_resource::<DeferredLightingLayout>();
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct DeferredOpaquePass3dPbrLightingNode;
|
|
|
|
impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
|
|
type ViewQuery = (
|
|
&'static ViewUniformOffset,
|
|
&'static ViewLightsUniformOffset,
|
|
&'static ViewFogUniformOffset,
|
|
&'static ViewLightProbesUniformOffset,
|
|
&'static ViewScreenSpaceReflectionsUniformOffset,
|
|
&'static ViewEnvironmentMapUniformOffset,
|
|
&'static MeshViewBindGroup,
|
|
&'static ViewTarget,
|
|
&'static DeferredLightingIdDepthTexture,
|
|
&'static DeferredLightingPipeline,
|
|
);
|
|
|
|
fn run(
|
|
&self,
|
|
_graph_context: &mut RenderGraphContext,
|
|
render_context: &mut RenderContext,
|
|
(
|
|
view_uniform_offset,
|
|
view_lights_offset,
|
|
view_fog_offset,
|
|
view_light_probes_offset,
|
|
view_ssr_offset,
|
|
view_environment_map_offset,
|
|
mesh_view_bind_group,
|
|
target,
|
|
deferred_lighting_id_depth_texture,
|
|
deferred_lighting_pipeline,
|
|
): QueryItem<Self::ViewQuery>,
|
|
world: &World,
|
|
) -> Result<(), NodeRunError> {
|
|
let pipeline_cache = world.resource::<PipelineCache>();
|
|
let deferred_lighting_layout = world.resource::<DeferredLightingLayout>();
|
|
|
|
let Some(pipeline) =
|
|
pipeline_cache.get_render_pipeline(deferred_lighting_pipeline.pipeline_id)
|
|
else {
|
|
return Ok(());
|
|
};
|
|
|
|
let deferred_lighting_pass_id =
|
|
world.resource::<ComponentUniforms<PbrDeferredLightingDepthId>>();
|
|
let Some(deferred_lighting_pass_id_binding) =
|
|
deferred_lighting_pass_id.uniforms().binding()
|
|
else {
|
|
return Ok(());
|
|
};
|
|
|
|
let bind_group_1 = render_context.render_device().create_bind_group(
|
|
"deferred_lighting_layout_group_1",
|
|
&deferred_lighting_layout.bind_group_layout_1,
|
|
&BindGroupEntries::single(deferred_lighting_pass_id_binding),
|
|
);
|
|
|
|
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
|
|
label: Some("deferred_lighting_pass"),
|
|
color_attachments: &[Some(target.get_color_attachment())],
|
|
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
|
view: &deferred_lighting_id_depth_texture.texture.default_view,
|
|
depth_ops: Some(Operations {
|
|
load: LoadOp::Load,
|
|
store: StoreOp::Discard,
|
|
}),
|
|
stencil_ops: None,
|
|
}),
|
|
timestamp_writes: None,
|
|
occlusion_query_set: None,
|
|
});
|
|
|
|
render_pass.set_render_pipeline(pipeline);
|
|
render_pass.set_bind_group(
|
|
0,
|
|
&mesh_view_bind_group.value,
|
|
&[
|
|
view_uniform_offset.offset,
|
|
view_lights_offset.offset,
|
|
view_fog_offset.offset,
|
|
**view_light_probes_offset,
|
|
**view_ssr_offset,
|
|
**view_environment_map_offset,
|
|
],
|
|
);
|
|
render_pass.set_bind_group(1, &bind_group_1, &[]);
|
|
render_pass.draw(0..3, 0..1);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
pub struct DeferredLightingLayout {
|
|
mesh_pipeline: MeshPipeline,
|
|
bind_group_layout_1: BindGroupLayout,
|
|
}
|
|
|
|
#[derive(Component)]
|
|
pub struct DeferredLightingPipeline {
|
|
pub pipeline_id: CachedRenderPipelineId,
|
|
}
|
|
|
|
impl SpecializedRenderPipeline for DeferredLightingLayout {
|
|
type Key = MeshPipelineKey;
|
|
|
|
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", not(feature = "webgpu")))]
|
|
shader_defs.push("WEBGL2".into());
|
|
|
|
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
|
|
shader_defs.push("TONEMAP_IN_SHADER".into());
|
|
shader_defs.push(ShaderDefVal::UInt(
|
|
"TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
|
|
TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
|
|
));
|
|
shader_defs.push(ShaderDefVal::UInt(
|
|
"TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
|
|
TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
|
|
));
|
|
|
|
let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
|
|
|
|
if method == MeshPipelineKey::TONEMAP_METHOD_NONE {
|
|
shader_defs.push("TONEMAP_METHOD_NONE".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
|
|
shader_defs.push("TONEMAP_METHOD_REINHARD".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
|
|
shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
|
|
shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
|
|
shader_defs.push("TONEMAP_METHOD_AGX".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
|
|
shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
|
|
shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
|
|
} else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
|
|
shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
|
|
}
|
|
|
|
// Debanding is tied to tonemapping in the shader, cannot run without it.
|
|
if key.contains(MeshPipelineKey::DEBAND_DITHER) {
|
|
shader_defs.push("DEBAND_DITHER".into());
|
|
}
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
|
|
shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
|
|
shader_defs.push("ENVIRONMENT_MAP".into());
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) {
|
|
shader_defs.push("IRRADIANCE_VOLUME".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());
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::SCREEN_SPACE_REFLECTIONS) {
|
|
shader_defs.push("SCREEN_SPACE_REFLECTIONS".into());
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
|
|
shader_defs.push("HAS_PREVIOUS_SKIN".into());
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
|
|
shader_defs.push("HAS_PREVIOUS_MORPH".into());
|
|
}
|
|
|
|
if key.contains(MeshPipelineKey::DISTANCE_FOG) {
|
|
shader_defs.push("DISTANCE_FOG".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 {
|
|
shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
|
|
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
|
|
shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
|
|
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
|
|
shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
|
|
}
|
|
if self.mesh_pipeline.binding_arrays_are_usable {
|
|
shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
|
|
shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
|
|
}
|
|
|
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
|
shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
|
|
|
|
RenderPipelineDescriptor {
|
|
label: Some("deferred_lighting_pipeline".into()),
|
|
layout: vec![
|
|
self.mesh_pipeline.get_view_layout(key.into()).clone(),
|
|
self.bind_group_layout_1.clone(),
|
|
],
|
|
vertex: VertexState {
|
|
shader: DEFERRED_LIGHTING_SHADER_HANDLE,
|
|
shader_defs: shader_defs.clone(),
|
|
entry_point: "vertex".into(),
|
|
buffers: Vec::new(),
|
|
},
|
|
fragment: Some(FragmentState {
|
|
shader: DEFERRED_LIGHTING_SHADER_HANDLE,
|
|
shader_defs,
|
|
entry_point: "fragment".into(),
|
|
targets: vec![Some(ColorTargetState {
|
|
format: if key.contains(MeshPipelineKey::HDR) {
|
|
ViewTarget::TEXTURE_FORMAT_HDR
|
|
} else {
|
|
TextureFormat::bevy_default()
|
|
},
|
|
blend: None,
|
|
write_mask: ColorWrites::ALL,
|
|
})],
|
|
}),
|
|
primitive: PrimitiveState::default(),
|
|
depth_stencil: Some(DepthStencilState {
|
|
format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
|
|
depth_write_enabled: false,
|
|
depth_compare: CompareFunction::Equal,
|
|
stencil: StencilState {
|
|
front: StencilFaceState::IGNORE,
|
|
back: StencilFaceState::IGNORE,
|
|
read_mask: 0,
|
|
write_mask: 0,
|
|
},
|
|
bias: DepthBiasState {
|
|
constant: 0,
|
|
slope_scale: 0.0,
|
|
clamp: 0.0,
|
|
},
|
|
}),
|
|
multisample: MultisampleState::default(),
|
|
push_constant_ranges: vec![],
|
|
zero_initialize_workgroup_memory: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromWorld for DeferredLightingLayout {
|
|
fn from_world(world: &mut World) -> Self {
|
|
let render_device = world.resource::<RenderDevice>();
|
|
let layout = render_device.create_bind_group_layout(
|
|
"deferred_lighting_layout",
|
|
&BindGroupLayoutEntries::single(
|
|
ShaderStages::VERTEX_FRAGMENT,
|
|
uniform_buffer::<PbrDeferredLightingDepthId>(false),
|
|
),
|
|
);
|
|
Self {
|
|
mesh_pipeline: world.resource::<MeshPipeline>().clone(),
|
|
bind_group_layout_1: layout,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn insert_deferred_lighting_pass_id_component(
|
|
mut commands: Commands,
|
|
views: Query<Entity, (With<DeferredPrepass>, Without<PbrDeferredLightingDepthId>)>,
|
|
) {
|
|
for entity in views.iter() {
|
|
commands
|
|
.entity(entity)
|
|
.insert(PbrDeferredLightingDepthId::default());
|
|
}
|
|
}
|
|
|
|
pub fn prepare_deferred_lighting_pipelines(
|
|
mut commands: Commands,
|
|
pipeline_cache: Res<PipelineCache>,
|
|
mut pipelines: ResMut<SpecializedRenderPipelines<DeferredLightingLayout>>,
|
|
deferred_lighting_layout: Res<DeferredLightingLayout>,
|
|
views: Query<(
|
|
Entity,
|
|
&ExtractedView,
|
|
Option<&Tonemapping>,
|
|
Option<&DebandDither>,
|
|
Option<&ShadowFilteringMethod>,
|
|
(
|
|
Has<ScreenSpaceAmbientOcclusion>,
|
|
Has<ScreenSpaceReflectionsUniform>,
|
|
Has<DistanceFog>,
|
|
),
|
|
(
|
|
Has<NormalPrepass>,
|
|
Has<DepthPrepass>,
|
|
Has<MotionVectorPrepass>,
|
|
Has<DeferredPrepass>,
|
|
),
|
|
Has<RenderViewLightProbes<EnvironmentMapLight>>,
|
|
Has<RenderViewLightProbes<IrradianceVolume>>,
|
|
)>,
|
|
) {
|
|
for (
|
|
entity,
|
|
view,
|
|
tonemapping,
|
|
dither,
|
|
shadow_filter_method,
|
|
(ssao, ssr, distance_fog),
|
|
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
|
|
has_environment_maps,
|
|
has_irradiance_volumes,
|
|
) in &views
|
|
{
|
|
// If there is no deferred prepass, remove the old pipeline if there was
|
|
// one. This handles the case in which a view using deferred stops using
|
|
// it.
|
|
if !deferred_prepass {
|
|
commands.entity(entity).remove::<DeferredLightingPipeline>();
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
view_key |= match tonemapping {
|
|
Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
|
|
Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
|
|
Tonemapping::ReinhardLuminance => {
|
|
MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE
|
|
}
|
|
Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
|
|
Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
|
|
Tonemapping::SomewhatBoringDisplayTransform => {
|
|
MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
|
|
}
|
|
Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
|
|
Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
|
|
};
|
|
}
|
|
if let Some(DebandDither::Enabled) = dither {
|
|
view_key |= MeshPipelineKey::DEBAND_DITHER;
|
|
}
|
|
}
|
|
|
|
if ssao {
|
|
view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
|
|
}
|
|
if ssr {
|
|
view_key |= MeshPipelineKey::SCREEN_SPACE_REFLECTIONS;
|
|
}
|
|
if distance_fog {
|
|
view_key |= MeshPipelineKey::DISTANCE_FOG;
|
|
}
|
|
|
|
// We don't need to check to see whether the environment map is loaded
|
|
// because [`gather_light_probes`] already checked that for us before
|
|
// adding the [`RenderViewEnvironmentMaps`] component.
|
|
if has_environment_maps {
|
|
view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
|
|
}
|
|
|
|
if has_irradiance_volumes {
|
|
view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
|
|
}
|
|
|
|
match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
|
|
ShadowFilteringMethod::Hardware2x2 => {
|
|
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
|
|
}
|
|
ShadowFilteringMethod::Gaussian => {
|
|
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
|
|
}
|
|
ShadowFilteringMethod::Temporal => {
|
|
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
|
|
}
|
|
}
|
|
|
|
let pipeline_id =
|
|
pipelines.specialize(&pipeline_cache, &deferred_lighting_layout, view_key);
|
|
|
|
commands
|
|
.entity(entity)
|
|
.insert(DeferredLightingPipeline { pipeline_id });
|
|
}
|
|
}
|