Changes to how paint and animation are handled in druid-shell
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 9.7k
- Forks
- 565
- PR merge metrics
- No merged PRs in 30d
Description
This is an attempt to sketch out certain changes I would like to see in druid-shell, related to how painting works. I haven't worked through this in detail, and it's possible something is missing: this is just my current thinking.
## currently
Currently painting works on a push model. When a druid application needs to repaint, it calls a method (like `request_paint`) that maps directly to some platform invalidation API, and then the platform calls back our paint method. This has caused problems, particularly on mac. One issue is that when using invalidation regions, newer versions of macOS will only keep track of some (small) number of regions, which can cause paint artifacts.
The bigger issue is with the `AnimFrame` event (sent during paint). After any event, we call `update` on *all* windows, since the event may have caused a data change. With more windows open, this becomes quadratic. With a single window, there is a single `paint` call, which means a single `AnimFrame` and thus a single `update` call. With two windows, though, each window gets `AnimFrame` and then each window gets *two* `update` calls. It's unclear to me whether this is a huge problem, since the `update` is often a no-op, but on certain versions of macOS there have been some hard-to-debug segfaults apparently related to `request_paint` being called in the middle of an existing `paint` call.
One part of this is a consequence of how animation works. Currently you begin an animation with `request_anim_frame`, and then before the next paint method you recieve an `AnimFrame` event, in which, if you are not finished animating, you call `request_anim_frame` again.
## proposed changes
I would like to move to a more manual, tick-based system.
The core difference here is that druid-shell would start some sort of media timer with the platform, which would then call back into druid-shell whenever it was time to draw a new frame.
On the druid side (in the normal, non-animation case) all invalidation would be tracked in the framework. When druid received the 'tick' call from the platform (which would be per-window), it would check to see if anything had been marked for repaint, and if so it would *then* actually invalidate with the framework, causing a call to `paint`. In the case where only some subregion of the window was invalid, druid would be responsible for figuring out the union damage region, and invalidating only that.
For animation, things are similar, but involve a slightly bigger change. Instead of just calling `request_anim_frame`, a widget that wanted to animate would call something like,
```rust
impl EventCtx {
fn request_animation(&mut self, duration: Duration) -> AnimToken; {
// ...
}
}
```
Druid would then keep track of animation requests (along with the requesting widget and the start time), and when a 'tick' happened, druid would check to see if there were any outstanding animations; if there were, those widgets would recieve `AnimFrame` and could update any necessary state, and the framework would handle invalidating and ensuring that `paint` was called, as above.
## questions
- can we be more efficient with timers, starting and stopping as needed?
- we may need different timers for different displays, which may have different refresh rates or different refresh timings
- how does this work if we're decoupling druid-shell from the paint context?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the druid-shell paint and animation paths described in the issue, tracing request_paint, request_anim_frame, AnimFrame, and per-window update handling. Investigate how platform timers, invalidation regions, multiple windows, and display refresh rates are currently represented. Done would require a settled design for tick-based painting and animation, including answers to the listed questions; the issue does not name specific files or tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- desktop, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100