Archived

This topic is now archived and is closed to further replies.

tile storing methods

This topic is 6351 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

hi im still working on this tileengine.. its pretty nice by now, created a little GUI to go with it and such.. until now I''ve been using a Tile class which loads a tile from a bmp file to a surface included in the class.. the class stores the filename so reloading can be done after a task switch etc.. I don''t like several hundred little tile files.. so im planning on developing a little nice TileSet class.. my question concerns storage methods.. in the tutorials they usually load one bitmap containing all the tile set to a surface, store the RECTs and blit directly from that surface.. or they use som sort of tileset file, in which the tiles are stored with a tile header and then the actual tile data, I guess their tileset class blits all the tiles (using bitblt or something) to one big surface and then use the above methods (with rects).. using dxsurface->Blt (or BltFast) to blit from the surface.. buuuut.. I''ve done some calculations.. and if I for example wants 256 tiles to be the maxtiles in each set.. I will need a surface (I use 64x64x16bit tiles) 2.097.152 bytes large.. I have a 4mb card (lame I know).. but say you use the program on a 2 mb card.. well the surface cannot be stored in vidmem.. so dx will place it in sysmen.. this sucks.. because then you won''t take advantage of the vidmen!!.. ALL the surface will be created in sysmen.. but if I on the other hand.. use the tileset file.. and loads the tileset to sysmen.. from there create one surface per tile.. it would take advantage of the vidmen available.. because if the giant surface is just one byte more than available vidmen it will be created in sysmen.. thereby not using the vidmen to just place SOME of the tiles in it.. (hope im not confusing you too much.. kindda tired :-).. but if you allocate the 255 tiles to 255 surfaces.. and the user then performs a taskswitch.. simple.. restore all the surfaces.. but the reload.. would be to slow to load all the tiles from disk.. but If I stored the entire tileset in mem.. and memcopied to the surfaces... but it would it not be bad programmering because I would have to same data in mem twice?!? help me out on this one you experienced tilers *lol* perhaps by telling me how YOU do it :-)

Share this post


Link to post
Share on other sites
In all honesty, my computer has 16megs or vidmem so I can still load all of the images into it and so havent really run into that problem yet on my home-pc, but i have tested my game on comps with only 2megs of vidmem and lower and I get extremely low frame rates, like 10fps in 1024x768x16, which is unnoticable except when using the smooth scrolling. As for trying to load as much of it as you can in vidmem, thats best with small files, cause if you have a large image file as you mentioned and its just slightly to large, the whole thing will go into sysmem, but also 1 large file is very nice and convient, but you might want to use stuff like civ2 does, dont put all tiles/units/objects ect.. all in 1 image, make a single image for the most used tiles and nothing else, and then load that first so it gets into vidmem, and then load the other less used tile images. Otherwise there isnt much you can do, a crappy computer is a crappy computer and can only play old crappy games.

As for task switching, i still lose all my surfaces and I am curious as to what the windows message is for that so I can
restore them.

Possibility

Share this post


Link to post
Share on other sites
ironically, i quit smoking two and a half days ago.

there are any number of things you can do to maximize performance of your tileset class. however, before you do any of them, you need to know if it is a necessary change, and measure how much performance is enhanced. if a greater speed increase can be accomplished elsewhere, do that instead.

putting something into video memory isnt always the most ideal conditions. Yes, blitting from sysmem to vidmem is costly, and you don''t want to do it more than you have to. however, sysmem to sysmem is pretty fast, if you cant get vidmem to vidmem, why not try that?

here''s an idea. keep a var (either in the tileset class, or global, or whatever) somewhere that keeps track of a "blit state". this will hold one of two values... BLIT_VIDMEM, or BLIT_SYSMEM.

when you try to load your tileset, first attempt to load it into vidmem. if it is successful, you set your blit state var to BLIT_VIDMEM

if it fails to be created in vidmem, create it in system memory intead, set your blit state var to BLT_SYSMEM. also, when this happens, create a surface in sysmem that is the same size as your screen.

here is some rendering pseudocode for you:

if(blitstate==BLT_VIDMEM)
RenderToBackBuffer()
else//blitstate==BLT_SYSMEM
{
RenderToOffscreenSurface();
BlitOffscreenSurfaceToBackBuffer();
}


with this, if your tileset is in vidmem, it will just render to the backbuffer, since vidmem->vidmem is best for transfers. if your tileset is in sysmem, it renders it to the offscreen surface you created in sysmem. this is much better than doing all of those blits from sys to vid. finally, you blit once and only once from the offscreen surface to the back buffer.

on systems with a lot of vidmem, it''ll run to its best capacity, but on a system without much vidmem, it wont just drag alot, since you are making the best for what you have.

Share this post


Link to post
Share on other sites
to TAN> thank you :-).. but is the idea of "a surface per tile" bad? will it cause performance problems? overhead? I probably only gonna need 200-300 tiles on this project.. probably less :-).. cause'' im actually not making a game.. im making an advanced chat/virtual community :-)

to Possibility:
cool this is the first time I (perhaps) are able to help someone on this forum :-).. this goes for taskswitching:
in your winproc.. check for the WM_ACTIVEAPP

LRESULT CALLBACK WinProc(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_ACTIVATEAPP:
{
Active = wParam; // Active is a global BOOL
}
}

with this code Active is only true when the app has FOCUS.
oki if we assume you call your GameMain() in the while-loop in winmain, do this:

while(Active)
{
...
GameMain();
...
}

this will prevent GameMain() from being executed when the application hasn''t got focus.
oki when user presses alt tab Active is set to FALSE and gameloop execution is paused.. but now the user activates the game again.. all surfaces are lost.. so all you got to do is to check for the DDERR_SURFACELOST return code (errorcode) in the first function which calls any DX calls capable of returning the code.. then you call RestoreAllSurfaces() on your DirectDraw object (assuming you''re coding for DX7, otherwise you have to loop trough all surfaces and use the Restore() method on each)
if RestoreAllSurfaces() returns DD_OK just reload all your bitmaps (good idea to implement an easy way to do this in your design :-).. I have a function in each of my classes which contain a surface with a loaded bmp on it called Reload()..

Share this post


Link to post
Share on other sites
quote:
Original post by smokes

cool this is the first time I (perhaps) are able to help someone on this forum :-).. this goes for taskswitching:
in your winproc.. check for the WM_ACTIVEAPP




Sorry if this is a stupid question or a stupid idea but: Is the WM_ACTIVATEAPP recieved before or after the surface(s) is lost?

I thought that maybe you could "backup" the tile surfaces to system memory just before the surfaces was lost. When your game became active again you could then restore them without the need to read from the disk and free the memory used by the backup again.

But then again this maybe used up too much memory? It may be needed by the program you are task switching to?

Regards

nicba

Share this post


Link to post
Share on other sites
smokes...

the idea of one surface per tile isnt necessarily a "bad" idea, but it can make tile management more of a problem, especially if you are going to come up with a "most commonly used" tile management algorithm, where the most heavily used tiles are in vidmem, and the lesser used tiles are in sysmem. this kind of thing can quickly turn into a resource management nightmare.

possibility & nicba...

yes, the WM_ACTIVATEAPP is the way to go, however, you probably want to use a scheme something like the following:

//globals
bool bActive=false;//true when this is the current app
bool bInitialized=false;//true when the data has been initialized

...(somewhere in the windowproc)

case WM_ACTIVATEAPP:
{
if(wParam)
{
bActive=true;
if(bInitialized)
{
RestoreSurfaces();//user defined function
}
}
else
{
bActive=false;
if(bInitialized)
{
SaveSurfaces();//user defined function
}
}
}break;

notes:
bInitialized is set to true during your initialization section, and it being true indicates that all of your dx objects have been instantiated.
bActive is used to suspend execution when the application is not active
RestoreSurfaces() should both restore the surfaces and the contents thereof (only necessary for vidmem surfaces)
SaveSurfaces() is something i havent tested, but you should be able to backup the contents of vidmem to sysmem right before the application loses focus. (by setting up some surfaces in sysmem temporarily and then destroying those surfaces in RestoreSurfaces()

Share this post


Link to post
Share on other sites
The way I restore surfaces isn''t like that approach at all, and works rather well, so I guess I Should share it with you fine folks here at gamedev.

With that approach, the function RestoreSurfaces() would have to go through every image you have and restore it, which I assume means you would have to add an image->Release() line every time you have a new item.

The way I have it set up is like so. I have a sprite class with all the basic functions, including Draw() and Restore() (and Load()) When you load a file it saves the filename in a filename variable for you.

Now in the Draw function, I have a call to Blt or BltFast()

if the blit routine doesnt return DD_OK, I check to see if it returned DDERR_SURFACELOST, if that is what it returned (Which is what it WILL return if you minimized and returned) it calls its own Restore() function and presto.

So, with this method, you dont even have to care about restoring your surfaces, it''ll take care of it for you by itself which is what I like about it. Load all the images you want, PLUS it''ll only restore surfaces that need restoring rather than restoring all surfaces no matter what.

So what do you think?



ByteMe95::~ByteMe95()

Share this post


Link to post
Share on other sites
ByteMe95 - That approach works well until you deceide to use resource files. Then you need to store two names, 1.) the name of the resource file and 2.) the name of the graphics file within the resource file.

Also, if your surface''s graphics are dynamic, maybe you add footprints or skid marks, etc. to the surface as the game is played. Then restoring from the original file does not work so well because you wipe out all the dynamic stuff.

I implemented a save/restore system in CDX just as described by TANS except it saves the surfaces as BMPs on the hard-drive. This is not ideal because of I/O is slow. I plan to make it an option to save to memory or disk. Saving to disk can be helpful for debugging because you can open the saved BMP and you are looking at the contents of your surface. Another option I plan on adding is a flag that says if you want system memory surfaces saved as well.

All the code is in CDX on sourceforge.com, and it is free!

hebertjo

Share this post


Link to post
Share on other sites
Byteme>>> I currently use your approach.. sort of.. RestoreAllSurfaces doesn''t involve me creating a loop iterating trough ALL surfaces and calling dxsurface->Restore() on them.. RestoreAllSurfaces is a DX(7?) function that does that for you.. you call it on the directdraw object and since this object is used in creating the surfaces it can obviously restore again :-). but hebertjo is right.. the bitmaps should be restored from sysmen.. not disk.. don''t expect user to be waiting for several minutes when all they want to do is check their ICQ''s :-) (example)

Share this post


Link to post
Share on other sites
Hey isn''t it just as bad to save all your surfaces to sysram? Cause if they''ve minimsed your app to do something else.. that something else may need the ram? You should theoretically free up the resources then take them again when you maximize, yes?

I''m building an Isometric RTS and it will load about 32 mb of graphics into sysram and use a syram back-buffer aproach (If you don''t know what this is search the board a bit) now my target system requirements are 64mb of ram but I''ll also have sound and map and unit data... so I won''t be leaving much room for other apps... it should free everything up and then load up again right?

Just my thoughts, haven''t actually implemented this yet...
- Ben

Share this post


Link to post
Share on other sites
Here''s a method I use which is configurable base on hardware available.

Tile/Object Surface caching:

Sometimes you have lots of vram at your disposal, othertimes you don''t. My idea is to take your most used graphics and place them in vram. All other less frequently used graphics go in system ram.

The basic idea is to improve performance by keeping surfaces that are more active in vram.

I''ve created a class that takes these surfaces and loads them into the appropriate ram type. You can play with cache sizes, locations etc. to tweak your performance.

The great thing is that as your walking through your map, regions of your map typically have graphics that are common, and as you leave those area''s the caching systems dynamically adapts to these changes.

You can take a look at my demo which implements these ideas.

http://www.geocities.com/SiliconValley/Modem/2514/index.html

Darrell

Share this post


Link to post
Share on other sites
Hmmm, i am getting stumped here, when I call
        
//-------- windows message handler --------------//

LRESULT CALLBACK
WindowProc (HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_ACTIVATEAPP:
// recover any lost surfaces

if (lpDDSPrimary->IsLost()==DDERR_SURFACELOST)
lpDDSPrimary->Restore();
break;
[/source]

it crashes!

But in my 3D program I can call the
[source]
if (lpDDSPrimary->IsLost()==DDERR_SURFACELOST)
lpDDSPrimary->Restore();


and it works just fine,
so why does it not like the IsLost() function in my 2D program?

also, it seems that the WM_ACTIVATEAPP is given All the time, not just when the program is made active again, is there a 1 time call thing when the program is made active again, and not a constant one for the when the program is currently active, if you know what I mean.

Possibility





Edited by - Possibility on July 21, 2000 7:42:04 PM

Share this post


Link to post
Share on other sites
I have never had a case where only a stray surface was lost, and I really don''t
know how such a case could happen when your program has exclusive full-screen
control. However, this could be different for windowed-mode. But, in my
experience, all the surfaces which are being used become lost when your DX app
loses focus. So, it seems logical to restore them all in one place at the
same time.

I also think you would have better control over re-creating surfaces that
dynamically change throughout the course of the game. Although, I think it
would be nearly impossible to re-create surfaces that are based on random number
generators. That''s if you don''t save -everything-.


Wayfarer

Share this post


Link to post
Share on other sites
I know that wayfarer, I just put up the IsLost and Restore for a single surface because I didnt think anyone wanted to read my entire list of 300 surfaces, and I also tried the RestoreAllSurfaces and that just crashes aswell, I cant figure out why.

Possibility

Share this post


Link to post
Share on other sites
Sorry Possibility, I was responding to someone else''s post, not the one
above.

WM_ACTIVATEAPP is called whenever your app gets focus, which means it probably
gets called when your app starts up. Most likely before you even have DX
created. So, you have to make sure your app is initialized first before trying
to restore anything. You can use a BOOL flag for checking for this like TAN did.
To be absolutely safe, destroy and release everything, including your primary
and back buffers. This is what the SDK docs do.

For the other posts:
I can think of some good cases where saving the entire screen surface
(for full-screen apps) to your hard disk might prove useful. For example,
when the player pauses the game or pops up an in-game option menu. If the
game action is frozen and stil visible in the background, you could save
the entire screen surface. Since the screen has an image on it that is
obviously something you can''t simply load from your bitmap resources, you
would probably just get a black screen if you didn''t save it.


Wayfarer

Share this post


Link to post
Share on other sites
poss..

as wayfarer pointed out, WM_ACTIVATEAPP is called whenever an application gets the focus. when you create a window, a number of messages are immediately sent to the message handler, including WM_CREATE, WM_ACTIVATEAPP, WM_PAINT, and a few others.

in most program''s i''ve written using DirectX, i create my window before i do my directx initialization, so, when you''re code hits this code:

case WM_ACTIVATEAPP:
{
if(lpdds->IsLost()==DDERR_SURFACELOST)...
}

lpdds is going to be NULL, and so, the call will be invalid, since no valid IDirectDrawSurface7 object exists at memory location zero.

two ways you can counter this problem.

one: you can call all of your directx initialization in response to the WM_CREATE message. WM_CREATE is called before WM_ACTIVATEAPP is.

two: you can check to see if directx has been initialized before trying to restore your surfaces. in the case of directdraw, you should just be able to check that your IDirectDraw7 pointer is non-null.

Share this post


Link to post
Share on other sites
Now that I think about it, it might be better to simply allocate some
temporary system memory instead of saving a surface image to disk. This
way you wouldn''t have to bother with stray files from possible program
crashes.


Wayfarer

Share this post


Link to post
Share on other sites
possibility:
why don''t you just use a boolean like I suggested? :-)
this way you would only use the message handler to alter the state of the bool.. which should be accessible at any time during execution.. then check your DX return codes.. if the code is DDERR_SURFACELOST restore.. and remember only to execute your gameloop when the "Activate"-bool is true :-)

Share this post


Link to post
Share on other sites
Yah, thats actually a good idea, but unfortunately (or fortunately depends on how you look at it, hehe) I will be going through the complicated task of making my game multiplayer, so i wont be using an activate bool as you suggested for the main game loop because in multiplayer you need to have the game continuously going, but I have worked out a similar approach, thanks for the help.

Possibility

Share this post


Link to post
Share on other sites