[SOLVED] Simple sprite moving causes stuttering

Hello guys,

this problem gave me a massive headache and I’m just completely lost.
After migrating from XNA to MonoGame everything works fine except for random image stuttering. I thought it might be the case of too many sprites to draw, so I made a simple test. I created a new project, loaded some 48x48 png image, made an update method (arrow keys increase postition by 1) and draw method (which just displays that image at position updated in update method). NOTE: I’m drawing image to RenderTarget, then to the screen (I have to use RenderTarget in my project). Unfortunately the effect was the same. I read a massive amount of tutorials and yet nothing seemed to work (for example this: https://github.com/MonoGame/MonoGame/issues/4202). This is what I tried to do to fix this:

MonoGame DIRECTX:

  1. IsFixedTimeStep = true, graphics.SynchronizeWithVerticalRetrace = true
    The stuttering occurs about 1 time per 3 seconds.
  2. IsFixedTimeStep = true, graphics.SynchronizeWithVerticalRetrace = false
    Effect same as in 1.
  3. IsFixedTimeStep = false, graphics.SynchronizeWithVerticalRetrace = true
    (of course i considered the position update by gameTime.ElapsedGameTime factor)
    Here stuttering is really hard, about 2 times per second.
  4. IsFixedTimeStep = false, graphics.SynchronizeWithVerticalRetrace = false
    (of course i considered the position update by gameTime.ElapsedGameTime factor)
    Here stuttering occurs rarely (sometimes i get 30 seconds without any stuttering, but sometimes i have 1 time per 3 seconds). It’s really weird, doesn’t seem to be stuttering consistently.

MonoGame DESKTOP GL:

  1. IsFixedTimeStep = true, graphics.SynchronizeWithVerticalRetrace = true
    A little more often than DirectX, probably about 1 time per 2 seconds.
  2. IsFixedTimeStep = true, graphics.SynchronizeWithVerticalRetrace = false
    Effect same as in 1.
  3. IsFixedTimeStep = false, graphics.SynchronizeWithVerticalRetrace = true
    (of course i considered the position update by gameTime.ElapsedGameTime factor)
    Here stuttering is the highest, probably 5-6 times per second.
  4. IsFixedTimeStep = false, graphics.SynchronizeWithVerticalRetrace = false
    (of course i considered the position update by gameTime.ElapsedGameTime factor)
    Here stuttering occurs rarely but definitely more often than DirectX. Probably 1 time per 5 seconds and it’s pretty consistent in comparison to DirectX.

Just to be 100% sure I made the same test in XNA:

  1. IsFixedTimeStep = true, graphics.SynchronizeWithVerticalRetrace = true
    No stuttering here. It’s smooth and perfect.
  2. IsFixedTimeStep = true, graphics.SynchronizeWithVerticalRetrace = false
    Here stuttering occurs 1 time per 2-3 seconds. Pretty much same result as MonoGame.
  3. IsFixedTimeStep = false, graphics.SynchronizeWithVerticalRetrace = true
    (of course i considered the position update by gameTime.ElapsedGameTime factor)
    Very similar stuttering to MonoGame, occurs pretty often.
  4. IsFixedTimeStep = false, graphics.SynchronizeWithVerticalRetrace = false
    (of course i considered the position update by gameTime.ElapsedGameTime factor)
    To my surprise it stutters here (!!!) but very rarely and inconsistently, same result as MonoGame DirectX.

Out of those 12 options only one is acceptable but I’d rather use MonoGame over XNA. I’m using the latest version of MonoGame (3.7.1) and XNA (4.0). I also tried to measure the system timer resolution (in every case i had constant 1ms).

Any help would be greatly appreciated because I can’t think of anything else.

Here are some recordings (all videos have their settings specified in the title)

  1. https://www.youtube.com/watch?v=OAoJZktZKlY
  2. https://www.youtube.com/watch?v=H4q8pIbYlzU
  3. https://www.youtube.com/watch?v=PtwOvaiu44I

I know that recording uses some CPU power but XNA isn’t stuttering at all.

Ya its been this way for a while. The thread.sleep(time) is too long probably no bias subtracted from it.

At one point it was working then it got messed up again. The timing is off its about 1.003 seconds of elapsed time continuously over a second. It should read .99----- ect and 60 frames.
We want a extra frame if anything not a missed frame.

When i took the screen shot here it’s just a oddity it almost always reads 1.003 seconds.

Which comes out to .00005 seconds of error per frame at least probably more, minuscule but it builds up to skip a frame.

1d / 180d TargetElapsedTime i get the below on windows desktop gl.

With vsync on it’s still messed up.

I only get a 1.0 flat Elapsed interval with fixed off vsync off at thousands of fps.
I can get a stable 60 fps though with fixed off, vsync on at a near 1.0 interval.

Nothing fancy going on in my timings either im just timing the number of draws and updates after a full second. It’s pretty borked.

        public void Update(GameTime gameTime)
        {
            now = gameTime.TotalGameTime.TotalSeconds;
            elapsed = (double)(now - last);

            // fps msg's
            if (elapsed >= msgFrequency) // frequency is 1.0 seconds here.
            {
                // time
                if (DisplayFrameRate)
                {
                    fps = (frames / elapsed);
                    ufRatio = (float)frames / (float)updates;

                    fpsmsg.Clear();
                    fpsmsg.Append(" Minutes Running: ").AppendTrim(now / 60d, 3).AppendLine(); // .Append(now).AppendLine();
                    fpsmsg.Append(" Fps: ").AppendTrim(fps).AppendLine();
                    fpsmsg.Append(" Draw to Update Ratio: ").AppendTrim(ufRatio).AppendLine();
                    fpsmsg.Append(" Elapsed interval: ").AppendTrim(elapsed).AppendLine();
                    fpsmsg.Append(" Updates: ").Append(updates).AppendLine();
                    fpsmsg.Append(" Frames: ").Append(frames).AppendLine();

                    //elapsed -= msgFrequency;
                    frames = 0;
                    updates = 0;
                    last = now;
                }

You could build monogame from source and add it or rather subtract it from the sleep time.

The best approach i think would be to track the app and see if a single frame is being skipped and decrease the sleep time by a bias up to some maximum.
Different systems might have different sleep time quirks.

Big thanks to nkast for helping me out and willmotil for an explanation behind the FixedTimeStep Thread.Sleep inaccuracy.

I looked up in my code and found the reason behind FixedTimeStep=false/VSync=false stuttering. I was rounding down the position of sprite (basically int cast). I turned it off and everything went smooth. But then it created a next problem cause I had to use RenderTarget. Drawing sprite here on non-integer position gives a kind of blurrish effect. So I had to round the position but a little bit smarter. I made it so the game checks every time if 1/60 s time interval has passed. After that time it updates the position by integer value and waits next 1/60 s to do this again. But of course FixedTimeStep=false mode uses 100% CPU power and I didn’t want to do so. I came up with idea of verifying the difference between GameTime and next 1/60 s threshold. If the difference was greater than 1ms then I would just do Thread.Sleep(1). So basically I’m simulating FixedTimeStep=true with spin lock Thread.Sleep(1). I tried to do Thread.Sleep with maximum time (1/60 s - previous update time) but it created too much stuttering so I decided to leave 1ms spin lock. This decreased my CPU usage from 100% to about 5-6%. Stuttering now occurs very very rarely but I found a solution to that too. I turned the V-Sync on and now it seems to have no stuttering at all (or maybe I’m lucky enough so I can’t see it).

Thanks once more guys :slight_smile:

2 Likes

I know this was solved a while ago, but i’ve fallen in a triky trap with setting the target elapsed time and wish to share it. I was setting the time as TimeSpan.FromSeconds(1/60.0), but this gets rounded to the closest millisecond.
If you use
game.TargetElapsedTime = TimeSpan.FromTicks((long)(TimeSpan.TicksPerMillisecond* (1000 / (double)GraphicSettings.frame_rate))); the target elapsedtime should be correctly set to a tick precision.

2 Likes

Thanks for this. I was doing the FromSeconds method too and wasn’t aware there would be accuracy issues.

1 Like