@@ -6,7 +6,6 @@ use bevy_app::prelude::*;
66use bevy_asset:: * ;
77use bevy_camera:: visibility:: InheritedVisibility ;
88use bevy_color:: { Alpha , ColorToComponents , LinearRgba } ;
9- use bevy_ecs:: entity:: EntityIndexMap ;
109use bevy_ecs:: prelude:: * ;
1110use bevy_ecs:: {
1211 prelude:: Component ,
@@ -17,7 +16,7 @@ use bevy_ecs::{
1716} ;
1817use bevy_math:: { vec2, Affine2 , FloatOrd , Rect , Vec2 } ;
1918use bevy_mesh:: VertexBufferLayout ;
20- use bevy_render:: sync_world:: { MainEntity , MainEntityHashMap , MainEntityHashSet } ;
19+ use bevy_render:: sync_world:: { MainEntity , MainEntityHashSet } ;
2120use bevy_render:: {
2221 render_phase:: * ,
2322 render_resource:: { binding_types:: uniform_buffer, * } ,
@@ -34,9 +33,12 @@ use bevy_ui::{
3433use bevy_utils:: default;
3534use bytemuck:: { Pod , Zeroable } ;
3635
37- use crate :: { BoxShadowSamples , RenderUiSystems , TransparentUi , UiCameraMap } ;
36+ use crate :: {
37+ queue_ui_items, BoxShadowSamples , CachedCameraView , ChangedUiObject , RenderUiSystems ,
38+ TransparentUi , UiCameraMap , UiRenderObject , UiRenderObjects ,
39+ } ;
3840
39- use super :: { stack_z_offsets, UiCameraView , QUAD_INDICES , QUAD_VERTEX_POSITIONS } ;
41+ use super :: { stack_z_offsets, QUAD_INDICES , QUAD_VERTEX_POSITIONS } ;
4042
4143/// A plugin that enables the rendering of box shadows.
4244pub struct BoxShadowPlugin ;
@@ -59,7 +61,7 @@ impl Plugin for BoxShadowPlugin {
5961 . add_systems (
6062 Render ,
6163 (
62- queue_shadows . in_set ( RenderSystems :: Queue ) ,
64+ queue_ui_items :: < ExtractedBoxShadow > . in_set ( RenderSystems :: Queue ) ,
6365 prepare_shadows. in_set ( RenderSystems :: PrepareBindGroups ) ,
6466 ) ,
6567 ) ;
@@ -194,17 +196,44 @@ pub struct ExtractedBoxShadow {
194196 pub size : Vec2 ,
195197}
196198
197- /// List of extracted shadows to be sorted and queued for rendering
198- #[ derive( Resource , Default ) ]
199- pub struct ExtractedBoxShadows {
200- /// The list of box shadows grouped by their main-world entity, along with
201- /// each group's target camera entity.
202- ///
203- /// This is a two-level data structure so that we can quickly remove all box
204- /// shadows associated with a main-world entity when it changes.
205- pub box_shadows : MainEntityHashMap < ( Entity , EntityIndexMap < ExtractedBoxShadow > ) > ,
199+ impl UiRenderObject for ExtractedBoxShadow {
200+ type DrawFunctions = DrawBoxShadows ;
201+ type ViewPipelineKeyBuilder = UiBoxShadowViewPipelineKeyBuilder ;
202+ type ViewQueryData = Option < & ' static BoxShadowSamples > ;
203+ type SpecializedRenderPipeline = BoxShadowPipeline ;
204+ type PipelineKeySystemParam = ( ) ;
205+
206+ fn get_sort_key ( & self ) -> FloatOrd {
207+ FloatOrd ( self . stack_index as f32 + stack_z_offsets:: BOX_SHADOW )
208+ }
209+
210+ fn create_view_pipeline_key_builder < ' w , ' s > (
211+ box_shadow_samples : Option < & BoxShadowSamples > ,
212+ ) -> Self :: ViewPipelineKeyBuilder {
213+ UiBoxShadowViewPipelineKeyBuilder {
214+ box_shadow_samples : box_shadow_samples. cloned ( ) ,
215+ }
216+ }
217+
218+ fn create_pipeline_key (
219+ & self ,
220+ cached_camera_view : & CachedCameraView < Self :: ViewPipelineKeyBuilder > ,
221+ _: & mut SystemParamItem < Self :: PipelineKeySystemParam > ,
222+ ) -> Option < BoxShadowPipelineKey > {
223+ Some ( BoxShadowPipelineKey {
224+ target_format : cached_camera_view. extracted_view . target_format ,
225+ samples : cached_camera_view
226+ . pipeline_key_builder
227+ . box_shadow_samples
228+ . unwrap_or_default ( )
229+ . 0 ,
230+ } )
231+ }
206232}
207233
234+ /// List of extracted shadows to be sorted and queued for rendering
235+ pub type ExtractedBoxShadows = UiRenderObjects < ExtractedBoxShadow > ;
236+
208237pub fn extract_shadows (
209238 mut commands : Commands ,
210239 mut extracted_box_shadows : ResMut < ExtractedBoxShadows > ,
@@ -269,6 +298,7 @@ pub fn extract_shadows(
269298 mut nodes_processed_this_frame : Local < MainEntityHashSet > ,
270299) {
271300 nodes_processed_this_frame. clear ( ) ;
301+ extracted_box_shadows. changed . clear ( ) ;
272302
273303 let mut mapping = camera_map. get_mapper ( ) ;
274304
@@ -281,14 +311,22 @@ pub fn extract_shadows(
281311 {
282312 let main_entity = MainEntity :: from ( entity) ;
283313
284- // If there were any previous box shadows for this entity, despawn them.
285- for ( render_entity, _) in extracted_box_shadows
286- . box_shadows
287- . get_mut ( & main_entity)
288- . iter_mut ( )
289- . flat_map ( |( _, shadows) | shadows. drain ( ..) )
314+ // If there were any previous box shadows for this entity, despawn them
315+ // and record them as changed so the render phase entry can be removed.
316+ if let Some ( ( prev_camera_entity, mut shadows) ) =
317+ extracted_box_shadows. objects . remove ( & main_entity)
290318 {
291- commands. entity ( render_entity) . despawn ( ) ;
319+ let changed = extracted_box_shadows
320+ . changed
321+ . entry ( main_entity)
322+ . or_default ( ) ;
323+ for ( render_entity, _) in shadows. drain ( ..) {
324+ commands. entity ( render_entity) . despawn ( ) ;
325+ changed. push ( ChangedUiObject {
326+ render_entity,
327+ prev_camera_entity,
328+ } ) ;
329+ }
292330 }
293331
294332 // Skip if no visible shadows
@@ -299,7 +337,7 @@ pub fn extract_shadows(
299337 let Some ( extracted_camera_entity) = mapping. map ( camera) else {
300338 continue ;
301339 } ;
302- if let Some ( ( camera_entity, _) ) = extracted_box_shadows. box_shadows . get_mut ( & main_entity) {
340+ if let Some ( ( camera_entity, _) ) = extracted_box_shadows. objects . get_mut ( & main_entity) {
303341 * camera_entity = extracted_camera_entity;
304342 }
305343
@@ -347,7 +385,7 @@ pub fn extract_shadows(
347385 } ;
348386
349387 extracted_box_shadows
350- . box_shadows
388+ . objects
351389 . entry ( main_entity)
352390 . or_insert_with ( || ( extracted_camera_entity, Default :: default ( ) ) )
353391 . 1
@@ -383,81 +421,31 @@ pub fn extract_shadows(
383421 if nodes_processed_this_frame. contains ( & main_entity) {
384422 continue ;
385423 }
386- let Some ( ( _, mut extracted_nodes) ) = extracted_box_shadows. box_shadows . remove ( & main_entity)
424+ let Some ( ( prev_camera_entity, mut extracted_nodes) ) =
425+ extracted_box_shadows. objects . remove ( & main_entity)
387426 else {
388427 continue ;
389428 } ;
429+ let changed = extracted_box_shadows
430+ . changed
431+ . entry ( main_entity)
432+ . or_default ( ) ;
390433 for ( render_entity, _) in extracted_nodes. drain ( ..) {
391434 commands. entity ( render_entity) . despawn ( ) ;
435+ changed. push ( ChangedUiObject {
436+ render_entity,
437+ prev_camera_entity,
438+ } ) ;
392439 }
393440 }
394441}
395442
396- #[ expect(
397- clippy:: too_many_arguments,
398- reason = "it's a system that needs a lot of them"
399- ) ]
400- pub fn queue_shadows (
401- extracted_box_shadows : ResMut < ExtractedBoxShadows > ,
402- box_shadow_pipeline : Res < BoxShadowPipeline > ,
403- mut pipelines : ResMut < SpecializedRenderPipelines < BoxShadowPipeline > > ,
404- mut transparent_render_phases : ResMut < ViewSortedRenderPhases < TransparentUi > > ,
405- render_views : Query < ( & UiCameraView , Option < & BoxShadowSamples > ) , With < ExtractedView > > ,
406- camera_views : Query < & ExtractedView > ,
407- pipeline_cache : Res < PipelineCache > ,
408- draw_functions : Res < DrawFunctions < TransparentUi > > ,
409- ) {
410- let draw_function = draw_functions. read ( ) . id :: < DrawBoxShadows > ( ) ;
411- let mut current_camera_entity = Entity :: PLACEHOLDER ;
412- let mut current_phase = None ;
413-
414- for ( main_entity, ( extracted_camera_entity, extracted_sub_shadows) ) in
415- extracted_box_shadows. box_shadows . iter ( )
416- {
417- if current_camera_entity != * extracted_camera_entity {
418- current_phase = render_views. get ( * extracted_camera_entity) . ok ( ) . and_then (
419- |( default_camera_view, shadow_samples) | {
420- camera_views
421- . get ( default_camera_view. 0 )
422- . ok ( )
423- . and_then ( |view| {
424- transparent_render_phases
425- . get_mut ( & view. retained_view_entity )
426- . map ( |transparent_phase| {
427- let pipeline = pipelines. specialize (
428- & pipeline_cache,
429- & box_shadow_pipeline,
430- BoxShadowPipelineKey {
431- target_format : view. target_format ,
432- samples : shadow_samples. copied ( ) . unwrap_or_default ( ) . 0 ,
433- } ,
434- ) ;
435- ( pipeline, transparent_phase)
436- } )
437- } )
438- } ,
439- ) ;
440- current_camera_entity = * extracted_camera_entity;
441- }
442-
443- let Some ( ( pipeline, transparent_phase) ) = current_phase. as_mut ( ) else {
444- continue ;
445- } ;
446- for ( entity, extracted_shadow) in extracted_sub_shadows. iter ( ) {
447- transparent_phase. add_transient ( TransparentUi {
448- draw_function,
449- pipeline : * pipeline,
450- entity : ( * entity, * main_entity) ,
451- sort_key : FloatOrd (
452- extracted_shadow. stack_index as f32 + stack_z_offsets:: BOX_SHADOW ,
453- ) ,
454-
455- batch_range : 0 ..0 ,
456- extra_index : PhaseItemExtraIndex :: None ,
457- indexed : true ,
458- } ) ;
459- }
460- }
443+ /// Information that the box shadow renderer needs from each view to construct
444+ /// the pipeline key.
445+ pub struct UiBoxShadowViewPipelineKeyBuilder {
446+ /// The number of samples that this view requests to render box shadows
447+ /// with.
448+ box_shadow_samples : Option < BoxShadowSamples > ,
461449}
462450
463451pub fn prepare_shadows (
@@ -491,7 +479,7 @@ pub fn prepare_shadows(
491479 for item_index in 0 ..ui_phase. items . len ( ) {
492480 let item = & mut ui_phase. items [ item_index] ;
493481 let Some ( ( extracted_camera_entity, box_shadow) ) = extracted_shadows
494- . box_shadows
482+ . objects
495483 . get ( & item. main_entity ( ) )
496484 . and_then ( |( extracted_camera_entity, sub_shadows) | {
497485 sub_shadows
0 commit comments