1mod main_opaque_pass_2d_node;
2mod main_transparent_pass_2d_node;
3
4pub mod graph {
5 use bevy_render::render_graph::{RenderLabel, RenderSubGraph};
6
7 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderSubGraph)]
8 pub struct Core2d;
9
10 pub mod input {
11 pub const VIEW_ENTITY: &str = "view_entity";
12 }
13
14 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
15 pub enum Node2d {
16 MsaaWriteback,
17 StartMainPass,
18 MainOpaquePass,
19 MainTransparentPass,
20 EndMainPass,
21 Wireframe,
22 StartMainPassPostProcessing,
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_camera::{Camera, Camera2d};
38use bevy_image::ToExtents;
39use bevy_platform::collections::{HashMap, HashSet};
40use bevy_render::{
41 batching::gpu_preprocessing::GpuPreprocessingMode,
42 camera::CameraRenderGraph,
43 render_phase::PhaseItemBatchSetKey,
44 view::{ExtractedView, RetainedViewEntity},
45};
46pub use main_opaque_pass_2d_node::*;
47pub use main_transparent_pass_2d_node::*;
48
49use crate::{
50 tonemapping::{DebandDither, Tonemapping, TonemappingNode},
51 upscaling::UpscalingNode,
52};
53use bevy_app::{App, Plugin};
54use bevy_ecs::prelude::*;
55use bevy_math::FloatOrd;
56use bevy_render::{
57 camera::ExtractedCamera,
58 extract_component::ExtractComponentPlugin,
59 render_graph::{EmptyNode, RenderGraphExt, ViewNodeRunner},
60 render_phase::{
61 sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
62 DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
63 ViewSortedRenderPhases,
64 },
65 render_resource::{
66 BindGroupId, CachedRenderPipelineId, TextureDescriptor, TextureDimension, TextureFormat,
67 TextureUsages,
68 },
69 renderer::RenderDevice,
70 sync_world::MainEntity,
71 texture::TextureCache,
72 view::{Msaa, ViewDepthTexture},
73 Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
74};
75
76use self::graph::{Core2d, Node2d};
77
78pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
79
80pub struct Core2dPlugin;
81
82impl Plugin for Core2dPlugin {
83 fn build(&self, app: &mut App) {
84 app.register_required_components::<Camera2d, DebandDither>()
85 .register_required_components_with::<Camera2d, CameraRenderGraph>(|| {
86 CameraRenderGraph::new(Core2d)
87 })
88 .register_required_components_with::<Camera2d, Tonemapping>(|| Tonemapping::None)
89 .add_plugins(ExtractComponentPlugin::<Camera2d>::default());
90
91 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
92 return;
93 };
94 render_app
95 .init_resource::<DrawFunctions<Opaque2d>>()
96 .init_resource::<DrawFunctions<AlphaMask2d>>()
97 .init_resource::<DrawFunctions<Transparent2d>>()
98 .init_resource::<ViewSortedRenderPhases<Transparent2d>>()
99 .init_resource::<ViewBinnedRenderPhases<Opaque2d>>()
100 .init_resource::<ViewBinnedRenderPhases<AlphaMask2d>>()
101 .add_systems(ExtractSchedule, extract_core_2d_camera_phases)
102 .add_systems(
103 Render,
104 (
105 sort_phase_system::<Transparent2d>.in_set(RenderSystems::PhaseSort),
106 prepare_core_2d_depth_textures.in_set(RenderSystems::PrepareResources),
107 ),
108 );
109
110 render_app
111 .add_render_sub_graph(Core2d)
112 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::StartMainPass)
113 .add_render_graph_node::<ViewNodeRunner<MainOpaquePass2dNode>>(
114 Core2d,
115 Node2d::MainOpaquePass,
116 )
117 .add_render_graph_node::<ViewNodeRunner<MainTransparentPass2dNode>>(
118 Core2d,
119 Node2d::MainTransparentPass,
120 )
121 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPass)
122 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::StartMainPassPostProcessing)
123 .add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(Core2d, Node2d::Tonemapping)
124 .add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPassPostProcessing)
125 .add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(Core2d, Node2d::Upscaling)
126 .add_render_graph_edges(
127 Core2d,
128 (
129 Node2d::StartMainPass,
130 Node2d::MainOpaquePass,
131 Node2d::MainTransparentPass,
132 Node2d::EndMainPass,
133 Node2d::StartMainPassPostProcessing,
134 Node2d::Tonemapping,
135 Node2d::EndMainPassPostProcessing,
136 Node2d::Upscaling,
137 ),
138 );
139 }
140}
141
142pub struct Opaque2d {
144 pub batch_set_key: BatchSetKey2d,
149 pub bin_key: Opaque2dBinKey,
151 pub representative_entity: (Entity, MainEntity),
154 pub batch_range: Range<u32>,
156 pub extra_index: PhaseItemExtraIndex,
159}
160
161#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
163pub struct Opaque2dBinKey {
164 pub pipeline: CachedRenderPipelineId,
166 pub draw_function: DrawFunctionId,
168 pub asset_id: UntypedAssetId,
173 pub material_bind_group_id: Option<BindGroupId>,
175}
176
177impl PhaseItem for Opaque2d {
178 #[inline]
179 fn entity(&self) -> Entity {
180 self.representative_entity.0
181 }
182
183 fn main_entity(&self) -> MainEntity {
184 self.representative_entity.1
185 }
186
187 #[inline]
188 fn draw_function(&self) -> DrawFunctionId {
189 self.bin_key.draw_function
190 }
191
192 #[inline]
193 fn batch_range(&self) -> &Range<u32> {
194 &self.batch_range
195 }
196
197 #[inline]
198 fn batch_range_mut(&mut self) -> &mut Range<u32> {
199 &mut self.batch_range
200 }
201
202 fn extra_index(&self) -> PhaseItemExtraIndex {
203 self.extra_index.clone()
204 }
205
206 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
207 (&mut self.batch_range, &mut self.extra_index)
208 }
209}
210
211impl BinnedPhaseItem for Opaque2d {
212 type BatchSetKey = BatchSetKey2d;
215
216 type BinKey = Opaque2dBinKey;
217
218 fn new(
219 batch_set_key: Self::BatchSetKey,
220 bin_key: Self::BinKey,
221 representative_entity: (Entity, MainEntity),
222 batch_range: Range<u32>,
223 extra_index: PhaseItemExtraIndex,
224 ) -> Self {
225 Opaque2d {
226 batch_set_key,
227 bin_key,
228 representative_entity,
229 batch_range,
230 extra_index,
231 }
232 }
233}
234
235#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
238pub struct BatchSetKey2d {
239 pub indexed: bool,
241}
242
243impl PhaseItemBatchSetKey for BatchSetKey2d {
244 fn indexed(&self) -> bool {
245 self.indexed
246 }
247}
248
249impl CachedRenderPipelinePhaseItem for Opaque2d {
250 #[inline]
251 fn cached_pipeline(&self) -> CachedRenderPipelineId {
252 self.bin_key.pipeline
253 }
254}
255
256pub struct AlphaMask2d {
258 pub batch_set_key: BatchSetKey2d,
263 pub bin_key: AlphaMask2dBinKey,
265 pub representative_entity: (Entity, MainEntity),
268 pub batch_range: Range<u32>,
270 pub extra_index: PhaseItemExtraIndex,
273}
274
275#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
277pub struct AlphaMask2dBinKey {
278 pub pipeline: CachedRenderPipelineId,
280 pub draw_function: DrawFunctionId,
282 pub asset_id: UntypedAssetId,
287 pub material_bind_group_id: Option<BindGroupId>,
289}
290
291impl PhaseItem for AlphaMask2d {
292 #[inline]
293 fn entity(&self) -> Entity {
294 self.representative_entity.0
295 }
296
297 #[inline]
298 fn main_entity(&self) -> MainEntity {
299 self.representative_entity.1
300 }
301
302 #[inline]
303 fn draw_function(&self) -> DrawFunctionId {
304 self.bin_key.draw_function
305 }
306
307 #[inline]
308 fn batch_range(&self) -> &Range<u32> {
309 &self.batch_range
310 }
311
312 #[inline]
313 fn batch_range_mut(&mut self) -> &mut Range<u32> {
314 &mut self.batch_range
315 }
316
317 fn extra_index(&self) -> PhaseItemExtraIndex {
318 self.extra_index.clone()
319 }
320
321 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
322 (&mut self.batch_range, &mut self.extra_index)
323 }
324}
325
326impl BinnedPhaseItem for AlphaMask2d {
327 type BatchSetKey = BatchSetKey2d;
330
331 type BinKey = AlphaMask2dBinKey;
332
333 fn new(
334 batch_set_key: Self::BatchSetKey,
335 bin_key: Self::BinKey,
336 representative_entity: (Entity, MainEntity),
337 batch_range: Range<u32>,
338 extra_index: PhaseItemExtraIndex,
339 ) -> Self {
340 AlphaMask2d {
341 batch_set_key,
342 bin_key,
343 representative_entity,
344 batch_range,
345 extra_index,
346 }
347 }
348}
349
350impl CachedRenderPipelinePhaseItem for AlphaMask2d {
351 #[inline]
352 fn cached_pipeline(&self) -> CachedRenderPipelineId {
353 self.bin_key.pipeline
354 }
355}
356
357pub struct Transparent2d {
359 pub sort_key: FloatOrd,
360 pub entity: (Entity, MainEntity),
361 pub pipeline: CachedRenderPipelineId,
362 pub draw_function: DrawFunctionId,
363 pub batch_range: Range<u32>,
364 pub extracted_index: usize,
365 pub extra_index: PhaseItemExtraIndex,
366 pub indexed: bool,
369}
370
371impl PhaseItem for Transparent2d {
372 #[inline]
373 fn entity(&self) -> Entity {
374 self.entity.0
375 }
376
377 #[inline]
378 fn main_entity(&self) -> MainEntity {
379 self.entity.1
380 }
381
382 #[inline]
383 fn draw_function(&self) -> DrawFunctionId {
384 self.draw_function
385 }
386
387 #[inline]
388 fn batch_range(&self) -> &Range<u32> {
389 &self.batch_range
390 }
391
392 #[inline]
393 fn batch_range_mut(&mut self) -> &mut Range<u32> {
394 &mut self.batch_range
395 }
396
397 #[inline]
398 fn extra_index(&self) -> PhaseItemExtraIndex {
399 self.extra_index.clone()
400 }
401
402 #[inline]
403 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
404 (&mut self.batch_range, &mut self.extra_index)
405 }
406}
407
408impl SortedPhaseItem for Transparent2d {
409 type SortKey = FloatOrd;
410
411 #[inline]
412 fn sort_key(&self) -> Self::SortKey {
413 self.sort_key
414 }
415
416 #[inline]
417 fn sort(items: &mut [Self]) {
418 radsort::sort_by_key(items, |item| item.sort_key().0);
420 }
421
422 fn indexed(&self) -> bool {
423 self.indexed
424 }
425}
426
427impl CachedRenderPipelinePhaseItem for Transparent2d {
428 #[inline]
429 fn cached_pipeline(&self) -> CachedRenderPipelineId {
430 self.pipeline
431 }
432}
433
434pub fn extract_core_2d_camera_phases(
435 mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
436 mut opaque_2d_phases: ResMut<ViewBinnedRenderPhases<Opaque2d>>,
437 mut alpha_mask_2d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask2d>>,
438 cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
439 mut live_entities: Local<HashSet<RetainedViewEntity>>,
440) {
441 live_entities.clear();
442
443 for (main_entity, camera) in &cameras_2d {
444 if !camera.is_active {
445 continue;
446 }
447
448 let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
450
451 transparent_2d_phases.insert_or_clear(retained_view_entity);
452 opaque_2d_phases.prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
453 alpha_mask_2d_phases
454 .prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
455
456 live_entities.insert(retained_view_entity);
457 }
458
459 transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
461 opaque_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
462 alpha_mask_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
463}
464
465pub fn prepare_core_2d_depth_textures(
466 mut commands: Commands,
467 mut texture_cache: ResMut<TextureCache>,
468 render_device: Res<RenderDevice>,
469 transparent_2d_phases: Res<ViewSortedRenderPhases<Transparent2d>>,
470 opaque_2d_phases: Res<ViewBinnedRenderPhases<Opaque2d>>,
471 views_2d: Query<(Entity, &ExtractedCamera, &ExtractedView, &Msaa), (With<Camera2d>,)>,
472) {
473 let mut textures = <HashMap<_, _>>::default();
474 for (view, camera, extracted_view, msaa) in &views_2d {
475 if !opaque_2d_phases.contains_key(&extracted_view.retained_view_entity)
476 || !transparent_2d_phases.contains_key(&extracted_view.retained_view_entity)
477 {
478 continue;
479 };
480
481 let Some(physical_target_size) = camera.physical_target_size else {
482 continue;
483 };
484
485 let cached_texture = textures
486 .entry(camera.target.clone())
487 .or_insert_with(|| {
488 let descriptor = TextureDescriptor {
489 label: Some("view_depth_texture"),
490 size: physical_target_size.to_extents(),
492 mip_level_count: 1,
493 sample_count: msaa.samples(),
494 dimension: TextureDimension::D2,
495 format: CORE_2D_DEPTH_FORMAT,
496 usage: TextureUsages::RENDER_ATTACHMENT,
497 view_formats: &[],
498 };
499
500 texture_cache.get(&render_device, descriptor)
501 })
502 .clone();
503
504 commands
505 .entity(view)
506 .insert(ViewDepthTexture::new(cached_texture, Some(0.0)));
507 }
508}