Game Development Community

Changing an image in an ImageMap

by baylor wetzel · in Torque Game Builder · 09/01/2009 (3:48 pm) · 6 replies

In a couple of earlier posts, people asked how they free memory in Torque. For some games (like my fashion game) that have a lot of graphics, the ability to unload images from memory is a make-it-or-break-it feature. Unfortunately, it doesn't look like anyone's posted an answer

So i decided to try something different - if i can't force Torque to free memory for an image, what if i keep a small pool of imageMaps and just swap the image files in them? It took me a while to figure out how to do that (there's so much semi-advanced stuff that isn't documented that it's normally hard to know how to proceed) but i have the code working now:

/**
Change the image in an ImageMap.
%imageFQN is in the format "game/data/images/imageNameWithoutExtension"
*/
function changeImage(%imageMap, %imageFQN)
{
	%imageMap.imageName = %imageFQN;
	%imageMap.compile();
}

The compile was the important part i was missing. Now i can instantly swap images without actually loading more ImageMaps

How much memory does it save? i have no clue. i've also asked in the past how one can measure the amount of memory TGB is using, especially texture memory. i never got a response and i was unable to figure it out myself. If i run the game through a debug EXE, when i originally load my images, i get console messages such as "PlatformMemory: allocating new page, total bytes allocated so far: 392,425,192 (total bytes in all pages=452,984,896)". When i swap images, i don't get any messages. i don't know if that means no more memory was allocated or if i didn't do whatever it is that triggers that message to be printed. But so far nothing bad has happened - everything appears to draw correctly and it doesn't crash any more than normal

Hopefully this helps someone (or causes someone to yell "you fool, you just trashed your memory!" and tell me how to fix it)

#1
09/01/2009 (4:41 pm)
I'm still a very raw beginner so I don't know if you're truly unloading the image when you do what you posted above. I'd guess that you're right and that your method is only loading one image at time. As I understand it, torque loads images into memory whenever it execs the datablock where the image is contained and keeps it around for a while depending on the setting in that datablock so here's how I handled loading images from memory.

So if you have,

new t2dImageMapDatablock(BigImage1ImageMap) {
      imageName = "~/data/images/BigImage1.png";
       //... Other stuff
      preload = "1";
      allowUnload = "0";
   };

   new t2dImageMapDatablock(BigImage2ImageMap) {
      imageName = "~/data/images/BigImage2.png";
       //... Other stuff
      preload = "1";
      allowUnload = "0";
   };

Torque will load both of those big images when it loads the file that these datablocks are in. Your method of having one datablock and then changing the imageName works to cut down the memory but there is another way.

I actually don't think that changing preload to 0 does anything (at least it didn't seem to have any effect when I changed it) but if you change allowUnload to 1, you can delete the datablock later and it will free up the memory. So the solution, I ended up with in my game was to separate the image datablocks into separate files depending on what level they were used in and exec that file when I was loading the level. In your case, it doesn't sound like that would work very well but you might have been able to do something like this:

function LoadNewImage(%imageMapName, %imageMapPath)
{

   %newImage = new t2dImageMapDatablock(%imageMapName) {
      imageName = %imageMapPath;
      imageMode = "FULL";
      frameCount = "-1";
      filterMode = "SMOOTH";
      filterPad = "0";
      preferPerf = "1";
      cellRowOrder = "1";
      cellOffsetX = "0";
      cellOffsetY = "0";
      cellStrideX = "0";
      cellStrideY = "0";
      cellCountX = "-1";
      cellCountY = "-1";
      cellWidth = "0";
      cellHeight = "0";
      preload = "0";
      allowUnload = "1";
      compressPVR = "0";
   };

   $globalContainer.add(%newImage);

}

Then when you'd want to delete it later, you'd find it in $globalContainer and call delete. I'm pretty sure that it works as I was using lots and lots of memory in my game and crashing but when I separated all the images I used into different datablocks that I loaded into a SimGroup and then deleted when I started the next level, I used a lot less memory. Here's a post I found quite helpful when I was having these issues [url] http://www.garagegames.com/community/forums/viewthread/94193 [/url]
#2
09/01/2009 (4:44 pm)
I realize from your previous posts that you know all this stuff already but it might be useful for someone stumbling upon it from a google search.
#3
09/01/2009 (6:12 pm)
I've also tried to put some brain power into if the memory used for images can be released after removal from the scene.

I'm looking at what they did here to see if the source changes can be applied to images as well.

Insight? Opinions?

@Baylor, I like your approach. Can we get memory usage in the debug header? Sorry, I'd check myself but I don't have tgb on this machine.
#4
09/01/2009 (8:06 pm)
Quote:
if you change allowUnload to 1, you can delete the datablock later and it will free up the memory. So the solution, I ended up with in my game was to separate the image datablocks into separate files depending on what level they were used in and exec that file when I was loading the level.

Nate, so deleting the datablocks works for you? i never had any confirmation from anyone that this worked and since i don't know of any way to get memory info within the game, i have no way of verifying that the memory is freed. i read one response on another thread from a GG employee that said deleting a datablock doesn't mean your memory is freed (and i believe said there's nothing you can do in script to free memory). i've seen the issue talked about several times with no one else from GG weighing in so i think we're on our own on the whole memory usage thing :(

Patrick, the debug banner has a lot of stuff but none of it appears to be memory related. Of course, i don't know what things like BinReloc, ParFree, ActRender, etc. are so maybe i'm wrong

If i wanted to be smart about it, i'd write something to test this and go find some tools to do so but i really would rather write a game than an engine right now. i suspect that, in the time it took me to learn the internals of the engine (for the memory problem, my unanswered render to texture question, my unanswered direct access to pixels question, the unresolved bug with garbage displaying on game loading, etc.), i could just write my own (obviously smaller) engine in SDL or somesuch

#5
09/01/2009 (9:29 pm)
Deleting the datablocks worked for me. I couldn't run my game on the iPhone because loading the images for 2+ levels would be more than my device could handle. I verified this by using xcode's memory tool and watching the total allocated memory. I used the technique in the link in my first post and my total memory use dropped enough so I could run my game. I load up the image datablocks when the level starts and then delete the datablocks when the level ends.

As for not being able to free memory, I can't imagine any game being able to run on the iphone without that feature. There's more graphically intensive games out there which use torque and there's no way that they can load all possible images at once.
#6
09/05/2009 (11:06 pm)
i replaced all the image loading stuff with the approach outlined in the original post (although Nate's idea might be smarter; haven't tested it). Everything works great. There was one problem - sometimes, i'd get lines on the right and bottom of an image. This happens, it turns out, when the earlier image is a power of 2, causing TGB to disable filter padding, and it's the lack of filter padding that causes those lines to show

i couldn't figure out how to fix that in TorqueScript so i ended up commenting out a single line of code in the engine - t2dImageMapDatablock.cc, calculateFullImageMap(), line 737 (or so) "mfilterPad = false;"

Now everything works well enough that i'm actually going to try deploying to the iPhone again (we had so many problems with iTGB -and probably memory- that we decided to make the game PC only)