Jump to content
  • Advertisement
Sign in to follow this  

XNA - SpriteBatch.Draw terribly slow *Fixed*

This topic is 3651 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to draw a large tilemap of 1024 tiles (each tile being 32x33 pixel) for an editor. The editor is a Windows Form Application using the .Net Framework and C#. I initially tried this using GDI+ but any tilemaps bigger then 8x8 would take seconds to draw and just got worse with larger maps. So, after researching Graphics.DrawImage here I read a few suggestions for using XNA. I switched the drawing of the tiles to use XNA but I'm seeing very little improvements. Is this not the intended functionality of the XNA framework? I have done an editor years ago using bitblt with no trouble on large maps. I find it hard to believe that hardware accelerated drawing is slower then the deprecated Win32 GDI. The managed environment of C# is just wonderful to work with, but if need be I will bite the bullet and restart using MFC and DirectX. Below; the drawing code used to render the tilemap. Also, I'm using XNA 3.0 CTP.
        private void DrawMap()
            sprSprites.Begin(SpriteBlendMode.None, SpriteSortMode.Texture, SaveStateMode.SaveState);

            //Draw each rect's data
            Vector2 vec = new Vector2();
            Microsoft.Xna.Framework.Rectangle sourceRect;
            for (int i = 0; i < (nMapRows * nMapCollums); ++i)
                vec.X = (float)rectMap.X;
                vec.Y = (float)rectMap.Y;
                sourceRect.X = rectTileset[nMapData].X;
                sourceRect.Y = rectTileset[nMapData].Y;
                sourceRect.Width = rectTileset[nMapData].Width;
                sourceRect.Height = rectTileset[nMapData].Height;

                sprSprites.Draw(txtTileset, vec, sourceRect, Microsoft.Xna.Framework.Graphics.Color.White);
                //e.Graphics.DrawImage(imgTileset, rectMap.X - nScrollMapX, rectMap.Y - nScrollMapY, rectTileset[nMapData], GraphicsUnit.Pixel);

            //Draw the grid over the map
            //for (int i = 0; i < nMapCollums; ++i)
            //    e.Graphics.DrawLine(Pens.Black, (i * nTileSize) - nScrollMapX, 0 - nScrollMapY, (i * nTileSize) - nScrollMapX, (nTileSize * nMapRows) - nScrollMapY);

            //for (int i = 0; i < nMapRows; ++i)
            //    e.Graphics.DrawLine(Pens.Black, 0 - nScrollMapX, (i * nTileSize) - nScrollMapY, (nTileSize * nMapCollums) - nScrollMapX, (i * nTileSize) - nScrollMapY);

[Edited by - LaZZer on August 16, 2008 12:43:25 PM]

Share this post

Link to post
Share on other sites
Are you drawing the tiles that are outside of your current viewport? If so you might want to add some checks in that ensure that you only draw the tiles you are looking at, rather than the tiles that are outside of the viewable area.

If I had more time I'd write up an example for you, I'll come back to this later.


Share this post

Link to post
Share on other sites
I'm drawing all the tiles. Mainly because they are all visible. Even on a small tilemap that's 12x12 it's still horribly slow. I do plan on 'culling' the tiles that are offscreen, but until I can get the ones onscreen drawing at a respectable rate I'm not concerned with it. Thank you for the suggestion though.

Share this post

Link to post
Share on other sites
I don't see what might be wrong with the code. XNA should internally batch the draw calls to improve performance.

Do you do anything else complex in the code you didn't post yet?

Share this post

Link to post
Share on other sites
Nothing at all complex. The form itself is still a skeleton with only a handful of controls.

Here's the Init. Nothing major in here. Just setting up the GraphicsDevice and a few other variables. Only concern here is the calls to Setstyle but with or without them I see no difference. I'm not doing any GDI+ draw calls anyway.

public Form1()

PresentationParameters pp = new PresentationParameters();
pp.IsFullScreen = false;
pp.SwapEffect = SwapEffect.Flip;

gfxDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Reference, this.pnlMap.Handle, pp);
sprSprites = new SpriteBatch(gfxDevice);

//Variable init
bTilesetLoaded = false;
nScrollTilesetX = nScrollTilesetY = 0;
nTilesetRows = nTilesetCollums = 0;
nTileSize = 32; //Defualt tile size
nScrollMapX = nScrollMapY = 0;
nSelectedTile = 0;

//Other init
vscrollMap.Enabled = false; //Disable scroll bars until everything is loaded
hscrollMap.Enabled = false;
vscrollTileset.Enabled = false;
hscrollTileset.Enabled = false;

this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

I'm loading in the bitmap for the tiles in another function. Here I setup an array of Rectangles. Load in bitmap and that's about it.

private void OpenTileset()
//Open the file dialog to browse for a tileset
if (ofdOpen.ShowDialog(this) == DialogResult.OK)
//imgTileset = Image.FromFile(ofdOpen.FileName); //Open the file
bTilesetLoaded = true; //We now have a valid tileset
//Setup the scroll bars
hscrollTileset.Enabled = true;
vscrollTileset.Enabled = true;
nScrollTilesetX = nScrollTilesetY = 0;
hscrollTileset.Minimum = (-1 * imgTileset.Width);
hscrollTileset.Maximum = imgTileset.Width;
vscrollTileset.Minimum = (-1 * imgTileset.Height);
vscrollTileset.Maximum = imgTileset.Height;
//Grab some information from the bitmap
nTilesetRows = (int)(imgTileset.Height / nTileSize);
nTilesetCollums = (int)(imgTileset.Width / nTileSize);
//Setup the rects
rectTileset = new System.Drawing.Rectangle[(nTilesetCollums * nTilesetRows)];
int count = 0;
for (int i = 0; i < nTilesetCollums; ++i)
for (int j = 0; j < nTilesetRows; ++j) //Setup the rect array
rectTileset[count].X = (nTileSize * j);
rectTileset[count].Width = nTileSize;
rectTileset[count].Y = (nTileSize * i);
rectTileset[count].Height = nTileSize;
txtTileset = Texture2D.FromFile(gfxDevice, ofdOpen.FileName); //load the texture
pnlTileset.Invalidate(); //Force a redraw of the tileset
pnlMap.Invalidate(); //Force a redraw of the map


I am using Windows Vista x86 and Visual Studio 2008. I don't see how that would affect me, but just trying to give as much information as possible.

Share this post

Link to post
Share on other sites
gfxDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Reference, this.pnlMap.Handle, pp);

You're using the reference device, which will operate in software mode and only work on computers with the DX SDK installed. Set it to DeviceType.Hardware


Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!