-
-
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
Implement WorldQuery for EntityRef #6960
Conversation
Co-authored-by: Carter Weinberg <[email protected]>
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.
The WorldQuery
impl looks right to me, and I don't see any real issues. Just a few things that could be added.
I imagine this will be very useful in niche situations.
!access.access().has_any_write(), | ||
"EntityRef conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", | ||
); | ||
access.read_all(); |
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.
Does a Query<EntityRef>
conflict with Res<Foo>
with a read_all
?
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.
No, but a ResMut<T>
would conflict under this access model. We could split this between "read all components" and "reads all resources" but I'm not sure how that would interact with the bitsets. Happy to try to do this in this PR, or defer it until later.
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.
I'd prefer to split that out (and I do think that should be done): that sort of work needs careful review.
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.
Should I address this as a part of this PR or should I leave this for later?
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.
Leave it until later please.
access: &mut Access<ArchetypeComponentId>, | ||
) { | ||
for component_id in archetype.components() { | ||
access.add_read(archetype.get_archetype_component_id(component_id).unwrap()); |
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.
This means that you can have a system with Query<(EntityRef, &A)>
that is fine, until you do commands.entity(entity).insert(A)
, right? Can we have a test for that?
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.
That's probably still fine since it only mutates the command queue and not the entity directly.
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 didn't expect it to be unsound, just something to keep in mind where a system may panic depending on the entities and components in the world.
Like how (a: Query<(&mut A, &C)>, b: Query<(&mut A, &B)>)
used to only conflict as soon as a (A, B, C) entity is spawned. https://bevyengine.org/news/bevy-0-5/#query-conflicts-use-componentid-instead-of-archetypecomponentid
Co-authored-by: Jakob Hellermann <[email protected]>
@james7132 I'm happy with this work and glad to see more steps towards runtime component access coming together! Once you've resolved conflicts feel free to merge <3 |
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.
This looks good to me!
I've resolved conflicts (and adjusted to the UnsafeWorldCell changes)
Objective
Partially address #5504. Fix #4278. Provide "whole entity" access in queries. This can be useful when you don't know at compile time what you're accessing (i.e. reflection via
ReflectComponent
).Solution
Implement
WorldQuery
forEntityRef
.EntityRef
can normally do.EntityRef
to the ECS prelude.To make this safe,
EntityRef::world
was removed as it gave potentialUnsafeCell
-like access to other parts of theWorld
including aliased mutable access to the components it would otherwise read safely.Performance
Not great beyond the additional parallelization opportunity over exclusive systems. The
EntityRef
is fetched fromEntities
like any other call toWorld::entity
, which can be very random access heavy. This could be simplified ifArchetypeRow
is available inWorldQuery::fetch
's arguments, but that's likely not something we should optimize for.Future work
An equivalent API where it gives mutable access to all components on a entity can be done with a scoped version of
EntityMut
where it does not provide&mut World
access nor allow for structural changes to the entity is feasible as well. This could be done as a safe alternative to exclusive system when structural mutation isn't required or the target set of entities is scoped.Changelog
Added:
Access::has_any_write
Added:
EntityRef
now implementsWorldQuery
. Allows read-only access to the entire entity, incompatible with any other mutable access, can be mixed withWith
/Without
filters for more targeted use.Added:
EntityRef
tobevy::ecs::prelude
.Removed:
EntityRef::world
Migration Guide
EntityRef::world
has been removed to makeEntityRef
sound to use as a query result. If you were retrievingEntityRef
viaWorld::entity
orWorld::get_entity
. Save a copy of the reference to theWorld
before callingWorld::entity
.