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 &pipeline_cache.get_bind_group_layout(©_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: BindGroupLayoutDescriptor,
119 pipeline_id: CachedRenderPipelineId,
120}
121
122pub fn init_copy_deferred_lighting_id_pipeline(
123 mut commands: Commands,
124 fullscreen_shader: Res<FullscreenShader>,
125 asset_server: Res<AssetServer>,
126 pipeline_cache: Res<PipelineCache>,
127) {
128 let layout = BindGroupLayoutDescriptor::new(
129 "copy_deferred_lighting_id_bind_group_layout",
130 &BindGroupLayoutEntries::single(
131 ShaderStages::FRAGMENT,
132 texture_2d(TextureSampleType::Uint),
133 ),
134 );
135
136 let vertex_state = fullscreen_shader.to_vertex_state();
137 let shader = load_embedded_asset!(asset_server.as_ref(), "copy_deferred_lighting_id.wgsl");
138
139 let pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
140 label: Some("copy_deferred_lighting_id_pipeline".into()),
141 layout: vec![layout.clone()],
142 vertex: vertex_state,
143 fragment: Some(FragmentState {
144 shader,
145 ..default()
146 }),
147 depth_stencil: Some(DepthStencilState {
148 format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
149 depth_write_enabled: true,
150 depth_compare: CompareFunction::Always,
151 stencil: StencilState::default(),
152 bias: DepthBiasState::default(),
153 }),
154 ..default()
155 });
156
157 commands.insert_resource(CopyDeferredLightingIdPipeline {
158 layout,
159 pipeline_id,
160 });
161}
162
163#[derive(Component)]
164pub struct DeferredLightingIdDepthTexture {
165 pub texture: CachedTexture,
166}
167
168fn prepare_deferred_lighting_id_textures(
169 mut commands: Commands,
170 mut texture_cache: ResMut<TextureCache>,
171 render_device: Res<RenderDevice>,
172 views: Query<(Entity, &ExtractedCamera), With<DeferredPrepass>>,
173) {
174 for (entity, camera) in &views {
175 if let Some(physical_target_size) = camera.physical_target_size {
176 let texture_descriptor = TextureDescriptor {
177 label: Some("deferred_lighting_id_depth_texture_a"),
178 size: physical_target_size.to_extents(),
179 mip_level_count: 1,
180 sample_count: 1,
181 dimension: TextureDimension::D2,
182 format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
183 usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
184 view_formats: &[],
185 };
186 let texture = texture_cache.get(&render_device, texture_descriptor);
187 commands
188 .entity(entity)
189 .insert(DeferredLightingIdDepthTexture { texture });
190 }
191 }
192}