Game Development Community

SuperFun Memory Management Question! (really, I mean it!)

by Warthog · in Torque Game Builder · 07/31/2008 (10:53 am) · 10 replies

Okay, this may not be super fun (though it is to some of us code geeks), but it is really important.

We have been trying to manage our memory requirements in our game and based on all the research we have done in the discussion forums (thanks to everyone who makes this area so great!) this is what we understand to be true, but we don't think it is:

When you create an imageMap datablock and you set preload to 0 (off), TGB does NOT load the graphic file into memory (neither main memory or video memory) at that time. Our understanding is that only when you set a sprite to use that imageMap does TGB actually load the graphic file from disk and put it in main memory. And then when that sprite is actually about to be visible in the window, TGB pulls it into video memory.

Can anyone please confirm or correct this? Melv, Melv are you out there?

The reason that we think this is not correct, is that we are running a giant flipbook animation at the start of the game to tell our opening story. By flipbook I mean that we are taking a static sprite and changing the imageMap in it a few times per second to create this animation. Because the animation is 800x600 this style of animation is required for all the obvious memory reasons. So, we have a few hundred frames, and we create a sweet little "while" loop to load the imageMap datablocks and we have the preload option set to 0 (off) and we have allowUnload set to 1 (on).

As we understand TGB this should NOT load the several hundred graphic files. AND YET, we find that when we run this code, windows runs out of virtual memory and TGB crashes.

IF loading an imageMap datablock with preload set to off doesn't load the graphic, then what the heck is going on? Melv? Anyone? Bueller? Bueller? Anyone?

I would love some clear documentation on how the memory management system of TGB actually works. Please?

We have a variety of places where we are counting on this understanding of how and when imageMap databolcks load the graphic into memory, including a very clever routine that only creates the background staticSprites for the game right before they appear on screen as it pans and then safeDeletes them after the camera has passed by. By everything we know from the forums, this should be a nifty way to lower our memory usage. Right? I hope?

Help, as always, would really be appreciated. Plus I think many would like to know for certain what is going on inside this TGB that we love so much! (and love it I do!)

Thanks,
Andy

#1
07/31/2008 (11:17 am)
I'll be corrected if I'm wrong but I don't think TGB can handle hundreds of giant sized images at once. You may want to look into the theora video object if you need fullscreen "flipbook animation".

I agree with you that clear documentation on the memory management system would be helpful.
#2
07/31/2008 (11:22 am)
Hi Joe, thanks for the tip, I'll look into it. I'm not asking TGB to handle hundreds of giant images at once, only one at a time is ever assigned to a staticSprite. Based on what we know, it should work. But maybe we don't know what it's really doing...

Andy
#3
08/04/2008 (2:52 pm)
Is everyone on vacation or does no one really know how memory works in TGB? I thought for sure someone on this forum would have a real clear idea of how torque handles this stuff. Melv? Any employees out there?

We are working around the problem by loading short sequences of frames at a time, but we still really need to know if the assumptions above are correct, or does TGB actually load the graphic when the ImageMap datablock is created, even if preload is set to false? And if the assumption is correct, why does the program choke reading a lot of large imageMap datablocks that are NOT set to preload.

Anyone out there? Melv? Someone must know..

Thanks,
Andy
#4
08/04/2008 (6:50 pm)
Well I was trying to find an article on the site talking about that (and I swear there is one), but I couldn't find it. But heres some on the general topics of t2dImageMapDatablock(s) and texture memory usage.

www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=10656
tdn.garagegames.com/wiki/2D_Art_Tips:_Learn_From_My_Mistakes
www.garagegames.com/blogs/53436/12318

Your best bet is to load up visual studio and debug it.
#5
08/04/2008 (10:39 pm)
Hi James, thanks for the input!

The problem is, we did our homework and read all of those sources you cite and several others before coming up with our strategy. However, I think those sources are either wrong or incomplete. Based on all of these sources, we should be able to load an almost unlimited number of imageMap datablocks as long as we set preload to off and don't assign the imagemap to a sprite. But we found out that isn't true. I think that TGB either loads the graphic into memory despite what preload is set to, or perhaps it is allocating the memory now for that time when the graphic will be needed.

Or, maybe the preload option only affects when the graphic is loaded into video ram and not when it is loaded into main memory?

The problem is, no one seems to know (except maybe Melv?). This is an essential piece of information for anyone designing a large game, I would think. It certainly is important to us.
#6
08/05/2008 (7:58 am)
Hi,

I just stepped through the t2dImageMapDatablock loading code with the debugger and found something interesting that could be considered a bug. We know that TGB's imagemap system "packs" the original images' frames into a texture, possibly saving some space and apply the filter pad if needed. And here it gets interesting: To determine what frames there actually are TGB loads the image file from disk and analyzes it. It calculates the source-frames-to-destination-frames mapping and then copies the frames from the source image from the disk to a newly generated image in memory which then is used to create a opengl-texture from it.

If preload is set to true the whole process runs through when the game starts. If preload is not true, the process stops after calculating the source-frames-to-destination-frames mapping, it does not dynamically generate the texture. And here is the deal: It does NOT UNLOAD THE SOURCE IMAGE after generating the mapping. So for every t2dImageMapDatablock in your project there will be at least one bitmap loaded in main memory at start-up. TGB will later unload the source image when allowUnload is true and the image has been loaded once and then unloaded but in your case that is to late.

I guess this could be fixed easily with just one call to unloadSrcBitmap() in the right place. If you've got the source code I can take a closer look at it and send you the changes if you want.

-Michael
#7
08/05/2008 (9:51 am)
Hi Michael,

Very cool! So I was right to suspect that the program is loading the image and holding it. This, of course, leads me to another question: Is this a bug or is it holding the srcBitmap for a reason? Do you know if when you finally use a graphic (that has not been set to preload) does it hit the disk again or does it read the srcBitmap that it has been hanging onto in memory?

And yes, we have the source code, and I would be very happy and appreciative to take you up on your offer to take a closer look and send the changes needed to fix this bug. I have not touched the source up to this point and was hoping I wouldn't have to, but this may be the time.

Thanks,
Andy
#8
08/05/2008 (11:04 am)
I would also very much appreciate seeing this source code. If possible, could you post the code changes into this forum? (Or submitted as a resource).

Failing all that, I'd be happy with an email.

Thanks for bringing this up and for all who are investigating.
#9
08/05/2008 (2:13 pm)
Ok, here is my take on this:

In t2dImageMapDatablock.cc in the calculateFrames()-method you should have this code section (about line 560):
// Are we preloading?
    if ( getLockReference() > 0 )
    {
        // Yes, so are textures loaded?
        if ( mTexturesLoaded )
        {
            // Yes, so unload textures.
            unloadTextures();
        }

        // Yes, so load Textures.
        CHECK_IMAGEMAP_ERROR( loadTextures() );
    }

    // Calculate Total Time Taken.
    mTotalTimeTaken = (Platform::getRealMilliseconds() - startTime) / 1000.0f;

Try to change it to this:
// Are we preloading?
    if ( getLockReference() > 0 )
    {
        // Yes, so are textures loaded?
        if ( mTexturesLoaded )
        {
            // Yes, so unload textures.
            unloadTextures();
        }

        // Yes, so load Textures.
        CHECK_IMAGEMAP_ERROR( loadTextures() );
    }[b]
    else
    {
        unloadSrcBitmap();
    }[/b]

    // Calculate Total Time Taken.
    mTotalTimeTaken = (Platform::getRealMilliseconds() - startTime) / 1000.0f;

This unloads the source image. This will also mean that the src image has to be loaded from the disk when it is actually used. But that would also be the case for any image with preload disabled and allowUnload enabled after it has been used at least once and then all references to it cleared.

Give it a try. I have only tested it minimally but it should work fine.

Something else that seems rather strange to me is that preload sets the mLockReference to 1 in t2dImageMapDatablock::onAdd(). This will prevent the textures from being unloaded EVER--even if allowUnload is true.

-Michael
#10
08/06/2008 (11:15 am)
Some great finds Michael... I'd be interested if anyone makes these changes and has some testing with these changes, maybe some perf tests to see how much of a difference it makes.