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 Wireframe,
23 Bloom,
24 PostProcessing,
25 Tonemapping,
26 Fxaa,
27 Smaa,
28 Upscaling,
29 ContrastAdaptiveSharpening,
30 EndMainPassPostProcessing,
31 }
32}
33
34use core::ops::Range;
35
36use bevy_asset::UntypedAssetId;
37use bevy_platform::collections::{HashMap, HashSet};
38use bevy_render::{
39 batching::gpu_preprocessing::GpuPreprocessingMode,
40 render_phase::PhaseItemBatchSetKey,
41 view::{ExtractedView, RetainedViewEntity},
42};
43pub use camera_2d::*;
44pub use main_opaque_pass_2d_node::*;
45pub use main_transparent_pass_2d_node::*;
46
47use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
48use bevy_app::{App, Plugin};
49use bevy_ecs::prelude::*;
50use bevy_math::FloatOrd;
51use bevy_render::{
52 camera::{Camera, ExtractedCamera},
53 extract_component::ExtractComponentPlugin,
54 render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
55 render_phase::{
56 sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
57 DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
58 ViewSortedRenderPhases,
59 },
60 render_resource::{
61 BindGroupId, CachedRenderPipelineId, Extent3d, TextureDescriptor, TextureDimension,
62 TextureFormat, TextureUsages,
63 },
64 renderer::RenderDevice,
65 sync_world::MainEntity,
66 texture::TextureCache,
67 view::{Msaa, ViewDepthTexture},
68 Extract, ExtractSchedule, Render, RenderApp, RenderSet,
69};
70
71use self::graph::{Core2d, Node2d};
72
73pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
74
75pub struct Core2dPlugin;
76
77impl Plugin for Core2dPlugin {
78 fn build(&self, app: &mut App) {
79 app.register_type::<Camera2d>()
80 .add_plugins(ExtractComponentPlugin::<Camera2d>::default());
81
82 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
83 return;
84 };
85 render_app
86 .init_resource::<DrawFunctions<Opaque2d>>()
87 .init_resource::<DrawFunctions<AlphaMask2d>>()
88 .init_resource::<DrawFunctions<Transparent2d>>()
89 .init_resource::<ViewSortedRenderPhases<Transparent2d>>()
90 .init_resource::<ViewBinnedRenderPhases<Opaque2d>>()
91 .init_resource::<ViewBinnedRenderPhases<AlphaMask2d>>()
92 .add_systems(ExtractSchedule, extract_core_2d_camera_phases)
93 .add_systems(
94 Render,
95 (
96 sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort),
97 prepare_core_2d_depth_textures.in_set(RenderSet::PrepareResources),
98 ),
99 );
100
101 render_app
102 .add_render_sub_graph(Core2d)
103 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::StartMainPass)
104 .add_render_graph_node::<ViewNodeRunner<MainOpaquePass2dNode>>(
105 Core2d,
106 Node2d::MainOpaquePass,
107 )
108 .add_render_graph_node::<ViewNodeRunner<MainTransparentPass2dNode>>(
109 Core2d,
110 Node2d::MainTransparentPass,
111 )
112 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPass)
113 .add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(Core2d, Node2d::Tonemapping)
114 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPassPostProcessing)
115 .add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(Core2d, Node2d::Upscaling)
116 .add_render_graph_edges(
117 Core2d,
118 (
119 Node2d::StartMainPass,
120 Node2d::MainOpaquePass,
121 Node2d::MainTransparentPass,
122 Node2d::EndMainPass,
123 Node2d::Tonemapping,
124 Node2d::EndMainPassPostProcessing,
125 Node2d::Upscaling,
126 ),
127 );
128 }
129}
130
131pub struct Opaque2d {
133 pub batch_set_key: BatchSetKey2d,
138 pub bin_key: Opaque2dBinKey,
140 pub representative_entity: (Entity, MainEntity),
143 pub batch_range: Range<u32>,
145 pub extra_index: PhaseItemExtraIndex,
148}
149
150#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
152pub struct Opaque2dBinKey {
153 pub pipeline: CachedRenderPipelineId,
155 pub draw_function: DrawFunctionId,
157 pub asset_id: UntypedAssetId,
162 pub material_bind_group_id: Option<BindGroupId>,
164}
165
166impl PhaseItem for Opaque2d {
167 #[inline]
168 fn entity(&self) -> Entity {
169 self.representative_entity.0
170 }
171
172 fn main_entity(&self) -> MainEntity {
173 self.representative_entity.1
174 }
175
176 #[inline]
177 fn draw_function(&self) -> DrawFunctionId {
178 self.bin_key.draw_function
179 }
180
181 #[inline]
182 fn batch_range(&self) -> &Range<u32> {
183 &self.batch_range
184 }
185
186 #[inline]
187 fn batch_range_mut(&mut self) -> &mut Range<u32> {
188 &mut self.batch_range
189 }
190
191 fn extra_index(&self) -> PhaseItemExtraIndex {
192 self.extra_index.clone()
193 }
194
195 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
196 (&mut self.batch_range, &mut self.extra_index)
197 }
198}
199
200impl BinnedPhaseItem for Opaque2d {
201 type BatchSetKey = BatchSetKey2d;
204
205 type BinKey = Opaque2dBinKey;
206
207 fn new(
208 batch_set_key: Self::BatchSetKey,
209 bin_key: Self::BinKey,
210 representative_entity: (Entity, MainEntity),
211 batch_range: Range<u32>,
212 extra_index: PhaseItemExtraIndex,
213 ) -> Self {
214 Opaque2d {
215 batch_set_key,
216 bin_key,
217 representative_entity,
218 batch_range,
219 extra_index,
220 }
221 }
222}
223
224#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
227pub struct BatchSetKey2d {
228 pub indexed: bool,
230}
231
232impl PhaseItemBatchSetKey for BatchSetKey2d {
233 fn indexed(&self) -> bool {
234 self.indexed
235 }
236}
237
238impl CachedRenderPipelinePhaseItem for Opaque2d {
239 #[inline]
240 fn cached_pipeline(&self) -> CachedRenderPipelineId {
241 self.bin_key.pipeline
242 }
243}
244
245pub struct AlphaMask2d {
247 pub batch_set_key: BatchSetKey2d,
252 pub bin_key: AlphaMask2dBinKey,
254 pub representative_entity: (Entity, MainEntity),
257 pub batch_range: Range<u32>,
259 pub extra_index: PhaseItemExtraIndex,
262}
263
264#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
266pub struct AlphaMask2dBinKey {
267 pub pipeline: CachedRenderPipelineId,
269 pub draw_function: DrawFunctionId,
271 pub asset_id: UntypedAssetId,
276 pub material_bind_group_id: Option<BindGroupId>,
278}
279
280impl PhaseItem for AlphaMask2d {
281 #[inline]
282 fn entity(&self) -> Entity {
283 self.representative_entity.0
284 }
285
286 #[inline]
287 fn main_entity(&self) -> MainEntity {
288 self.representative_entity.1
289 }
290
291 #[inline]
292 fn draw_function(&self) -> DrawFunctionId {
293 self.bin_key.draw_function
294 }
295
296 #[inline]
297 fn batch_range(&self) -> &Range<u32> {
298 &self.batch_range
299 }
300
301 #[inline]
302 fn batch_range_mut(&mut self) -> &mut Range<u32> {
303 &mut self.batch_range
304 }
305
306 fn extra_index(&self) -> PhaseItemExtraIndex {
307 self.extra_index.clone()
308 }
309
310 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
311 (&mut self.batch_range, &mut self.extra_index)
312 }
313}
314
315impl BinnedPhaseItem for AlphaMask2d {
316 type BatchSetKey = BatchSetKey2d;
319
320 type BinKey = AlphaMask2dBinKey;
321
322 fn new(
323 batch_set_key: Self::BatchSetKey,
324 bin_key: Self::BinKey,
325 representative_entity: (Entity, MainEntity),
326 batch_range: Range<u32>,
327 extra_index: PhaseItemExtraIndex,
328 ) -> Self {
329 AlphaMask2d {
330 batch_set_key,
331 bin_key,
332 representative_entity,
333 batch_range,
334 extra_index,
335 }
336 }
337}
338
339impl CachedRenderPipelinePhaseItem for AlphaMask2d {
340 #[inline]
341 fn cached_pipeline(&self) -> CachedRenderPipelineId {
342 self.bin_key.pipeline
343 }
344}
345
346pub struct Transparent2d {
348 pub sort_key: FloatOrd,
349 pub entity: (Entity, MainEntity),
350 pub pipeline: CachedRenderPipelineId,
351 pub draw_function: DrawFunctionId,
352 pub batch_range: Range<u32>,
353 pub extracted_index: usize,
354 pub extra_index: PhaseItemExtraIndex,
355 pub indexed: bool,
358}
359
360impl PhaseItem for Transparent2d {
361 #[inline]
362 fn entity(&self) -> Entity {
363 self.entity.0
364 }
365
366 #[inline]
367 fn main_entity(&self) -> MainEntity {
368 self.entity.1
369 }
370
371 #[inline]
372 fn draw_function(&self) -> DrawFunctionId {
373 self.draw_function
374 }
375
376 #[inline]
377 fn batch_range(&self) -> &Range<u32> {
378 &self.batch_range
379 }
380
381 #[inline]
382 fn batch_range_mut(&mut self) -> &mut Range<u32> {
383 &mut self.batch_range
384 }
385
386 #[inline]
387 fn extra_index(&self) -> PhaseItemExtraIndex {
388 self.extra_index.clone()
389 }
390
391 #[inline]
392 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
393 (&mut self.batch_range, &mut self.extra_index)
394 }
395}
396
397impl SortedPhaseItem for Transparent2d {
398 type SortKey = FloatOrd;
399
400 #[inline]
401 fn sort_key(&self) -> Self::SortKey {
402 self.sort_key
403 }
404
405 #[inline]
406 fn sort(items: &mut [Self]) {
407 radsort::sort_by_key(items, |item| item.sort_key().0);
409 }
410
411 fn indexed(&self) -> bool {
412 self.indexed
413 }
414}
415
416impl CachedRenderPipelinePhaseItem for Transparent2d {
417 #[inline]
418 fn cached_pipeline(&self) -> CachedRenderPipelineId {
419 self.pipeline
420 }
421}
422
423pub fn extract_core_2d_camera_phases(
424 mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
425 mut opaque_2d_phases: ResMut<ViewBinnedRenderPhases<Opaque2d>>,
426 mut alpha_mask_2d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask2d>>,
427 cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
428 mut live_entities: Local<HashSet<RetainedViewEntity>>,
429) {
430 live_entities.clear();
431
432 for (main_entity, camera) in &cameras_2d {
433 if !camera.is_active {
434 continue;
435 }
436
437 let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
439
440 transparent_2d_phases.insert_or_clear(retained_view_entity);
441 opaque_2d_phases.prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
442 alpha_mask_2d_phases
443 .prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
444
445 live_entities.insert(retained_view_entity);
446 }
447
448 transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
450 opaque_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
451 alpha_mask_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
452}
453
454pub fn prepare_core_2d_depth_textures(
455 mut commands: Commands,
456 mut texture_cache: ResMut<TextureCache>,
457 render_device: Res<RenderDevice>,
458 transparent_2d_phases: Res<ViewSortedRenderPhases<Transparent2d>>,
459 opaque_2d_phases: Res<ViewBinnedRenderPhases<Opaque2d>>,
460 views_2d: Query<(Entity, &ExtractedCamera, &ExtractedView, &Msaa), (With<Camera2d>,)>,
461) {
462 let mut textures = <HashMap<_, _>>::default();
463 for (view, camera, extracted_view, msaa) in &views_2d {
464 if !opaque_2d_phases.contains_key(&extracted_view.retained_view_entity)
465 || !transparent_2d_phases.contains_key(&extracted_view.retained_view_entity)
466 {
467 continue;
468 };
469
470 let Some(physical_target_size) = camera.physical_target_size else {
471 continue;
472 };
473
474 let cached_texture = textures
475 .entry(camera.target.clone())
476 .or_insert_with(|| {
477 let size = Extent3d {
479 depth_or_array_layers: 1,
480 width: physical_target_size.x,
481 height: physical_target_size.y,
482 };
483
484 let descriptor = TextureDescriptor {
485 label: Some("view_depth_texture"),
486 size,
487 mip_level_count: 1,
488 sample_count: msaa.samples(),
489 dimension: TextureDimension::D2,
490 format: CORE_2D_DEPTH_FORMAT,
491 usage: TextureUsages::RENDER_ATTACHMENT,
492 view_formats: &[],
493 };
494
495 texture_cache.get(&render_device, descriptor)
496 })
497 .clone();
498
499 commands
500 .entity(view)
501 .insert(ViewDepthTexture::new(cached_texture, Some(0.0)));
502 }
503}