New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slight stuttering issue #6542

Open
srodrigo opened this Issue Nov 16, 2018 · 23 comments

Comments

Projects
None yet
6 participants
@srodrigo
Copy link

srodrigo commented Nov 16, 2018

I get a slight stuttering problem on a very basic example (just a 16x32 texture moving through the screen).

I'm printing out the elapsed seconds on the Draw method. The rate seems to be unstable (with 0.0333334 peaks, sometimes more, instead of the -I assume- expected 0.0166667). Please find
the attached file: stutter.txt.

The elapsed seconds on the Update method seem to be stable (0.0166667), as expected.

I've noticed the issue gets worse if I have an external screen plugged in (so the graphics card is doing more work), but, as discussed with @MichaelDePiazzi, this should still work fine.

This is the source code:

using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Stuttering.MacOS
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D texture;
        Point enemy = new Point(0, 0);
        
        const int WIDTH = 2 * 640;
        const int HEIGHT = 2 * 360;
        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this)
            {
                PreferredBackBufferWidth = WIDTH,
                PreferredBackBufferHeight = HEIGHT
            };
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }
        
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            
            texture = Content.Load<Texture2D>("enemy-1");
        }
        
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            enemy += new Point((int)(200.0f * gameTime.ElapsedGameTime.TotalSeconds), 0);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            Debug.WriteLine("Draw - gametime total seconds: " + gameTime.ElapsedGameTime.TotalSeconds);
            graphics.GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();
            spriteBatch.Draw(texture, enemy.ToVector2(), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

I wanted to narrow it down to the minimum, but in case this helps, I include another file
Game1.txt with the texture changing direction when reaching the screen bounds (this makes it easier to see, as sometimes it takes a few seconds). Also, the texture is scaled to make it easier to see, but this shouldn't affect the performance much?

What version of MonoGame does the bug occur on:

  • 3.8.0.132

What operating system are you using:

  • Mac

What MonoGame platform are you using:

  • DesktopGL
@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 16, 2018

First I'll just give some clarification about the values you get from ElapsedGameTime -- When using fixed time step, the ElapsedGameTime in the Update method is always the target elapsed time (e.g. default is 16.7ms for 60fps). The ElapsedGameTime in Draw is usually going to be the same, but will be a multiple of it if frames were missed (e.g. if 1 frame was missed, then it will be 33.3ms). If you want to get the true elapsed time when using fixed time step (e.g. if you are benchmarking the timing accuracy), you have to measure it yourself.

But you're seeing these higher values in Draw because something is causing a delay resulting in the updates falling behind. MonoGame is then running multiple updates in a row to catch up from it, followed by a Draw with the increased ElapsedTime to account for the missed frames.

Hope this all makes sense (it's getting a bit late for me here). But anyway, I'll leave you with a few things to try:

  1. Build a release build, and then run the .exe directly (i.e. don't run it via Visual Studio). See if you can notice any stuttering running it this way.

  2. If that doesn't resolve it, try simplifying your update method as follows (just in case it is something screwy with the time values):

enemy.X += 3;
  1. If you're still getting stuttering, maybe try changing the power plan on your laptop. I'm not familiar with MacOS, but on Windows you can usually choose between "Power Saver", "Balanced", and "High Performance". I remember years ago on my old laptop that I would sometimes get stuttering when using "Balanced" mode. But switching to "High Performance" would get rid of it.

So give this a go and let me know your results.

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 16, 2018

@MichaelDePiazzi That makes total sense, and I suspected that "catching up" with missing frames was what those 0.0333, 0.05, etc. values meant (multiples of the seconds in one frame).

I built a release build on Mac, and run it outside VS. Neither 1) nor 2) solved the issue. I made sure I cleaned the build before each attempt.

I had a look at 3) yesterday. I've had a second look and there aren't many options here, just to put the hard disc into sleep mode, which I disabled. The problem still exists when running the game with the power cable connected, so I imagine it's not economising anything in this case.

I've also tried closing all the apps (Chrome, terminals, everything except for Finder) before running the game. I still get the issue, so I would assume it's not related to a moderate or high load on my laptop (the CPU usage was 3-6% before I closed the monitor and ran the game).

@RUSshy

This comment has been minimized.

Copy link

RUSshy commented Nov 16, 2018

Try enable vsync ? graphics.SynchronizeWithVerticalRetrace = true;

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 16, 2018

@RUSshy I just tried, and didn't make any difference.

@HopefulToad

This comment has been minimized.

Copy link
Contributor

HopefulToad commented Nov 16, 2018

Have you tried updating your graphics drivers?

Try explicitly disabling VSync, as well, by putting this line in the constructor:

graphics.SynchronizeWithVerticalRetrace = false;

Also, that Debug line in the Draw method is probably creating garbage since it's creating a new string each frame. That might have something to do with it.

@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 17, 2018

@srodrigo Bummer none of my suggestions helped. But please definitely try @HopefulToad's suggestions also. I meant to actually suggest disabling v-sync last night, but it slipped my mind. (V-sync is enabled by default, so disabling it should help prove whether or not it is playing a part in this issue.)

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 17, 2018

@HopefulToad OSX is supposed to install the latest drivers, my system is up to date. I did some research to see whether it's possible to install them manually, but apparently everything directs to the app store.

@HopefulToad @MichaelDePiazzi I tried graphics.SynchronizeWithVerticalRetrace = false; and removing the Debug lines. Again, I closed all the apps, unplugged my external screen, and the problem still happens.

Not sure what else to try. In any case, I'll get a Windows machine in a few weeks, so this is not critical. I wish I had a more recent Mac to see if the problem is reproducible there. I suppose it will be fine on Windows, so never mind :)

I wonder if we could ask any other Mac users with newer machines.

@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 17, 2018

@srodrigo This is bizarre... How are you referencing MonoGame in your project? Are you referencing the latest compiled binaries from the develop branch, or are you directly referencing the latest source code? And are you absolutely certain that you are definitely referencing the latest develop branch build from your project?

And I would just love to ignore this given you are getting a new Windows machine soon. But the problem is there are plenty of people out there that play games built with MonoGame that will have similar hardware to you.

@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 17, 2018

@srodrigo This is a long shot, but try adding this to your constructor:

InactiveSleepTime = TimeSpan.Zero;

Also out of curiosity, does the latest develop build perform any differently for you than the latest stable v3.7 release?

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 17, 2018

@MichaelDePiazzi I've tried with both

            graphics.SynchronizeWithVerticalRetrace = false;
            InactiveSleepTime = TimeSpan.Zero;

and the issue persists.

I just downloaded the 3.8 build from Teamcity and installed it as usual. Then I created a new project (although my old projects got the new version referenced, but just to be 100% sure). If I open References / MonoGame.Framework, the assembly version shown is [assembly: AssemblyVersion ("3.8.0.132")]

I just noticed something interesting though.. On this image
captura de pantalla 2018-11-17 a las 18 44 53, there is an error that says Assembly not found for framework .NET Framework 4.5. Could this be related? I didn't notice this problem when I checked the assembly version yesterday.

EDIT: I forgot to mention that I didn't compare the performance on 3.7, as by the time I realised that unplugging the external screen made the stuttering less obvious, I already had the development version installed.

@HopefulToad

This comment has been minimized.

Copy link
Contributor

HopefulToad commented Nov 17, 2018

A good test to help narrow down the problem would be to install Windows on your Mac and check if the same problem persists there, in both a DirectX and OpenGL project. That way, we could determine whether or not the problem is Mac-related, OpenGL-related, or hardware-related. Unfortunately, Windows costs money, so that's not an option.

I don't really know how development works on Mac, but have you tried updating to the latest version of Mono, if that's relevant?

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 17, 2018

@HopefulToad I've heard that the OpenGL drivers are not great on Mac, so who knows.

I'm quite new to C#, so I'm not 100% sure, but apparently I've got a newer version than the latest on the "Visual Studio Channel", which is the recommended one on Mac:
captura de pantalla 2018-11-17 a las 19 36 25

Mine is 5.12.0:

Sergios-MacBook-Pro:~ sergiorodrigo$ mono --version
Mono JIT compiler version 5.12.0.309 (2018-02/39d89a335c8 Thu Sep 27 06:54:53 EDT 2018)

I got it installed when I installed Unity. I wonder if downgrading to the recommended version would help.

@HopefulToad

This comment has been minimized.

Copy link
Contributor

HopefulToad commented Nov 17, 2018

I got it installed when I installed Unity. I wonder if downgrading to the recommended version would help.

Probably not, I would imagine, though I don't know for sure. It probably wouldn't hurt to give it a try, just in case.

I'm unfamiliar with Mac and Mono, and I don't have access to a Mac computer, so I won't be able to help much with figuring out the cause of the problem. I'm not sure who among the maintainers of MonoGame uses a Mac, but whoever does should probably be able to help more.

@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 18, 2018

@srodrigo Damn... I'm at a bit of a loss on what else to try at the moment. But I'll try rack my brain some more and let you know if I come up with anything else.

Unfortunately, I'm in the same position as @HopefulToad - i.e. I'm unfamiliar with Mac and Mono, and I don't have access to a Mac computer. So this makes it a bit more tricky for me to help you on this.

But I guess as long as the latest dev build isn't any worse than the current stable v3.7 release, then at least my changes haven't caused a regression for you. So if you could possibly test this again with v3.7 and confirm this, then that would be very helpful.

P.S. I'm not sure what that error about the missing assembly is about. I doubt that it's related, but then again, I can't say for sure either.

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 18, 2018

@HopefulToad @MichaelDePiazzi I've just tested with 3.7 and I can't see any difference, so 3.8 didn't make it worse, at least with the last parameters I tried:

    graphics.SynchronizeWithVerticalRetrace = false;
    InactiveSleepTime = TimeSpan.Zero;

Thanks both for your input. This doesn't cause me big troubles for now, and I don't plan to develop for/on Mac anymore, but if someone does, it would be interesting to see if they get the same issue and on which hardware.

@HopefulToad

This comment has been minimized.

Copy link
Contributor

HopefulToad commented Nov 18, 2018

Out of curiosity, do you notice stuttering with IsFixedTimeStep set to false, with or without v-sync enabled?

@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 19, 2018

@srodrigo Thanks for confirming this!

One other thing I found which might be helpful -
Apparently MacBook Pro's have 2 graphics adapters - one for normal use, and one for performance. By default, it automatically chooses which one to use and when to switch to it.

So it may be that it's only choosing the normal graphics adapter in this case. So you should try disabling the automatic switching to force the high-performance graphics to be used.

I found some instructions which tell you how to do this on both the older and newer MacBook Pro's:
https://dj.rane.com/support/knowledge-base/how-to-optimizae-your-mac-for-live-performance
(just scroll down a bit, there is a section further down - "disable automatic graphics switching")

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 20, 2018

@MichaelDePiazzi I already had a look at the graphics adapters. My macbook is a 13'' one, so it only has the integrated graphics card, so those options are not available.

@HopefulToad Bingo! The stuttering goes away with IsFixedTimeStep = false;, with both v-sync on and off. I have no idea what this means though. It only works if I unplug the external screen, otherwise it displays badly, but with the screen laptop only, it doesn't happen anymore.

What is isFixedTimeStep exactly for? From the name, I guess it forces 0.016 ms per frame, or something like that? (I haven't look at the source code).

Is it usually a good idea to disable it though?

@HopefulToad

This comment has been minimized.

Copy link
Contributor

HopefulToad commented Nov 20, 2018

@srodrigo Setting fixed time step to true means that the code will make an effort to update exactly sixty times a second, which is the default, or however many times based on the TargetElapsedTime you've set. If the game is unable to fit both an update and a draw into 1/60th of a second, it will skip draw calls until it's caught up. In other words, the Update-Draw loop might look like this:

Update
Draw
Update
Draw // This draw call pushes the loop past 1/60th of a second; the game is now behind schedule
Update
Update // A draw call is skipped in an effort to catch up again
Draw
Update
Draw

The skipped draw calls are likely the source of the stuttering you're observing.

Fixed time step allows you to write code without worrying about how much time has passed since the last update. So, code like enemy.Position.X += 5 works fine. However, writing code like that means the logic is tied to the time step, which would make it hard to switch to variable time step at a later time and means your game is limited to a specific frame rate.

Variable time step always calls update, then draw, and back again, regardless of how long it takes. You then need to account for how much time has passed since the last update using GameTime. Using variable time step makes your game logic no longer tied to time step or frame rate, and means draw calls won't be skipped. It also means the frame rate can fluctuate.

If you want to use variable time step, you have to be willing to deal with the increased complication of accounting for GameTime, as well as accepting the possiblity that your game may misbehave in extreme circumstances, such as if one update-draw loop takes half a second or a full second on a low-end computer. Using fixed time step simplifies development and ensures consistent behavior, but also locks the framerate and can introduce stuttering if draw calls are skipped.

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 22, 2018

Thanks @HopefulToad that's really useful. I'll think about what I want to use, but probably fixed time for now (although I usually move all the objects relative to the time elapsed anyway, to make it easier to change later in case of need).

@MichaelDePiazzi

This comment has been minimized.

Copy link
Contributor

MichaelDePiazzi commented Nov 24, 2018

Sorry for the late response on this, been crazy busy these past few days!

@srodrigo So in your case, I'd guess that variable time step is likely just masking whatever the underlying issue is. If you measured the actual times between frames, I'm guessing you'd see that there'd be some fluctuations and spikes in there. Fixed time step is just showing this up more because something is causing an occasional choke which results in the inability to deliver a stable frame rate.

I really wish we could find what the issue is, but unfortunately I'm out of ideas on this one... :/

@srodrigo

This comment has been minimized.

Copy link

srodrigo commented Nov 27, 2018

No worries @MichaelDePiazzi I'll let you know if the issue gets solved by later releases.

Thanks both!

@willmotil

This comment has been minimized.

Copy link
Contributor

willmotil commented Dec 2, 2018

Since im on here today and i just read this i was just thinking...

If the problem goes away with fixed timestep off essentially it means his framerate went up so he cant see it.

That also would indicate that there is some problem with him getting the system clock time or calling sleep for the right period.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment