@@ -11,6 +11,7 @@ use bevy_core_pipeline::{
11
11
} ;
12
12
use bevy_ecs:: {
13
13
prelude:: * ,
14
+ storage:: SparseSet ,
14
15
system:: { lifetimeless:: * , SystemParamItem , SystemState } ,
15
16
} ;
16
17
use bevy_math:: { Rect , Vec2 } ;
@@ -294,7 +295,6 @@ impl SpecializedRenderPipeline for SpritePipeline {
294
295
}
295
296
}
296
297
297
- #[ derive( Component , Clone , Copy ) ]
298
298
pub struct ExtractedSprite {
299
299
pub transform : GlobalTransform ,
300
300
pub color : Color ,
@@ -310,6 +310,11 @@ pub struct ExtractedSprite {
310
310
pub anchor : Vec2 ,
311
311
}
312
312
313
+ #[ derive( Resource , Default ) ]
314
+ pub struct ExtractedSprites {
315
+ pub sprites : SparseSet < Entity , ExtractedSprite > ,
316
+ }
317
+
313
318
#[ derive( Resource , Default ) ]
314
319
pub struct SpriteAssetEvents {
315
320
pub images : Vec < AssetEvent < Image > > ,
@@ -339,8 +344,7 @@ pub fn extract_sprite_events(
339
344
}
340
345
341
346
pub fn extract_sprites (
342
- mut commands : Commands ,
343
- mut previous_len : Local < usize > ,
347
+ mut extracted_sprites : ResMut < ExtractedSprites > ,
344
348
texture_atlases : Extract < Res < Assets < TextureAtlas > > > ,
345
349
sprite_query : Extract <
346
350
Query < (
@@ -361,13 +365,12 @@ pub fn extract_sprites(
361
365
) > ,
362
366
> ,
363
367
) {
364
- let mut extracted_sprites: Vec < ( Entity , ExtractedSprite ) > = Vec :: with_capacity ( * previous_len) ;
365
368
for ( entity, visibility, sprite, transform, handle) in sprite_query. iter ( ) {
366
369
if !visibility. is_visible ( ) {
367
370
continue ;
368
371
}
369
372
// PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
370
- extracted_sprites. push ( (
373
+ extracted_sprites. sprites . insert (
371
374
entity,
372
375
ExtractedSprite {
373
376
color : sprite. color ,
@@ -380,7 +383,7 @@ pub fn extract_sprites(
380
383
image_handle_id : handle. id ( ) ,
381
384
anchor : sprite. anchor . as_vec ( ) ,
382
385
} ,
383
- ) ) ;
386
+ ) ;
384
387
}
385
388
for ( entity, visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query. iter ( ) {
386
389
if !visibility. is_visible ( ) {
@@ -399,7 +402,7 @@ pub fn extract_sprites(
399
402
)
400
403
} ) ,
401
404
) ;
402
- extracted_sprites. push ( (
405
+ extracted_sprites. sprites . insert (
403
406
entity,
404
407
ExtractedSprite {
405
408
color : atlas_sprite. color ,
@@ -413,11 +416,9 @@ pub fn extract_sprites(
413
416
image_handle_id : texture_atlas. texture . id ( ) ,
414
417
anchor : atlas_sprite. anchor . as_vec ( ) ,
415
418
} ,
416
- ) ) ;
419
+ ) ;
417
420
}
418
421
}
419
- * previous_len = extracted_sprites. len ( ) ;
420
- commands. insert_or_spawn_batch ( extracted_sprites) ;
421
422
}
422
423
423
424
#[ repr( C ) ]
@@ -471,6 +472,8 @@ const QUAD_UVS: [Vec2; 4] = [
471
472
#[ derive( Component ) ]
472
473
pub struct SpriteBatch {
473
474
range : Range < u32 > ,
475
+ image : Handle < Image > ,
476
+ colored : bool ,
474
477
}
475
478
476
479
#[ derive( Resource , Default ) ]
@@ -486,8 +489,7 @@ pub fn queue_sprites(
486
489
mut pipelines : ResMut < SpecializedRenderPipelines < SpritePipeline > > ,
487
490
pipeline_cache : Res < PipelineCache > ,
488
491
msaa : Res < Msaa > ,
489
- extracted_sprites : Query < ( Entity , & ExtractedSprite ) > ,
490
- gpu_images : Res < RenderAssets < Image > > ,
492
+ extracted_sprites : Res < ExtractedSprites > ,
491
493
mut views : Query < (
492
494
& mut RenderPhase < Transparent2d > ,
493
495
& VisibleEntities ,
@@ -542,14 +544,10 @@ pub fn queue_sprites(
542
544
543
545
transparent_phase
544
546
. items
545
- . reserve ( extracted_sprites. iter ( ) . len ( ) ) ;
547
+ . reserve ( extracted_sprites. sprites . len ( ) ) ;
546
548
547
- for ( entity, extracted_sprite) in extracted_sprites. iter ( ) {
548
- if !view_entities. contains ( entity. index ( ) as usize )
549
- || gpu_images
550
- . get ( & Handle :: weak ( extracted_sprite. image_handle_id ) )
551
- . is_none ( )
552
- {
549
+ for ( entity, extracted_sprite) in extracted_sprites. sprites . iter ( ) {
550
+ if !view_entities. contains ( entity. index ( ) as usize ) {
553
551
continue ;
554
552
}
555
553
@@ -561,7 +559,7 @@ pub fn queue_sprites(
561
559
transparent_phase. add ( Transparent2d {
562
560
draw_function : draw_sprite_function,
563
561
pipeline : colored_pipeline,
564
- entity,
562
+ entity : * entity ,
565
563
sort_key,
566
564
// batch size will be calculated in prepare_sprites
567
565
batch_size : 0 ,
@@ -570,7 +568,7 @@ pub fn queue_sprites(
570
568
transparent_phase. add ( Transparent2d {
571
569
draw_function : draw_sprite_function,
572
570
pipeline,
573
- entity,
571
+ entity : * entity ,
574
572
sort_key,
575
573
// batch size will be calculated in prepare_sprites
576
574
batch_size : 0 ,
@@ -591,7 +589,7 @@ pub fn prepare_sprites(
591
589
sprite_pipeline : Res < SpritePipeline > ,
592
590
mut image_bind_groups : ResMut < ImageBindGroups > ,
593
591
gpu_images : Res < RenderAssets < Image > > ,
594
- extracted_sprites : Query < & ExtractedSprite > ,
592
+ mut extracted_sprites : ResMut < ExtractedSprites > ,
595
593
mut phases : Query < & mut RenderPhase < Transparent2d > > ,
596
594
events : Res < SpriteAssetEvents > ,
597
595
) {
@@ -639,22 +637,17 @@ pub fn prepare_sprites(
639
637
// Compatible items share the same entity.
640
638
for item_index in 0 ..transparent_phase. items . len ( ) {
641
639
let item = & transparent_phase. items [ item_index] ;
642
- if let Ok ( extracted_sprite) = extracted_sprites. get ( item. entity ) {
640
+ if let Some ( extracted_sprite) = extracted_sprites. sprites . get ( item. entity ) {
643
641
// Take a reference to an existing compatible batch if one exists
644
- let existing_batch = batches. last_mut ( ) . filter ( |_| {
642
+ let mut existing_batch = batches. last_mut ( ) . filter ( |_| {
645
643
batch_image_handle == extracted_sprite. image_handle_id
646
644
&& batch_colored == ( extracted_sprite. color != Color :: WHITE )
647
645
} ) ;
648
646
649
- // Either keep the reference to the compatible batch or create a new batch
650
- let ( _, target_batch) = match existing_batch {
651
- Some ( batch) => batch,
652
- None => {
653
- // We ensure the associated image exists in queue_sprites
654
- let gpu_image = gpu_images
655
- . get ( & Handle :: weak ( extracted_sprite. image_handle_id ) )
656
- . unwrap ( ) ;
657
-
647
+ if existing_batch. is_none ( ) {
648
+ if let Some ( gpu_image) =
649
+ gpu_images. get ( & Handle :: weak ( extracted_sprite. image_handle_id ) )
650
+ {
658
651
batch_item_index = item_index;
659
652
batch_image_size = Vec2 :: new ( gpu_image. size . x , gpu_image. size . y ) ;
660
653
batch_image_handle = extracted_sprite. image_handle_id ;
@@ -666,6 +659,8 @@ pub fn prepare_sprites(
666
659
} else {
667
660
index..index
668
661
} ,
662
+ colored : batch_colored,
663
+ image : Handle :: weak ( batch_image_handle) ,
669
664
} ;
670
665
671
666
batches. push ( ( item. entity , new_batch) ) ;
@@ -693,10 +688,11 @@ pub fn prepare_sprites(
693
688
layout : & sprite_pipeline. material_layout ,
694
689
} )
695
690
} ) ;
696
-
697
- batches. last_mut ( ) . unwrap ( )
691
+ existing_batch = batches. last_mut ( ) ;
692
+ } else {
693
+ continue ;
698
694
}
699
- } ;
695
+ }
700
696
701
697
// Calculate vertex data for this item
702
698
let mut uvs = QUAD_UVS ;
@@ -755,7 +751,9 @@ pub fn prepare_sprites(
755
751
index += QUAD_INDICES . len ( ) as u32 ;
756
752
}
757
753
transparent_phase. items [ batch_item_index] . batch_size += 1 ;
758
- target_batch. range . end += QUAD_INDICES . len ( ) as u32 ;
754
+ existing_batch. unwrap ( ) . 1 . range . end += QUAD_INDICES . len ( ) as u32 ;
755
+ } else {
756
+ batch_image_handle = HandleId :: Id ( Uuid :: nil ( ) , u64:: MAX )
759
757
}
760
758
}
761
759
}
@@ -768,6 +766,7 @@ pub fn prepare_sprites(
768
766
* previous_len = batches. len ( ) ;
769
767
commands. insert_or_spawn_batch ( batches) ;
770
768
}
769
+ extracted_sprites. sprites . clear ( ) ;
771
770
}
772
771
773
772
pub type DrawSprite = (
@@ -802,25 +801,18 @@ pub struct SetSpriteTextureBindGroup<const I: usize>;
802
801
impl < P : PhaseItem , const I : usize > RenderCommand < P > for SetSpriteTextureBindGroup < I > {
803
802
type Param = SRes < ImageBindGroups > ;
804
803
type ViewWorldQuery = ( ) ;
805
- type ItemWorldQuery = Read < ExtractedSprite > ;
804
+ type ItemWorldQuery = Read < SpriteBatch > ;
806
805
807
806
fn render < ' w > (
808
807
_item : & P ,
809
808
_view : ( ) ,
810
- sprite : & ' _ ExtractedSprite ,
809
+ batch : & ' _ SpriteBatch ,
811
810
image_bind_groups : SystemParamItem < ' w , ' _ , Self :: Param > ,
812
811
pass : & mut TrackedRenderPass < ' w > ,
813
812
) -> RenderCommandResult {
814
813
let image_bind_groups = image_bind_groups. into_inner ( ) ;
815
814
816
- pass. set_bind_group (
817
- I ,
818
- image_bind_groups
819
- . values
820
- . get ( & Handle :: weak ( sprite. image_handle_id ) )
821
- . unwrap ( ) ,
822
- & [ ] ,
823
- ) ;
815
+ pass. set_bind_group ( I , image_bind_groups. values . get ( & batch. image ) . unwrap ( ) , & [ ] ) ;
824
816
RenderCommandResult :: Success
825
817
}
826
818
}
@@ -829,22 +821,22 @@ pub struct DrawSpriteBatch;
829
821
impl < P : PhaseItem > RenderCommand < P > for DrawSpriteBatch {
830
822
type Param = SRes < SpriteMeta > ;
831
823
type ViewWorldQuery = ( ) ;
832
- type ItemWorldQuery = ( Read < SpriteBatch > , Read < ExtractedSprite > ) ;
824
+ type ItemWorldQuery = Read < SpriteBatch > ;
833
825
834
826
fn render < ' w > (
835
827
_item : & P ,
836
828
_view : ( ) ,
837
- ( sprite_batch , sprite ) : ( & ' _ SpriteBatch , & ' _ ExtractedSprite ) ,
829
+ batch : & ' _ SpriteBatch ,
838
830
sprite_meta : SystemParamItem < ' w , ' _ , Self :: Param > ,
839
831
pass : & mut TrackedRenderPass < ' w > ,
840
832
) -> RenderCommandResult {
841
833
let sprite_meta = sprite_meta. into_inner ( ) ;
842
- if sprite . color != Color :: WHITE {
834
+ if batch . colored {
843
835
pass. set_vertex_buffer ( 0 , sprite_meta. colored_vertices . buffer ( ) . unwrap ( ) . slice ( ..) ) ;
844
836
} else {
845
837
pass. set_vertex_buffer ( 0 , sprite_meta. vertices . buffer ( ) . unwrap ( ) . slice ( ..) ) ;
846
838
}
847
- pass. draw ( sprite_batch . range . clone ( ) , 0 ..1 ) ;
839
+ pass. draw ( batch . range . clone ( ) , 0 ..1 ) ;
848
840
RenderCommandResult :: Success
849
841
}
850
842
}
0 commit comments