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