Tuesday, April 30, 2019

xna - In Monogame, why is multiple tile drawing slow when rendering in "windowed fullscreen"?


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



Answer



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.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...