bevyengine / bevyengine/bevy

Inaccurate `Time<Real>`, missed `FixedUpdate`

Open
#21,229 10 comments 1 reaction 0 assignees View on GitHub
A-Time A-Windowing C-Bug D-Complex S-Needs-Design
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.