-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move sprite batches to resource #17636
base: main
Are you sure you want to change the base?
Conversation
Currently, `prepare_sprite_image_bind_group` spawns sprite batches onto an individual representative entity of the batch. This poses significant problems for multi-camera setups, since an entity may appear in multiple phase instances. Instead, move batches into a resource that is keyed off the view and the representative entity. Long term we should switch to mesh2d and use the existing BinnedRenderPhase functionality rather than naively queueing into transparent and doing our own ad-hoc batching logic. Fixes bevyengine#16867, bevyengine#17351
I guess this is technically a breaking change but can probably be safely picked into |
Ran codes provided by each of the linked issues and they seem to work as intended now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While i was looking for info on the issues, i saw line 565
let index = extracted_sprite.original_entity.unwrap_or(*entity).index();
should the value inside the unwrap_or
be main_entity.id()
?
Yes, the |
crates/bevy_sprite/src/render/mod.rs
Outdated
@@ -650,7 +653,8 @@ pub fn prepare_sprite_image_bind_groups( | |||
|
|||
let image_bind_groups = &mut *image_bind_groups; | |||
|
|||
for transparent_phase in phases.values_mut() { | |||
for (retained_view, transparent_phase) in phases.iter_mut() { | |||
let mut batch_key = None; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike how the way the flow of the method requires that this is initialized with None
, i would personally collect all distinct phase items into something like a HashMap<AssetId<Image>, Vec<Entity>>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without having reminded myself of how this code works, not having thought enough about what you mean, using HashMap to hold collections of entities with not-highly-optimised keys seems likely to introduce significant performance regressions.
Do you have any Tracy traces to show the performance of bevymark using the sprite mode with say 100-150k sprites? |
@@ -483,7 +484,10 @@ pub struct SpriteViewBindGroup { | |||
pub value: BindGroup, | |||
} | |||
|
|||
#[derive(Component, PartialEq, Eq, Clone)] | |||
#[derive(Resource, Deref, DerefMut, Default)] | |||
pub struct SpriteBatches(HashMap<(RetainedViewEntity, MainEntity), SpriteBatch>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have an optimised hash of a pair of entities yet? If not then perhaps we need one? In the degenerate case with one batch per sprite I suppose this would hurt. But, sprites should use sprite sheets so that should not happen for large numbers of sprites in practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using EntityHash
should work no matter how many entities you use in the key. I'll include that instead of the default hasher and confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This didn't seem to matter in my testing.
@@ -690,8 +694,7 @@ pub fn prepare_sprite_image_bind_groups( | |||
}); | |||
|
|||
batch_item_index = item_index; | |||
batches.push(( | |||
item.entity(), | |||
current_batch = Some(batches.entry((*retained_view, item.main_entity())).insert( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to use the whole RetainedViewEntity
in the key, it seems like just using retained_view.main_entity
would be enough to avoid collisions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it probably would be fine, especially because this is not yet retained and we are rebuilding this every frame, but we're moving towards using RetainedViewEntity
everywhere for safety reasons. In my brief benching, using just the MainEntity
didn't seem to have a notable performance impact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. The batches aren't overwritten any more, the example from #17351 works correctly now.
Objective
Currently,
prepare_sprite_image_bind_group
spawns sprite batches onto an individual representative entity of the batch. This poses significant problems for multi-camera setups, since an entity may appear in multiple phase instances.Solution
Instead, move batches into a resource that is keyed off the view and the representative entity. Long term we should switch to mesh2d and use the existing BinnedRenderPhase functionality rather than naively queueing into transparent and doing our own ad-hoc batching logic.
Fixes #16867, #17351
Testing
Tested repros in above issues.