KelvinShadewing / brux-gdk

Free runtime and development kit using SDL and Squirrel
GNU Affero General Public License v3.0
39 stars 20 forks source link

SDL_Delay results in slighty-inaccurate framerates #33

Closed ghost closed 1 year ago

ghost commented 2 years ago

Figure since you're already going through a lot of effort with this frame timing stuff, that it'd be worthwhile to try to help fix this problem once-and-for-all. Before I go into the issue, just keep in mind that this is nowhere near as drastic as the prior issue, so you could get away with not doing this. Fixing this would only correct it by 1-2 FPS, but for the sake of having accurate-as-possible frame timing, I think it's worthwhile to try.

If you look at the value of gvFPS now, such as through STA or an FPS monitor, then you'll now notice that what's supposed to be a maximum of 60FPS is now resulting in 62FPS. The catch this time however is that the issue isn't with any of the logic you're using, but rather it lies with the SDL_Delay function itself.

As you're probably aware from the data types, SDL_Delay accepts a duration in milliseconds as a Uint32 integer. For enforcing a frame rate, the fact that it requires an integer causes problems. Case-and-point is the framerate that causes the issue, 60FPS. 1000/60 = 16.67ms, which rounds down to 16ms in Uint32. Rearrange the math to calculate the framerate with said delay, 1000/16 = 62.5FPS, and thus that explains the difference of 2 FPS. Trying to offset the FPS from the maximum won't work, as you can run the math yourself and see that you'll still wind up at incorrect delays due to the lack of precision. Even if you changed every possible variable to a float, the fact that SDL_Delay doesn't allow for any precision beyond integer milliseconds inevitably causes problems with this kind of use-case for it.

There are probably a few ways to go about addressing this, but the most ideal solution I think would be to use C++ libraries to do the same task you're trying to do with SDL_Delay, just with better precision. More specifically, the chrono and thread libraries have the means to this end. The chrono library has the steady clock, which does the same thing as SDL_GetTicks(). The thread library has this_thread::sleep_for, which does the same thing as SDL_Delay with a chrono-provided duration of whichever precision.

It'd require some fiddling around due to some things like durations being their own data type, and this_thread::sleep_for having a slight delay within the delay due to OS scheduling, but using these methods would allow you to create frametiming that's much closer to being the value that's intended.

ghost commented 1 year ago

Fixed by https://github.com/KelvinShadewing/brux-gdk/pull/44