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);
}
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