bug in 3_5

picture says it all.

Maybe you should take a look at these R.B. Whitaker tutorials (4, 5, 6 and 7), AFAIK, XNA, then MG, TRY to keep things as desired (one update-draw call sequence, or 60 FPS) but it doesn’t mean it will always succeed (high disk usage at the same time for example).

Ain’t draw and update running on different threads in xna/monogame ?

Just solved It, It turns out in my video cards settings. I had altered vsync to half adaptive for a different test.
Overriding vsync and causing all this strange behavior.

VerticalRetrace was using the card settings in the case of IsFixedTimeStep = false;
And here Present Interval is executing correctly so i guess that is alright.

Setting it true then also fixed PresentInterval so its just showing wonky behavior because i had set my vsync to half adaptive on the card it’s crazy how it affected the updates that way.

In xna it was basically single threaded. With update having priority and able to force a skip of the subsequent draw by setting IsRunningSlowly when draw didn’t complete in time on the previous time slice.

Can you post the code on how to get GC Memory(kb) ? :slightly_smiling:

@mpeg88


http://www.dotnetperls.com/gc-collect

var bytes = GC.GetTotalMemory(false);
var kibibytes = bytes / 1024f;
1 Like

MonoGame (and XNA) are single threaded for the most part. Small parts such as audio may be on another thread.

Whats important is not seeing the actual gc memory amount but instead, that, you are Not seeing the text that will draw to the screen after that. Which only shows up when you lose gc memory. That means some of your used memory is actually being de-allocated and is in fact garbage.

Here is the Test game1 class below, if you would like to test it yourself.
You can copy paste it over a new projects game1 class and put in your own spritefont. If you wish to see the console output as well in VS debug->properties->application->output type (select console from the dropdown)

To reproduce the described results.
Set your video cards global vsync (vertical retrace) to Half Adaptive which is 30fps.
Then run the project with a desired frame rate of 60fps or higher.

Notes at the bottom explain why you see the behavior.

code to reproduce this test
`

public class Game1 : Game
{
    #region initial variable and object declarations

    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    SpriteFont MgGeneratedFont001;

    private static string msgformat = "+##.###;-##.###;#------";

    double desiredframerate = 60d; // change this to the desired fps
    double secs = .08d;

    bool hascycled = false;
    int updates = 1;
    int frames = 1;
    StringBuilder sb_targettime = new StringBuilder("  TargetElapsedTime (in seconds): ");
    StringBuilder sb_drawupdateratio = new StringBuilder("\n  Draw To Update Ratio: ");
    StringBuilder sb_updates = new StringBuilder("\n  Updates: ");
    StringBuilder sb_draws = new StringBuilder("  Draws: ");
    double timelast = 0d;
    double timenow = 1d;
    double fps = 1f;
    StringBuilder sb_time_last = new StringBuilder("\n  Time Last: ");
    StringBuilder sb_time_now = new StringBuilder("  Time Now: ");
    StringBuilder sb_time_elapsed = new StringBuilder("\n  Elapsed Time Interval: ");
    StringBuilder sb_time_fps = new StringBuilder("\n  Fps: ");
    long gcnow = 0;
    long gclast = 0;
    long gclost = 0;
    StringBuilder sb_gc_now = new StringBuilder("\n  GC Memory(kb) Now: ");
    StringBuilder sb_gc_lost = new StringBuilder("  GC Memory(kb) Lost: ");
    StringBuilder sb_perf_msg = new StringBuilder(512);

    bool consoleran = false;

    #endregion

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        MgGeneratedFont001 = Content.Load<SpriteFont>("MgGeneratedFont001");

        this.IsFixedTimeStep = false;
        this.TargetElapsedTime = TimeSpan.FromSeconds((double)(1.0d / desiredframerate));
        graphics.SynchronizeWithVerticalRetrace = false;
        graphics.GraphicsDevice.PresentationParameters.PresentationInterval = PresentInterval.Default;

        secs = TargetElapsedTime.TotalSeconds;
    }

    protected override void UnloadContent()
    {
    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();


        if (updates >= 60)
        {
            consoleran = true;
            //timenow = TimeSpan.FromSeconds((double)(1.0d / 60d)).TotalSeconds;
            timenow = gameTime.TotalGameTime.TotalSeconds;
            double dif = (timenow - timelast);
            fps = frames / dif;
            sb_perf_msg.Length = 0;
            sb_perf_msg.Append(sb_targettime);
            sb_perf_msg.Append(secs);
            sb_perf_msg.Append(sb_time_last);
            sb_perf_msg.Append(timelast);
            sb_perf_msg.Append(sb_time_now);
            sb_perf_msg.Append(timenow);
            sb_perf_msg.Append(sb_time_elapsed);
            sb_perf_msg.Append(dif.ToString(msgformat));
            sb_perf_msg.Append(sb_time_fps);
            sb_perf_msg.Append(fps.ToString(msgformat));
            sb_perf_msg.Append(sb_drawupdateratio);
            float ratio = (float)frames / (float)updates;
            sb_perf_msg.Append(ratio.ToString(msgformat));
            sb_perf_msg.Append(sb_updates);
            sb_perf_msg.Append(updates.ToString(msgformat));
            sb_perf_msg.Append(sb_draws);
            sb_perf_msg.Append(frames.ToString(msgformat));
            timelast = timenow;
            frames = 1;
            updates = 1;
            gclast = gcnow;
            gcnow = GC.GetTotalMemory(false);
            gcnow = (long)(gcnow / 1024);
            sb_perf_msg.Append(sb_gc_now);
            sb_perf_msg.Append(gcnow);
            gclost = gcnow - gclast;
            if (gclost < 0)
            {
                sb_perf_msg.Append(sb_gc_lost);
                sb_perf_msg.Append(gclost);
            }
        }
        if (!consoleran) { Console.WriteLine("update"); }
        //
        updates++;
        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Wheat);

        spriteBatch.Begin();

        spriteBatch.DrawString(MgGeneratedFont001, sb_perf_msg, new Vector2(10f, 10f), Color.MonoGameOrange);

        spriteBatch.End();

        if (!consoleran) { Console.WriteLine("draw"); }

        hascycled = true;
        //
        frames++;
        base.Draw(gameTime);
    }

}

`

Global settings on your card can override monogames graphics.SynchronizeWithVerticalRetrace setting essentially forcing it to true.
when you set your card to half adaptive 30 fps monogame cannot possibly both update and draw in 16 mics (microseconds) or 1/60th of a second this means IsRunningSlowly is set true, though i never did test it im sure it is. then every other frame a draw is skipped.

Which means although graphics.GraphicsDevice.PresentationParameters.PresentationInterval = PresentInterval.Default; (which in xna defaulted to .One as it does in mg) this is not overriden it is the fact that is running slowly is nearly exactly executing every draw.
Which then forces update to skip the next draw as is proper behavior under normal conditions.

However setting the card yourself makes for un-normal conditions.

In the case of this.IsFixedTimeStep = true or false with the mg waiting for the vsync which has now been forcibly set to only occur every 32 mics on the card yet fixed is set to 16 mics in mg.
Then IsFixedTimeStep is essentially executing yet invalidated by the card in either case of it being set to true or false, as the TargetElapsedTime is now effectively being limited by the cards very long vsync time

To add to that if it is not clear.
Since the vsync is such a long time at half adaptive forced by the card the monitor refresh signal mg is waiting for takes all the frame time and then more 1/30th of a second 32 micro seconds (.032 seconds) between vsyncs (aka refreshes) so in essence the cards settings are either interfering with or in the case of vysny (aka monitor refresh rate) overriding all 3 values.

The lesson you may take from this is as follows.
When you have options in your game that allow the user to change things like framerate or vsync you should place a comment above all those settings, especially if you allow the user to set any of them, so he knows his cards vsync can limit those settings if set to very low values this is especially important for games that do set IsFixedTimeStep = false and possibly only not important for games that are set to run at 30 fps or lower.

For fun using this example.
Try turning off your vsync on your card and on mg as well as switching off IsFixedTimeStep and look at your framerate as a test of your maximum fps you might get screen tearing doing this so don’t be startled by it.