FPS flickering / high CPU usage

Hello MonoGame community,

First time here and I would like to ask for your insight! This problem is bothering me for a while now and I can’t seems to find the solution.

My game is constantly switching its FPS along with a high CPU usage (60%). The FPS flickers between 20-30-40 FPS on a slower laptop and on a full high-end desktop it flickers between 32-62 (both W7). On a W10 low-end tablet the game is running smoothly at 62 FPS. Full screen does help a little on the laptop/desktop.

Besides the FPS, on my laptop the game uses 60% of the CPU for I don’t know… When I measure my update/draw loop it takes max 2-3 MS to update/draw.

I altered the graphic values alot, but nothing seems to work. Below are the current values. Debug or release, doesnt change a thing.

//Graphic values
public int screenWidth = 800;
public int screenHeight = 600;
public bool screenFull = false;
public bool verticalRetrace = true;
public bool multiSampling = false;
public bool fixedTimeStep = true;
public float deltaTimeStep = 62;

Hopefully someone can point me in the right direction!

Greetings,

Robin

Edit: Switching off any second monitor seems to help.

what does your update/draw function look like? maybe some screen shots of your code will help

The update & draw loop does have a Mutex on it. Tough that is not the problem since commenting the mutex is not helping + nothing is blocking it when I debug in offline modus. The update/draw calls a SystemManager from my Entity Component System architecture, in which all the systems are updated and then drawn. All the systems together do not even need 1ms to update and at most 2-3 ms to draw.

Thanks for your reply in advance!

Update / Draw loop:

    protected override void Update(GameTime _gameTime)
    {
        base.Update(_gameTime);

        try
        {
            this.gameComp.gameTime = _gameTime;

            if (EntityManager.GetInstance().lockEcs.WaitOne())
            {
                SystemManager.GetInstance().Update();
                EntityManager.GetInstance().lockEcs.ReleaseMutex();
            }
        }
        catch (Exception e)
        {
            Logger.CreateLog("Update loop failed", e);

            WebLog.SendLog(Logger.logSession);

            game.Exit();
        }
    }

    protected override void Draw(GameTime _gameTime)
    {
        base.Draw(_gameTime);
        
        try
        {
            this.gameComp.gameTime = _gameTime;

            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);

            if (EntityManager.GetInstance().lockEcs.WaitOne())
            {
                SystemManager.GetInstance().Draw();
                EntityManager.GetInstance().lockEcs.ReleaseMutex();
            }

            spriteBatch.End();
        }
        catch (Exception e)
        {
            Logger.CreateLog("Draw loop failed", e);

            game.Exit();
        }
    }

SystemManager:

public class SystemManager
{
    private static SystemManager manager;

    //List of systems
    private List<ASystem> systems;

    //Indicates if a update should stop
    private bool stopped;

    private Stopwatch stopwatch;

    public event SystemResetHandler systemResetHandle;
    public delegate void SystemResetHandler();


    private SystemManager()
    {
        this.systems    = new List<ASystem>();

        this.stopwatch  = new Stopwatch();
    }

    public static SystemManager GetInstance()
    {
        if (manager == null)
            manager = new SystemManager();

        return manager;
    }

    ///<summary>
    ///Reset the systemmanager, clear the list
    ///</summary>
    public void Reset()
    {
        this.stopped = true;

        for (int i = 0; i < this.systems.Count; i++)
            this.systems[i].Stop();

        this.systems            = new List<ASystem>();

        //Handle system reset events
        if(systemResetHandle != null)
            systemResetHandle();
    }

    
    /// <summary>
    /// Add a system
    /// </summary>
    /// <param name="_system">System to be added</param>
    public void AddSystem(ASystem _system)
    {
        //Cant have double systems
        if(!this.systems.Contains(_system))
        {
            //Start the system
            _system.Start();

            _system.systemCheck = new SystemCheck();

            //Add the system to the list of systems
            this.systems.Add(_system);
        }
    }

    /// <summary>
    /// Remove a system
    /// </summary>
    /// <param name="_system">System to be removed</param>
    public void RemoveSystem(ASystem _system)
    {
        _system.Stop();

        this.systems.Remove(_system);
    }

    ///<summary>
    ///Update all systems on a priority
    ///</summary>
    public void Update()
    {
        this.stopped = false;

        this.stopwatch.Start();

        long lastTick = 0;
        long overalTime;

        //Loop trough all systems and make them update
        for (int i = 0; i < this.systems.Count; i++)
        {
            this.systems[i].Update();

            //If a system made the manager reset, we should stop updating this iteration
            if (this.stopped)
                break;

            long curTick    = stopwatch.ElapsedTicks;
            overalTime      = curTick - lastTick;

            this.systems[i].systemCheck.updates++;
            this.systems[i].systemCheck.countUpdate += overalTime;

            if (overalTime > this.systems[i].systemCheck.highestUpdate)
                this.systems[i].systemCheck.highestUpdate = overalTime;

            if (overalTime < this.systems[i].systemCheck.lowestUpdate)
                this.systems[i].systemCheck.lowestUpdate = overalTime;

            lastTick = curTick;
        }

        this.stopwatch.Reset();
    }

    ///<summary>
    ///Update all systems on a priority
    ///</summary>
    public void Draw()
    {
        this.stopwatch.Start();

        long lastTick = 0;
        long overalTime;

        //Loop trough all systems and make them draw
        for (int i = 0; i < this.systems.Count && !this.stopped; i++)
        {
            this.systems[i].Draw();

            long curTick    = stopwatch.ElapsedTicks;
            overalTime      = curTick - lastTick;

            this.systems[i].systemCheck.draws++;
            this.systems[i].systemCheck.countDraw += overalTime;

            if (overalTime > this.systems[i].systemCheck.highestDraw)
                this.systems[i].systemCheck.highestDraw = overalTime;

            if (overalTime < this.systems[i].systemCheck.lowestDraw)
                this.systems[i].systemCheck.lowestDraw = overalTime;

            lastTick = curTick;
        }

        this.stopwatch.Reset();
    }

    /// <summary>
    /// Return a list of all the systems
    /// </summary>
    /// <returns></returns>
    public List<ASystem> GetSystems()
    {
        if (this.systems == null)
            return new List<ASystem>();

        return this.systems;
    }
}

///<summary>
///Abstract Update System with a start, end and update method
///</summary>
public class ASystem
{
    public SystemCheck systemCheck;

    ///<summary>
    ///Start up the system
    ///</summary>
    public virtual void Start() { }

    ///<summary>
    ///End the system
    ///</summary>
    public virtual void Stop() { }

    ///<summary>
    ///Update the system
    ///</summary>
    public virtual void Update() { }

    ///<summary>
    ///Draw the system
    ///</summary>
    public virtual void Draw() { }
}

public class SystemCheck
{
    public int updates;
    public long highestUpdate;
    public long lowestUpdate = Int64.MaxValue;
    public long countUpdate;

    public int draws;
    public long highestDraw;
    public long lowestDraw = Int64.MaxValue;
    public long countDraw;
}

If you just outcomment stuff in your draw and update function you should find out what’s causing the problem.

For framerate tests it’s best to keep verticalRetrace = false. I’ve never used fixedTimeStep, maybe that’s causing some problems as well.