-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Dynamic queries and builder API #9774
Dynamic queries and builder API #9774
Conversation
You added a new example but didn't add metadata for it. Please update the root Cargo.toml file. |
You added a new example but didn't add metadata for it. Please update the root Cargo.toml file. |
ebdec5c
to
c16e0ec
Compare
With #9686 merged too, I wonder if there's a way we could automatically check or enforce that all of the methods and docs are kept in sync 🤔 A trait won't work, both because of ergonomics / discoverability, and because |
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.
Very cool stuff! Some thoughts:
- The machinery is going to be a lot easier to review with even basic doc comments on each of the new types and traits :) I'm happy to help refine things when you're ready.
- We should try to merge a simple benchmark for these (that directly mirrors a static one) with this PR or shortly after.
- Adapting an existing example is not very informative for reviewers, since it doesn't showcase the power at all. Good to verify that things work as expected for now, but we'll need something better. Maybe two: one that demonstrates this feature in isolation, and another working with
ComponentId
? - There's a lot of code, but it seems quite high quality: it's all about as direct as I can think to solve this particular problem. Good tests too!
Thanks! Agree on all accounts. Going to take a pass at documentation tomorrow, I have mirrored the existing query benchmarks in my perf branch but I need to clean that up to integrate into the PR. The example definitely isn't very illustrative, it was mostly a sanity check for me and I hadn't removed it yet. It doesn't prove much beyond what's shown in the tests which are more thorough. I want to create one just showing the query used statically and another showing some kind of scripting/dynamic use case. |
You added a new example but didn't add metadata for it. Please update the root Cargo.toml file. |
… more consistent and descriptive
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.
Made some suggested edits to some of the safety comments on FilteredEntityRef
and FilteredEntityMut
. The original comments felt a bit too vague. I'm not 100% on them yet though. Need to look more over the chain from UnsafeWorldCell
-> UnsafeEntityCell
-> FilteredEntityMut
.
I think everything besides that can be safely punted to follow up prs.
/// Equivalent to [`Self::transmute_lens`] but also includes a [`WorldQueryFilter`] type. | ||
/// | ||
/// Note that the lens will iterate the same tables and archetypes as the original query. This means that | ||
/// additional archetypal query terms like [`With`](crate::query::With) and [`Without`](crate::query::Without) |
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.
feels weird that you could add With<A>
, but some of the archetypes don't have an A
or Without<A>
and the archetype might have an A
. Feels like we should be panicking if the user tries to do that.
Not really a safety issue, so I'd be fine with punting this into an issue.
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.
Yeah I'm not a fan but as you say it's not a safety issue. As I'm sure you're aware the primary motivating use case is Changed
and Added
which aren't archetypal so won't continue to apply if not included, we could definitely panic on terms who's conditions no longer apply in a follow up.
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.
As a follow-up, we should see if it would be worth adding a marker trait to forbid archetypal queries here.
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 to me.
Should open a couple issues once this is merged for followups for these comments.
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.
Really happy with the state of this PR, and what it opens up 🌈. I'm hoping to approve this soon, though I wanna review it deeply a couple more times.
I'm not thrilled with the fact that we have yet another set of EnityRef
and EntityMut
types, but it's worth it to get dynamic queries. Hopefully we can improve this in the future. Maybe whatever solution we land on to improve performance of FilteredEntityRef
will allow us to merge it back with EntityRef
.
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.
Thanks for all your work. I think this will be ready once the existing conversations are resolved.
Missed the review process but just wanted to thank @james-j-obrien for the great work and getting this feature over the line! Looks like it's time to revisit scripting for Bevy :) |
`update_archetype_component_access` was removed from queries in bevyengine#9774, but some documentation still refered to it.
# Objective `update_archetype_component_access` was removed from queries in #9774, but some documentation still refers to it. ## Solution Update the documentation. Since a bunch of these were in SAFETY comments it would be nice if someone who knows the details better could check that the rest of those comments are still valid.
# Objective `update_archetype_component_access` was removed from queries in bevyengine#9774, but some documentation still refers to it. ## Solution Update the documentation. Since a bunch of these were in SAFETY comments it would be nice if someone who knows the details better could check that the rest of those comments are still valid.
# Objective `update_archetype_component_access` was removed from queries in bevyengine#9774, but some documentation still refers to it. ## Solution Update the documentation. Since a bunch of these were in SAFETY comments it would be nice if someone who knows the details better could check that the rest of those comments are still valid.
Objective
Expand the existing
Query
API to support more dynamic use cases i.e. scripting.Prior Art
WorldQuery
with runtime values #6390Solution
QueryBuilder
with runtime methods to define the set of component accesses for a built query.WorldQueryData
implementationsFilteredEntityMut
andFilteredEntityRef
as variants ofEntityMut
andEntityRef
that provide run time checked access to the components included in a given query.Query
to create "query lens" with a subset of the access of the initial query.Query Builder
The
QueryBuilder
API allows you to define a query at runtime. At it's most basic use it will simply create a query with the corresponding type signature:Before calling
.build()
you also have the opportunity to add additional accesses and filters. Here is a simple example where we add additional filter terms:This alone is useful in that allows you to decide which archetypes your query will match at runtime. However it is also very limited, consider a case like the following:
This will grant the query an additional read access to component B however we have no way of accessing the data while iterating as the type signature still only includes &A. For an even more concrete example of this consider dynamic components:
With this in mind the
QueryBuilder
API seems somewhat incomplete by itself, we need some way method of accessing the components dynamically. So here's one:Query Transmutation
If the problem is not having the component in the type signature why not just add it? This PR also adds transmute methods to
QueryBuilder
andQueryState
. Here's a simple example:The
QueryState
andQueryBuilder
transmute methods look quite similar but are different in one respect. Transmuting a builder will always succeed as it will just add the additional accesses needed for the new terms if they weren't already included. Transmuting aQueryState
will panic in the case that the new type signature would give it access it didn't already have, for example:This is quite an appealing API to also have available on
Query
however it does pose one additional wrinkle: In order to to change the iterator we need to create a newQueryState
to back it.Query
doesn't own it's own state though, it just borrows it, so we need a place to borrow it from. This is whyQueryLens
exists, it is a place to store the new state so it can be borrowed when you call.query()
leaving you with an API like this:Now you may be thinking: Hey, wait a second, you introduced the problem with dynamic components and then described a solution that only works for static components! Ok, you got me, I guess we need a bit more:
Filtered Entity References
Currently the only way you can access dynamic components on entities through a query is with either
EntityMut
orEntityRef
, however these can access all components and so conflict with all other accesses. This PR introducesFilteredEntityMut
andFilteredEntityRef
as alternatives that have additional runtime checking to prevent accessing components that you shouldn't. This way you can build a query with aQueryBuilder
and actually access the components you asked for, and only those you asked for:You can still use
EntityMut
orEntityRef
in scenarios where you don't need to respect the restricted component access, the query will still respect filter terms.For the most part these new structs have the exact same methods as their non-filtered equivalents.
Putting all of this together we can do some truly dynamic ECS queries, check out the
dynamic
example to see it in action:Changelog
transmute_lens
methods toQuery
.QueryBuilder
,FilteredEntityMut
,FilteredEntityRef
andQueryLens
update_archetype_component_access
has been removed, archetype component accesses are now determined by the accesses set inupdate_component_access
set_access
toWorldQuery
, this is called beforeupdate_component_access
for queries that have a restricted set of accesses, such as those built byQueryBuilder
orQueryLens
. This is primarily used by theFilteredEntity*
variants and has an empty trait implementation.get_state
toWorldQuery
as a fallible version ofinit_state
when you don't have&mut World
access.Future Work
Improve performance of
FilteredEntityMut
andFilteredEntityRef
, currently they have to determine the accesses a query has in a given archetype during iteration which is far from ideal, especially since we already did the work when matching the archetype in the first place. To avoid making more internal API changes I have left it out of this PR.