-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Input lag with vsync enabled due to limited polling rates #3317
Comments
IIRC, we are limited by the I had some ideas that I could prototype, that effectively act as a frame-limiter, which is related to #1343. How bevy currently behaves with vsync on:
How bevy currently behaves with vsync off:
What I'd like bevy to do with vsync on:
We could prototype this by adding a system at the end of the event loop that sleeps for a while after the frame has been sent to the GPU. I'm not intimately familiar with how all that works, but I can try something out. |
I believe mailbox vsync should behave similar to disabled vsync in terms of latency.
This is called frame pacing, right? You will have to be careful to ensure that variation in render time doesn't cause frames to be submitted too late. Predicting the right time to sleep can be difficult. |
That's what I would expect, but not what I experience in bevy apps.
Definitely. My use case for this would be in applications though, I care more about reducing input latency without just letting the app run at 300fps, draining battery. In fact, for the application use case you could safely add a 2x safety factor to predicted frame render time, and still get a huge improvement in latency without risking dropped frames, because applications like this take very little time to render - on the order of 1-3ms. |
Why sleep before the frame, if you can also sleep at the end?
To be honest, this is what I'd expect a frame-limiting strategy to look like. Just wait at the end until the specified time-frame is over, so that there is no timing issue later on in case the renderer is slower than expected :) |
That doesn't get around the timing problem, presenting it this way just makes it seem like the problem doesn't exist. That's why I presented it in reverse - it makes the timing problem more apparent. You need to make sure the time between "send to GPU" is always <16ms to prevent frame drops. In the order you present, you would need to add a sleep between the time the frame is finished and sent to the GPU to act as your factor of safety for any frame time variability. The "gotcha" here is that it seems like you can just sleep until you've hit a total of 16ms, because your total frametime is always 16ms, but it's masking the fact that what you actually care about is the time between "send to GPU" being 16ms. Anywho, the proof is in the pudding. We should make some prototypes to see if we can make something that works. 😄 |
I did some work on this.
It works like this:
Here's a trace to better visualize, notice the large blue bar with the label "framerate limiter" I don't have any empirical measurements, but so far it seems promising. Mailbox vsync, framerate limiter enabled bevy.2021-12-19.17-07-51_Trim_Trim.mp4Mailbox vsync, framerate limiter disabled: bevy.2021-12-19.17-08-14_Trim.mp4With the framerate limiter, the 3d cursor feels perceptively less sluggish, but it's still not as good as with vsync off. I tested without vsync both with and without the framerate limiter, however I was still seeing some tearing with the framerate limited to ~60, though on the plus side I did see significantly less GPU/CPU usage. |
I modified the prototype to add a second sleep system that caps the frame rate to exactly what you specify.
Red annotation: forward estimation. Blue annotation: precise frametime limiter accounting for error and margin in the last frame's forward estimation. I'm seeing some really awesome results, the 3d cursor is basically glued to the mouse cursor: bevy.2021-12-19.23-48-46_Trim.mp4In addition, I can bring my safety margin pretty low without frame drops - on the order of 100μs. I've bumped it up to 500μm to reduce chances of frame drops, but at the cost of only 400μs more input lag. Edit: this wasn't possible without |
Some other neat byproducts of this, it's now really easy to framelimit to an arbitrarily low FPS for power use or other reasons. However, because our input -> render latency is constant (3.5ms in my case), the game/app still feels really responsive at low framerates! Here's the demo locked to only 20fps, yet the 3d cursor still doesn't lag behind the OS cursor very much: bevy.2021-12-20.01-42-14.mp4It doesn't feel laggy or jello-y, because the motion-to-photon time is still low, instead it only feels choppy because it doesn't update very frequently. This brings up an interesting idea for system scheduling too. If a game or application is sensitive to input responsiveness, but needs a large frametime budget, they could schedule everything compute intensive after the render stage, instead of between input and the render stage. This means those changes would take up to a full frametime to display, but you now have the ability to only put critical things (like transforming objects in the world based on user input) in the pre-render schedule. |
I posted this in discussions, but it's also relevant for this issue... Here is my quick-and-dirty fix based on #6503 (pipelined rendering) which enables multiple app/input updates per single rendered frame. Thus processing input immediately, even when using VSync. It probably breaks all sorts of stuff that implicitly assumes one app frame equals to one render frame. Also, probably doesn't work on all archs - I tested only in Linux where it works fine. https://github.com/dafteran4/bevy/tree/multi-app-step-while-rendering |
What's the current progress of improving input lag? I have my own ideas on how to improve input timings, but it seems like the problem is MUCH worse then that. All the Bevy programs I've tested have absolutely terrible input lag, taking many frames to process inputs. Tests: This might not sound bad, but it feels absolutely horrible and I can't even consider using Bevy unless this is fixed |
Have you experimented with We're looking to upstream that, and to expose and use UI time stamps as well. |
How exactly do you use that with Bevy 0.10? The version on crates.io is for bevy 0.9 and I can't figure out how to use cargo workspaces to use the version in the pull request |
Bevy 0.10 was released on the 6th of this month. I can see it just fine on crates.io. |
The question was how do you use this: aevyrie/bevy_framepace#32. You should be able to specify Alice's fork like so: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories |
That worked, thanks! |
Using The latency you experience depends on the The other thing worth mentioning is the new parallel pipelined renderer will add latency if enabled, as the CPU simulation + GPU render end-to-end can take longer than a single frame. |
Thank's a lot for this help ! With frame_pace, I was able to greatly reduce the latency of my piano app, it was unusable before and now it works ! |
While I'm not professional game developer and just playing with bevy, such behaviour looks weird and broken, no matter what reason causes this. There should be a way to get rid of input lag without disabling vsync or adding some third party crate. |
Problem
@aevyrie has observed noticeable input lag in Bevy applications when vsync is enabled.
The most immediate source of this is quite obvious: we're only fetching the input state at the start of each frame, but are rendering is done at the end of the frame.
Input events appear to be moved into the Bevy app by the
winit
runner:bevy/crates/bevy_winit/src/lib.rs
Line 221 in de8edd3
At 60 fps, this means 16 ms of lag, which is noticeable for some applications: namely for precise cursor movement (FPS, GUI applications) and rhythm games.
Possible solutions
Either solution will involve some trickiness, as we must pierce the Bevy schedule in some fashion in order to insert fresh input events into the World at the right time, rather than merely at the beginning of each pass over the schedule.
The text was updated successfully, but these errors were encountered: