1mod camera_2d;
2mod main_opaque_pass_2d_node;
3mod main_transparent_pass_2d_node;
4
5pub mod graph {
6 use bevy_render::render_graph::{RenderLabel, RenderSubGraph};
7
8 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderSubGraph)]
9 pub struct Core2d;
10
11 pub mod input {
12 pub const VIEW_ENTITY: &str = "view_entity";
13 }
14
15 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
16 pub enum Node2d {
17 MsaaWriteback,
18 StartMainPass,
19 MainOpaquePass,
20 MainTransparentPass,
21 EndMainPass,
22 Bloom,
23 PostProcessing,
24 Tonemapping,
25 Fxaa,
26 Smaa,
27 Upscaling,
28 ContrastAdaptiveSharpening,
29 EndMainPassPostProcessing,
30 }
31}
32
33use core::ops::Range;
34
35use bevy_asset::UntypedAssetId;
36use bevy_utils::HashMap;
37pub use camera_2d::*;
38pub use main_opaque_pass_2d_node::*;
39pub use main_transparent_pass_2d_node::*;
40
41use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
42use bevy_app::{App, Plugin};
43use bevy_ecs::{entity::EntityHashSet, prelude::*};
44use bevy_math::FloatOrd;
45use bevy_render::sync_world::MainEntity;
46use bevy_render::{
47 camera::{Camera, ExtractedCamera},
48 extract_component::ExtractComponentPlugin,
49 render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
50 render_phase::{
51 sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
52 DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
53 ViewSortedRenderPhases,
54 },
55 render_resource::{
56 BindGroupId, CachedRenderPipelineId, Extent3d, TextureDescriptor, TextureDimension,
57 TextureFormat, TextureUsages,
58 },
59 renderer::RenderDevice,
60 sync_world::RenderEntity,
61 texture::TextureCache,
62 view::{Msaa, ViewDepthTexture},
63 Extract, ExtractSchedule, Render, RenderApp, RenderSet,
64};
65
66use self::graph::{Core2d, Node2d};
67
68pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
69
70pub struct Core2dPlugin;
71
72impl Plugin for Core2dPlugin {
73 fn build(&self, app: &mut App) {
74 app.register_type::<Camera2d>()
75 .add_plugins(ExtractComponentPlugin::<Camera2d>::default());
76
77 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
78 return;
79 };
80 render_app
81 .init_resource::<DrawFunctions<Opaque2d>>()
82 .init_resource::<DrawFunctions<AlphaMask2d>>()
83 .init_resource::<DrawFunctions<Transparent2d>>()
84 .init_resource::<ViewSortedRenderPhases<Transparent2d>>()
85 .init_resource::<ViewBinnedRenderPhases<Opaque2d>>()
86 .init_resource::<ViewBinnedRenderPhases<AlphaMask2d>>()
87 .add_systems(ExtractSchedule, extract_core_2d_camera_phases)
88 .add_systems(
89 Render,
90 (
91 sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort),
92 prepare_core_2d_depth_textures.in_set(RenderSet::PrepareResources),
93 ),
94 );
95
96 render_app
97 .add_render_sub_graph(Core2d)
98 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::StartMainPass)
99 .add_render_graph_node::<ViewNodeRunner<MainOpaquePass2dNode>>(
100 Core2d,
101 Node2d::MainOpaquePass,
102 )
103 .add_render_graph_node::<ViewNodeRunner<MainTransparentPass2dNode>>(
104 Core2d,
105 Node2d::MainTransparentPass,
106 )
107 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPass)
108 .add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(Core2d, Node2d::Tonemapping)
109 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPassPostProcessing)
110 .add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(Core2d, Node2d::Upscaling)
111 .add_render_graph_edges(
112 Core2d,
113 (
114 Node2d::StartMainPass,
115 Node2d::MainOpaquePass,
116 Node2d::MainTransparentPass,
117 Node2d::EndMainPass,
118 Node2d::Tonemapping,
119 Node2d::EndMainPassPostProcessing,
120 Node2d::Upscaling,
121 ),
122 );
123 }
124}
125
126pub struct Opaque2d {
128 pub key: Opaque2dBinKey,
130 pub representative_entity: (Entity, MainEntity),
133 pub batch_range: Range<u32>,
135 pub extra_index: PhaseItemExtraIndex,
138}
139
140#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
142pub struct Opaque2dBinKey {
143 pub pipeline: CachedRenderPipelineId,
145 pub draw_function: DrawFunctionId,
147 pub asset_id: UntypedAssetId,
152 pub material_bind_group_id: Option<BindGroupId>,
154}
155
156impl PhaseItem for Opaque2d {
157 #[inline]
158 fn entity(&self) -> Entity {
159 self.representative_entity.0
160 }
161
162 fn main_entity(&self) -> MainEntity {
163 self.representative_entity.1
164 }
165
166 #[inline]
167 fn draw_function(&self) -> DrawFunctionId {
168 self.key.draw_function
169 }
170
171 #[inline]
172 fn batch_range(&self) -> &Range<u32> {
173 &self.batch_range
174 }
175
176 #[inline]
177 fn batch_range_mut(&mut self) -> &mut Range<u32> {
178 &mut self.batch_range
179 }
180
181 fn extra_index(&self) -> PhaseItemExtraIndex {
182 self.extra_index
183 }
184
185 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
186 (&mut self.batch_range, &mut self.extra_index)
187 }
188}
189
190impl BinnedPhaseItem for Opaque2d {
191 type BinKey = Opaque2dBinKey;
192
193 fn new(
194 key: Self::BinKey,
195 representative_entity: (Entity, MainEntity),
196 batch_range: Range<u32>,
197 extra_index: PhaseItemExtraIndex,
198 ) -> Self {
199 Opaque2d {
200 key,
201 representative_entity,
202 batch_range,
203 extra_index,
204 }
205 }
206}
207
208impl CachedRenderPipelinePhaseItem for Opaque2d {
209 #[inline]
210 fn cached_pipeline(&self) -> CachedRenderPipelineId {
211 self.key.pipeline
212 }
213}
214
215pub struct AlphaMask2d {
217 pub key: AlphaMask2dBinKey,
219 pub representative_entity: (Entity, MainEntity),
222 pub batch_range: Range<u32>,
224 pub extra_index: PhaseItemExtraIndex,
227}
228
229#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
231pub struct AlphaMask2dBinKey {
232 pub pipeline: CachedRenderPipelineId,
234 pub draw_function: DrawFunctionId,
236 pub asset_id: UntypedAssetId,
241 pub material_bind_group_id: Option<BindGroupId>,
243}
244
245impl PhaseItem for AlphaMask2d {
246 #[inline]
247 fn entity(&self) -> Entity {
248 self.representative_entity.0
249 }
250
251 #[inline]
252 fn main_entity(&self) -> MainEntity {
253 self.representative_entity.1
254 }
255
256 #[inline]
257 fn draw_function(&self) -> DrawFunctionId {
258 self.key.draw_function
259 }
260
261 #[inline]
262 fn batch_range(&self) -> &Range<u32> {
263 &self.batch_range
264 }
265
266 #[inline]
267 fn batch_range_mut(&mut self) -> &mut Range<u32> {
268 &mut self.batch_range
269 }
270
271 fn extra_index(&self) -> PhaseItemExtraIndex {
272 self.extra_index
273 }
274
275 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
276 (&mut self.batch_range, &mut self.extra_index)
277 }
278}
279
280impl BinnedPhaseItem for AlphaMask2d {
281 type BinKey = AlphaMask2dBinKey;
282
283 fn new(
284 key: Self::BinKey,
285 representative_entity: (Entity, MainEntity),
286 batch_range: Range<u32>,
287 extra_index: PhaseItemExtraIndex,
288 ) -> Self {
289 AlphaMask2d {
290 key,
291 representative_entity,
292 batch_range,
293 extra_index,
294 }
295 }
296}
297
298impl CachedRenderPipelinePhaseItem for AlphaMask2d {
299 #[inline]
300 fn cached_pipeline(&self) -> CachedRenderPipelineId {
301 self.key.pipeline
302 }
303}
304
305pub struct Transparent2d {
307 pub sort_key: FloatOrd,
308 pub entity: (Entity, MainEntity),
309 pub pipeline: CachedRenderPipelineId,
310 pub draw_function: DrawFunctionId,
311 pub batch_range: Range<u32>,
312 pub extra_index: PhaseItemExtraIndex,
313}
314
315impl PhaseItem for Transparent2d {
316 #[inline]
317 fn entity(&self) -> Entity {
318 self.entity.0
319 }
320
321 #[inline]
322 fn main_entity(&self) -> MainEntity {
323 self.entity.1
324 }
325
326 #[inline]
327 fn draw_function(&self) -> DrawFunctionId {
328 self.draw_function
329 }
330
331 #[inline]
332 fn batch_range(&self) -> &Range<u32> {
333 &self.batch_range
334 }
335
336 #[inline]
337 fn batch_range_mut(&mut self) -> &mut Range<u32> {
338 &mut self.batch_range
339 }
340
341 #[inline]
342 fn extra_index(&self) -> PhaseItemExtraIndex {
343 self.extra_index
344 }
345
346 #[inline]
347 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
348 (&mut self.batch_range, &mut self.extra_index)
349 }
350}
351
352impl SortedPhaseItem for Transparent2d {
353 type SortKey = FloatOrd;
354
355 #[inline]
356 fn sort_key(&self) -> Self::SortKey {
357 self.sort_key
358 }
359
360 #[inline]
361 fn sort(items: &mut [Self]) {
362 radsort::sort_by_key(items, |item| item.sort_key().0);
364 }
365}
366
367impl CachedRenderPipelinePhaseItem for Transparent2d {
368 #[inline]
369 fn cached_pipeline(&self) -> CachedRenderPipelineId {
370 self.pipeline
371 }
372}
373
374pub fn extract_core_2d_camera_phases(
375 mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
376 mut opaque_2d_phases: ResMut<ViewBinnedRenderPhases<Opaque2d>>,
377 mut alpha_mask_2d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask2d>>,
378 cameras_2d: Extract<Query<(RenderEntity, &Camera), With<Camera2d>>>,
379 mut live_entities: Local<EntityHashSet>,
380) {
381 live_entities.clear();
382
383 for (entity, camera) in &cameras_2d {
384 if !camera.is_active {
385 continue;
386 }
387 transparent_2d_phases.insert_or_clear(entity);
388 opaque_2d_phases.insert_or_clear(entity);
389 alpha_mask_2d_phases.insert_or_clear(entity);
390
391 live_entities.insert(entity);
392 }
393
394 transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
396 opaque_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
397 alpha_mask_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
398}
399
400pub fn prepare_core_2d_depth_textures(
401 mut commands: Commands,
402 mut texture_cache: ResMut<TextureCache>,
403 render_device: Res<RenderDevice>,
404 transparent_2d_phases: Res<ViewSortedRenderPhases<Transparent2d>>,
405 opaque_2d_phases: Res<ViewBinnedRenderPhases<Opaque2d>>,
406 views_2d: Query<(Entity, &ExtractedCamera, &Msaa), (With<Camera2d>,)>,
407) {
408 let mut textures = HashMap::default();
409 for (view, camera, msaa) in &views_2d {
410 if !opaque_2d_phases.contains_key(&view) || !transparent_2d_phases.contains_key(&view) {
411 continue;
412 };
413
414 let Some(physical_target_size) = camera.physical_target_size else {
415 continue;
416 };
417
418 let cached_texture = textures
419 .entry(camera.target.clone())
420 .or_insert_with(|| {
421 let size = Extent3d {
423 depth_or_array_layers: 1,
424 width: physical_target_size.x,
425 height: physical_target_size.y,
426 };
427
428 let descriptor = TextureDescriptor {
429 label: Some("view_depth_texture"),
430 size,
431 mip_level_count: 1,
432 sample_count: msaa.samples(),
433 dimension: TextureDimension::D2,
434 format: CORE_2D_DEPTH_FORMAT,
435 usage: TextureUsages::RENDER_ATTACHMENT,
436 view_formats: &[],
437 };
438
439 texture_cache.get(&render_device, descriptor)
440 })
441 .clone();
442
443 commands
444 .entity(view)
445 .insert(ViewDepthTexture::new(cached_texture, Some(0.0)));
446 }
447}