Extract sprites into a Vec
(#17619)
# Objective Extract sprites into a `Vec` instead of a `HashMap`. ## Solution Extract UI nodes into a `Vec` instead of an `EntityHashMap`. Add an index into the `Vec` to `Transparent2d`. Compare both the index and render entity in prepare so there aren't any collisions. ## Showcase yellow this PR, red main ``` cargo run --example many_sprites --release --features "trace_tracy" ``` `extract_sprites` <img width="452" alt="extract_sprites" src="https://github.com/user-attachments/assets/66c60406-7c2b-4367-907d-4a71d3630296" /> `queue_sprites` <img width="463" alt="queue_sprites" src="https://github.com/user-attachments/assets/54b903bd-4137-4772-9f87-e10e1e050d69" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
958c9bb652
commit
4d8bc6161b
@ -349,6 +349,7 @@ pub struct Transparent2d {
|
|||||||
pub pipeline: CachedRenderPipelineId,
|
pub pipeline: CachedRenderPipelineId,
|
||||||
pub draw_function: DrawFunctionId,
|
pub draw_function: DrawFunctionId,
|
||||||
pub batch_range: Range<u32>,
|
pub batch_range: Range<u32>,
|
||||||
|
pub extracted_index: usize,
|
||||||
pub extra_index: PhaseItemExtraIndex,
|
pub extra_index: PhaseItemExtraIndex,
|
||||||
/// Whether the mesh in question is indexed (uses an index buffer in
|
/// Whether the mesh in question is indexed (uses an index buffer in
|
||||||
/// addition to its vertex buffer).
|
/// addition to its vertex buffer).
|
||||||
|
@ -341,6 +341,7 @@ fn queue_line_gizmos_2d(
|
|||||||
sort_key: FloatOrd(f32::INFINITY),
|
sort_key: FloatOrd(f32::INFINITY),
|
||||||
batch_range: 0..1,
|
batch_range: 0..1,
|
||||||
extra_index: PhaseItemExtraIndex::None,
|
extra_index: PhaseItemExtraIndex::None,
|
||||||
|
extracted_index: usize::MAX,
|
||||||
indexed: false,
|
indexed: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -362,6 +363,7 @@ fn queue_line_gizmos_2d(
|
|||||||
sort_key: FloatOrd(f32::INFINITY),
|
sort_key: FloatOrd(f32::INFINITY),
|
||||||
batch_range: 0..1,
|
batch_range: 0..1,
|
||||||
extra_index: PhaseItemExtraIndex::None,
|
extra_index: PhaseItemExtraIndex::None,
|
||||||
|
extracted_index: usize::MAX,
|
||||||
indexed: false,
|
indexed: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -421,6 +423,7 @@ fn queue_line_joint_gizmos_2d(
|
|||||||
sort_key: FloatOrd(f32::INFINITY),
|
sort_key: FloatOrd(f32::INFINITY),
|
||||||
batch_range: 0..1,
|
batch_range: 0..1,
|
||||||
extra_index: PhaseItemExtraIndex::None,
|
extra_index: PhaseItemExtraIndex::None,
|
||||||
|
extracted_index: usize::MAX,
|
||||||
indexed: false,
|
indexed: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -880,6 +880,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
|
|||||||
// Batching is done in batch_and_prepare_render_phase
|
// Batching is done in batch_and_prepare_render_phase
|
||||||
batch_range: 0..1,
|
batch_range: 0..1,
|
||||||
extra_index: PhaseItemExtraIndex::None,
|
extra_index: PhaseItemExtraIndex::None,
|
||||||
|
extracted_index: usize::MAX,
|
||||||
indexed: mesh.indexed(),
|
indexed: mesh.indexed(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ use bevy_ecs::{
|
|||||||
use bevy_image::{BevyDefault, Image, ImageSampler, TextureAtlasLayout, TextureFormatPixelInfo};
|
use bevy_image::{BevyDefault, Image, ImageSampler, TextureAtlasLayout, TextureFormatPixelInfo};
|
||||||
use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
|
use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
|
||||||
use bevy_platform_support::collections::HashMap;
|
use bevy_platform_support::collections::HashMap;
|
||||||
use bevy_render::sync_world::MainEntity;
|
|
||||||
use bevy_render::view::{RenderVisibleEntities, RetainedViewEntity};
|
use bevy_render::view::{RenderVisibleEntities, RetainedViewEntity};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
@ -32,7 +31,7 @@ use bevy_render::{
|
|||||||
*,
|
*,
|
||||||
},
|
},
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
sync_world::{RenderEntity, TemporaryRenderEntity},
|
sync_world::RenderEntity,
|
||||||
texture::{DefaultImageSampler, FallbackImage, GpuImage},
|
texture::{DefaultImageSampler, FallbackImage, GpuImage},
|
||||||
view::{
|
view::{
|
||||||
ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms,
|
ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms,
|
||||||
@ -339,13 +338,15 @@ pub struct ExtractedSprite {
|
|||||||
pub anchor: Vec2,
|
pub anchor: Vec2,
|
||||||
/// For cases where additional [`ExtractedSprites`] are created during extraction, this stores the
|
/// For cases where additional [`ExtractedSprites`] are created during extraction, this stores the
|
||||||
/// entity that caused that creation for use in determining visibility.
|
/// entity that caused that creation for use in determining visibility.
|
||||||
pub original_entity: Option<Entity>,
|
pub original_entity: Entity,
|
||||||
pub scaling_mode: Option<ScalingMode>,
|
pub scaling_mode: Option<ScalingMode>,
|
||||||
|
pub render_entity: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub struct ExtractedSprites {
|
pub struct ExtractedSprites {
|
||||||
pub sprites: HashMap<(Entity, MainEntity), ExtractedSprite>,
|
//pub sprites: HashMap<(Entity, MainEntity), ExtractedSprite>,
|
||||||
|
pub sprites: Vec<ExtractedSprite>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
@ -388,19 +389,12 @@ pub fn extract_sprites(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(slices) = slices {
|
if let Some(slices) = slices {
|
||||||
extracted_sprites.sprites.extend(
|
extracted_sprites.sprites.extend(slices.extract_sprites(
|
||||||
slices
|
&mut commands,
|
||||||
.extract_sprites(transform, original_entity, sprite)
|
transform,
|
||||||
.map(|e| {
|
original_entity,
|
||||||
(
|
sprite,
|
||||||
(
|
));
|
||||||
commands.spawn(TemporaryRenderEntity).id(),
|
|
||||||
original_entity.into(),
|
|
||||||
),
|
|
||||||
e,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
let atlas_rect = sprite
|
let atlas_rect = sprite
|
||||||
.texture_atlas
|
.texture_atlas
|
||||||
@ -419,22 +413,20 @@ pub fn extract_sprites(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
extracted_sprites.sprites.insert(
|
extracted_sprites.sprites.push(ExtractedSprite {
|
||||||
(entity, original_entity.into()),
|
render_entity: entity,
|
||||||
ExtractedSprite {
|
color: sprite.color.into(),
|
||||||
color: sprite.color.into(),
|
transform: *transform,
|
||||||
transform: *transform,
|
rect,
|
||||||
rect,
|
// Pass the custom size
|
||||||
// Pass the custom size
|
custom_size: sprite.custom_size,
|
||||||
custom_size: sprite.custom_size,
|
flip_x: sprite.flip_x,
|
||||||
flip_x: sprite.flip_x,
|
flip_y: sprite.flip_y,
|
||||||
flip_y: sprite.flip_y,
|
image_handle_id: sprite.image.id(),
|
||||||
image_handle_id: sprite.image.id(),
|
anchor: sprite.anchor.as_vec(),
|
||||||
anchor: sprite.anchor.as_vec(),
|
original_entity,
|
||||||
original_entity: Some(original_entity),
|
scaling_mode: sprite.image_mode.scale(),
|
||||||
scaling_mode: sprite.image_mode.scale(),
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,10 +553,10 @@ pub fn queue_sprites(
|
|||||||
.items
|
.items
|
||||||
.reserve(extracted_sprites.sprites.len());
|
.reserve(extracted_sprites.sprites.len());
|
||||||
|
|
||||||
for ((entity, main_entity), extracted_sprite) in extracted_sprites.sprites.iter() {
|
for (index, extracted_sprite) in extracted_sprites.sprites.iter().enumerate() {
|
||||||
let index = extracted_sprite.original_entity.unwrap_or(*entity).index();
|
let view_index = extracted_sprite.original_entity.index();
|
||||||
|
|
||||||
if !view_entities.contains(index as usize) {
|
if !view_entities.contains(view_index as usize) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,11 +567,15 @@ pub fn queue_sprites(
|
|||||||
transparent_phase.add(Transparent2d {
|
transparent_phase.add(Transparent2d {
|
||||||
draw_function: draw_sprite_function,
|
draw_function: draw_sprite_function,
|
||||||
pipeline,
|
pipeline,
|
||||||
entity: (*entity, *main_entity),
|
entity: (
|
||||||
|
extracted_sprite.render_entity,
|
||||||
|
extracted_sprite.original_entity.into(),
|
||||||
|
),
|
||||||
sort_key,
|
sort_key,
|
||||||
// `batch_range` is calculated in `prepare_sprite_image_bind_groups`
|
// `batch_range` is calculated in `prepare_sprite_image_bind_groups`
|
||||||
batch_range: 0..0,
|
batch_range: 0..0,
|
||||||
extra_index: PhaseItemExtraIndex::None,
|
extra_index: PhaseItemExtraIndex::None,
|
||||||
|
extracted_index: index,
|
||||||
indexed: true,
|
indexed: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -664,7 +660,12 @@ pub fn prepare_sprite_image_bind_groups(
|
|||||||
// Compatible items share the same entity.
|
// Compatible items share the same entity.
|
||||||
for item_index in 0..transparent_phase.items.len() {
|
for item_index in 0..transparent_phase.items.len() {
|
||||||
let item = &transparent_phase.items[item_index];
|
let item = &transparent_phase.items[item_index];
|
||||||
let Some(extracted_sprite) = extracted_sprites.sprites.get(&item.entity) else {
|
|
||||||
|
let Some(extracted_sprite) = extracted_sprites
|
||||||
|
.sprites
|
||||||
|
.get(item.extracted_index)
|
||||||
|
.filter(|extracted_sprite| extracted_sprite.render_entity == item.entity())
|
||||||
|
else {
|
||||||
// If there is a phase item that is not a sprite, then we must start a new
|
// If there is a phase item that is not a sprite, then we must start a new
|
||||||
// batch to draw the other phase item(s) and to respect draw order. This can be
|
// batch to draw the other phase item(s) and to respect draw order. This can be
|
||||||
// done by invalidating the batch_image_handle
|
// done by invalidating the batch_image_handle
|
||||||
|
@ -6,6 +6,7 @@ use bevy_ecs::prelude::*;
|
|||||||
use bevy_image::Image;
|
use bevy_image::Image;
|
||||||
use bevy_math::{Rect, Vec2};
|
use bevy_math::{Rect, Vec2};
|
||||||
use bevy_platform_support::collections::HashSet;
|
use bevy_platform_support::collections::HashSet;
|
||||||
|
use bevy_render::sync_world::TemporaryRenderEntity;
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
|
|
||||||
/// Component storing texture slices for tiled or sliced sprite entities
|
/// Component storing texture slices for tiled or sliced sprite entities
|
||||||
@ -24,12 +25,13 @@ impl ComputedTextureSlices {
|
|||||||
/// * `sprite` - The sprite component
|
/// * `sprite` - The sprite component
|
||||||
/// * `handle` - The sprite texture handle
|
/// * `handle` - The sprite texture handle
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn extract_sprites<'a>(
|
pub(crate) fn extract_sprites<'a, 'w, 's>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
commands: &'a mut Commands<'w, 's>,
|
||||||
transform: &'a GlobalTransform,
|
transform: &'a GlobalTransform,
|
||||||
original_entity: Entity,
|
original_entity: Entity,
|
||||||
sprite: &'a Sprite,
|
sprite: &'a Sprite,
|
||||||
) -> impl ExactSizeIterator<Item = ExtractedSprite> + 'a {
|
) -> impl ExactSizeIterator<Item = ExtractedSprite> + 'a + use<'a, 'w, 's> {
|
||||||
let mut flip = Vec2::ONE;
|
let mut flip = Vec2::ONE;
|
||||||
let [mut flip_x, mut flip_y] = [false; 2];
|
let [mut flip_x, mut flip_y] = [false; 2];
|
||||||
if sprite.flip_x {
|
if sprite.flip_x {
|
||||||
@ -44,7 +46,8 @@ impl ComputedTextureSlices {
|
|||||||
let offset = (slice.offset * flip).extend(0.0);
|
let offset = (slice.offset * flip).extend(0.0);
|
||||||
let transform = transform.mul_transform(Transform::from_translation(offset));
|
let transform = transform.mul_transform(Transform::from_translation(offset));
|
||||||
ExtractedSprite {
|
ExtractedSprite {
|
||||||
original_entity: Some(original_entity),
|
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||||
|
original_entity,
|
||||||
color: sprite.color.into(),
|
color: sprite.color.into(),
|
||||||
transform,
|
transform,
|
||||||
rect: Some(slice.texture_rect),
|
rect: Some(slice.texture_rect),
|
||||||
|
@ -204,24 +204,19 @@ pub fn extract_text2d_sprite(
|
|||||||
}
|
}
|
||||||
let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
|
let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
|
||||||
|
|
||||||
extracted_sprites.sprites.insert(
|
extracted_sprites.sprites.push(ExtractedSprite {
|
||||||
(
|
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||||
commands.spawn(TemporaryRenderEntity).id(),
|
transform: transform * GlobalTransform::from_translation(position.extend(0.)),
|
||||||
original_entity.into(),
|
color,
|
||||||
),
|
rect: Some(atlas.textures[atlas_info.location.glyph_index].as_rect()),
|
||||||
ExtractedSprite {
|
custom_size: None,
|
||||||
transform: transform * GlobalTransform::from_translation(position.extend(0.)),
|
image_handle_id: atlas_info.texture.id(),
|
||||||
color,
|
flip_x: false,
|
||||||
rect: Some(atlas.textures[atlas_info.location.glyph_index].as_rect()),
|
flip_y: false,
|
||||||
custom_size: None,
|
anchor: Anchor::Center.as_vec(),
|
||||||
image_handle_id: atlas_info.texture.id(),
|
original_entity,
|
||||||
flip_x: false,
|
scaling_mode: None,
|
||||||
flip_y: false,
|
});
|
||||||
anchor: Anchor::Center.as_vec(),
|
|
||||||
original_entity: Some(original_entity),
|
|
||||||
scaling_mode: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,6 +412,7 @@ pub fn queue_colored_mesh2d(
|
|||||||
// This material is not batched
|
// This material is not batched
|
||||||
batch_range: 0..1,
|
batch_range: 0..1,
|
||||||
extra_index: PhaseItemExtraIndex::None,
|
extra_index: PhaseItemExtraIndex::None,
|
||||||
|
extracted_index: usize::MAX,
|
||||||
indexed: mesh.indexed(),
|
indexed: mesh.indexed(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user