-
-
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
Add AssetChanged
query filter
#16810
base: main
Are you sure you want to change the base?
Conversation
Why does this revert the |
Because I didn't know that. Will change it back! |
Separately, how should the resource access in |
|
||
/// Filter that selects entities with a `A` for an asset that changed | ||
/// after the system last ran, where `A` is a component that implements | ||
/// [`AsAssetId`]. |
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.
What about components that store handles to multiple assets of the same type? We don't need to do that right now, but we might have to revisit it later. In my app I have components with handles to dozens of AnimationClip
s for example. I think you could maybe just have AsAssetId
's method return -> impl Iterator<Item = AssetId>
.
This isn't blocking, just a suggestion.
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 bigger issue is how to handle the associated type. We might have to use some kind of indirection via UntypedAssetId instead to be able to handle multiple kinds of assets.
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.
Sorry, I also misunderstood you. Multiple assets of the same type should be much easier to handle. I'd been hung up on if, for example we decided to merge mesh and material as has been discussed before.
How would the user forge an id if they do not have access to the underlying type? |
Co-authored-by: Patrick Walton <[email protected]>
Overall this looks fine to me, but as I mentioned we should have an ECS expert look over the |
#[inline] | ||
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) { | ||
<&A>::update_component_access(&state.asset_id, access); | ||
access.add_resource_read(state.resource_id); |
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 don't think this line is quite enough. We need Query
to report the access in Access<ArchetypeComponentId>
for it to be used by the scheduler, and it currently does that in QueryState::update_archetype_component_access()
, which only looks at component_reads_and_writes()
and component_writes()
.
I think you need modify QueryState::new_with_access()
to look at resource_reads_and_writes()
and resource_writes()
and convert them to ArchetypeComponentId
s.
I think you also need to modify FilteredAccess::is_compatible()
to consider resources to conflict even when the filters don't overlap. (That may need to get reworked in the future for resources-as-components, but the semantics seem reasonable.)
It would be good to add a unit test that verifies that the access is set up correctly. Maybe check that fn system(r: ResMut<AssetChanges<A>>, q: Query<Entity, AssetChanged<A>>)
panics when passed to assert_system_does_not_conflict
, and again with the parameters reversed.
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.
Alright thanks for the close review. Will add a test to validate.
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.
So just added a test and without making any other changes am getting the following error:
error[B0001]: Query<MyComponent, AssetChanged<MyComponent>> in system bevy_asset::asset_changed::tests::resource_conflict::system accesses component(s)AssetChanges<MyAsset> in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevyengine.org/learn/errors/b0001
Thoughts?
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.
Oh, right, ResMut
does unfiltered access, so FilteredAccess::is_compatible()
will see that the filters overlap and catch it. Great! So that won't be a problem unless someone tries to do mutable resource access from a query.
I think it's just the Access<ArchetypeComponentId>
that would be an issue, then. I'm not coming up with a simple way to write a test for that, though.
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.
So, I think it's just adding the following to QueryState::update_archetype_component_access
? d0130a3
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.
Not quite. That adds access to components on entities in archetypes matched by the query. But you want to add access to resources from the world. Resources on the world have their own ArchetypeComponentId
s that don't appear in any archetypes.
If we just want to support this use case of a finite set of read access, I think it's enough to add code to Query::new_with_access
that does
for component_id in state.component_access.resource_reads() {
access.add_resource_read(world.initialize_resource_internal(component_id).id());
}
More complete support would need to check for writes and for reads_all
and writes_all
, and look a bit like the code in FilteredResourcesMutParamBuilder
bevy/crates/bevy_ecs/src/system/builder.rs
Line 644 in 6178ce9
if access.has_read_all_resources() { |
The background here is that adding access in a QueryData
has only ever added access to entities matched by the query. There has never been a way to declare that you want access to resources from the world.
But the code you wrote looks like it should give access to resources! And that access is useful! So I think we should add the plumbing to make it work.
It might be cleaner to do that in a separate PR, though, and put this one back to what you had originally. This one would just be unsound until the other one was merged. If you'd like, I can try to put one together on Monday.
@@ -583,6 +583,16 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> { | |||
{ | |||
access.add_component_write(archetype_component_id); | |||
} | |||
if self.component_access.access.has_resource_read(component_id) { |
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.
Oh right, this is an existing soundness bug. Re-adding the tag.
Co-authored-by: Alice Cecile <[email protected]>
Objective
Implement a new
AssetChanged
query filter that allows users to query for entities whose related assets may have changed.cold-specialization
, a key rendering optimization for unlocking ancillary benefits of the retained render world, is blocked on being unable detect all scenarios in which an entity's mesh/material changes using events and observers. AnAssetChanged
filter will drastically simplify our implementation and be more robust to future changes.Originally implemented by @nicopap in #5080.
Solution
AssetChanged
query filter that initializes aAssetChanges<A>
resource that tracks changed assets and ticks inasset_events
.Reverts constrain WorldQuery::get_state to only use &Components #13343 and changes the api ofget_state
to acceptimpl Into<UnsafeWorldCell<'w>>
to allow accessing theAssetChanges<A>
resource.AsAssetId
trait used for newtype handle wrappers (e.g.Mesh3d
) that allows associating a component with the underlyingAsset
it represents.Testing
AssetChanged
.Mesh3d
andMeshMaterial3d
(etc) in the renderer. Long term wins in render performance this unblocks should swamp tracking overhead for any realistic workload.Migration Guide
asset_events
system is no longer public. Users should order their systems relative to theAssetEvents
system set.