-
-
Couldn't load subscription status.
- Fork 4.2k
Dynamic queries and builder API #9774
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
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
QueryAPI to support more dynamic use cases i.e. scripting.Prior Art
WorldQuerywith runtime values #6390Solution
QueryBuilderwith runtime methods to define the set of component accesses for a built query.WorldQueryDataimplementationsFilteredEntityMutandFilteredEntityRefas variants ofEntityMutandEntityRefthat provide run time checked access to the components included in a given query.Queryto create "query lens" with a subset of the access of the initial query.Query Builder
The
QueryBuilderAPI 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
QueryBuilderAPI 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
QueryBuilderandQueryState. Here's a simple example:The
QueryStateandQueryBuildertransmute 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 aQueryStatewill 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
Queryhowever it does pose one additional wrinkle: In order to to change the iterator we need to create a newQueryStateto back it.Querydoesn't own it's own state though, it just borrows it, so we need a place to borrow it from. This is whyQueryLensexists, 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
EntityMutorEntityRef, however these can access all components and so conflict with all other accesses. This PR introducesFilteredEntityMutandFilteredEntityRefas alternatives that have additional runtime checking to prevent accessing components that you shouldn't. This way you can build a query with aQueryBuilderand actually access the components you asked for, and only those you asked for:You can still use
EntityMutorEntityRefin 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
dynamicexample to see it in action:Changelog
transmute_lensmethods toQuery.QueryBuilder,FilteredEntityMut,FilteredEntityRefandQueryLensupdate_archetype_component_accesshas been removed, archetype component accesses are now determined by the accesses set inupdate_component_accessset_accesstoWorldQuery, this is called beforeupdate_component_accessfor queries that have a restricted set of accesses, such as those built byQueryBuilderorQueryLens. This is primarily used by theFilteredEntity*variants and has an empty trait implementation.get_statetoWorldQueryas a fallible version ofinit_statewhen you don't have&mut Worldaccess.Future Work
Improve performance of
FilteredEntityMutandFilteredEntityRef, 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.