Skip to main content

bevy_pbr/deferred/
mod.rs

1use crate::{
2    irradiance_volume::IRRADIANCE_VOLUMES_ARE_USABLE, MeshPipeline, MeshPipelineKey,
3    MeshPipelineSystems, MeshViewBindGroup, ViewKeyCache, TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
4    TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
5};
6use bevy_app::prelude::*;
7use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
8use bevy_core_pipeline::{
9    core_3d::main_opaque_pass_3d,
10    deferred::{
11        copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
12    },
13    prepass::DeferredPrepass,
14    schedule::{Core3d, Core3dSystems},
15};
16use bevy_ecs::prelude::*;
17use bevy_render::{
18    camera::ExtractedCamera,
19    diagnostic::RecordDiagnostics,
20    extract_component::{
21        ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
22    },
23    render_resource::{binding_types::uniform_buffer, *},
24    renderer::{RenderContext, ViewQuery},
25    view::{ExtractedView, ViewTarget},
26    Render, RenderApp, RenderSystems,
27};
28use bevy_render::{GpuResourceAppExt, RenderStartup};
29use bevy_shader::{Shader, ShaderDefVal};
30use bevy_utils::default;
31
32pub struct DeferredPbrLightingPlugin;
33
34pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1;
35
36/// Component with a `depth_id` for specifying which corresponding materials should be rendered by this specific PBR deferred lighting pass.
37///
38/// Will be automatically added to entities with the [`DeferredPrepass`] component that don't already have a [`PbrDeferredLightingDepthId`].
39#[derive(impl bevy_ecs::component::Component for PbrDeferredLightingDepthId where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::Table;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {}
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}Component, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PbrDeferredLightingDepthId { }
#[automatically_derived]
impl ::core::clone::Clone for PbrDeferredLightingDepthId {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PbrDeferredLightingDepthId { }Copy, impl bevy_extract::sync_component::SyncComponent<RenderApp> for
    PbrDeferredLightingDepthId where Self: ::core::clone::Clone {
    type Target = Self;
}
impl bevy_extract::extract_component::ExtractComponent<RenderApp> for
    PbrDeferredLightingDepthId where Self: ::core::clone::Clone {
    type QueryData = &'static Self;
    type QueryFilter = ();
    type Out = Self;
    fn extract_component(item:
            bevy_ecs::query::QueryItem<'_, '_, Self::QueryData>)
        -> ::core::option::Option<Self::Out> {
        ::core::option::Option::Some(item.clone())
    }
}ExtractComponent, impl bevy_render::render_resource::encase::private::ShaderType for
    PbrDeferredLightingDepthId where
    u32: bevy_render::render_resource::encase::private::ShaderType {
    type ExtraMetadata =
        bevy_render::render_resource::encase::private::StructMetadata<1usize>;
    const METADATA:
        bevy_render::render_resource::encase::private::Metadata<Self::ExtraMetadata>
        =
        {
            let struct_alignment =
                bevy_render::render_resource::encase::private::AlignmentValue::max([<u32
                                    as
                                    bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment()]);
            let extra =
                {
                    let mut paddings = [0; 1usize];
                    let mut offsets = [0; 1usize];
                    let mut offset = 0;
                    offset +=
                        <u32 as
                                bevy_render::render_resource::encase::private::ShaderSize>::SHADER_SIZE.get();
                    paddings[0usize] =
                        struct_alignment.padding_needed_for(offset);
                    bevy_render::render_resource::encase::private::StructMetadata {
                        offsets,
                        paddings,
                    }
                };
            let min_size =
                {
                    let mut offset = extra.offsets[1usize - 1];
                    offset +=
                        <u32 as
                                    bevy_render::render_resource::encase::private::ShaderType>::METADATA.min_size().get();
                    bevy_render::render_resource::encase::private::SizeValue::new(struct_alignment.round_up(offset))
                };
            bevy_render::render_resource::encase::private::Metadata {
                alignment: struct_alignment,
                has_uniform_min_alignment: true,
                min_size,
                is_pod: false,
                extra,
            }
        };
    const UNIFORM_COMPAT_ASSERT: fn() =
        ||
            bevy_render::render_resource::encase::private::consume_zsts([<u32
                                as
                                bevy_render::render_resource::encase::private::ShaderType>::UNIFORM_COMPAT_ASSERT(),
                        if let ::core::option::Option::Some(min_alignment) =
                                <u32 as
                                        bevy_render::render_resource::encase::private::ShaderType>::METADATA.uniform_min_alignment()
                            {
                            let offset =
                                <Self as
                                        bevy_render::render_resource::encase::private::ShaderType>::METADATA.offset(0usize);
                            {

                                #[allow(clippy :: equatable_if_let)]
                                if let false = min_alignment.is_aligned(offset) {
                                    {
                                        let mut fmt: ::const_panic::FmtArg =
                                            ::const_panic::FmtArg::DEBUG;
                                        match &[::const_panic::StdWrapper(&match &"offset of field '"
                                                                            {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
                                                        ::const_panic::StdWrapper(&match &"depth_id" {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
                                                        ::const_panic::StdWrapper(&match &"' must be a multiple of "
                                                                            {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
                                                        ::const_panic::StdWrapper(&match &min_alignment.get() {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt)).deref_panic_vals(),
                                                        ::const_panic::StdWrapper(&match &" (current offset: " {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
                                                        ::const_panic::StdWrapper(&match &offset {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt)).deref_panic_vals(),
                                                        ::const_panic::StdWrapper(&match &")" {
                                                                            reff =>
                                                                                ::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
                                                                        }.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals()]
                                            {
                                            args => ::const_panic::concat_panic(args),
                                        }
                                    }
                                }
                            }
                        }, ()]);
    fn size(&self) -> ::core::num::NonZeroU64 {
        let mut offset = Self::METADATA.last_offset();
        offset +=
            bevy_render::render_resource::encase::private::ShaderType::size(&self.depth_id).get();
        bevy_render::render_resource::encase::private::SizeValue::new(Self::METADATA.alignment().round_up(offset)).0
    }
}
impl bevy_render::render_resource::encase::private::WriteInto for
    PbrDeferredLightingDepthId where
    Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
    = bevy_render::render_resource::encase::private::StructMetadata<1usize>>,
    for<'__> u32: bevy_render::render_resource::encase::private::WriteInto {
    #[inline]
    fn write_into<B: bevy_render::render_resource::encase::private::BufferMut>(&self,
        writer:
            &mut bevy_render::render_resource::encase::private::Writer<B>) {
        bevy_render::render_resource::encase::private::WriteInto::write_into(&self.depth_id,
            writer);
        bevy_render::render_resource::encase::private::Writer::advance(writer,
            <Self as
                        bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(0usize)
                as ::core::primitive::usize);
    }
}
impl bevy_render::render_resource::encase::private::ReadFrom for
    PbrDeferredLightingDepthId where
    Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
    = bevy_render::render_resource::encase::private::StructMetadata<1usize>>,
    for<'__> u32: bevy_render::render_resource::encase::private::ReadFrom {
    #[inline]
    fn read_from<B: bevy_render::render_resource::encase::private::BufferRef>(&mut self,
        reader:
            &mut bevy_render::render_resource::encase::private::Reader<B>) {
        bevy_render::render_resource::encase::private::ReadFrom::read_from(&mut self.depth_id,
            reader);
        bevy_render::render_resource::encase::private::Reader::advance(reader,
            <Self as
                        bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(0usize)
                as ::core::primitive::usize);
    }
}
impl bevy_render::render_resource::encase::private::CreateFrom for
    PbrDeferredLightingDepthId where
    Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
    = bevy_render::render_resource::encase::private::StructMetadata<1usize>>,
    for<'__> u32: bevy_render::render_resource::encase::private::CreateFrom {
    #[inline]
    fn create_from<B: bevy_render::render_resource::encase::private::BufferRef>(reader:
            &mut bevy_render::render_resource::encase::private::Reader<B>)
        -> Self {
        let depth_id =
            bevy_render::render_resource::encase::private::CreateFrom::create_from(reader);
        bevy_render::render_resource::encase::private::Reader::advance(reader,
            <Self as
                        bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(0usize)
                as ::core::primitive::usize);
        {
            let mut uninit_struct =
                ::core::mem::MaybeUninit::<Self>::uninit();
            let ptr =
                ::core::mem::MaybeUninit::as_mut_ptr(&mut uninit_struct);
            let field_ptr = unsafe { &raw mut (*ptr).depth_id };
            unsafe { field_ptr.write(depth_id) };
            ;
            unsafe { ::core::mem::MaybeUninit::assume_init(uninit_struct) }
        }
    }
}
impl bevy_render::render_resource::encase::private::ShaderSize for
    PbrDeferredLightingDepthId where
    u32: bevy_render::render_resource::encase::private::ShaderSize {}ShaderType)]
40#[extract_app(RenderApp)]
41pub struct PbrDeferredLightingDepthId {
42    depth_id: u32,
43
44    #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
45    _webgl2_padding_0: f32,
46    #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
47    _webgl2_padding_1: f32,
48    #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
49    _webgl2_padding_2: f32,
50}
51
52impl PbrDeferredLightingDepthId {
53    pub fn new(value: u8) -> PbrDeferredLightingDepthId {
54        PbrDeferredLightingDepthId {
55            depth_id: value as u32,
56
57            #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
58            _webgl2_padding_0: 0.0,
59            #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
60            _webgl2_padding_1: 0.0,
61            #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
62            _webgl2_padding_2: 0.0,
63        }
64    }
65
66    pub fn set(&mut self, value: u8) {
67        self.depth_id = value as u32;
68    }
69
70    pub fn get(&self) -> u8 {
71        self.depth_id as u8
72    }
73}
74
75impl Default for PbrDeferredLightingDepthId {
76    fn default() -> Self {
77        PbrDeferredLightingDepthId {
78            depth_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID as u32,
79
80            #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
81            _webgl2_padding_0: 0.0,
82            #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
83            _webgl2_padding_1: 0.0,
84            #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
85            _webgl2_padding_2: 0.0,
86        }
87    }
88}
89
90impl Plugin for DeferredPbrLightingPlugin {
91    fn build(&self, app: &mut App) {
92        app.add_plugins((
93            ExtractComponentPlugin::<PbrDeferredLightingDepthId>::default(),
94            UniformComponentPlugin::<PbrDeferredLightingDepthId>::default(),
95        ))
96        .add_systems(PostUpdate, insert_deferred_lighting_pass_id_component);
97
98        {
    {
        let mut embedded =
            app.world_mut().resource_mut::<::bevy_asset::io::embedded::EmbeddedAssetRegistry>();
        let path =
            {
                let crate_name =
                    "bevy_pbr::deferred".split(':').next().unwrap();
                ::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
                    "src".as_ref(), "src/deferred/mod.rs".as_ref(),
                    "deferred_lighting.wesl".as_ref())
            };
        let watched_path =
            ::bevy_asset::io::embedded::watched_path("src/deferred/mod.rs",
                "deferred_lighting.wesl");
        embedded.insert_asset(watched_path, &path,
            b"import package::{\n    prepass::utils as prepass_utils,\n    render::{\n        pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT,\n        pbr_functions,\n        pbr_lighting as lighting,\n        mesh_view_bindings::{deferred_prepass_texture, view},\n    },\n    deferred::{\n        functions::pbr_input_from_deferred_gbuffer,\n        types::unpack_unorm3x4_plus_unorm_20_,\n    },\n};\n\n@if(SCREEN_SPACE_AMBIENT_OCCLUSION) import package::render::mesh_view_bindings::screen_space_ambient_occlusion_texture;\n@if(SCREEN_SPACE_AMBIENT_OCCLUSION) import package::ssao::utils::ssao_multibounce;\n\nstruct FullscreenVertexOutput {\n    @builtin(position)\n    position: vec4<f32>,\n    @location(0)\n    uv: vec2<f32>,\n};\n\nstruct PbrDeferredLightingDepthId {\n    depth_id: u32, // limited to u8\n    @if(SIXTEEN_BYTE_ALIGNMENT)\n    // WebGL2 structs must be 16 byte aligned.\n    _webgl2_padding_0: f32,\n    @if(SIXTEEN_BYTE_ALIGNMENT)\n    _webgl2_padding_1: f32,\n    @if(SIXTEEN_BYTE_ALIGNMENT)\n    _webgl2_padding_2: f32,\n}\n@group(2) @binding(0)\nvar<uniform> depth_id: PbrDeferredLightingDepthId;\n\n@vertex\nfn vertex(@builtin(vertex_index) vertex_index: u32) -> FullscreenVertexOutput {\n    // See the full screen vertex shader for explanation above for how this works.\n    let uv = vec2<f32>(f32(vertex_index >> 1u), f32(vertex_index & 1u)) * 2.0;\n    // Depth is stored as unorm, so we are dividing the u8 depth_id by 255.0 here.\n    let clip_position = vec4<f32>(uv * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), f32(depth_id.depth_id) / 255.0, 1.0);\n\n    return FullscreenVertexOutput(clip_position, uv);\n}\n\n@fragment\nfn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {\n    var frag_coord = vec4(in.position.xy, 0.0, 0.0);\n\n    let deferred_data = textureLoad(deferred_prepass_texture, vec2<i32>(frag_coord.xy), 0);\n\n    @if(WEBGL2)\n    frag_coord.z = unpack_unorm3x4_plus_unorm_20_(deferred_data.b).w;\n    @elif(DEPTH_PREPASS)\n    frag_coord.z = prepass_utils::prepass_depth(in.position, 0u);\n\n    var pbr_input = pbr_input_from_deferred_gbuffer(frag_coord, deferred_data);\n    var output_color = vec4(0.0);\n\n    // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit\n    if ((pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {\n\n@if(SCREEN_SPACE_AMBIENT_OCCLUSION) {\n        let ssao_tex_pos = vec2<i32>(in.position.xy - view.viewport.xy);\n        let ssao = textureLoad(screen_space_ambient_occlusion_texture, ssao_tex_pos, 0i).r;\n        let ssao_multibounce = ssao_multibounce(ssao, pbr_input.material.base_color.rgb);\n        pbr_input.diffuse_occlusion = min(pbr_input.diffuse_occlusion, ssao_multibounce);\n\n        // Neubelt and Pettineo 2013, \"Crafting a Next-gen Material Pipeline for The Order: 1886\"\n        let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); \n        var perceptual_roughness: f32 = pbr_input.material.perceptual_roughness;\n        let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);\n        // Use SSAO to estimate the specular occlusion.\n        // Lagarde and Rousiers 2014, \"Moving Frostbite to Physically Based Rendering\"\n        pbr_input.specular_occlusion =  saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao);\n}\n\n        output_color = pbr_functions::apply_pbr_lighting(pbr_input);\n    } else {\n        output_color = pbr_input.material.base_color;\n    }\n\n    output_color = pbr_functions::main_pass_post_lighting_processing(pbr_input, output_color);\n\n    return output_color;\n}\n");
    }
};embedded_asset!(app, "deferred_lighting.wesl");
99
100        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
101            return;
102        };
103
104        render_app
105            .init_gpu_resource::<SpecializedRenderPipelines<DeferredLightingLayout>>()
106            .add_systems(
107                RenderStartup,
108                init_deferred_lighting_layout.after(MeshPipelineSystems),
109            )
110            .add_systems(
111                Render,
112                prepare_deferred_lighting_pipelines.in_set(RenderSystems::Prepare),
113            )
114            .add_systems(
115                Core3d,
116                deferred_lighting
117                    .before(main_opaque_pass_3d)
118                    .in_set(Core3dSystems::MainPass),
119            );
120    }
121}
122
123pub fn deferred_lighting(
124    view: ViewQuery<(
125        &MeshViewBindGroup,
126        &ViewTarget,
127        &DeferredLightingIdDepthTexture,
128        &DeferredLightingPipeline,
129    )>,
130    pipeline_cache: Res<PipelineCache>,
131    deferred_lighting_layout: Res<DeferredLightingLayout>,
132    deferred_lighting_pass_id: Res<ComponentUniforms<PbrDeferredLightingDepthId>>,
133    mut ctx: RenderContext,
134) {
135    let (
136        mesh_view_bind_group,
137        target,
138        deferred_lighting_id_depth_texture,
139        deferred_lighting_pipeline,
140    ) = view.into_inner();
141
142    let Some(pipeline) = pipeline_cache.get_render_pipeline(deferred_lighting_pipeline.pipeline_id)
143    else {
144        return;
145    };
146
147    let Some(deferred_lighting_pass_id_binding) = deferred_lighting_pass_id.uniforms().binding()
148    else {
149        return;
150    };
151
152    let diagnostics = ctx.diagnostic_recorder();
153    let diagnostics = diagnostics.as_deref();
154
155    let bind_group_2 = ctx.render_device().create_bind_group(
156        "deferred_lighting_layout_group_2",
157        &pipeline_cache.get_bind_group_layout(&deferred_lighting_layout.bind_group_layout_2),
158        &BindGroupEntries::single(deferred_lighting_pass_id_binding),
159    );
160
161    let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
162        label: Some("deferred_lighting"),
163        color_attachments: &[Some(target.get_color_attachment())],
164        depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
165            view: &deferred_lighting_id_depth_texture.texture.default_view,
166            depth_ops: Some(Operations {
167                load: LoadOp::Load,
168                store: StoreOp::Discard,
169            }),
170            stencil_ops: None,
171        }),
172        timestamp_writes: None,
173        occlusion_query_set: None,
174        multiview_mask: None,
175    });
176    let pass_span = diagnostics.pass_span(&mut render_pass, "deferred_lighting");
177
178    render_pass.set_render_pipeline(pipeline);
179
180    render_pass.set_bind_group(
181        0,
182        &mesh_view_bind_group.main,
183        &mesh_view_bind_group.main_offsets,
184    );
185    render_pass.set_bind_group(1, &mesh_view_bind_group.binding_array, &[]);
186    render_pass.set_bind_group(2, &bind_group_2, &[]);
187    render_pass.draw(0..3, 0..1);
188    pass_span.end(&mut render_pass);
189}
190
191#[derive(impl bevy_ecs::component::Component for DeferredLightingLayout where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<DeferredLightingLayout>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<DeferredLightingLayout>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for DeferredLightingLayout where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
192pub struct DeferredLightingLayout {
193    mesh_pipeline: MeshPipeline,
194    bind_group_layout_2: BindGroupLayoutDescriptor,
195    deferred_lighting_shader: Handle<Shader>,
196}
197
198#[derive(impl bevy_ecs::component::Component for DeferredLightingPipeline where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::Table;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {}
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}Component)]
199pub struct DeferredLightingPipeline {
200    pub pipeline_id: CachedRenderPipelineId,
201}
202
203impl SpecializedRenderPipeline for DeferredLightingLayout {
204    type Key = MeshPipelineKey;
205
206    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
207        let mut shader_defs = Vec::new();
208
209        // Let the shader code know that it's running in a deferred pipeline.
210        shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into());
211
212        #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
213        shader_defs.push("WEBGL2".into());
214
215        if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
216            shader_defs.push("TONEMAP_IN_SHADER".into());
217            shader_defs.push(ShaderDefVal::UInt(
218                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
219                TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
220            ));
221            shader_defs.push(ShaderDefVal::UInt(
222                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
223                TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
224            ));
225
226            let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
227
228            if method == MeshPipelineKey::TONEMAP_METHOD_LINEAR {
229                shader_defs.push("TONEMAP_METHOD_LINEAR".into());
230            } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
231                shader_defs.push("TONEMAP_METHOD_REINHARD".into());
232            } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
233                shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
234            } else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
235                shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
236            } else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
237                shader_defs.push("TONEMAP_METHOD_AGX".into());
238            } else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
239                shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
240            } else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
241                shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
242            } else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
243                shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
244            } else if method == MeshPipelineKey::TONEMAP_METHOD_PBR_NEUTRAL {
245                shader_defs.push("TONEMAP_METHOD_PBR_NEUTRAL".into());
246            }
247
248            // Debanding is tied to tonemapping in the shader, cannot run without it.
249            if key.contains(MeshPipelineKey::DEBAND_DITHER) {
250                shader_defs.push("DEBAND_DITHER".into());
251            }
252        }
253
254        if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
255            shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
256        }
257
258        if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
259            shader_defs.push("ENVIRONMENT_MAP".into());
260        }
261
262        if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) && IRRADIANCE_VOLUMES_ARE_USABLE {
263            shader_defs.push("IRRADIANCE_VOLUME".into());
264        }
265
266        if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
267            shader_defs.push("NORMAL_PREPASS".into());
268        }
269
270        if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
271            shader_defs.push("DEPTH_PREPASS".into());
272        }
273
274        if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
275            shader_defs.push("MOTION_VECTOR_PREPASS".into());
276        }
277
278        if key.contains(MeshPipelineKey::SCREEN_SPACE_REFLECTIONS) {
279            shader_defs.push("SCREEN_SPACE_REFLECTIONS".into());
280        }
281
282        if key.contains(MeshPipelineKey::CONTACT_SHADOWS) {
283            shader_defs.push("CONTACT_SHADOWS".into());
284        }
285
286        if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
287            shader_defs.push("HAS_PREVIOUS_SKIN".into());
288        }
289
290        if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
291            shader_defs.push("HAS_PREVIOUS_MORPH".into());
292        }
293
294        if key.contains(MeshPipelineKey::DISTANCE_FOG) {
295            shader_defs.push("DISTANCE_FOG".into());
296        }
297        if key.contains(MeshPipelineKey::ATMOSPHERE) {
298            shader_defs.push("ATMOSPHERE".into());
299        }
300        shader_defs.push("STANDARD_MATERIAL_CLEARCOAT".into());
301
302        // Always true, since we're in the deferred lighting pipeline
303        shader_defs.push("DEFERRED_PREPASS".into());
304
305        let shadow_filter_method =
306            key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
307        if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
308            shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
309        } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
310            shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
311        } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
312            shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
313        }
314        if self.mesh_pipeline.binding_arrays_are_usable {
315            shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
316            shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
317        }
318
319        if IRRADIANCE_VOLUMES_ARE_USABLE {
320            shader_defs.push("IRRADIANCE_VOLUMES_ARE_USABLE".into());
321        }
322
323        if self.mesh_pipeline.clustered_decals_are_usable {
324            shader_defs.push("CLUSTERED_DECALS_ARE_USABLE".into());
325            if truecfg!(feature = "pbr_light_textures") {
326                shader_defs.push("LIGHT_TEXTURES".into());
327            }
328        }
329
330        #[cfg(feature = "experimental_pbr_pcss")]
331        shader_defs.push("PCSS_SAMPLERS_AVAILABLE".into());
332
333        #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
334        shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
335
336        if truecfg!(feature = "bluenoise_texture") {
337            shader_defs.push("BLUE_NOISE_TEXTURE".into());
338        }
339        if truecfg!(feature = "dfg_lut") {
340            shader_defs.push("DFG_LUT".into());
341        }
342        if truecfg!(feature = "area_light_luts") {
343            shader_defs.push("AREA_LIGHT_LUTS".into());
344        }
345
346        let layout = self.mesh_pipeline.get_view_layout(key.into());
347        RenderPipelineDescriptor {
348            label: Some("deferred_lighting_pipeline".into()),
349            layout: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [layout.main_layout, layout.binding_array_layout,
                self.bind_group_layout_2.clone()]))vec![
350                layout.main_layout,
351                layout.binding_array_layout,
352                self.bind_group_layout_2.clone(),
353            ],
354            vertex: VertexState {
355                shader: self.deferred_lighting_shader.clone(),
356                shader_defs: shader_defs.clone(),
357                ..default()
358            },
359            fragment: Some(FragmentState {
360                shader: self.deferred_lighting_shader.clone(),
361                shader_defs,
362                targets: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Some(ColorTargetState {
                        format: key.target_format(),
                        blend: None,
                        write_mask: ColorWrites::ALL,
                    })]))vec![Some(ColorTargetState {
363                    format: key.target_format(),
364                    blend: None,
365                    write_mask: ColorWrites::ALL,
366                })],
367                ..default()
368            }),
369            depth_stencil: Some(DepthStencilState {
370                format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
371                depth_write_enabled: Some(false),
372                depth_compare: Some(CompareFunction::Equal),
373                stencil: StencilState {
374                    front: StencilFaceState::IGNORE,
375                    back: StencilFaceState::IGNORE,
376                    read_mask: 0,
377                    write_mask: 0,
378                },
379                bias: DepthBiasState {
380                    constant: 0,
381                    slope_scale: 0.0,
382                    clamp: 0.0,
383                },
384            }),
385            ..default()
386        }
387    }
388}
389
390pub fn init_deferred_lighting_layout(
391    mut commands: Commands,
392    mesh_pipeline: Res<MeshPipeline>,
393    asset_server: Res<AssetServer>,
394) {
395    let layout = BindGroupLayoutDescriptor::new(
396        "deferred_lighting_layout",
397        &BindGroupLayoutEntries::single(
398            ShaderStages::VERTEX_FRAGMENT,
399            uniform_buffer::<PbrDeferredLightingDepthId>(false),
400        ),
401    );
402    commands.insert_resource(DeferredLightingLayout {
403        mesh_pipeline: mesh_pipeline.clone(),
404        bind_group_layout_2: layout,
405        deferred_lighting_shader: {
    let (path, asset_server) =
        {
            let path =
                {
                    {
                        let crate_name =
                            "bevy_pbr::deferred".split(':').next().unwrap();
                        ::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
                            "src".as_ref(), "src/deferred/mod.rs".as_ref(),
                            "deferred_lighting.wesl".as_ref())
                    }
                };
            let path =
                ::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
            let asset_server =
                ::bevy_asset::io::embedded::GetAssetServer::get_asset_server(asset_server.as_ref());
            (path, asset_server)
        };
    asset_server.load(path)
}load_embedded_asset!(
406            asset_server.as_ref(),
407            "deferred_lighting.wesl"
408        ),
409    });
410}
411
412pub fn insert_deferred_lighting_pass_id_component(
413    mut commands: Commands,
414    views: Query<Entity, (With<DeferredPrepass>, Without<PbrDeferredLightingDepthId>)>,
415) {
416    for entity in views.iter() {
417        commands
418            .entity(entity)
419            .insert(PbrDeferredLightingDepthId::default());
420    }
421}
422
423pub fn prepare_deferred_lighting_pipelines(
424    mut commands: Commands,
425    pipeline_cache: Res<PipelineCache>,
426    view_key_cache: Res<ViewKeyCache>,
427    mut pipelines: ResMut<SpecializedRenderPipelines<DeferredLightingLayout>>,
428    deferred_lighting_layout: Res<DeferredLightingLayout>,
429    cameras: Query<
430        (
431            Entity,
432            &ExtractedView,
433            Has<DeferredPrepass>,
434            Has<SkipDeferredLighting>,
435        ),
436        With<ExtractedCamera>,
437    >,
438) {
439    for (entity, view, deferred_prepass, skip_deferred_lighting) in &cameras {
440        // If there is no deferred prepass or we want to skip the deferred lighting pass,
441        // remove the old pipeline if there was one. This handles the case in which a
442        // view using deferred stops using it.
443        if !deferred_prepass || skip_deferred_lighting {
444            commands.entity(entity).remove::<DeferredLightingPipeline>();
445            continue;
446        }
447
448        // The deferred lighting pass runs the same lighting code as the forward
449        // pass, so it must be specialized with the same view key. Reusing
450        // [`ViewKeyCache`] keeps the two in sync automatically.
451        let Some(view_key) = view_key_cache.get(&view.retained_view_entity) else {
452            continue;
453        };
454
455        let pipeline_id =
456            pipelines.specialize(&pipeline_cache, &deferred_lighting_layout, *view_key);
457
458        commands
459            .entity(entity)
460            .insert(DeferredLightingPipeline { pipeline_id });
461    }
462}
463
464/// Component to skip running the deferred lighting pass in [`deferred_lighting`] for a specific view.
465///
466/// This works like [`crate::PbrPlugin::add_default_deferred_lighting_plugin`], but is per-view instead of global.
467///
468/// Useful for cases where you want to generate a gbuffer, but skip the built-in deferred lighting pass
469/// to run your own custom lighting pass instead.
470///
471/// Insert this component in the render world only.
472#[derive(impl bevy_ecs::component::Component for SkipDeferredLighting where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::Table;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {}
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}Component, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for SkipDeferredLighting { }
#[automatically_derived]
impl ::core::clone::Clone for SkipDeferredLighting {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SkipDeferredLighting { }Copy, #[automatically_derived]
impl ::core::default::Default for SkipDeferredLighting {
    #[inline]
    fn default() -> Self { Self }
}Default)]
473pub struct SkipDeferredLighting;