bevy_core_pipeline/deferred/
copy_lighting_id.rs

1use crate::{
2    prepass::{DeferredPrepass, ViewPrepassTextures},
3    FullscreenShader,
4};
5use bevy_app::prelude::*;
6use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer};
7use bevy_ecs::prelude::*;
8use bevy_image::ToExtents;
9use bevy_render::{
10    camera::ExtractedCamera,
11    diagnostic::RecordDiagnostics,
12    render_resource::{binding_types::texture_2d, *},
13    renderer::RenderDevice,
14    texture::{CachedTexture, TextureCache},
15    view::ViewTarget,
16    Render, RenderApp, RenderStartup, RenderSystems,
17};
18
19use super::DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT;
20use bevy_ecs::query::QueryItem;
21use bevy_render::{
22    render_graph::{NodeRunError, RenderGraphContext, ViewNode},
23    renderer::RenderContext,
24};
25use bevy_utils::default;
26
27pub struct CopyDeferredLightingIdPlugin;
28
29impl Plugin for CopyDeferredLightingIdPlugin {
30    fn build(&self, app: &mut App) {
31        embedded_asset!(app, "copy_deferred_lighting_id.wgsl");
32        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
33            return;
34        };
35        render_app
36            .add_systems(RenderStartup, init_copy_deferred_lighting_id_pipeline)
37            .add_systems(
38                Render,
39                (prepare_deferred_lighting_id_textures.in_set(RenderSystems::PrepareResources),),
40            );
41    }
42}
43
44#[derive(Default)]
45pub struct CopyDeferredLightingIdNode;
46impl CopyDeferredLightingIdNode {
47    pub const NAME: &'static str = "copy_deferred_lighting_id";
48}
49
50impl ViewNode for CopyDeferredLightingIdNode {
51    type ViewQuery = (
52        &'static ViewTarget,
53        &'static ViewPrepassTextures,
54        &'static DeferredLightingIdDepthTexture,
55    );
56
57    fn run(
58        &self,
59        _graph: &mut RenderGraphContext,
60        render_context: &mut RenderContext,
61        (_view_target, view_prepass_textures, deferred_lighting_id_depth_texture): QueryItem<
62            Self::ViewQuery,
63        >,
64        world: &World,
65    ) -> Result<(), NodeRunError> {
66        let copy_deferred_lighting_id_pipeline = world.resource::<CopyDeferredLightingIdPipeline>();
67
68        let pipeline_cache = world.resource::<PipelineCache>();
69
70        let Some(pipeline) =
71            pipeline_cache.get_render_pipeline(copy_deferred_lighting_id_pipeline.pipeline_id)
72        else {
73            return Ok(());
74        };
75        let Some(deferred_lighting_pass_id_texture) =
76            &view_prepass_textures.deferred_lighting_pass_id
77        else {
78            return Ok(());
79        };
80
81        let diagnostics = render_context.diagnostic_recorder();
82
83        let bind_group = render_context.render_device().create_bind_group(
84            "copy_deferred_lighting_id_bind_group",
85            &copy_deferred_lighting_id_pipeline.layout,
86            &BindGroupEntries::single(&deferred_lighting_pass_id_texture.texture.default_view),
87        );
88
89        let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
90            label: Some("copy_deferred_lighting_id"),
91            color_attachments: &[],
92            depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
93                view: &deferred_lighting_id_depth_texture.texture.default_view,
94                depth_ops: Some(Operations {
95                    load: LoadOp::Clear(0.0),
96                    store: StoreOp::Store,
97                }),
98                stencil_ops: None,
99            }),
100            timestamp_writes: None,
101            occlusion_query_set: None,
102        });
103
104        let pass_span = diagnostics.pass_span(&mut render_pass, "copy_deferred_lighting_id");
105
106        render_pass.set_render_pipeline(pipeline);
107        render_pass.set_bind_group(0, &bind_group, &[]);
108        render_pass.draw(0..3, 0..1);
109
110        pass_span.end(&mut render_pass);
111
112        Ok(())
113    }
114}
115
116#[derive(Resource)]
117struct CopyDeferredLightingIdPipeline {
118    layout: BindGroupLayout,
119    pipeline_id: CachedRenderPipelineId,
120}
121
122pub fn init_copy_deferred_lighting_id_pipeline(
123    mut commands: Commands,
124    render_device: Res<RenderDevice>,
125    fullscreen_shader: Res<FullscreenShader>,
126    asset_server: Res<AssetServer>,
127    pipeline_cache: Res<PipelineCache>,
128) {
129    let layout = render_device.create_bind_group_layout(
130        "copy_deferred_lighting_id_bind_group_layout",
131        &BindGroupLayoutEntries::single(
132            ShaderStages::FRAGMENT,
133            texture_2d(TextureSampleType::Uint),
134        ),
135    );
136
137    let vertex_state = fullscreen_shader.to_vertex_state();
138    let shader = load_embedded_asset!(asset_server.as_ref(), "copy_deferred_lighting_id.wgsl");
139
140    let pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
141        label: Some("copy_deferred_lighting_id_pipeline".into()),
142        layout: vec![layout.clone()],
143        vertex: vertex_state,
144        fragment: Some(FragmentState {
145            shader,
146            ..default()
147        }),
148        depth_stencil: Some(DepthStencilState {
149            format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
150            depth_write_enabled: true,
151            depth_compare: CompareFunction::Always,
152            stencil: StencilState::default(),
153            bias: DepthBiasState::default(),
154        }),
155        ..default()
156    });
157
158    commands.insert_resource(CopyDeferredLightingIdPipeline {
159        layout,
160        pipeline_id,
161    });
162}
163
164#[derive(Component)]
165pub struct DeferredLightingIdDepthTexture {
166    pub texture: CachedTexture,
167}
168
169fn prepare_deferred_lighting_id_textures(
170    mut commands: Commands,
171    mut texture_cache: ResMut<TextureCache>,
172    render_device: Res<RenderDevice>,
173    views: Query<(Entity, &ExtractedCamera), With<DeferredPrepass>>,
174) {
175    for (entity, camera) in &views {
176        if let Some(physical_target_size) = camera.physical_target_size {
177            let texture_descriptor = TextureDescriptor {
178                label: Some("deferred_lighting_id_depth_texture_a"),
179                size: physical_target_size.to_extents(),
180                mip_level_count: 1,
181                sample_count: 1,
182                dimension: TextureDimension::D2,
183                format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
184                usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
185                view_formats: &[],
186            };
187            let texture = texture_cache.get(&render_device, texture_descriptor);
188            commands
189                .entity(entity)
190                .insert(DeferredLightingIdDepthTexture { texture });
191        }
192    }
193}