Faster FilteredEntity(Ref|Mut)
and Entity(Ref|Mut)Except
by borrowing Access
#20111
+277
−267
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Objective
Improve the performance of queries using
FilteredEntityRef
,FilteredEntityMut
,EntityRefExcept
, andEntityMutExcept
. In particular, this appears to speed upbevy_animation::animate_targets
by 10% in many-foxes.FilteredEntity(Ref|Mut)
needs to store anAccess
to determine which components may be accessed. Prior to #15396, this required cloning theAccess
for each instance. Now, we can borrow theAccess
from the query state and make cheap pointer copies.Entity(Ref|Mut)Except
avoided needing to clone anAccess
by calling functions on theBundle
trait. Unfortunately, that meant we needed to convert from a type to aComponentId
for every component in the bundle on every check. Now, we can do those conversions up front and pass references to anAccess
.Finally, fix a bug where
Entity(Ref|Mut)Except
would not initialize their components duringinit_state
. I noticed this while updatinginit_state
and fixed it while I was there. That was normally harmless because the components would be registered elsewhere, but a system likefn system(_q1: Query<EntityMutExcept<C>>, _q2: Query<&mut C>) {}
would fail to find theComponentId
forC
and not exclude it from the access forq1
, and then panic with conflicting access fromq2
.Solution
Change
FilteredEntityRef
andFilteredEntityMut
to store&'s Access
instead ofAccess
, and changeEntityRefExcept
andEntityMutExcept
to store an extra&'s Access
.This adds the
's
lifetime to those four types, and most changes are adding lifetimes as appropriate.Change the
WorldQuery::State
forEntity(Ref|Mut)Except
to store anAccess
that can be borrowed from, replacing theSmallVec<[ComponentId; 4]>
that was used only to set the query access.To support the conversions from
EntityRef
andEntityMut
, we need to be able to create a&'static Access
for read-all or write-all. I could not changefn read_all_components()
to beconst
because it called the non-const
FixedBitSet::clear()
, so I created separate constructor functions.Testing
Ran
cargo run --example many_foxes --features bevy/trace_tracy --release
before and after, and compared the results ofanimate_targets
, since that is the only in-engine use ofEntityMutExcept
and was the motivation for creating it.Yellow is this PR, red is main: