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 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 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 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 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 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 on other sites
Thank you FoxHunter2 that did the trick! Thats the kind of fix I like; a single parameter.

Share on other sites
changing SaveStateMode to None will also result in a large increase in FPS.

Share on other sites
Right, and here is the explanation why this is the case, it's worth reading.

Create an account

Register a new account

• Forum Statistics

• Total Topics
628333
• Total Posts
2982130

• 24
• 9
• 9
• 13
• 11