Win7: Frame pacing / Vsync issues

Monogame Version: 3.7.0.1250 (09th February 2018)
OS: Windows 7 x64
Rendering: DX11


Today I noticed that for some reason the sprite movement on screen seems to be stuttering for a few seconds every now and then. It feels like the framerate suddenly drops to about half the normal rate for no obvious reason.

Observations:

A rock solid 60fps all the time, no drops or stuttering. Measured with Fraps and Intel GPA.
–> Not a framerate but a frame pacing problem?

Garbage collection blocking the cpu:
I added a GC.Collect() call to the Update() function.
–> No change at all.

Tested on another system (Windows 10 x64):
Issue is gone!
–> Maybe a Windows 7 related problem?

Frame stuttering gets triggered if I move the mouse cursor around at the minimize, maximize, close buttons.
Or if the game runs at fullscreened window mode. At true fullscreen mode it’s gone.

Measure frame times:

Update Function takes about 0.9-1.2ms per frame at 1% cpu load.
Drawing takes about 0.2ms per frame at 6% gpu load.
–> So I’m quite sure its not a performance related issue.

So I measured the time between the end of Draw() and the next Update() it’s about 15-16ms.
Obviously to wait for the next 60fps refresch tick.
But as soon as the stuttering kicks in the wait time strarts to alternate betwenn 30 and 0ms…
A few seconds later everything is back to normal 15-16ms times.

Conclusions:

–> Two frames get rendered right after another, becaus monogame is running slow?
–> Windows window refresch and game drawing runs out of sync?
–> Monogame is waiting for windows to update the window content?
–> Maybe vsync?

So I tried to disable Vsync and setup a target frame time myself:

graphics.SynchronizeWithVerticalRetrace = false;
TargetElapsedTime = TimeSpan.FromMilliseconds(1000d / 60d);
IsFixedTimeStep = true;

The stuttering issues persists! But the wait time alternating between 30ms and 0ms is gone. It now stays at normal 15-16ms.

–> Monogame pushes the rendered screen to Windows but does not wait for it to complete?
Frame gets stuck at Windows updating the window?
–> If I setup a target framerate of 100fps, the stuttering gets less noticable.
Most likely becaus the actual framerate gets halfed if the stuttering kicks in? 100/50fps.

I’m out of ideas. Is it a windows issues, or is it on my side? I don’t know. Last resort might be to include the monogame source into my project and step into everything that happens between Draw() and Update() to track down the culprit.

Or hopefully someone else knows what is going on. So far thank you for reading.

Yeah, this a known issue. It was an issue in XNA as well and occurs when using the fixed time step mode. I’ve been trying to solve it myself. I have a solution, but it ends up impacting keyboard input (but not controller input) for some reason. As best as I can tell, XNA randomly calls Update() twice before calling Draw() which results in the stutter that you are seeing.

Try the follwing:

In your Game1 class add:

protected override bool BeginDraw() {
return false;
}

This will inhibit the default draw calls. With isFixedTimeStep = true the logic calls Draw() “whenever they feel like it,” to quote one of the developers of XNA. So, you are not guaranteed a Draw() call for each Update() call.

Then, add this to the end of your Update method:

if (base.BeginDraw()) {
Draw(gameTime);
base.EndDraw();
}

This will give you a Draw() call after each Update(). It will be up to you to make sure that your code runs inside of the TargetElapsedTime for each frame.

So… I guarantee doing the above will resolve your issue. However, after your game is running for a bit (10-30 seconds) you’ll see that keyboard input becomes unresponsive. You will have to hold down keys to get the input to register. Controller input, on the other hand, is completely unaffected. I have yet to figure out why. My guess is it has something to do with the IsRunningSlowly flag. Implementing the above will basically have the IsRunningSlowly flag always set to ‘true’ even if your Update() and Draw() methods are executed well within the TargetElapsedTime.

Anyway, hopefully this is helpful and maybe between the two of us we can figure out why this impacts the keyboard input.

Lastly, your other option is to not use a fixed time step. This issue completely goes away, but there are other challenges associated with programming against a variable time step, which is why I’ve stuck with the fixed time step.

Thank you for your response. Very interesting. I will try that out.

–> Tried it.

Please tell me if I followed your instructions correctly:

graphics.SynchronizeWithVerticalRetrace = false;
this.IsFixedTimeStep = false;

Add:

protected override bool BeginDraw()
{
    return false;
}

Apend to Update():

if (base.BeginDraw())
{
    Draw(gameTime);
    base.EndDraw();
}

*Draw() requires gametime not spritebatch?

Then I calculated a waiting time and made a Thread.Sleep(waittime) to keep up with my target frame time.

–> Works, Draw gets only called when I call it manually. But the “stuttering” issue persists.
Feels identical to when I disable vsync and set a target frame time + IsFixedTimeStep.

IsFixedTimeStep should be true. I’d also set SynchronizeWithVerticalRetrace to true. And yes, The Draw() call that’s appended to Update() requires gameTime and not spriteBatch. My mistake. Make those changes and try again!

Also, I’d remove the the Thread.Sleep call. Fixed time step mode will handle all of your target frame time logic for you…

Ok, applied the changes. Has definitely some positive impact on my issue. The stuttering happens a lot less frequent now.
But it’s not entirely gone. And you were right, there is something wrong with the input recognition. Maybe mouse input is also affected? Sometimes a click gets ignored or apllied with a lag.

Yeah, I think somehow this implementation messes up the keyboard (and sounds like mouse) input handler. Again, controller input is completely unaffected.

Definitely don’t use the Thread.Sleep call as it is unreliable from my understanding (it won’t always sleep for the exact time that you want it to).

If you still have stuttering then there is something else going on and I’m unsure of what to tell you…

No problem, thank you for your advice. I already removed Thread.Sleep, since I am back to fixed time step.
I am currentlich looking into the monogame source and try to figure out, what else might be blocking the window refresh. But that might be a time consuming process…

@AquilaAbriel

Try those two patches:


Thank you for your reply.

First thing I tried after reading those threads was to entirely remove Thread.Sleep() from Game.Tick() Function.
To eliminate any form of inaccuracy while sleeping. Of course this results in a 100% cpu load on one core. But sadly the issue persists.

I figured out that once the stuttering starts EndDraw() --> Platform.Present() suddenly takes up to 30ms to execute.
Before that it normaly takes 0ms to run.

recordet times:
0
17
29
0
29
1
29
0
29
29
0
and so on

Next I will try to look into Platform.Present()

Finally leads me to SwapChain.Present()
which means I need to continue diggin in SharpDX or give in…