-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
Reliable Change Detection #54
Conversation
Closes #52 |
…reliable-change-detection
5d6cc41
to
cb5030f
Compare
Todo: add ring buffer diagram. |
Slides: https://docs.google.com/presentation/d/1gggEX4dfOaBdKcZJySm8i7TmxIJkyModEcl8dDkrqQw/edit?usp=sharing Also make sure to link to the talk |
Steal simpler explanation of the algorithm from the talk. |
✅ Deploy Preview for leafwing-studios ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
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 sure if you're still working on this, but I did a first pass.
Definitely an interesting write-up, especially since I tried to find out just recently if it only works until the end of the frame or if it "wraps around". (Of course the docs had the answer though :D)
Comments are mostly suggestions to link to the docs where it makes sense.
I purposefully used links to the 0.8 docs, so that the links don't break (or stop making sense) in future Bevy versions.
draft = true | ||
+++ | ||
|
||
**An abridged version of this post was also presented as a 10 minute talk for the Rust East Coast meetup group ([slides](https://docs.google.com/presentation/d/1gggEX4dfOaBdKcZJySm8i7TmxIJkyModEcl8dDkrqQw/edit#slide=id.p), [video](TODO: add link))*. |
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.
Don't forget the TODO 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.
The Leafwing logo on the Docs is the old one, but it's probably weird to update the slides after the talk
|
||
**An abridged version of this post was also presented as a 10 minute talk for the Rust East Coast meetup group ([slides](https://docs.google.com/presentation/d/1gggEX4dfOaBdKcZJySm8i7TmxIJkyModEcl8dDkrqQw/edit#slide=id.p), [video](TODO: add link))*. | ||
|
||
Over the past half-year or so, I've dedicated much of my time to improving [Bevy](https://bevyengine.org/), an ECS-first game engine in Rust. |
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 "past half-year" is probably not accurate anymore
// TODO: WRITE EXAMPLE | ||
// Text-only | ||
// Spawning system, changes creation |
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.
Don't forget the TODO here :)
Today, I want to talk about one of the features I helped build and love using: **reliable change detection**. | ||
|
||
For those of you who've never used Bevy before, all of the game logic occurs in **systems**: ordinary Rust functions that are automatically scheduled to run each frame. | ||
These receive their data from the central ECS data store by requesting access to specific data, typically via **queries**, which carefully slice the data and request only the specific data needed. |
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.
Consider linking to Query
here:
These receive their data from the central ECS data store by requesting access to specific data, typically via **queries**, which carefully slice the data and request only the specific data needed. | |
These receive their data from the central ECS data store by requesting access to specific data, typically via [**queries**](https://docs.rs/bevy/0.8.0/bevy/prelude/struct.Query.html), which carefully slice the data and request only the specific data needed. |
As we build out our game, we can combine these systems to spectacular effect, carefully slicing our data and performing elaborate feats of automatically-parallelized behavior-first execution. | ||
|
||
By default though, queries fetch data from *all* of the entities with the appropriate components. | ||
This can be quite expensive when we only need to operate on a subset, even if we carefully slice our queries using `With` or `Without` **query filters**. |
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.
Consider linking to the docs here:
This can be quite expensive when we only need to operate on a subset, even if we carefully slice our queries using `With` or `Without` **query filters**. | |
This can be quite expensive when we only need to operate on a subset, even if we carefully slice our queries using [`With`](https://docs.rs/bevy/0.8.0/bevy/prelude/struct.With.html) or [`Without`](https://docs.rs/bevy/0.8.0/bevy/prelude/struct.Without.html) **query filters**. |
To do so, we store timers in three separate locations: | ||
|
||
* On the `World` as a global indicator of current time. Whenever a system begins execution, [this increments once](https://github.com/bevyengine/bevy/pull/1471/files#diff-5499ffc12ee4011981ff6d5069a1986197d824648c65ed24056e6b2772b3cd81R146). | ||
* On each piece of `Resource` or `Component` data, [marking when the data was last changed](https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) in terms of the global timer. |
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.
Consider linking to the docs here:
* On each piece of `Resource` or `Component` data, [marking when the data was last changed](https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) in terms of the global timer. | |
* On each piece of [`Res`ource](https://docs.rs/bevy/0.8.0/bevy/prelude/struct.Res.html) or [`Component`](https://docs.rs/bevy/0.8.0/bevy/prelude/trait.Component.html) data, [marking when the data was last changed](https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) in terms of the global timer. |
|
||
* On the `World` as a global indicator of current time. Whenever a system begins execution, [this increments once](https://github.com/bevyengine/bevy/pull/1471/files#diff-5499ffc12ee4011981ff6d5069a1986197d824648c65ed24056e6b2772b3cd81R146). | ||
* On each piece of `Resource` or `Component` data, [marking when the data was last changed](https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) in terms of the global timer. | ||
* On each `System`, marking when the system last ran in terms of the global timer. |
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.
Consider linking to the docs here:
* On each `System`, marking when the system last ran in terms of the global timer. | |
* On each [`System`](https://docs.rs/bevy/0.8.0/bevy/prelude/trait.System.html), marking when the system last ran in terms of the global timer. |
|
||
1. **Ergonomic.** Yes, all of the magic happens behind-the-scenes. | ||
2. **Automatic.** Yes, there's no need to opt in. | ||
3. **Low memory overhead.** Yes, we're only storing a single u32 per component, no matter how many systems we have. |
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.
Formatting:
3. **Low memory overhead.** Yes, we're only storing a single u32 per component, no matter how many systems we have. | |
3. **Low memory overhead.** Yes, we're only storing a single [`u32`](https://doc.rust-lang.org/std/primitive.u32.html) per component, no matter how many systems we have. |
No matter the scenario, this solution detects each change *exactly* once per system if the data has been mutably accessed at least once since the system last ran. | ||
|
||
What's that? Oh right, I must apologize: there is one case where a change may be missed with a warning. | ||
If, while your system is asleep, more than [`u32::MAX * 3 / 4`](https://github.com/bevyengine/bevy/pull/1471#issuecomment-787009753) (3221225471) systems have run, you will miss changes that occurred. |
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.
Formatting:
If, while your system is asleep, more than [`u32::MAX * 3 / 4`](https://github.com/bevyengine/bevy/pull/1471#issuecomment-787009753) (3221225471) systems have run, you will miss changes that occurred. | |
If, while your system is asleep, more than [`u32::MAX * 3 / 4`](https://github.com/bevyengine/bevy/pull/1471#issuecomment-787009753) (3,221,225,471) systems have run, you will miss changes that occurred. |
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.
Also, why is it 3 / 4
?
* On each `System`, marking when the system last ran in terms of the global timer. | ||
|
||
If the data's change tick is greater than the system's change tick, the data has changed since it was last processed. | ||
We use [wrapping subtraction]((https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) to determine what "greater than" means, giving us **ring buffer** behavior, allowing our change detection to continue functioning indefinitely rather than running out of space as time goes on. |
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.
Typo:
We use [wrapping subtraction]((https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) to determine what "greater than" means, giving us **ring buffer** behavior, allowing our change detection to continue functioning indefinitely rather than running out of space as time goes on. | |
We use [wrapping subtraction](https://github.com/bevyengine/bevy/pull/1471/files#diff-0e94997025571f709abd7cac97f03bb4e8ec8bf29650068a7e5ec07170011892R137) to determine what "greater than" means, giving us **ring buffer** behavior, allowing our change detection to continue functioning indefinitely rather than running out of space as time goes on. |
Thanks for the feedback! I think I'm going to close this out though; not sure it has enough value at this point. |
Rendered
A technical post on solving reliable change detection for Bevy.