How to draw multiple sprites from one texture at their corresponding position?

I’m looking for something like:

SpriteBatch.Draw (Texture2D, Vector2[], Nullable<Rectangle>[])

I want to to draw my world with different parts of a texture (tileset). Like a block/tile of sand then rock, water, water, sand,…

In webGL sample code looks like this:

gl.bindBuffer(gl.ARRAY_BUFFER, vertexTextureCoordBuffer); gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, vertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

or is there no way doing this in MonoGame? or is it equal in speed as if I do something like:

foreach( tile in VisibleTiles) SpriteBatch.Draw (texture2D, tile.positionOnMap, tile.rectInTexture, tile.Color )

or how about making a SpriteFont (with each letter being a specific tile in my tileset) and use DrawString method?

SpriteBatch.DrawString (SpriteFont_tileset, string_tilesVisible, Vector2_topleft, Color)

I believe it would be the same speed as putting it in a for statement, however foreach your loosing a tiny bit of performance. If you only draw what the Camera can see though, very unlikely you’ll have performance drawbacks due to this.

I did some testing with a SpriteFont texture (512x512) with each tile/letter being 24x24 pixel in size and one pixel inbetween (and some border around). With that I had 20x20=400 tiles/letters. I used it as SpriteFont (sfont in DrawString) and as a Texture (sfonttext in Draw). Used FullScreen 2560x1440 -> about 6360 tiles on screen.

For comparison: Drawing only the full font texture once I got about 4000fps:


With drawing a string of 6360 chars (2Byte each) + a new line at the end of the screen ( + 60 times “\n” = 6420) I got only about 240fps:
spriteBatch.DrawString(sfont, tileString, Vector2.Zero, Color.White);

With drawing each tile on his position I got about 300fps:

for (int y = 0; y < tilesNumHeight; y++)
    for (int x = 0; x < tilesNumWidth ; x++)                    
        spriteBatch.Draw(sfontText, new Vector2(x*tileSize ,y*tileSize), 
            new Rectangle(6 + (x%num)*(tileSize+1) , 6 + (y%num)*(tileSize+1), tileSize, tileSize),

From 4000 down to 300 frames per second is quite a lot. May it be faster in 3D with textured triangles?

Well that´s because every .draw has to process and upload 4 vertices every frame, they are not persistent (apart from object pooling). Given that every vertex carries UV coords (of source rectangle) float2, color (4 bytes) and position float3. It mainly creates quite brutal CPU overhead, I suggest you to look at github into spritebatch.cs and spritebatcher.cs. There are several ways how to approach it, first you can write specialized and optimized version of spritebatch. Second is getting your information to GPU in different manner. Personally I am using texture to create uv map for all visible tiles that I send to shader which uses it to sample spritesheets.

(Spritebatch of course does other things as well, texture sorting, etc, you don´t change texture in this case… obviously… but spritebatcher still checks if flush on screen isn´t required and so on).

Internally, SpriteBatch is merging your calls into a single “batch” which it then uploads to the GPU. This should perform perfectly well enough for nearly any 2D game, even with several thousand calls to SpriteBatch.Draw(). Ravendarke is correct in that the overhead is probably because of the individual calls to Draw(), but to clarify, this will still result in only one upload to the GPU per frame (well one for every SpriteBatch Begin/End and for every different texture sheet used).

The only reason I can see for compiling a single texture of your current screen’s tile state would be if you 1) really really needed to squeeze every ounce of performance out and 2) had a static non-scrolling level. As soon as you start scrolling any reasonable distance you’d have to rebuild this texture anyway.

Hi, thanks you for your responses

because every .draw has to process and upload 4 vertices every frame

Yeh, so I searched for something were I can hand over more data (position in World/Screen, texture) at once. Thank you for the hint with “spritebatch.cs”. As Aranda already wrote there is only one upload to the GPU (for each texture) but there is some basic computing for each Draw call. Most could be done faster for a larger dataset.

The only reason I can see for compiling a single texture of your current
screen’s tile state […] rebuild this texture anyway.

Is this related to Ravendarke solution ("…texture to create uv map for all visible tiles…") or my?
My sample code above is a bit misleading. The current state of a tile would be stored in the Rectangle (in Draw) or in the string (DrawString). My texture is only the (global) tileset. The Rectangles.Position or the string would change if you move around.

In meantime I learned some more and tried a solution with a list of Triangles and a shader to map the right texture to each tile visible on the screen.
I got about 1800fps.
If I change them each update cycle it gos down to 650fp
With Draw from 300 down to 250. With DrawString from 250 down to 214.

I think I got a winner.
Currently I’m drawing 4 vertices for each tile. I could optimize that as well with a texture same as Ravendarke did.

…rebuild this texture anyway

You may update only a small part of the texture and translate it with mod-command.

I wrote a new solution with a texture as a map of the current screen. With 1900fps it’s a bit faster. Furthermore the size of each tile and or the number of tiles on screnn have nearly no influence on the frame rate. If I go down to 4x4 (instead 24x24) I still have 1750fps. The tirangle solution goes down to 600 fps with a small tile size.
So far so good, but the texture solution is a tricky one. Easy to do some wrong maths. The update is also not that fast. If you realy want to change all tiles in each update cycle it goes down to 270fps (triangle 650). But for a normal tileset game you only need to update a small part of all tiles, so I think I will stick to that.

I was talking about Ravendarke’s solution. You make a good point about only needing to modify sections of the texture, when scrolling or making map changes.

By the way in many cases you can do with 2 corners per tile (in texture obviously, it would work also with geometry if you would have access to Geometry shader in mono, which is not the case).