bevy/examples/ecs
Patrick Walton 3c41586154
Add EntityRefExcept and EntityMutExcept world queries, in preparation for generalized animation. (#15207)
This commit adds two new `WorldQuery` types: `EntityRefExcept` and
`EntityMutExcept`. These types work just like `EntityRef` and
`EntityMut`, but they prevent access to a statically-specified list of
components. For example, `EntityMutExcept<(AnimationPlayer,
Handle<AnimationGraph>)>` provides mutable access to all components
except for `AnimationPlayer` and `Handle<AnimationGraph>`. These types
are useful when you need to be able to process arbitrary queries while
iterating over the results of another `EntityMut` query.

The motivating use case is *generalized animation*, which is an upcoming
feature that allows animation of any component property, not just
rotation, translation, scaling, or morph weights. To implement this, we
must change the current `AnyOf<(&mut Transform, &mut MorphWeights)>` to
instead be `EntityMutExcept<(AnimationPlayer, Handle<AnimationGraph>)>`.
It's possible to use `FilteredEntityMut` in conjunction with a
dynamically-generated system instead, but `FilteredEntityMut` isn't
optimized for the use case of a large number of allowed components
coupled with a small set of disallowed components. No amount of
optimization of `FilteredEntityMut` produced acceptable performance on
the `many_foxes` benchmark. `Query<EntityMut, Without<AnimationPlayer>>`
will not suffice either, as it's legal and idiomatic for an
`AnimationTarget` and an `AnimationPlayer` to coexist on the same
entity.

An alternate proposal was to implement a somewhat-more-general
`Except<Q, CL>` feature, where Q is a `WorldQuery` and CL is a
`ComponentList`. I wasn't able to implement that proposal in a
reasonable way, because of the fact that methods like
`EntityMut::get_mut` and `EntityRef::get` are inherent methods instead
of methods on `WorldQuery`, and therefore there was no way to delegate
methods like `get` and `get_mut` to the inner query in a generic way.
Refactoring those methods into a trait would probably be possible.
However, I didn't see a use case for a hypothetical `Except` with
arbitrary queries: `Query<Except<(&Transform, &Visibility),
Visibility>>` would just be a complicated equivalent to
`Query<&Transform>`, for instance. So, out of a desire for simplicity, I
omitted a generic `Except` mechanism.

I've tested the performance of generalized animation on `many_foxes` and
found that, with this patch, `animate_targets` has a 7.4% slowdown over
`main`. With `FilteredEntityMut` optimized to use `Arc<Access>`, the
slowdown is 75.6%, due to contention on the reference count. Without
`Arc<Access>`, the slowdown is even worse, over 2x.

## Testing

New tests have been added that check that `EntityRefExcept` and
`EntityMutExcept` allow and disallow access to components properly and
that the query engine can correctly reject conflicting queries involving
those types.

A Tracy profile of `many_foxes` with 10,000 foxes showing generalized
animation using `FilteredEntityMut` (red) vs. main (yellow) is as
follows:

![Screenshot 2024-09-12
225914](https://github.com/user-attachments/assets/2993d74c-a513-4ba4-85bd-225672e7170a)

A Tracy profile of `many_foxes` with 10,000 foxes showing generalized
animation using this `EntityMutExcept` (yellow) vs. main (red) is as
follows:

![Screenshot 2024-09-14
205831](https://github.com/user-attachments/assets/4241015e-0c5d-44ef-835b-43f78a24e604)
2024-09-17 14:53:39 +00:00
..
change_detection.rs Track source location in change detection (#14034) 2024-07-30 12:02:38 +00:00
component_hooks.rs style: simplify string formatting for readability (#15033) 2024-09-03 23:35:49 +00:00
custom_query_param.rs Fix some doc warnings (#12961) 2024-04-14 15:23:44 +00:00
custom_schedule.rs Refactor App and SubApp internals for better separation (#9202) 2024-03-31 03:16:10 +00:00
dynamic.rs Add EntityRefExcept and EntityMutExcept world queries, in preparation for generalized animation. (#15207) 2024-09-17 14:53:39 +00:00
ecs_guide.rs Add example enum Component usage to ecs_guide (#13777) 2024-06-25 12:57:11 +00:00
event.rs Created an EventMutator for when you want to mutate an event before reading (#13818) 2024-07-08 14:53:06 +00:00
fixed_timestep.rs Unify FixedTime and Time while fixing several problems (#8964) 2023-10-16 01:57:55 +00:00
generic_system.rs Add insert_state to App. (#11043) 2023-12-21 14:09:24 +00:00
hierarchy.rs Fix comment in example (#15096) 2024-09-08 16:59:37 +00:00
iter_combinations.rs Fix floating point math (#15239) 2024-09-16 23:28:12 +00:00
nondeterministic_system_order.rs Fix non-functional nondeterministic_system_order example (#10719) 2023-11-25 21:13:35 +00:00
observer_propagation.rs Apply unused_qualifications lint (#14828) 2024-08-21 12:29:33 +00:00
observers.rs Return Results from Camera's world/viewport conversion methods (#14989) 2024-09-03 19:45:15 +00:00
one_shot_systems.rs Rename Commands::register_one_shot_system -> register_system (#14910) 2024-08-25 14:12:13 +00:00
parallel_query.rs Parallel event reader (#12554) 2024-04-22 16:37:42 +00:00
removal_detection.rs Use observers for removal detection in example (#14895) 2024-08-23 23:45:01 +00:00
run_conditions.rs docs: Fix incorrect docs in the run conditions example (#14377) 2024-07-20 16:51:05 +00:00
send_and_receive_events.rs style: simplify string formatting for readability (#15033) 2024-09-03 23:35:49 +00:00
startup_system.rs Schedule-First: the new and improved add_systems (#8079) 2023-03-18 01:45:34 +00:00
system_closure.rs Allow tuples and single plugins in add_plugins, deprecate add_plugin (#8097) 2023-06-21 20:51:03 +00:00
system_param.rs Inverse missing_docs logic (#11676) 2024-02-03 21:40:55 +00:00
system_piping.rs Add support for updating the tracing subscriber in LogPlugin (#10822) 2024-01-15 15:26:13 +00:00
system_stepping.rs Remove stepping from default features (#12847) 2024-04-03 19:16:02 +00:00