Monogame: Gameloop with fixed frame rate 60 FPS produce unstable timing

Created on 6 Nov 2018  Â·  13Comments  Â·  Source: MonoGame/MonoGame


Gameloop with fixed frame rate 60 FPS produce unstable timing.
The problem exist very clearly and should also affect low GPU load.
Screen's with lower refresh rate might not encounter this problem.

I have identified two problems with the gameloop timing.

  1. The gameloop drift and is just below 60 fps. This will make at some point one frame loss (same frame drawn twice)
  2. When idle and running Thread.Sleep() timing is cast to (int) making the wait little to short.
    Because it then do RetryTick: it could make another Thread.Sleep() that will produce a significant long frame because Thread.Sleep() is not exactly and often can't produce very short delays.

Comment:
I suggest that the multiple Update() for long framerates and IsRunningSlowly could be removed or possible made as optional on/off. It is very confusing and hard to understand that there could be multiple Updates() for one Draw(). (I have comment out the code).

Solution:

  1. Make the timing a little faster to produce slightly more than 60 FPS.
    Decrease the sleep wait with one ms. This makes the game at some point produce one extra frame.
  2. After Thread.Sleep() don't goto RetryTick() move forward instead.

Code:
```
// Calculate the accumulated elapsed time.
var currentTicks = _gameTimer.Elapsed.Ticks;
_accumulatedElapsedTime = TimeSpan.FromTicks(currentTicks - _previousTicks);

        // If were in the fixed timestep mode and not enough time has elapsed
        // to perform an update we sleep off the the remaining time to save battery
        // life and/or release CPU time to other threads and processes.
        if (IsFixedTimeStep && _accumulatedElapsedTime < TargetElapsedTime)
        {
            var sleepTime = (int)(TargetElapsedTime - _accumulatedElapsedTime).TotalMilliseconds;
            if (sleepTime > 0) {
                sleepTime--;
            }

            // NOTE: While sleep can be inaccurate in general it is 
            // accurate enough for frame limiting purposes if some
            // fluctuation is an acceptable result.

if WINRT

            Task.Delay(sleepTime).Wait();

else

            System.Threading.Thread.Sleep(sleepTime);

endif

            currentTicks = _gameTimer.Elapsed.Ticks;
            _accumulatedElapsedTime = TimeSpan.FromTicks(currentTicks - _previousTicks);
        }
        _previousTicks = currentTicks;

….
//_accumulatedElapsedTime = TimeSpan.Zero;

```

What version of MonoGame does the bug occur on:

  • MonoGame 3.6 (with some of 3.7 updates)

What operating system are you using:

  • Windows 10 , Screen with 60 Hz refresh rate

What MonoGame platform are you using:

  • WindowsDX

Most helpful comment

Nice! All my tests showed this as a stable fix with WindowsDX. Already in my local branch.

All 13 comments

I came across this same problem as well a few years ago. The main issue is that Sleep is not accurate and depends entirely on the current resolution of the system timer.

I actually submitted PR #4207 which solves this issue. Unfortunately, it never got merged and keeps getting pushed back (I'm not really sure why, I wasn't able to get any response). So after 3 years, I've pretty much given up on it. But if someone is interested in looking at it and trying to get it merged, then I'm happy to do whatever I can to try make that happen.

But anyway, take a look at my PR for a solution to your problem. It even still uses Sleep on Windows because it can measure the resolution of the timer and compensate for it. On other platforms where it can't do this, it just spins the CPU to maintain accuracy.

I tried to find related issues but missed this, sorry for that.
I will try it out on my local branch. The CPU spin really make it extremely exact.

Have you made any investigation in bringing up the thread priority to minimize interference from other threads? I hade problem with that to, because we some times are CPU bound.

I tried to find related issues but missed this, sorry for that.

That's ok. There was originally an issue related to the PR, but for some reason the author closed it before the PR was merged. So hopefully your new issue will help bring some attention to this again.

I will try it out on my local branch. The CPU spin really make it extremely exact.

Cool, let me know how it goes for you.

Have you made any investigation in bringing up the thread priority to minimize interference from other threads? I hade problem with that to, because we some times are CPU bound.

No, I haven't really had any problems with this. The main external issues I had which caused frame stuttering was this frame timing issue and the garbage collector.

@MichaelDePiazzi I was one of the people involved in the original issue you mentioned. I've been waiting for the PR you made to be merged, and it never was...
I still sometimes notice an occasional "jump" when using fixed time step. I recently made a game for a contest where I experimented with using variable time step, and "jumps" never seem to occur when using variable time step and with v-sync off.

@MichaelDePiazzi considering the extensive experiments and testing reported in your PR, I'm willing to merge the changes if you manage to recreate the changes or bring them up to date :)

@Jjagg Awesome, thanks! Hopefully I might even get a chance to look at this sometime today. But if not, I will aim to get on to this by the end of the week.

..."jumps" never seem to occur when using variable time step and with v-sync off.

@HopefulToad Yes, that makes sense. This issue only affects fixed time step. When using variable time step (with v-sync off), you are expected to deal with the frame timing yourself.

Yeah. I've normally shied away from variable time step because it's easier to think of a game object moving five pixels a frame rather than worrying about how much time has passed since the last update, but it really wasn't too bad to work with GameTime, at least with the simple game I was working on, and it allowed arbitrarily slowing down time in-game, as well.

@MichaelDePiazzi I just tried out my contest game using fixed time step, and didn't notice any stuttering or jitter without the debugger attached. When I ran it _with_ the debugger attached, I did notice some motion jitter with fixed time step, but much more jitter with variable time step. I'm starting to think that any "jumps" I've noticed have been the result of running in debug mode, and not of any major flaw in the current implementation of fixed time step. At least on a DirectX project.

@HopefulToad

When I ran it _with_ the debugger attached, I did notice some motion jitter with fixed time step, but much more jitter with variable time step.

Never benchmark with a debugger attached. You can't get reliable results. Only ever benchmark with the release build.

I'm starting to think that any "jumps" I've noticed have been the result of running in debug mode, and not of any major flaw in the current implementation of fixed time step.

Running in debug mode can definitely cause performance problems. But the timing issues with fixed time step depend a lot on the current resolution of the system timer (which can be influenced by many things). If the resolution is high (e.g. 0.5-1ms), then the problems won't be so apparent. If the resolution is low (e.g. 15.6ms), then it will be very noticeable. Also, when v-sync is enabled the graphics driver then also plays a part in the timing. So to properly test just MonoGame's timing, v-sync should be disabled.

I remember going through all of this with you when this issue first came up (see issue #4202). We did a lot of tests to eliminate as many variables as we could (including the debugger, and v-sync). And we discovered in your case, at that time, that in some cases your system timer resolution was low. And when you tried my fix which eliminated the inaccuracy with Sleep, it resolved it. So I'm confident that this fix is still important and the right way forward.

But I'm guessing now at this time that your system timer resolution is high, so any jitter in the frame timing is much smaller and not nearly as noticeable.

Anyway, I'm going to spend some time on getting this PR sorted out now. So hopefully we can finally put this issue to bed! :)

@MichaelDePiazzi

Never benchmark with a debugger attached. You can't get reliable results. Only ever benchmark with the release build.

Well, I _was_ testing it in release mode... just with the debugger attached, as well, which apparently makes a major difference. I now know that if I want to see how the game really runs, I should specifically run it without the debugger attached (Ctrl + F5 in Visual Studio) and in Release mode.

I remember going through all of this with you when this issue first came up (see issue #4202).

Yeah, I was using a different laptop back then. Also, I probably wasn't remembering to run it without the debugger attached, and my situation was much improved by simply updating my video drivers. That said, I think there is definitely benefit to making sure the fixed time step is as accurate as possible.

Nice! All my tests showed this as a stable fix with WindowsDX. Already in my local branch.

@svantechgit Awesome! That's great to hear :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rds1983 picture rds1983  Â·  5Comments

Grabiobot picture Grabiobot  Â·  5Comments

MontyHimself picture MontyHimself  Â·  5Comments

MichaelDePiazzi picture MichaelDePiazzi  Â·  4Comments

Ellesent picture Ellesent  Â·  5Comments