Sign in to follow this  
LaZZer

XNA - SpriteBatch.Draw terribly slow *Fixed*

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()
        {
            gfxDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.Blue);
            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[i].X;
                vec.Y = (float)rectMap[i].Y;
                sourceRect.X = rectTileset[nMapData[i]].X;
                sourceRect.Y = rectTileset[nMapData[i]].Y;
                sourceRect.Width = rectTileset[nMapData[i]].Width;
                sourceRect.Height = rectTileset[nMapData[i]].Height;

                sprSprites.Draw(txtTileset, vec, sourceRect, Microsoft.Xna.Framework.Graphics.Color.White);
                //e.Graphics.DrawImage(imgTileset, rectMap[i].X - nScrollMapX, rectMap[i].Y - nScrollMapY, rectTileset[nMapData[i]], 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);
            sprSprites.End();
            gfxDevice.Present();
        }


[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.

-SD

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
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()
{
InitializeComponent();

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;
++count;
}
}
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

regards

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this