Skip to content
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

Make component access in the Query abstraction safe #51

Closed
cart opened this issue Jul 12, 2020 · 3 comments
Closed

Make component access in the Query abstraction safe #51

cart opened this issue Jul 12, 2020 · 3 comments

Comments

@cart
Copy link
Member

cart commented Jul 12, 2020

Unlike the old legion api, bevy_ecs systems now no longer have direct world (or subworld) access. Instead the plan is to give system queries safe direct access to the components/archetypes in the query.

fn system(mut query: Query<(&mut A, &B )>) {
  // iteration
  for (a, b) in &mut query.iter() {
    // do stuff here
  }

  // direct component access
  let component = query.get::<A>(some_entity).unwrap();
}

Currently direct component access works by just letting queries access their internal World reference directly:

query.get::<Component>(entity) is equivalent to world.get::<Component>(entity).

This is technically safe because hecs doesnt allow parallel access to components (it would panic if two queries tried to get write access to the same component in parallel), but its harder to reason about and could cause programs that appear to work to intermittently fail when invalid accesses occur. Queries should have a layer of checks when accessing an entity's components directly to ensure the query has permission to the given entity's component. This allows potentially invalid component accesses to fail fast (and consistently). It would mean that if you want to access an entity's component directly from a query, that component needs to match the query's archetype / mutability exactly.

When combined with a proper parallel scheduler, this gets us most of the way there to acceptable / consistent safety levels, because two systems with conflicting sets of queries would not be scheduled to run at the same time. The last "gap" would then be two queries within the same system potentially accessing the same components.

for example:

fn system(mut query_1: Query<(&mut A, &B )>, mut query_2: Query<&mut A>) {
  for (a, b) in &mut query_1.iter() {
    for a_might_collide in &mut query_2.iter() { /* might panic here */ }
  }
}

There are a few solutions to this problem:

  1. re-add "subworlds" with a world-splitting api like legion. this would add compile time "safety" at the cost of some clarity and ergonomics. note: legion's approach still has runtime panics for passing in the wrong subworld into a query.
  2. add some shared state between queries that ensures we fail immediately when something is potentially unsafe. this would be relatively inexpensive and would actually be logically similar to what happens in legion's subworld splitting apis.
  3. just rely on the existing safety checks

I personally prefer (2). I really like not needing to pass in and split subworlds. I think the planned Query api is much more pleasant to use and I'm willing to allow run time failures in this case as long as we fail fast and consistently if you try to do something "wrong".

That being said, if anyone has other ideas or opinions on the right path forward, feel free to chime in.

@cart
Copy link
Member Author

cart commented Jul 12, 2020

Oooh good news: it turns out I was wrong about the existing safety checks in the query_1 and query_2 example. They will already immediately fail if two mutable archetype accesses collide, the checks arent per-entity. hecs already implement the solution outlined in (2) for us.

So the main question at this point is: "do we want to rely on that behavior or use a legion-style world api".

@Moxinilian
Copy link
Member

We should rely on the original behavior in my opinion. Not only is it easier to reason about for users, it’s also less work and does exactly what is needed.

@cart
Copy link
Member Author

cart commented Jul 15, 2020

closed by a7bab75

@cart cart closed this as completed Jul 15, 2020
pcwalton pushed a commit to pcwalton/bevy that referenced this issue Aug 30, 2021
Support rendering 3D trimesh, with example
pcwalton added a commit to pcwalton/bevy that referenced this issue Feb 20, 2024
blended together.

This is an implementation of RFC bevyengine#51: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md

Note that the implementation strategy is different from the one outlined
in that RFC, because two-phase animation has now landed.
github-merge-queue bot pushed a commit that referenced this issue Mar 7, 2024
…e blended together. (#11989)

This is an implementation of RFC #51:
https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md

Note that the implementation strategy is different from the one outlined
in that RFC, because two-phase animation has now landed.

# Objective

Bevy needs animation blending. The RFC for this is [RFC 51].

## Solution

This is an implementation of the RFC. Note that the implementation
strategy is different from the one outlined there, because two-phase
animation has now landed.

This is just a draft to get the conversation started. Currently we're
missing a few things:

- [x] A fully-fleshed-out mechanism for transitions
- [x] A serialization format for `AnimationGraph`s
- [x] Examples are broken, other than `animated_fox`
- [x] Documentation

---

## Changelog

### Added

* The `AnimationPlayer` has been reworked to support blending multiple
animations together through an `AnimationGraph`, and as such will no
longer function unless a `Handle<AnimationGraph>` has been added to the
entity containing the player. See [RFC 51] for more details.

* Transition functionality has moved from the `AnimationPlayer` to a new
component, `AnimationTransitions`, which works in tandem with the
`AnimationGraph`.

## Migration Guide

* `AnimationPlayer`s can no longer play animations by themselves and
need to be paired with a `Handle<AnimationGraph>`. Code that was using
`AnimationPlayer` to play animations will need to create an
`AnimationGraph` asset first, add a node for the clip (or clips) you
want to play, and then supply the index of that node to the
`AnimationPlayer`'s `play` method.

* The `AnimationPlayer::play_with_transition()` method has been removed
and replaced with the `AnimationTransitions` component. If you were
previously using `AnimationPlayer::play_with_transition()`, add all
animations that you were playing to the `AnimationGraph`, and create an
`AnimationTransitions` component to manage the blending between them.

[RFC 51]:
https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
spectria-limina pushed a commit to spectria-limina/bevy that referenced this issue Mar 9, 2024
…e blended together. (bevyengine#11989)

This is an implementation of RFC bevyengine#51:
https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md

Note that the implementation strategy is different from the one outlined
in that RFC, because two-phase animation has now landed.

# Objective

Bevy needs animation blending. The RFC for this is [RFC 51].

## Solution

This is an implementation of the RFC. Note that the implementation
strategy is different from the one outlined there, because two-phase
animation has now landed.

This is just a draft to get the conversation started. Currently we're
missing a few things:

- [x] A fully-fleshed-out mechanism for transitions
- [x] A serialization format for `AnimationGraph`s
- [x] Examples are broken, other than `animated_fox`
- [x] Documentation

---

## Changelog

### Added

* The `AnimationPlayer` has been reworked to support blending multiple
animations together through an `AnimationGraph`, and as such will no
longer function unless a `Handle<AnimationGraph>` has been added to the
entity containing the player. See [RFC 51] for more details.

* Transition functionality has moved from the `AnimationPlayer` to a new
component, `AnimationTransitions`, which works in tandem with the
`AnimationGraph`.

## Migration Guide

* `AnimationPlayer`s can no longer play animations by themselves and
need to be paired with a `Handle<AnimationGraph>`. Code that was using
`AnimationPlayer` to play animations will need to create an
`AnimationGraph` asset first, add a node for the clip (or clips) you
want to play, and then supply the index of that node to the
`AnimationPlayer`'s `play` method.

* The `AnimationPlayer::play_with_transition()` method has been removed
and replaced with the `AnimationTransitions` component. If you were
previously using `AnimationPlayer::play_with_transition()`, add all
animations that you were playing to the `AnimationGraph`, and create an
`AnimationTransitions` component to manage the blending between them.

[RFC 51]:
https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants