Skip to content

Add component change detection conditions #9746

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

Closed
wants to merge 3 commits into from

Conversation

A-Walrus
Copy link
Contributor

@A-Walrus A-Walrus commented Sep 10, 2023

Added any_component_changed and any_component_changed_or_removed

Objective

I believe these conditions are generally useful, and having them built in makes sense. Also makes it more symmetric with the resource conditions

Solution

Added them to the common_conditions module


Changelog

Added any_component_changed , any_component_changed_or_removed and any_component_added run conditions

`any_component_changed` and `any_component_changed_or_removed`
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Sep 10, 2023
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should be able to specify this for T: WorldQuery, which would make this quite a bit more powerful.

Copy link
Member

@joseph-gio joseph-gio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to have a variant that checks for changed but not added components. We don't have this variant for Mut or Ref since it's easy enough to do val.is_changed() && !val.is_added(), but it's less convenient to add a custom run condition that checks this.

@A-Walrus
Copy link
Contributor Author

I think that we should be able to specify this for T: WorldQuery, which would make this quite a bit more powerful.

@alice-i-cecile I tried making this change and am getting this monster of an error:

error[E0599]: the method `is_empty` exists for struct `Query<'_, '_, (), Changed<T>>`, but its trait bounds were not satisfied
   --> crates/bevy_ecs/src/schedule/condition.rs:955:52
    |
955 |         move |query: Query<(), Changed<T>>| !query.is_empty()
    |                                                    ^^^^^^^^ method cannot be called on `Query<'_, '_, (), Changed<T>>` due to unsatisfied trait bounds
    |
   ::: crates/bevy_ecs/src/system/query.rs:325:1
    |
325 | pub struct Query<'world, 'state, Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
    | --------------------------------------------------------------------------- method `is_empty` not found for this struct
    |
   ::: crates/bevy_ecs/src/query/filter.rs:396:9
    |
396 |         pub struct $name<T>(PhantomData<T>);
    |         ------------------------------------
    |         |
    |         doesn't satisfy `<_ as WorldQuery>::ReadOnly = Changed<T>`
    |         doesn't satisfy `filter::Changed<T>: fetch::ReadOnlyWorldQuery`
    |         doesn't satisfy `filter::Changed<T>: fetch::WorldQuery`
    |
note: the following trait bounds were not satisfied:
      `<filter::Changed<T> as fetch::WorldQuery>::ReadOnly = filter::Changed<T>`
      `filter::Changed<T>: fetch::WorldQuery`
   --> crates/bevy_ecs/src/query/fetch.rs:452:38
    |
452 | pub unsafe trait ReadOnlyWorldQuery: WorldQuery<ReadOnly = Self> {}
    |                  ------------------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                                      |          |
    |                                      |          unsatisfied trait bound introduced here
    |                                      unsatisfied trait bound introduced here
note: trait bound `filter::Changed<T>: fetch::ReadOnlyWorldQuery` was not satisfied
   --> crates/bevy_ecs/src/system/query.rs:351:32
    |
351 | impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
    |                                ^^^^^^^^^^^^^^^^^^  -------------------
    |                                |
    |                                unsatisfied trait bound introduced here
note: the traits `fetch::WorldQuery` and `fetch::ReadOnlyWorldQuery` must be implemented
   --> crates/bevy_ecs/src/query/fetch.rs:316:1
    |
316 | pub unsafe trait WorldQuery {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
452 | pub unsafe trait ReadOnlyWorldQuery: WorldQuery<ReadOnly = Self> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0599`.
error: could not compile `bevy_ecs` (lib) due to previous error

@SkiFire13
Copy link
Contributor

The problem is that Changed<T> only works for T: Component, not T: WorldQuery.

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I can see why that change failed. I do think an added variant would be nice here, but it doesn't have to be this PR.

Also fixed doc comment
// NOTE: The check for removed components must come before the check for changed compoents.
// If `query.is_empty()` is called first, then the short-circuiting behavior means that the removed
// components buffer may not be consumed every frame.
any_component_removed::<T>().or_else(any_component_changed::<T>())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really matter, but I'd like to comment that using the or_else combinator has slightly more overhead than just doing removals.iter().count() != 0 || !changed.is_empty()

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Sep 13, 2023
Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was previously attempted to be added in #7711, which was marked controversial for the same comments I'm adding below.

@@ -946,6 +946,31 @@ pub mod common_conditions {
move |mut removals: RemovedComponents<T>| removals.iter().count() != 0
}

/// Generates a [`Condition`](super::Condition)-satisfying closure that
/// returns `true` if there are any entities with a component of the given
/// type changed. Newly added components are considered changed.
Copy link
Member

@james7132 james7132 Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs a potentially very expensive query (iterating through all instances of component T) single-threaded and inline in the scheduler. This blocks the scheduler from launching other systems and can be a very big performance footgun, particularly when there are a large number of entities in the world with T. At a minimum, this should be noted in the doc comments, and it should recommend directly using change detection in the system as the recommenced alternative.


/// Generates a [`Condition`] that returns `true` if there are any entities
/// with a component of the given type changed or removed. Newly added
/// components are considered changed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar performance footgun concerns here.


/// Generates a [`Condition`](super::Condition)-satisfying closure that
/// returns `true` if there are any entities with a component of the given
/// type that were just added.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar performance footgun concerns here.

@james7132 james7132 added X-Controversial There is active debate or serious implications around merging this PR and removed S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it labels Sep 14, 2023
@A-Walrus
Copy link
Contributor Author

@james7132 makes a really good point. I don't think it makes sense to add this to bevy with the current performance drawbacks. Checking for changes within the system is more performant, and not much worse ergonomics. If in the future we track this kind of stuff directly, then these conditions would be nice. Closing this PR

@A-Walrus A-Walrus closed this Sep 16, 2023
@SkiFire13
Copy link
Contributor

Since this is the second time a PR like this was opened, would it be reasonable to leave a comment in the source code explaining why these functions are missing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants