Slow render performance

Hello! I’m trying to fill 1920x1080 screen with sprites 16x16 size (120x67 tiles). For testing worst possible scenario i switch texture every tile. I know it is wrong and i should use tilemap and switch texture less often, but it’s only test.

My code is here

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

namespace Fna1
{
    public class Tile
    {
        public Texture2D texture;
    }

    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D t1;
        Texture2D t2;
        SpriteFont font;

        int sizeX = 120;
        int sizeY = 67;
        Tile[,] tiles;

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

        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 1920;
            graphics.PreferredBackBufferHeight = 1080;
            graphics.SynchronizeWithVerticalRetrace = false;
            IsFixedTimeStep = false;
            graphics.ApplyChanges();
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            t1 = GenerateTexture(Color.DarkGreen);
            t2 = GenerateTexture(Color.DarkCyan);
            font = Content.Load<SpriteFont>("font");

            int c = 0;
            tiles = new Tile[sizeX, sizeY];
            for (int x = 0; x < sizeX; x++)
            {
                for (int y = 0; y < sizeY; y++)
                {
                    tiles[x, y] = new Tile();
                    tiles[x, y].texture = (c % 2) == 0 ? t1 : t2;
                    c++;
                }
            }
        }

        private Texture2D GenerateTexture(Color color)
        {
            Texture2D texture = new Texture2D(GraphicsDevice, 16, 16);
            var cdata = new Color[16 * 16];
            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    cdata[i * 16 + j] = color;
                }
            }
            texture.SetData(cdata);
            return texture;
        }

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

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

            spriteBatch.Begin();
            for (int x = 0; x < sizeX; x++)
            {
                for (int y = 0; y < sizeY; y++)
                {
                    spriteBatch.Draw(tiles[x, y].texture, new Vector2(x * 16, y * 16), Color.White);
                }
            }
            spriteBatch.End();

            var fps = 1 / gameTime.ElapsedGameTime.TotalSeconds;

            spriteBatch.Begin();
            spriteBatch.DrawString(font, fps.ToString(), Vector2.Zero, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

On my hardware (Geforce 760 GT) i have about 100 fps in release build. But later i tested the same code on FNA and received 500 fps! Both my projects is net.core 3.1 if it matters.
I checked source code of SpriteBatch.cs on both MonoGame and FNA. And saw that FNA uses DrawIndexedPrimitives and DynamicVertexBuffer instead of DrawUserIndexedPrimitives in Monogame. Then i recreated FNA’s SpriteBatch from source code and make some fixes. And get only 130 fps.
Сan someone explain to me what’s the matter?

This is very rough. I have a Gtx 1650 card and OpenGL 50-55fps, DirectX is around 66-70fps in full screen. I haven’t tried FNA. Maybe the processor matters more? I have an older amd fx 6200 that I run on 4.1Ghz. If I commented the spritebatch.draw line, it also hits 4000fps. And in the NVIDIA control panel, everything is performance-centric.
At first I thought the trouble was that you create the new vector in the draw method every cycle. Outside, I loaded a Vector2 array with the appropriate parameters, and when I draw it, I just pass it, but the fps didn’t improve. SpriteShortMode.Deferred did not solve the problem either.
Unfortunately, I don’t understand programming at such a low level, but hopefully someone will look into what might be in the background.

Can you share with me an FNA project that I just need to run in full screen mode? I wonder how much more fps I will have.

My project files
https://drive.google.com/file/d/1StJX5TvE6xOmwOHaB-zX8C2KPoaU6C55/view?usp=sharing
In bin/release folder you can run compiled exe. FNA project x5 times faster then Monogame

FNA project 16 168 draw events per frame
Monogame project 64 379… this is not MG issue, something horrible was done to MG variant of the project. I can investigate later. (also it is bit problem to figure out what’s exactly going on since Frame analysis with 64k+ draw events is pretty unstable)

Where i can see draw events?
Also tried desktop windows DX Monogame project and got 170 fps.
Maybe FNA uses more simple sprite effect shader?

I check Terraria game. Every single type of tile has own texture. If i built almost full screen from different tile types it runs smooth 60fps with thousands projectiles, particles, enemies, collisions and even some network sync code. Terraria’s tile batcher doesn’t do any sprite sort and only allow assign different color for each sprite verticle (for their lighting system)

If i have only 100 fps for rendering tiles, it’s bad. I know that it’s bad practise to use different textures for each tile, and tilemaps will be much faster. But terraria doesnt use tilemaps (only for different visual for same tile type). And FNA works very well. But something strange with Monogame or my hands :slight_smile:

I gave this a test run. Mostly to see how my custom build is comparing against FNA.
I have a GTX 1660.

FNA 310fps
MG-GL 50fps
MG-DX 80fps

My custom build with additional optimizations
MG9K-GL 50fps
MG9K-DX 350fps

The project can be found here. It’s Net48 since i don’t yet provide core3.1 in the nugets, i build it against core3.1 locally and gave me about +10 more frames.

for sure the GL implementation has room for improvement.

Wow, I didn’t know about MG9K. Your project give me 530 fps, little more then FNA.
I tried to install your build from nuget to net48 empty project and it works!
DX version 530 fps
GL version 70 fps
But why your optimizations not included in standart MonoGame? And what the reason of such perfomance improvements?
Also i dont understand what FNA uses - GL or DX, and how switch it

They never got merged so I withdraw them after a year or two. After that I stopped pushing any updates.

For this particular test I suspect that the main win comes from a SpriteBatcher specialized for DX.
I write the new vertices directly to the mapped memory of VertexBuffer, instead of going through the vb.SetData(). Also I fill the vb and update it once with all sprites. Then call Draw with an offset on each texture switch.

2 Likes

I get 250-260fps with FNA. But Monogame only 53-56.

Very decent proposed optimization.

Now i installed win7, vs2010 and xna4.0 refresh on virtualbox, created the same project and got 120 fps. More then monogame GL :slight_smile: