1
\$\begingroup\$

I have this drawing function (recommended as a solution here). It draws tiles on the whole window with no problem but my game slows down to ~30fps after maximizing it to "windowed fullscreen", which is 1600x1200 on my PC. (So 1600/16 * 1200/16 = 7500 tiles in the window)

While I want to add more advanced stuff to this simple rendering I wonder if it is possible to draw this tiles in instantiated way in monogames? (Like in OpenGL 3.3) Or is there any other way to speed it up?

BTW. Lowering the number of tiles is not a way and i need to update whole screen per frame because of incoming transparent GUI.

protected override void Draw(GameTime gameTime)
{
    frameCounter++;

    GraphicsDevice.Clear(Color.CornflowerBlue);

    spriteBatch.Begin();

    for (int x = 0; x < Window.ClientBounds.Width / 16; x++)
    {
        for (int y = 0; y < Window.ClientBounds.Height / 16; y++)
        {
            int offX = x + mainCamera.Position.X;
            int offY = y + mainCamera.Position.Y;

            Tile t = gameMap.GroundTiles[offX, offY, mainCamera.Position.Z];
            spriteBatch.Draw(t.Texture, new Vector2(x * 16, y * 16));

        }
    }

    spriteBatch.End();

    base.Draw(gameTime);
}

enter image description here

\$\endgroup\$
  • 1
    \$\begingroup\$ There's a whole bunch of factors to consider outside the code you've shown that could cause it to be slow. The first I would look at is how many times you're switching textures. Texture switching can be very bad for performance, so consider using a texture atlas instead of switching textures for for every tile. \$\endgroup\$ – craftworkgames Sep 2 '14 at 1:07
  • \$\begingroup\$ There are no factor outside this code, as you see im taking Tile instantion from 3d array and use its Texture2D property. Forloop is looping over whole screen. All that is debugged and time-measured. I'll use texture atlas and check in with results. \$\endgroup\$ – ddl Sep 2 '14 at 5:08
  • \$\begingroup\$ My concern is that the Texture property is pointing at a different image for each tile. If this is the case the SpriteBatch.End call will be breaking it up into hundreds of batches before sending everything to the video card. Each batch has a performance cost. The simplest way to test this theory is to set all the tiles to the same texture and see if your performance issues improve. \$\endgroup\$ – craftworkgames Sep 2 '14 at 5:21
  • \$\begingroup\$ I joined my tiles into one texture and now I have ~60 FPS all the time. Thank you. \$\endgroup\$ – ddl Sep 2 '14 at 15:01
  • \$\begingroup\$ Cool. I'm glad you got it sorted, I've added an answer for you so you can accept it. Thanks. \$\endgroup\$ – craftworkgames Sep 3 '14 at 6:12
3
\$\begingroup\$

I suspect your issue lies in the way a sprite batch works in MonoGame. The performance cost is coming from using different textures for each tile.

Let's take a peek into the MonoGame source code and see what's going on. If you follow the code down through SpriteBatch.End you eventually end up in the SpriteBatcher.cs class around about here:

https://github.com/mono/MonoGame/blob/develop/MonoGame.Framework/Graphics/SpriteBatcher.cs#L243

        foreach ( SpriteBatchItem item in _batchItemList )
        {
            // if the texture changed, we need to flush and bind the new texture
            if ( item.TextureID != texID )
            {
                FlushVertexArray( startIndex, index );
                startIndex = index;
                texID = item.TextureID;
                GL.BindTexture ( All.Texture2D, texID );
            }
            // store the SpriteBatchItem data in our vertexArray
            _vertexArray[index++] = item.vertexTL;
            _vertexArray[index++] = item.vertexTR;
            _vertexArray[index++] = item.vertexBL;
            _vertexArray[index++] = item.vertexBR;

            _freeBatchItemQueue.Enqueue ( item );
        }
        // flush the remaining vertexArray data
        FlushVertexArray(startIndex, index);

As you can see from this code, the way it batches things up internally is by checking the TextureID of each item in the _batchItemList and if it has changed, flushes the vertex array and binds a new texture.

This approach is pretty typical of how sprite batching works in most 2D engines (from my understanding) and usually turns out okay. However, if you have too many texture switches it can be pretty costly on performance.

The usual way to deal with this in a tile based engine is to use a texture atlas. That is, a single texture that stores all your tiles and during rendering you pick the tile's "rectangle" from the source texture.

So it's okay to have a few different textures in your game, but try to keep the texture switching to a minimum. For example, 1 texture per layer in tile based game should be fine (assuming you draw each layer one at a time) but for tiles within a single layer, try store them all on a single texture.

\$\endgroup\$
  • \$\begingroup\$ Great answer! All that SpriteBatch.Begin()-SpriteBatch.End() mystery in both XNA and MonoGame docs should be better expaned, because performance issues are absolutely not obvious for beginners, because, the texture mapping process is very well (if not to say completely) hidden in this frameworks. \$\endgroup\$ – Petr Abdulin Sep 3 '14 at 7:22
  • \$\begingroup\$ I agree. I've been using MonoGame for my games for a while now but I'm only really familiar with this stuff because I've been experimenting with building my own framework in my spare time. \$\endgroup\$ – craftworkgames Sep 3 '14 at 12:35
  • \$\begingroup\$ @craftworkgames I don't want to do too invasive of an edit but you might want to add the DirectXTK (a porting of XNA to C++ by EX XNA Devs) code that demonstrates XNA does the same sort of flush/rebinding. Line 243 through 266. github.com/mono/MonoGame/blob/develop/MonoGame.Framework/… \$\endgroup\$ – ClassicThunder Sep 3 '14 at 15:00
  • \$\begingroup\$ @ClassicThunder You make a good point but I don't think it's going to add value to the answer. The code is simply there to demonstrate the point, not to imply some sort of flaw in MonoGame. As I said in the 4th paragraph, this is a pretty typical sprite batch implementation. \$\endgroup\$ – craftworkgames Sep 3 '14 at 23:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.