
# Objective Improve the performance of `FilteredEntity(Ref|Mut)` and `Entity(Ref|Mut)Except`. `FilteredEntityRef` needs an `Access<ComponentId>` to determine what components it can access. There is one stored in the query state, but query items cannot borrow from the state, so it has to `clone()` the access for each row. Cloning the access involves memory allocations and can be expensive. ## Solution Let query items borrow from their query state. Add an `'s` lifetime to `WorldQuery::Item` and `WorldQuery::Fetch`, similar to the one in `SystemParam`, and provide `&'s Self::State` to the fetch so that it can borrow from the state. Unfortunately, there are a few cases where we currently return query items from temporary query states: the sorted iteration methods create a temporary state to query the sort keys, and the `EntityRef::components<Q>()` methods create a temporary state for their query. To allow these to continue to work with most `QueryData` implementations, introduce a new subtrait `ReleaseStateQueryData` that converts a `QueryItem<'w, 's>` to `QueryItem<'w, 'static>`, and is implemented for everything except `FilteredEntity(Ref|Mut)` and `Entity(Ref|Mut)Except`. `#[derive(QueryData)]` will generate `ReleaseStateQueryData` implementations that apply when all of the subqueries implement `ReleaseStateQueryData`. This PR does not actually change the implementation of `FilteredEntity(Ref|Mut)` or `Entity(Ref|Mut)Except`! That will be done as a follow-up PR so that the changes are easier to review. I have pushed the changes as chescock/bevy#5. ## Testing I ran performance traces of many_foxes, both against main and against chescock/bevy#5, both including #15282. These changes do appear to make generalized animation a bit faster: (Red is main, yellow is chescock/bevy#5)  ## Migration Guide The `WorldQuery::Item` and `WorldQuery::Fetch` associated types and the `QueryItem` and `ROQueryItem` type aliases now have an additional lifetime parameter corresponding to the `'s` lifetime in `Query`. Manual implementations of `WorldQuery` will need to update the method signatures to include the new lifetimes. Other uses of the types will need to be updated to include a lifetime parameter, although it can usually be passed as `'_`. In particular, `ROQueryItem` is used when implementing `RenderCommand`. Before: ```rust fn render<'w>( item: &P, view: ROQueryItem<'w, Self::ViewQuery>, entity: Option<ROQueryItem<'w, Self::ItemQuery>>, param: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult; ``` After: ```rust fn render<'w>( item: &P, view: ROQueryItem<'w, '_, Self::ViewQuery>, entity: Option<ROQueryItem<'w, '_, Self::ItemQuery>>, param: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult; ``` --- Methods on `QueryState` that take `&mut self` may now result in conflicting borrows if the query items capture the lifetime of the mutable reference. This affects `get()`, `iter()`, and others. To fix the errors, first call `QueryState::update_archetypes()`, and then replace a call `state.foo(world, param)` with `state.query_manual(world).foo_inner(param)`. Alternately, you may be able to restructure the code to call `state.query(world)` once and then make multiple calls using the `Query`. Before: ```rust let mut state: QueryState<_, _> = ...; let d1 = state.get(world, e1); let d2 = state.get(world, e2); // Error: cannot borrow `state` as mutable more than once at a time println!("{d1:?}"); println!("{d2:?}"); ``` After: ```rust let mut state: QueryState<_, _> = ...; state.update_archetypes(world); let d1 = state.get_manual(world, e1); let d2 = state.get_manual(world, e2); // OR state.update_archetypes(world); let d1 = state.query(world).get_inner(e1); let d2 = state.query(world).get_inner(e2); // OR let query = state.query(world); let d1 = query.get_inner(e1); let d1 = query.get_inner(e2); println!("{d1:?}"); println!("{d2:?}"); ```
50 lines
2.0 KiB
Rust
50 lines
2.0 KiB
Rust
//! A trait for components that let you traverse the ECS.
|
|
|
|
use crate::{
|
|
entity::Entity,
|
|
query::{ReadOnlyQueryData, ReleaseStateQueryData},
|
|
relationship::Relationship,
|
|
};
|
|
|
|
/// A component that can point to another entity, and which can be used to define a path through the ECS.
|
|
///
|
|
/// Traversals are used to [specify the direction] of [event propagation] in [observers].
|
|
/// The default query is `()`.
|
|
///
|
|
/// Infinite loops are possible, and are not checked for. While looping can be desirable in some contexts
|
|
/// (for example, an observer that triggers itself multiple times before stopping), following an infinite
|
|
/// traversal loop without an eventual exit will cause your application to hang. Each implementer of `Traversal`
|
|
/// is responsible for documenting possible looping behavior, and consumers of those implementations are responsible for
|
|
/// avoiding infinite loops in their code.
|
|
///
|
|
/// Traversals may be parameterized with additional data. For example, in observer event propagation, the
|
|
/// parameter `D` is the event type given in `On<E>`. This allows traversal to differ depending on event
|
|
/// data.
|
|
///
|
|
/// [specify the direction]: crate::event::EntityEvent::Traversal
|
|
/// [event propagation]: crate::observer::On::propagate
|
|
/// [observers]: crate::observer::Observer
|
|
pub trait Traversal<D: ?Sized>: ReadOnlyQueryData + ReleaseStateQueryData {
|
|
/// Returns the next entity to visit.
|
|
fn traverse(item: Self::Item<'_, '_>, data: &D) -> Option<Entity>;
|
|
}
|
|
|
|
impl<D> Traversal<D> for () {
|
|
fn traverse(_: Self::Item<'_, '_>, _data: &D) -> Option<Entity> {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// This provides generalized hierarchy traversal for use in [event propagation].
|
|
///
|
|
/// # Warning
|
|
///
|
|
/// Traversing in a loop could result in infinite loops for relationship graphs with loops.
|
|
///
|
|
/// [event propagation]: crate::observer::On::propagate
|
|
impl<R: Relationship, D> Traversal<D> for &R {
|
|
fn traverse(item: Self::Item<'_, '_>, _data: &D) -> Option<Entity> {
|
|
Some(item.get())
|
|
}
|
|
}
|