Map editor running very slowly (Visual C#)

Started by
6 comments, last by laztrezort 13 years, 9 months ago
I'm attempting to create a simple map editor in Visual C#. It's pretty much my first ever Windows application and it's going fairly slowly but I'm making solid progress.

However, after adding a grid to my map I noticed that it was running extremely slowly. It works fine when not drawing the grid but is very sluggish when I turn it on.

The tiles are 32x32 and I draw the grid in the picturebox's paint event as follows:
void mapEditorPicture_Paint(object sender, PaintEventArgs e)        {            // Draw the tiles               e.Graphics.DrawImage(cursor, cursorMap.X * dimensions - 1,                cursorMap.Y * dimensions - 1);            if (tileMapPicture.Image != null)            {              //  GraphicsUnit units = GraphicsUnit.World;              ////  RectangleF bmpRectangleF = tileMapPicture.Image.GetBounds(ref units);              ////  Rectangle bmpRectangle = Rectangle.Round(bmpRectangleF);                                e.Graphics.DrawImage(tileMapPicture.Image,                     new Rectangle(cursorMap.X * dimensions,                    cursorMap.Y * dimensions, dimensions, dimensions),                    new Rectangle(cursorTile.X * dimensions, cursorTile.Y * dimensions,                        dimensions, dimensions),                    GraphicsUnit.Pixel);              }                        // Draw the grid             if (mapGridCheck.Checked)            {                                DrawGrid(mapEditorPicture.Size.Width, mapEditorPicture.Size.Height,                    e.Graphics);            }        }// Code for drawing the gridcells        private void DrawGrid(int width, int height, Graphics graphics)        {            for (int i = 0; i < width; i++)            {                for (int j = 0; j < height; j++)                {                    Point point = new Point(dimensions * i, dimensions * j);                    graphics.DrawImage(gridCell, point);                }            }        }


I'm guessing the problem is the thousands of iterations it must go through every time the paint method is called (3000 for a 100x30 grid). Am I using the paint event correctly?

Even when scrolling it seems to be extremely slow when the grid is on, telling me that it's repainting then too.

A couple of solutions that I've thought up are:
1. Only drawing a grid for the visible part of the picture, so instead of 3000 iterations, it might only be 300.
2. Drawing the grid in another picturebox on top of the first (sort of like a new layer), so that the only time it has to redraw the grid is when it is toggled on/off.

Is there a better way to go about this? Also, once I've got my tiles displaying properly, I'm guessing I'll run into the same problem. Is there am inbuilt way to get the paint event to redraw ONLY the parts of the screen that need redrawing? For example, if I place a tile at (0,0) it'll only redraw that single tile as everything else is unchanged? As well as only drawing what's actually displayed on the screen and not everything else? Or will I have to code it up myself?
Advertisement
What does the profiler tell you?

You can guess what the bottlenecks are (and generally be wrong about it) or you can use a profiler to isolate the bottlenecks, measure them, and compare the results after your changes.

If you don't have a version of Visual Studio that includes the profiler, you can use the free command line version. Google can help you find several others of the VS Profiler isn't enough.



You've already likely hit on one of the offenders, since all those function calls do have overhead. Drawing on a per-pixel basis can be very slow. Consider doing the whole thing in fewer function calls, or only drawing it once and storing the results. But without actual profiler numbers, you are just guessing at the root cause.
rafehi when you draw your grid are you drawing a grid image on top of your map, if you are then this is probably the reason why it is so slow, I think it would be easier if you just drew lines.

So if your mapEditor picture is 640 * 640 say and your tiles are 32 * 32 then you would only have to draw 20 lines across the way and 20 lines down the way. That would be your grid.

When I drew a grid for my editor(done in C++ with SDL and guichan) this is what I did.
I'm using tiles of 64 * 64

w = width, q = height, 640 = width of map, 1280 = height of map.

for(int i = 0; i < mapHeight; i++)	{		graphics->drawLine(0, w, 640, w);		w += 64;	}	for(int j = 0; j < mapWidth; j++)	{		graphics->drawLine(q, 0, q, 1280);		q += 64;			}
How about them apples?
Quote:Original post by frob
What does the profiler tell you?

You can guess what the bottlenecks are (and generally be wrong about it) or you can use a profiler to isolate the bottlenecks, measure them, and compare the results after your changes.

If you don't have a version of Visual Studio that includes the profiler, you can use the free command line version. Google can help you find several others of the VS Profiler isn't enough.



You've already likely hit on one of the offenders, since all those function calls do have overhead. Drawing on a per-pixel basis can be very slow. Consider doing the whole thing in fewer function calls, or only drawing it once and storing the results. But without actual profiler numbers, you are just guessing at the root cause.


I've never used a profiler and I've got VSEE, so it's not included. I downloaded the CLR profiler and all I got was an unhandled expection error...
Quote:Original post by popcorn
rafehi when you draw your grid are you drawing a grid image on top of your map, if you are then this is probably the reason why it is so slow, I think it would be easier if you just drew lines.

So if your mapEditor picture is 640 * 640 say and your tiles are 32 * 32 then you would only have to draw 20 lines across the way and 20 lines down the way. That would be your grid.

When I drew a grid for my editor(done in C++ with SDL and guichan) this is what I did.
I'm using tiles of 64 * 64

w = width, q = height, 640 = width of map, 1280 = height of map.

*** Source Snippet Removed ***


Thanks for that, it's definitely a much faster implementation than mine.

But it doesn't really solve the problem. When I draw the tiles themselves, I've got no choice but to do it tile by tile and so I'm guessing I'll encounter the problem again.

Frob, what other option do I have but per pixel drawing? Also, what do you mean by storing the results?
Quote:Original post by rafehi
Quote:Original post by frob
What does the profiler tell you?

You can guess what the bottlenecks are (and generally be wrong about it) or you can use a profiler to isolate the bottlenecks, measure them, and compare the results after your changes.

If you don't have a version of Visual Studio that includes the profiler, you can use the free command line version. Google can help you find several others of the VS Profiler isn't enough.



You've already likely hit on one of the offenders, since all those function calls do have overhead. Drawing on a per-pixel basis can be very slow. Consider doing the whole thing in fewer function calls, or only drawing it once and storing the results. But without actual profiler numbers, you are just guessing at the root cause.


I've never used a profiler and I've got VSEE, so it's not included. I downloaded the CLR profiler and all I got was an unhandled expection error...

Thats cause its a pretty crappily piece of crappish.

try out http://code.google.com/p/slimtune/
Alright, using SlimTunes and looking at the results (not really sure how to interpret), I'm getting ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop (int, int, int)
as taking up well over 80% of the time. No idea what that is?

My old drawing function was taking ~15% of time whereas using popcorn's implentation, that's dropped to well below 1%. Working much faster too.

Any reason why the FPushMessageLoop is no high?
I'm no .Net guru, but just some random thoughts:

I would try implementing the 2 optimization methods in your first post if you haven't already. #1 may be especially important: you want to reduce the number of blits as much as possible. Only draw the tiles or grid lines that are within the map viewport. This should make a significant difference in performance.

Also, make sure you are not unnecessarily invalidating the map viewport somewhere else so that OnPaint() is getting called too often.

I've had some speed issues with blitting images under System.Drawing, but I don't remember exactly the problem. I believe it had to do with alpha blending (transparency) - I think the solution I found (through trial and error) was to use PNG's with the alpha channel instead of using a color keyed transparency. You don't seem to be using alpha, however, so this probably doesn't apply.

I'm not sure about the FPushMessageLoop(). I've never used SlimTune, but I know that some profilers introduce extra overhead into the executable - perhaps this is what you are seeing? Have you tried to execute the revised code without the profiler to subjectively measure the performance difference?

Of course, there is always the option of going a completely different route. GDI+ may not be the best tool for performance graphics. I know, for example, that XNA integrates easily with Winforms, and there are even some very in-depth tutorials on building a tile map editor using this technique. However, an inefficient algorithm will perform badly no matter how much hardware acceleration you put up against, so it may be worth your while to investigate further.

Good luck!

This topic is closed to new replies.

Advertisement