Skip to content

Commit ec0e7a7

Browse files
Fix bevy_ui and improve bevy_sprite
1 parent 1cbaff0 commit ec0e7a7

File tree

5 files changed

+337
-303
lines changed

5 files changed

+337
-303
lines changed

crates/bevy_sprite/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ impl Plugin for SpritePlugin {
7979
.init_resource::<ImageBindGroups>()
8080
.init_resource::<SpecializedRenderPipelines<SpritePipeline>>()
8181
.init_resource::<SpriteMeta>()
82+
.init_resource::<ExtractedSprites>()
8283
.init_resource::<SpriteAssetEvents>()
8384
.add_render_command::<Transparent2d, DrawSprite>()
8485
.add_systems(

crates/bevy_sprite/src/render/mod.rs

Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use bevy_core_pipeline::{
1111
};
1212
use bevy_ecs::{
1313
prelude::*,
14+
storage::SparseSet,
1415
system::{lifetimeless::*, SystemParamItem, SystemState},
1516
};
1617
use bevy_math::{Rect, Vec2};
@@ -294,7 +295,6 @@ impl SpecializedRenderPipeline for SpritePipeline {
294295
}
295296
}
296297

297-
#[derive(Component, Clone, Copy)]
298298
pub struct ExtractedSprite {
299299
pub transform: GlobalTransform,
300300
pub color: Color,
@@ -310,6 +310,11 @@ pub struct ExtractedSprite {
310310
pub anchor: Vec2,
311311
}
312312

313+
#[derive(Resource, Default)]
314+
pub struct ExtractedSprites {
315+
pub sprites: SparseSet<Entity, ExtractedSprite>,
316+
}
317+
313318
#[derive(Resource, Default)]
314319
pub struct SpriteAssetEvents {
315320
pub images: Vec<AssetEvent<Image>>,
@@ -339,8 +344,7 @@ pub fn extract_sprite_events(
339344
}
340345

341346
pub fn extract_sprites(
342-
mut commands: Commands,
343-
mut previous_len: Local<usize>,
347+
mut extracted_sprites: ResMut<ExtractedSprites>,
344348
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
345349
sprite_query: Extract<
346350
Query<(
@@ -361,13 +365,12 @@ pub fn extract_sprites(
361365
)>,
362366
>,
363367
) {
364-
let mut extracted_sprites: Vec<(Entity, ExtractedSprite)> = Vec::with_capacity(*previous_len);
365368
for (entity, visibility, sprite, transform, handle) in sprite_query.iter() {
366369
if !visibility.is_visible() {
367370
continue;
368371
}
369372
// 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(
371374
entity,
372375
ExtractedSprite {
373376
color: sprite.color,
@@ -380,7 +383,7 @@ pub fn extract_sprites(
380383
image_handle_id: handle.id(),
381384
anchor: sprite.anchor.as_vec(),
382385
},
383-
));
386+
);
384387
}
385388
for (entity, visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
386389
if !visibility.is_visible() {
@@ -399,7 +402,7 @@ pub fn extract_sprites(
399402
)
400403
}),
401404
);
402-
extracted_sprites.push((
405+
extracted_sprites.sprites.insert(
403406
entity,
404407
ExtractedSprite {
405408
color: atlas_sprite.color,
@@ -413,11 +416,9 @@ pub fn extract_sprites(
413416
image_handle_id: texture_atlas.texture.id(),
414417
anchor: atlas_sprite.anchor.as_vec(),
415418
},
416-
));
419+
);
417420
}
418421
}
419-
*previous_len = extracted_sprites.len();
420-
commands.insert_or_spawn_batch(extracted_sprites);
421422
}
422423

423424
#[repr(C)]
@@ -471,6 +472,8 @@ const QUAD_UVS: [Vec2; 4] = [
471472
#[derive(Component)]
472473
pub struct SpriteBatch {
473474
range: Range<u32>,
475+
image: Handle<Image>,
476+
colored: bool,
474477
}
475478

476479
#[derive(Resource, Default)]
@@ -486,8 +489,7 @@ pub fn queue_sprites(
486489
mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
487490
pipeline_cache: Res<PipelineCache>,
488491
msaa: Res<Msaa>,
489-
extracted_sprites: Query<(Entity, &ExtractedSprite)>,
490-
gpu_images: Res<RenderAssets<Image>>,
492+
extracted_sprites: Res<ExtractedSprites>,
491493
mut views: Query<(
492494
&mut RenderPhase<Transparent2d>,
493495
&VisibleEntities,
@@ -542,14 +544,10 @@ pub fn queue_sprites(
542544

543545
transparent_phase
544546
.items
545-
.reserve(extracted_sprites.iter().len());
547+
.reserve(extracted_sprites.sprites.len());
546548

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) {
553551
continue;
554552
}
555553

@@ -561,7 +559,7 @@ pub fn queue_sprites(
561559
transparent_phase.add(Transparent2d {
562560
draw_function: draw_sprite_function,
563561
pipeline: colored_pipeline,
564-
entity,
562+
entity: *entity,
565563
sort_key,
566564
// batch size will be calculated in prepare_sprites
567565
batch_size: 0,
@@ -570,7 +568,7 @@ pub fn queue_sprites(
570568
transparent_phase.add(Transparent2d {
571569
draw_function: draw_sprite_function,
572570
pipeline,
573-
entity,
571+
entity: *entity,
574572
sort_key,
575573
// batch size will be calculated in prepare_sprites
576574
batch_size: 0,
@@ -591,7 +589,7 @@ pub fn prepare_sprites(
591589
sprite_pipeline: Res<SpritePipeline>,
592590
mut image_bind_groups: ResMut<ImageBindGroups>,
593591
gpu_images: Res<RenderAssets<Image>>,
594-
extracted_sprites: Query<&ExtractedSprite>,
592+
mut extracted_sprites: ResMut<ExtractedSprites>,
595593
mut phases: Query<&mut RenderPhase<Transparent2d>>,
596594
events: Res<SpriteAssetEvents>,
597595
) {
@@ -639,22 +637,17 @@ pub fn prepare_sprites(
639637
// Compatible items share the same entity.
640638
for item_index in 0..transparent_phase.items.len() {
641639
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) {
643641
// 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(|_| {
645643
batch_image_handle == extracted_sprite.image_handle_id
646644
&& batch_colored == (extracted_sprite.color != Color::WHITE)
647645
});
648646

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+
{
658651
batch_item_index = item_index;
659652
batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y);
660653
batch_image_handle = extracted_sprite.image_handle_id;
@@ -666,6 +659,8 @@ pub fn prepare_sprites(
666659
} else {
667660
index..index
668661
},
662+
colored: batch_colored,
663+
image: Handle::weak(batch_image_handle),
669664
};
670665

671666
batches.push((item.entity, new_batch));
@@ -693,10 +688,11 @@ pub fn prepare_sprites(
693688
layout: &sprite_pipeline.material_layout,
694689
})
695690
});
696-
697-
batches.last_mut().unwrap()
691+
existing_batch = batches.last_mut();
692+
} else {
693+
continue;
698694
}
699-
};
695+
}
700696

701697
// Calculate vertex data for this item
702698
let mut uvs = QUAD_UVS;
@@ -755,7 +751,9 @@ pub fn prepare_sprites(
755751
index += QUAD_INDICES.len() as u32;
756752
}
757753
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)
759757
}
760758
}
761759
}
@@ -768,6 +766,7 @@ pub fn prepare_sprites(
768766
*previous_len = batches.len();
769767
commands.insert_or_spawn_batch(batches);
770768
}
769+
extracted_sprites.sprites.clear();
771770
}
772771

773772
pub type DrawSprite = (
@@ -802,25 +801,18 @@ pub struct SetSpriteTextureBindGroup<const I: usize>;
802801
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteTextureBindGroup<I> {
803802
type Param = SRes<ImageBindGroups>;
804803
type ViewWorldQuery = ();
805-
type ItemWorldQuery = Read<ExtractedSprite>;
804+
type ItemWorldQuery = Read<SpriteBatch>;
806805

807806
fn render<'w>(
808807
_item: &P,
809808
_view: (),
810-
sprite: &'_ ExtractedSprite,
809+
batch: &'_ SpriteBatch,
811810
image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
812811
pass: &mut TrackedRenderPass<'w>,
813812
) -> RenderCommandResult {
814813
let image_bind_groups = image_bind_groups.into_inner();
815814

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(), &[]);
824816
RenderCommandResult::Success
825817
}
826818
}
@@ -829,22 +821,22 @@ pub struct DrawSpriteBatch;
829821
impl<P: PhaseItem> RenderCommand<P> for DrawSpriteBatch {
830822
type Param = SRes<SpriteMeta>;
831823
type ViewWorldQuery = ();
832-
type ItemWorldQuery = (Read<SpriteBatch>, Read<ExtractedSprite>);
824+
type ItemWorldQuery = Read<SpriteBatch>;
833825

834826
fn render<'w>(
835827
_item: &P,
836828
_view: (),
837-
(sprite_batch, sprite): (&'_ SpriteBatch, &'_ ExtractedSprite),
829+
batch: &'_ SpriteBatch,
838830
sprite_meta: SystemParamItem<'w, '_, Self::Param>,
839831
pass: &mut TrackedRenderPass<'w>,
840832
) -> RenderCommandResult {
841833
let sprite_meta = sprite_meta.into_inner();
842-
if sprite.color != Color::WHITE {
834+
if batch.colored {
843835
pass.set_vertex_buffer(0, sprite_meta.colored_vertices.buffer().unwrap().slice(..));
844836
} else {
845837
pass.set_vertex_buffer(0, sprite_meta.vertices.buffer().unwrap().slice(..));
846838
}
847-
pass.draw(sprite_batch.range.clone(), 0..1);
839+
pass.draw(batch.range.clone(), 0..1);
848840
RenderCommandResult::Success
849841
}
850842
}

crates/bevy_text/src/text2d.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use bevy_render::{
1717
view::{ComputedVisibility, Visibility},
1818
Extract,
1919
};
20-
use bevy_sprite::{Anchor, ExtractedSprite, TextureAtlas};
20+
use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas};
2121
use bevy_transform::prelude::{GlobalTransform, Transform};
2222
use bevy_utils::HashSet;
2323
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
@@ -78,12 +78,11 @@ pub struct Text2dBundle {
7878

7979
pub fn extract_text2d_sprite(
8080
mut commands: Commands,
81-
mut previous_len: Local<usize>,
81+
mut extracted_sprites: ResMut<ExtractedSprites>,
8282
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
8383
windows: Extract<Query<&Window, With<PrimaryWindow>>>,
8484
text2d_query: Extract<
8585
Query<(
86-
Entity,
8786
&ComputedVisibility,
8887
&Text,
8988
&TextLayoutInfo,
@@ -92,15 +91,14 @@ pub fn extract_text2d_sprite(
9291
)>,
9392
>,
9493
) {
95-
let mut extracted_sprites: Vec<(Entity, ExtractedSprite)> = Vec::with_capacity(*previous_len);
9694
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
9795
let scale_factor = windows
9896
.get_single()
9997
.map(|window| window.resolution.scale_factor() as f32)
10098
.unwrap_or(1.0);
10199
let scaling = GlobalTransform::from_scale(Vec3::splat(scale_factor.recip()));
102100

103-
for (entity, computed_visibility, text, text_layout_info, anchor, global_transform) in
101+
for (computed_visibility, text, text_layout_info, anchor, global_transform) in
104102
text2d_query.iter()
105103
{
106104
if !computed_visibility.is_visible() {
@@ -127,8 +125,8 @@ pub fn extract_text2d_sprite(
127125
}
128126
let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
129127

130-
extracted_sprites.push((
131-
entity,
128+
extracted_sprites.sprites.insert(
129+
commands.spawn_empty().id(),
132130
ExtractedSprite {
133131
transform: transform * GlobalTransform::from_translation(position.extend(0.)),
134132
color,
@@ -139,11 +137,9 @@ pub fn extract_text2d_sprite(
139137
flip_y: false,
140138
anchor: Anchor::Center.as_vec(),
141139
},
142-
));
140+
);
143141
}
144142
}
145-
*previous_len = extracted_sprites.len();
146-
commands.insert_or_spawn_batch(extracted_sprites);
147143
}
148144

149145
/// Updates the layout and size information whenever the text or style is changed.

0 commit comments

Comments
 (0)