Making a fast renderer for my map editor

Started by
8 comments, last by Zimans 23 years, 11 months ago
I''m currently working on the map editor for my game. Everything is going along fine except for the fact that my renderer doesn''t seem to be that fast... I was wondering if anyone knew anything about dealing with the windows GDI system that might make my renderer faster.. I know it''s possible as the Warcraft II map editort was designed back before Directx came around, and that editor is pretty fast.. My setup right now is I have the render func called under the wm_paint event. The func has two for loops to draw all the tiles in the allotted space. A func, putimage is called that draws the image onto a off-screen DC. The putimage maintains a cache of HBITMAP handles and loads an image into the cache if it can''t find it. Then the offscreen DC is copied to the window DC. The draw routine isn''t that slow, but you can see flicker when the update occurs. If anyone has any suggestions/tips I''d be grateful.. -Zims
Advertisement
I don''t know tons about the GDI, (and this doesn''t really concern it) but here are is my $0.02:

Load all the images into the cache and never remove them until the program is sure it will not need them anymore. That should stop it from loading them if it can''t find the image. If it is loading images, this will make it a bit faster. (a blt faster? )

--------------------


You are not a real programmer until you end all your sentences with semicolons;

Yanroy@usa.com

Visit the ROAD Programming Website for more programming help.

--------------------

You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming
You are unique. Just like everybody else.
"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com

Couple suggestions:

1) You seem to be blting to a back buffer which is good.

2) Only blt what you need too, and only call WM_PAINT when you need too. Don''t invalidate the entire screen, rather just invalidate the region that the pixels changed.

3) The flicker happens because you are erasing the entire image, then redrawing the entire image, which is a slow process. Try not to erase the image if possible.

(This involves a lot of UpdateWindow(hWnd,false) messages.

4) A way to accomplish new drawing is when you are copying the backbuffer over, rather than just a copy, try Or''ing, And''ing, and other messages to see if they give you the effect of what you want.
I''ve messed around with the render stuff some more, and now I seem to have broken it.. I changed the cacheing system over to a linked list. (Started out as an array but decided there were no benefits to using an array over a linked-list so converted it) But that shouldn''t have effected it as I''m still loading the bitmaps and storing the HBITMAP. Now the first square draws fine, and every square thereafter is a single white dot in the upper left... I can''t seem to figure out what I did to break it...

-Zims
Zimans, can you post the code you are using to display the bitmaps onto the screen?
This quick snippet is a hack to just draw the window.

for (indexY=0; indexY < MapWin.WinHeight; indexY++)
{
for (indexX=0; indexX < MapWin.WinWidth; indexX++)
{
PutImage (hWnd, "Square", indexX * MAP_TILE_WIDTH, indexY * MAP_TILE_HEIGHT);
}
}

BitBlt (GetDC(hWnd), MapWin.OffsetX, MapWin.OffsetY, (MapWin.WinWidth * MAP_TILE_WIDTH), (MapWin.WinHeight * MAP_TILE_WIDTH), MapWin.DrawPadDC, 0, 0, SRCCOPY);


Here''s the code for drawing the tiles.

void PutImage (HWND hWnd, char *Name, int X, int Y)
{
HBITMAP OldBmp;
BITMAP Bitmap;
HDC ImageHDC;

entry_typ *Entry;
mapcache_typ *CEntry;
mapcache_typ *CEntry2;

char File[MAX_PATH];

// Search for desired entry
CEntry = MapWin.Cache;
while (CEntry != NULL)
{
if (stricmp(CEntry->Name, Name) == 0)
break;

CEntry = CEntry->NextEntry;
}

if (CEntry == NULL)
{ // Couldn''t Find it, so load it.
if (MapWin.numCache == MAX_CACHE)
{
// Search for Least Used entry;
CEntry = MapWin.Cache;
CEntry2 = MapWin.Cache;
while (CEntry != NULL)
{
if (CEntry->LastUsed < CEntry2->LastUsed)
CEntry2 = CEntry;

CEntry = CEntry->NextEntry;
}

// Then Delete it.
CEntry = CEntry2;
DeleteObject (CEntry->hBitmap);
if (CEntry->PrevEntry != NULL)
CEntry->PrevEntry->NextEntry = CEntry->NextEntry;
else
MapWin.Cache = CEntry->NextEntry;
if (CEntry->NextEntry != NULL)
CEntry->NextEntry->PrevEntry = CEntry->PrevEntry;
else
MapWin.LastCache = CEntry->PrevEntry;
free (CEntry);
CEntry = NULL;
MapWin.numCache --;
}

// Now find the image name in the Dat List
Entry = Project.Dat;
while (Entry != NULL)
{
if (stricmp (Entry->Name, Name) == 0)
break;

Entry = Entry->NextEntry;
}

// If null, no such entry.
if (Entry == NULL)
{
// FIXME: paint the spot black.
return;
}

// Now Create Cache Entry;
CEntry = (mapcache_typ *)malloc(sizeof(mapcache_typ));
if (CEntry == NULL)
return;

// And load bitmap into it.
strcpy (File, Project.RootDir);
strcat (File, Entry->FileName);

CEntry->hBitmap = (HBITMAP)LoadImage(NULL, File, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (CEntry->hBitmap == NULL)
{
free (CEntry);
// FIXME: Paint Spot Black.
return;
}

// Finish Setting up cache entry;
CEntry->NextEntry = NULL;
CEntry->PrevEntry = MapWin.LastCache;
MapWin.LastCache = CEntry;
if (MapWin.Cache == NULL)
MapWin.Cache = CEntry;
strcpy (CEntry->Name, Entry->Name);

// Increment the cache counter.
MapWin.numCache ++;
}

// Get size of the bitmap
GetObject(CEntry->hBitmap, sizeof(Bitmap), &Bitmap);

// Create DC and put bitmap into it.
ImageHDC = CreateCompatibleDC (MapWin.DrawPadDC);
OldBmp = (HBITMAP)SelectObject (MapWin.DrawPadDC, CEntry->hBitmap);

// Copy the bitmap to the surface.
// BitBlt (GetDC(hWnd), X, Y, MAP_TILE_WIDTH, MAP_TILE_HEIGHT, ImageHDC, 0, 0, SRCCOPY);
StretchBlt(MapWin.DrawPadDC, X, Y, MAP_TILE_WIDTH, MAP_TILE_HEIGHT, ImageHDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, SRCCOPY);

// Remove DC.
SelectObject (ImageHDC, OldBmp);
DeleteDC (ImageHDC);

//Update CEntry last usage
CEntry->LastUsed = MapWin.Clock;
}


Any help is apreciated.

-Zims
Zimans, several suggestions:

After I saw your code, I decided to write a little program for you or anyone in the future who might want or need it. Just copy your source code onto it, and press converttohtml. Then just copy what it then displays and post it on this board. It formats the text to look as it is intended to on this board, and in the source box. It fixes the <>| symbols that many people forget about when posting code up on this board.

If you want, you can grab it here.

Also, I can't see anything immediately wrong with your code (although I haven't exactly stressed myself looking for mistakes), but in all honesty, I can see several optimizations you can make.

With Win32, it usually is not a smart idea to load your bitmaps on the fly. Load them in advance, check to make sure that they are there before you get into your loop. Cache the pictures before you start, and you should see a big jump in speed, and shouldn't need to error check as much in your PutImage function.
It is possible to only have to create and delete DC's, handles, etc once.

You have no error checking besides returning. If you have VC++, put breakpoints on the blt's and see what values for height and width you are getting.

You have a return out of the loop, but you have no error checking which covers the BitBlt to your main hdc.

Get rid of the nested loops.

How often are you calling WM_PAINT? It's usually a bad thing to redraw the entire window at once, at least with windows GDI. Try creating a child window within the main window that handles the map. Then you will have less to update, resulting in less flickering.

Edited by - Nytegard on June 13, 2000 11:06:21 PM
Thx for the prog, that will make things look alot neater.

The nature of my design is such that I can't pre-load the images on the fly. (This is a relatively all-round editor) So for me it's easier to load them into a cache as needed and keep them there till they expire. As for the error checking, I just haven't written it yet. The map window is a self-containing child window. The main part of the editor tells the mapedit section a few things, but other than that, it takes care of itself. The nested if's really aren't that bad. That whole huge chunk only really gets executed if the image was not already in the cache.

I apologize if I seem like I'm shooting down you suggestions. I thank you for trying to help me, and I know how hard it is to try and suggest stuff when you don't know all the details of how the system works.

I seem to have fixed my problem. Seems I can't just load the bitmaps into hBitmaps and use those. I have to create DC's, put the bitmap into the DC (I'm still not sure if SelectObject copies the bitmap or just makes reference to it. I think it copies it.) and then use the DC. No big deal really. As far as speed it seems to run pretty well, There aren't alot of cases where I will really have to update the entire screen. And the win isn't that big. (Alot of space taken up by list boxes, etc.)

For testing I resize the main window (Wich will force an update) and see how it redraws. A few times as I was repetedly resizing it it slowed down.. Not sure why but I don't think it will be a big deal.

on a side note:
There seem to be alot of pages on the web talking about DirectX and dealing with implementations, but there doesn't seem to be much in the way of dealing with the old windows GDI.. I guess it's because GDI stuff has been around longer...

Thx

-Zims

Edited by - Zimans on June 13, 2000 11:51:59 PM
have you considered putting a directX interface inside of your map editor interfaces? I did it pretty successfully in mine and i''d like to say that it renders pretty damn fast. Basically all you have to do is create a windowed DirectDRaw interface pointing to the window''s hwnd. if you use mfc like i am, it should be the m_hWnd variable under the CWnd class. Since the CWnd class derives the CDialog and CView classes, you can have a tile selection dialog with DirectX inside of it and also the editor view can have its own.

it works VERY well, i''d have to say... if you try it, i''d like to see it when you are done... mine will be done tonight ;-)

hope this helps,
david
--david@neonstar.netneonstar entertainment
I originally started out with a directx drawing interface, but I couldn''t get the blt to work.. (Most directx stuff I''ve done so far I do all my drawing in memory myself than copy it to the back buffer, not alot of directx involved) I wasn''t having much luck, so I decided "hey, it''s windows why not use GDI". I probably will scrap the GDI stuff, but it''s been quite a learning expirience.

BTW, I would like to see your editor. I''ll gladly share mine as soon as I get it, well, nearer to completion..

-Zims

This topic is closed to new replies.

Advertisement