bevy_core_pipeline/deferred/
copy_lighting_id.rs1use 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 ©_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}