Inaccurate `Time<Real>`, missed `FixedUpdate`
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version and features
* v0.16.1 (main and older versions also look affected; the code is at least several years old)
* vsync disabled
## What you did
While trying to implement a simple frame limiter system using `thread::sleep`, I noticed `Update` was running too often compared to `FixedUpdate`, causing persistent frame stutters.
## Analysis
After adding some logging, I discovered the thread was correctly sleeping for the expected e.g. ~15ms, but `Time` (and thus all other `Time`'s) were only moving forward e.g. ~500µs. I tracked `Time` updates back to [`time_system`](https://github.com/bevyengine/bevy/blob/v0.16.1/crates/bevy_time/src/lib.rs#L150), where I added a line to log the received time:
```txt
sys_sleep -
now: Instant { tv_sec: 5601, tv_nsec: 324402504 }
Time: 7.652275701s (delta: 16.982329ms)
sys_sleep - slept 16.334885ms
time_system - received time: Instant { tv_sec: 5601, tv_nsec: 324451814 }
sys_sleep -
now: Instant { tv_sec: 5601, tv_nsec: 341332892 }
Time: 7.652884464s (delta: 608.763µs)
```
We can see above that actual wall clock time moved forward 16.93ms, but `Time` only moved forward 608.763µs.
This leads to `FixedUpdate` not running, as the requisite amount of time does not *appear* to have passed. On the next frame, everything catches back up, and `FixedUpdate` is run twice. Rinse and repeat, causing stutters.
## What went wrong
`Time` does not track wall clock time. Instead, it consumes timestamps sent from the `RenderApp`, from [`render_system`](https://github.com/bevyengine/bevy/blob/v0.16.1/crates/bevy_render/src/renderer/mod.rs#L111) in the `Render` schedule. This causes the main app to consistently lag by the time it takes to process any load between `RenderApp` `Render` and main app `First`. A sleep in this time frame makes the issue more obvious.
Someone here probably knows, but it's not clear from the code why it was designed this way. It seems suspect to me that `RenderApp` effectively owns `Time`, and not the main app. If time really does need to be synchronized in this way, I would have expected it to be the other way around.
## Additional information
Potentially related/impacted:
* [Res\
Caused by: https://github.com/bevyengine/bevy/pull/4744
Contributor guide
Research direction
Start with time_system in crates/bevy_time/src/lib.rs and render_system in crates/bevy_render/src/renderer/mod.rs, then reproduce the reported behavior with a thread::sleep frame limiter and logging. Trace how timestamps move between RenderApp and the main app, and verify that the resulting timing lets FixedUpdate run at the expected cadence without repeated catch-up stutters.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100