tinne26 / tps-vs-fps

TPS vs FPS explained for Ebitengine users.
28 stars 0 forks source link

Explanation gap for dealing with high refresh rate displays #1

Open tinne26 opened 2 years ago

tinne26 commented 2 years ago

In non-pixel-art games, smooth interpolation of game elements at >60Hz (e.g: 144Hz and 240Hz displays) is not uncommon. The current explanation doesn't really explain how to deal with this, and kinda brushes it away as if saying "just stick to 60 ticks per second and draw accordingly".

Adjusting TPS based on the screen's refresh rate is not a good idea, you are basically just doing Draw logic inside Update but with an extra artificial split, and you can no longer use fixed delta values in Update. Additionally, Ebitengine API doesn't provide any function to get the display's maximum refresh rate.

On practical solutions:

I don't think Ebitengine has an "official" or recommended way to deal with these situations (TPS != FPS without lag or vsync off being the causes) or acknowledges it as a significant concern. Otherwise I believe at least a DisplayRefreshRate function would most likely exist.

There are also optimization concerns. When handling consecutive draws on pixel-art games, in many cases (when not using time-dependent shaders or similar effects) you can use SetScreenClearedEveryFrame(false) and ignore the draws, as nothing has changed. Otherwise high refresh rate displays can start burning quite a lot of processing time for no good reason. We also have no function to limit the FPS and let Ebitengine do this internally for us.

Summarizing, there's definitely a gap in the documentation around this issue, but the situation is unclear for Ebitengine itself too. But now at least anyone interested in this will know about some practical options or can start bothering Hajime Hoshi on my behalf.

tinne26 commented 2 years ago

For FPS < TPS, we have the following cases:

None of these are really a problem.

For FPS > TPS, we have the following cases:

In some of these cases, redrawing each time is wasteful and there's nothing to interpolate to make the game look better. In others, redrawing each time will normally result in the same image unless you are interpolating element positions. Interpolating introduces lag so it's not always so great.

tinne26 commented 1 year ago

Some more thoughts...

The new SkipDraw feature (see https://github.com/hajimehoshi/ebiten/issues/2341) will be very helpful to spare GPU power for those applications that do not really take advantage of high refresh rates and are redrawing the same screen again and again.

Also, I'm starting to believe that real-time position interpolation is ok for high refresh rate displays, but only because the extra frame delay is much smaller than on regular 60Hz displays. On 60Hz displays it really hurts latency. At 120Hz, latency is ok because you are at a very similar place as with 60Hz without interpolation, but then one might also be polling input events more often, which can help make the system more responsive. And at those rates, prediction failures (if using that) will be less noticeable. So... best support requires using different strategies for different refresh rates.

Is that correct?

tinne26 commented 1 year ago

No, not fully correct: on a fixed-timestep loop, input polling will be delayed one tick anyway, no matter the display refresh rate.

tinne26 commented 1 year ago

I have another solution that seems better than anything else I've previously thought of:

This system remains simple to use (tick based), easy to test with only 3 main levels, but supporting higher refresh rates and input responsivity for the computers that can handle that. This seems like a decent compromise. The main downside is that we may break determinism in some cases if we are not careful, so that's one extra thing to keep in mind when relevant.

Another downside is that then we can't use TPS for turbo/slow modes so freely. That was always kind of a hacky way to implement such things, though, so it's not a big deal. You can still set TPS to 480 anyway, but then you will need yet a few more hacks to compensate the main usage of TPS. A more thorough model for implementing turbo modes / slowdowns that doesn't impact input responsivity should be explored. It's fairly clear, though, that it should be applied in its own logical layer.

To be seen: whether to configure the TPS manually or automatically based on empirical FPS measures.