1mod main_opaque_pass_2d_node;
2mod main_transparent_pass_2d_node;
3
4use core::ops::Range;
5
6use bevy_asset::UntypedAssetId;
7use bevy_camera::{Camera, Camera2d};
8use bevy_ecs::entity::EntityHash;
9use bevy_image::ToExtents;
10use bevy_platform::collections::{HashMap, HashSet};
11use bevy_render::{
12 batching::gpu_preprocessing::GpuPreprocessingMode,
13 camera::CameraRenderGraph,
14 render_phase::PhaseItemBatchSetKey,
15 view::{ExtractedView, RetainedViewEntity},
16};
17use indexmap::IndexMap;
18pub use main_opaque_pass_2d_node::*;
19pub use main_transparent_pass_2d_node::*;
20
21use crate::schedule::Core2d;
22use crate::tonemapping::{tonemapping, DebandDither, Tonemapping};
23use crate::upscaling::upscaling;
24use crate::Core2dSystems;
25use bevy_app::{App, Plugin};
26use bevy_ecs::prelude::*;
27use bevy_math::FloatOrd;
28use bevy_render::{
29 camera::ExtractedCamera,
30 extract_component::ExtractComponentPlugin,
31 render_phase::{
32 sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
33 DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
34 ViewSortedRenderPhases,
35 },
36 render_resource::{
37 CachedRenderPipelineId, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
38 },
39 renderer::RenderDevice,
40 sync_world::MainEntity,
41 texture::TextureCache,
42 view::{Msaa, ViewDepthStencilTexture},
43 Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
44};
45
46pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
47
48pub struct Core2dPlugin;
49
50impl Plugin for Core2dPlugin {
51 fn build(&self, app: &mut App) {
52 app.register_required_components::<Camera2d, DebandDither>()
53 .register_required_components_with::<Camera2d, CameraRenderGraph>(|| {
54 CameraRenderGraph::new(Core2d)
55 })
56 .register_required_components_with::<Camera2d, Tonemapping>(|| Tonemapping::Linear)
57 .add_plugins(ExtractComponentPlugin::<Camera2d>::default());
58
59 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
60 return;
61 };
62 render_app
63 .init_resource::<DrawFunctions<Opaque2d>>()
64 .init_resource::<DrawFunctions<AlphaMask2d>>()
65 .init_resource::<DrawFunctions<Transparent2d>>()
66 .init_resource::<ViewSortedRenderPhases<Transparent2d>>()
67 .init_resource::<ViewBinnedRenderPhases<Opaque2d>>()
68 .init_resource::<ViewBinnedRenderPhases<AlphaMask2d>>()
69 .allow_ambiguous_resource::<ViewSortedRenderPhases<Transparent2d>>()
70 .allow_ambiguous_resource::<ViewBinnedRenderPhases<Opaque2d>>()
71 .allow_ambiguous_resource::<ViewBinnedRenderPhases<AlphaMask2d>>()
72 .add_systems(ExtractSchedule, extract_core_2d_camera_phases)
73 .add_systems(
74 Render,
75 (
76 sort_phase_system::<Transparent2d>.in_set(RenderSystems::PhaseSort),
77 prepare_core_2d_depth_textures.in_set(RenderSystems::PrepareResources),
78 ),
79 )
80 .add_schedule(Core2d::base_schedule())
81 .add_systems(
82 Core2d,
83 (
84 (main_opaque_pass_2d, main_transparent_pass_2d)
85 .chain()
86 .in_set(Core2dSystems::MainPass),
87 tonemapping.in_set(Core2dSystems::PostProcess),
88 upscaling.after(Core2dSystems::PostProcess),
89 ),
90 );
91 }
92}
93
94pub struct Opaque2d {
96 pub batch_set_key: BatchSetKey2d,
101 pub bin_key: Opaque2dBinKey,
103 pub representative_entity: (Entity, MainEntity),
106 pub batch_range: Range<u32>,
108 pub extra_index: PhaseItemExtraIndex,
111}
112
113#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
115pub struct Opaque2dBinKey {
116 pub pipeline: CachedRenderPipelineId,
118 pub draw_function: DrawFunctionId,
120 pub asset_id: UntypedAssetId,
125 pub material_bind_group_index: Option<u32>,
127}
128
129impl PhaseItem for Opaque2d {
130 #[inline]
131 fn entity(&self) -> Entity {
132 self.representative_entity.0
133 }
134
135 fn main_entity(&self) -> MainEntity {
136 self.representative_entity.1
137 }
138
139 #[inline]
140 fn draw_function(&self) -> DrawFunctionId {
141 self.bin_key.draw_function
142 }
143
144 #[inline]
145 fn batch_range(&self) -> &Range<u32> {
146 &self.batch_range
147 }
148
149 #[inline]
150 fn batch_range_mut(&mut self) -> &mut Range<u32> {
151 &mut self.batch_range
152 }
153
154 fn extra_index(&self) -> PhaseItemExtraIndex {
155 self.extra_index.clone()
156 }
157
158 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
159 (&mut self.batch_range, &mut self.extra_index)
160 }
161}
162
163impl BinnedPhaseItem for Opaque2d {
164 type BatchSetKey = BatchSetKey2d;
167
168 type BinKey = Opaque2dBinKey;
169
170 fn new(
171 batch_set_key: Self::BatchSetKey,
172 bin_key: Self::BinKey,
173 representative_entity: (Entity, MainEntity),
174 batch_range: Range<u32>,
175 extra_index: PhaseItemExtraIndex,
176 ) -> Self {
177 Opaque2d {
178 batch_set_key,
179 bin_key,
180 representative_entity,
181 batch_range,
182 extra_index,
183 }
184 }
185}
186
187#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
190pub struct BatchSetKey2d {
191 pub indexed: bool,
193}
194
195impl PhaseItemBatchSetKey for BatchSetKey2d {
196 fn indexed(&self) -> bool {
197 self.indexed
198 }
199}
200
201impl CachedRenderPipelinePhaseItem for Opaque2d {
202 #[inline]
203 fn cached_pipeline(&self) -> CachedRenderPipelineId {
204 self.bin_key.pipeline
205 }
206}
207
208pub struct AlphaMask2d {
210 pub batch_set_key: BatchSetKey2d,
215 pub bin_key: AlphaMask2dBinKey,
217 pub representative_entity: (Entity, MainEntity),
220 pub batch_range: Range<u32>,
222 pub extra_index: PhaseItemExtraIndex,
225}
226
227#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
229pub struct AlphaMask2dBinKey {
230 pub pipeline: CachedRenderPipelineId,
232 pub draw_function: DrawFunctionId,
234 pub asset_id: UntypedAssetId,
239 pub material_bind_group_index: Option<u32>,
241}
242
243impl PhaseItem for AlphaMask2d {
244 #[inline]
245 fn entity(&self) -> Entity {
246 self.representative_entity.0
247 }
248
249 #[inline]
250 fn main_entity(&self) -> MainEntity {
251 self.representative_entity.1
252 }
253
254 #[inline]
255 fn draw_function(&self) -> DrawFunctionId {
256 self.bin_key.draw_function
257 }
258
259 #[inline]
260 fn batch_range(&self) -> &Range<u32> {
261 &self.batch_range
262 }
263
264 #[inline]
265 fn batch_range_mut(&mut self) -> &mut Range<u32> {
266 &mut self.batch_range
267 }
268
269 fn extra_index(&self) -> PhaseItemExtraIndex {
270 self.extra_index.clone()
271 }
272
273 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
274 (&mut self.batch_range, &mut self.extra_index)
275 }
276}
277
278impl BinnedPhaseItem for AlphaMask2d {
279 type BatchSetKey = BatchSetKey2d;
282
283 type BinKey = AlphaMask2dBinKey;
284
285 fn new(
286 batch_set_key: Self::BatchSetKey,
287 bin_key: Self::BinKey,
288 representative_entity: (Entity, MainEntity),
289 batch_range: Range<u32>,
290 extra_index: PhaseItemExtraIndex,
291 ) -> Self {
292 AlphaMask2d {
293 batch_set_key,
294 bin_key,
295 representative_entity,
296 batch_range,
297 extra_index,
298 }
299 }
300}
301
302impl CachedRenderPipelinePhaseItem for AlphaMask2d {
303 #[inline]
304 fn cached_pipeline(&self) -> CachedRenderPipelineId {
305 self.bin_key.pipeline
306 }
307}
308
309pub struct Transparent2d {
311 pub sort_key: FloatOrd,
312 pub entity: (Entity, MainEntity),
313 pub pipeline: CachedRenderPipelineId,
314 pub draw_function: DrawFunctionId,
315 pub batch_range: Range<u32>,
316 pub extracted_index: usize,
317 pub extra_index: PhaseItemExtraIndex,
318 pub indexed: bool,
321}
322
323impl PhaseItem for Transparent2d {
324 #[inline]
325 fn entity(&self) -> Entity {
326 self.entity.0
327 }
328
329 #[inline]
330 fn main_entity(&self) -> MainEntity {
331 self.entity.1
332 }
333
334 #[inline]
335 fn draw_function(&self) -> DrawFunctionId {
336 self.draw_function
337 }
338
339 #[inline]
340 fn batch_range(&self) -> &Range<u32> {
341 &self.batch_range
342 }
343
344 #[inline]
345 fn batch_range_mut(&mut self) -> &mut Range<u32> {
346 &mut self.batch_range
347 }
348
349 #[inline]
350 fn extra_index(&self) -> PhaseItemExtraIndex {
351 self.extra_index.clone()
352 }
353
354 #[inline]
355 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
356 (&mut self.batch_range, &mut self.extra_index)
357 }
358}
359
360impl SortedPhaseItem for Transparent2d {
361 type SortKey = FloatOrd;
362
363 #[inline]
364 fn sort_key(&self) -> Self::SortKey {
365 self.sort_key
366 }
367
368 #[inline]
369 fn sort(items: &mut IndexMap<(Entity, MainEntity), Transparent2d, EntityHash>) {
370 items.sort_by_key(|_, item| item.sort_key());
371 }
372
373 fn recalculate_sort_keys(
374 _: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
375 _: &ExtractedView,
376 ) {
377 }
379
380 fn indexed(&self) -> bool {
381 self.indexed
382 }
383}
384
385impl CachedRenderPipelinePhaseItem for Transparent2d {
386 #[inline]
387 fn cached_pipeline(&self) -> CachedRenderPipelineId {
388 self.pipeline
389 }
390}
391
392pub fn extract_core_2d_camera_phases(
393 mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
394 mut opaque_2d_phases: ResMut<ViewBinnedRenderPhases<Opaque2d>>,
395 mut alpha_mask_2d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask2d>>,
396 cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
397 mut live_entities: Local<HashSet<RetainedViewEntity>>,
398) {
399 live_entities.clear();
400
401 for (main_entity, camera) in &cameras_2d {
402 if !camera.is_active {
403 continue;
404 }
405
406 let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
408
409 transparent_2d_phases.prepare_for_new_frame(retained_view_entity);
410 opaque_2d_phases.prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
411 alpha_mask_2d_phases
412 .prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
413
414 live_entities.insert(retained_view_entity);
415 }
416
417 transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
419 opaque_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
420 alpha_mask_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
421}
422
423pub fn prepare_core_2d_depth_textures(
424 mut commands: Commands,
425 mut texture_cache: ResMut<TextureCache>,
426 render_device: Res<RenderDevice>,
427 transparent_2d_phases: Res<ViewSortedRenderPhases<Transparent2d>>,
428 opaque_2d_phases: Res<ViewBinnedRenderPhases<Opaque2d>>,
429 views_2d: Query<(Entity, &ExtractedCamera, &ExtractedView, &Msaa), (With<Camera2d>,)>,
430) {
431 let mut textures = <HashMap<_, _>>::default();
432 for (view, camera, extracted_view, msaa) in &views_2d {
433 if !opaque_2d_phases.contains_key(&extracted_view.retained_view_entity)
434 || !transparent_2d_phases.contains_key(&extracted_view.retained_view_entity)
435 {
436 continue;
437 };
438
439 let Some(physical_target_size) = camera.physical_target_size else {
440 continue;
441 };
442
443 let cached_texture = textures
444 .entry(camera.target.clone())
445 .or_insert_with(|| {
446 let descriptor = TextureDescriptor {
447 label: Some("view_depth_texture"),
448 size: physical_target_size.to_extents(),
450 mip_level_count: 1,
451 sample_count: msaa.samples(),
452 dimension: TextureDimension::D2,
453 format: CORE_2D_DEPTH_FORMAT,
454 usage: TextureUsages::RENDER_ATTACHMENT,
455 view_formats: &[],
456 };
457
458 texture_cache.get(&render_device, descriptor)
459 })
460 .clone();
461
462 commands.entity(view).insert(ViewDepthStencilTexture::new(
463 cached_texture,
464 Some(0.0),
465 None,
466 ));
467 }
468}