Revert "Fix sprite performance regression since retained render world (#17078)" (#17123)

# Objective

Fixes #17098

It seems that it's not totally obvious how to fix this, but that
reverting might be part of the solution anyway.

Let's get the repo back into a working state.

## Solution

Revert the [recent
optimization](https://github.com/bevyengine/bevy/pull/17078) that broke
"many-to-one main->render world entities" for 2d.

## Testing

`cargo run --example text2d`
`cargo run --example sprite_slice`
This commit is contained in:
Rob Parrett 2025-01-03 16:22:18 -08:00 committed by GitHub
parent 39f38a191e
commit 859c2d77f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 37 deletions

View File

@ -19,6 +19,7 @@ use bevy_ecs::{
}; };
use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo}; use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo};
use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4}; use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
use bevy_render::sync_world::MainEntity;
use bevy_render::view::RenderVisibleEntities; use bevy_render::view::RenderVisibleEntities;
use bevy_render::{ use bevy_render::{
render_asset::RenderAssets, render_asset::RenderAssets,
@ -31,7 +32,7 @@ use bevy_render::{
*, *,
}, },
renderer::{RenderDevice, RenderQueue}, renderer::{RenderDevice, RenderQueue},
sync_world::{MainEntityHashMap, RenderEntity, TemporaryRenderEntity}, sync_world::{RenderEntity, TemporaryRenderEntity},
texture::{DefaultImageSampler, FallbackImage, GpuImage}, texture::{DefaultImageSampler, FallbackImage, GpuImage},
view::{ view::{
ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms,
@ -340,12 +341,11 @@ pub struct ExtractedSprite {
/// 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: Option<Entity>,
pub render_entity: Entity,
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct ExtractedSprites { pub struct ExtractedSprites {
pub sprites: MainEntityHashMap<ExtractedSprite>, pub sprites: HashMap<(Entity, MainEntity), ExtractedSprite>,
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
@ -390,13 +390,16 @@ pub fn extract_sprites(
if let Some(slices) = slices { if let Some(slices) = slices {
extracted_sprites.sprites.extend( extracted_sprites.sprites.extend(
slices slices
.extract_sprites( .extract_sprites(transform, original_entity, sprite)
transform, .map(|e| {
original_entity, (
(
commands.spawn(TemporaryRenderEntity).id(), commands.spawn(TemporaryRenderEntity).id(),
sprite, original_entity.into(),
),
e,
) )
.map(|e| (original_entity.into(), e)), }),
); );
} else { } else {
let atlas_rect = sprite let atlas_rect = sprite
@ -417,7 +420,7 @@ 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.insert(
original_entity.into(), (entity, original_entity.into()),
ExtractedSprite { ExtractedSprite {
color: sprite.color.into(), color: sprite.color.into(),
transform: *transform, transform: *transform,
@ -429,7 +432,6 @@ pub fn extract_sprites(
image_handle_id: sprite.image.id(), image_handle_id: sprite.image.id(),
anchor: sprite.anchor.as_vec(), anchor: sprite.anchor.as_vec(),
original_entity: Some(original_entity), original_entity: Some(original_entity),
render_entity: entity,
}, },
); );
} }
@ -556,11 +558,8 @@ pub fn queue_sprites(
.items .items
.reserve(extracted_sprites.sprites.len()); .reserve(extracted_sprites.sprites.len());
for (main_entity, extracted_sprite) in extracted_sprites.sprites.iter() { for ((entity, main_entity), extracted_sprite) in extracted_sprites.sprites.iter() {
let index = extracted_sprite let index = extracted_sprite.original_entity.unwrap_or(*entity).index();
.original_entity
.unwrap_or(extracted_sprite.render_entity)
.index();
if !view_entities.contains(index as usize) { if !view_entities.contains(index as usize) {
continue; continue;
@ -573,7 +572,7 @@ 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: (extracted_sprite.render_entity, *main_entity), entity: (*entity, *main_entity),
sort_key, sort_key,
// batch_range and dynamic_offset will be calculated in prepare_sprites // batch_range and dynamic_offset will be calculated in prepare_sprites
batch_range: 0..0, batch_range: 0..0,
@ -663,7 +662,7 @@ 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.1) else { let Some(extracted_sprite) = extracted_sprites.sprites.get(&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

View File

@ -28,7 +28,6 @@ impl ComputedTextureSlices {
&'a self, &'a self,
transform: &'a GlobalTransform, transform: &'a GlobalTransform,
original_entity: Entity, original_entity: Entity,
render_entity: Entity,
sprite: &'a Sprite, sprite: &'a Sprite,
) -> impl ExactSizeIterator<Item = ExtractedSprite> + 'a { ) -> impl ExactSizeIterator<Item = ExtractedSprite> + 'a {
let mut flip = Vec2::ONE; let mut flip = Vec2::ONE;
@ -54,7 +53,6 @@ impl ComputedTextureSlices {
flip_y, flip_y,
image_handle_id: sprite.image.id(), image_handle_id: sprite.image.id(),
anchor: Self::redepend_anchor_from_sprite_to_slice(sprite, slice), anchor: Self::redepend_anchor_from_sprite_to_slice(sprite, slice),
render_entity,
} }
}) })
} }

View File

@ -206,7 +206,10 @@ 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.insert(
(
commands.spawn(TemporaryRenderEntity).id(),
original_entity.into(), original_entity.into(),
),
ExtractedSprite { ExtractedSprite {
transform: transform * GlobalTransform::from_translation(position.extend(0.)), transform: transform * GlobalTransform::from_translation(position.extend(0.)),
color, color,
@ -217,7 +220,6 @@ pub fn extract_text2d_sprite(
flip_y: false, flip_y: false,
anchor: Anchor::Center.as_vec(), anchor: Anchor::Center.as_vec(),
original_entity: Some(original_entity), original_entity: Some(original_entity),
render_entity: commands.spawn(TemporaryRenderEntity).id(),
}, },
); );
} }

View File

@ -13,7 +13,7 @@ use bevy::{
render_asset::RenderAssetUsages, render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat}, render_resource::{Extent3d, TextureDimension, TextureFormat},
}, },
sprite::{AlphaMode2d, SpritePlugin}, sprite::AlphaMode2d,
utils::Duration, utils::Duration,
window::{PresentMode, WindowResolution}, window::{PresentMode, WindowResolution},
winit::{UpdateMode, WinitSettings}, winit::{UpdateMode, WinitSettings},
@ -132,8 +132,7 @@ fn main() {
App::new() App::new()
.add_plugins(( .add_plugins((
DefaultPlugins DefaultPlugins.set(WindowPlugin {
.set(WindowPlugin {
primary_window: Some(Window { primary_window: Some(Window {
title: "BevyMark".into(), title: "BevyMark".into(),
resolution: WindowResolution::new(1920.0, 1080.0) resolution: WindowResolution::new(1920.0, 1080.0)
@ -142,10 +141,6 @@ fn main() {
..default() ..default()
}), }),
..default() ..default()
})
.set(SpritePlugin {
#[cfg(feature = "bevy_sprite_picking_backend")]
add_picking: false,
}), }),
FrameTimeDiagnosticsPlugin, FrameTimeDiagnosticsPlugin,
LogDiagnosticsPlugin::default(), LogDiagnosticsPlugin::default(),