+ Reply to Thread
Results 1 to 6 of 6

Thread: Shrink that memory footprint!

  1. #1
    Moderator
    Join Date
    Jul 2004
    Location
    Zürich, Switzerland
    Posts
    1,966

    Default Shrink that memory footprint!

    This post continues the spirit of the classic Dexterity threads Shrink that installer! and Shrink that installer Part II. I want to share my recent experiences with the memory footprint of a game.

    Why
    I got the ocassional Betty's Beer Bar crash report where the game failed with an "out of memory" error. At first, since the game is 5 MB compressed, I thought the machines were already short of memory, but after adding logging code for that, I found out that was not the case - BBB was dying even when it had 80-90 MB free memory at start.

    So I wrote a little script to calculate the size of every image in the game; to my amazement, BBB's 4.8 MB of datafiles packed 96 MB worth of pixels! Not counting the images that are generated at runtime by pasting some images over another images to make the animation frames.

    But it's worse : I implemented lazy loading of images, and in compliance with the KISS philosophy that guides my life (Keep It Simple, Stupid), I never unloaded them - it's some sort of very simple cache. Of course, I didn't realize it was 96 MB.

    What
    My other problem is that, also because of being lazy, all the frames of a given animation have the same size, regardless of the actual size of that particular frame, for easier compositing. Of course, empty and transparent pixels compress very well, that's why I could pack that 96 MB into less than 5, but they are fully expanded in memory.

    So what I did was adding yet another preprocessing step to my already complex build process. Every image is autocropped, and its original size and position are stored in a spec file. At load time, this spec is read, and the Image class (which is over 2000 lines of code - and blitting is done somewhere else) has been modified to have the notion of a "virtual canvas" - externally, the image behaves exactly as if it had the original dimensions.

    The tricky part is adjusting the blitting wrappers to correctly handle virtual over physical, physical over virtual, and virtual over virtual. The "over virtual" cases may resize the physical size of the image while maintaining the virtual size if the image blitted over it would use space that was previously cropped.

    Conclusion
    All in all, doing this took about 4 hours including testing, and all the images now take 38 MB. It's still a lot, but it's the minimum possible, and only 40% of the original size. It also probably loads faster and compresses slightly better.

    Lessons learned :
    - Do YOU know the memory footprint of your game?
    - Having cleanly designed classes and respecting encapsulation lets you do this kind of complex thing without touching even one line of client code.

    I hope this was useful for someone!
    Gabriel Gambetta
    Google Zürich - Formerly Mystery Studio

  2. #2
    Moderator
    Join Date
    Jul 2004
    Posts
    365

    Default

    Wow... I've also tried to shrink memory footprint, but in another method: blitting straight from packed data. apperantly, the unpacking was reasonable (speed/FPS - wise), but the blitting method (Lock() and Unlock() ) was too slow for my game :-\

    so now i'm just loading the data from my own packed resource file.


    also, a question about measuring memory footprint: does the task manager (winXP) memory usage gives the result i'm looking for? does it include video memory along with system memory?

  3. #3
    Administrator
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,769

    Default

    Good point. Our automatic art processing pipeline crops all frames and adjusts the “hot spot” accordingly. As you point out, this is especially important for animated sequences which have different sizes in each frame. We also RLE encode all the images to avoid storing all the transparent pixels around the edge. Unless your art is rectangular, you probably have a lot of waited memory storing pixels that will not be drawn. RLE encoding the pixels can not only save memory but also speed up draws.

    If you have a lot of sound effects you may want to keep them compressed in RAM. I am assuming you use OGG or MP3 or some kind of audio compression to reduce the download size. Some games decompress these at install time. Others decompress them into RAM when the game loads. But if you do a streaming decompression as it is played you save a lot of RAM.

    You can also waist RAM if you allocate a lot of little objects on the heap. If you have thousands of sprites or particles or linked list nodes or any other objects, they probably waist 8 bytes each unless you specially allocate them in large arrays of some kind of memory pools. 8 bytes isn’t going to matter unless you do a whole lot of them. But it is especially wasteful if the object you are allocating is only 12 bytes big itself. As a general rule, assume all calls to new or malloc will add 8 bytes of overhead in addition to the memory you are allocating. To avoid (or reduce) this problem, allocate large chucks of memory and manage it yourself IF you can do it with little or no overhead. I have a “memory pool” system which stores all same sized allocations in the same pool. This eliminates the need to store the size of the allocation with each allocation. This works well for many small objects all the same size.

  4. #4
    Member
    Join Date
    Aug 2004
    Location
    Poland
    Posts
    66

    Default

    Gilzu:nope, it gives you only system memory

    You can read video memory data from directx

    And You can keep packed data in system memory and unpack it when you copy data to surface/texture (i assume you want to use 2d/3d acceleration for blitting, but still keep image size small in sysmemory)

    So what you do is:
    1.load all packed data into sysmem
    2.as data is needed you unpack it/transfer it to vidememory (and images that aren't needed are pushed out of vidmem)

    This should run at a very good speed (assuming you will fit with data into vidmem, which you obviously do)

    For transparent images use RLE compression, on most(95+%) images decompression will be MUCH faster then normal transparent blit and you can store your data in systememory in compressed format.

  5. #5
    Moderator
    Join Date
    Jul 2004
    Location
    Zürich, Switzerland
    Posts
    1,966

    Default

    Gilzu : That's a good idea, although it loads the CPU. I'm doing a lot of alpha blitting so the CPU is already having a good workload. Besides, I'm doing the really low level stuff with SDL, so I don't actually go that low.

    James : Very good tips. SDL already does RLE compression of alpha, and even when I had full size images, I was blitting only the non-transparent part (I was using them as cropped anyway, now I actually got rid of the unused pixels). OGG streaming would be great, but SDL doesn't do it yet...

    Memory pools are high on my todo list... I never got around to do it. I know my linked lists and hashes would benefit from this.
    Gabriel Gambetta
    Google Zürich - Formerly Mystery Studio

  6. #6
    Senior Member
    Join Date
    Jul 2004
    Location
    Isle of Wight, UK
    Posts
    3,773

    Default

    - Do YOU know the memory footprint of your game?
    Yup. My art is stored as RLE and thats how I draw it. If the game needs to composite two images, it does indeed make a 16 bit image but I try to keep this to a minimum - it shouldn't be too much of a problem to build these images as you draw them by overblitting. Even older computers have horsepower to spare for stuff like this.

    The reason I store and use my data as RLE is important though - with the way caching systems work these days, it's way quicker to blit large art around than to do big lookups from essentially a 2D array of pixels. You still thrash the ram but not as much and it makes a big difference. This obviously doesn't apply to people using DX to do 2D games with, but maybe they should consider dumping it. I'm all software with a final blit to the desktop and you'd be amazed how much even a 1Ghz pc can do these days - sure a shitload more than a flaky old TNT using 'acceleration'. You can also do these alpha blends and other mixing effects mentioned in another thread too. For 2D, DX has had it's day imo - any video card that can beat software blitting will be in a machine for which neither matters (in 2D!) anymore...

    - Having cleanly designed classes and respecting encapsulation lets you do this kind of complex thing without touching even one line of client code.
    Nope! (I'm trying to keep off *that* subject again)
    I do use a C library with a platform independent API that separates the games from the engine though - I think most people do these days and of those that don't it's only because they've not been bitten enough times yet
    Regards,
    Paul Johnson

    [Great BIG War Game: iOS | Android] [Great Little War Game: iOS | Android] [Fruit Blitz: iOS | Android] [Yachty Deluxe: iOS | Android]

+ Reply to Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts