Search code examples
iosiphonecocos2d-iphone

Massive memory consumption with cocos2d sprite sheets


Per the cocos2d best practices, I've packed my key assets for my game into 4 separate sprite sheets. FWIW, I created these sheets with TexturePacker and have crunched down the resultant .pngs with TinyPng. The largest sprite sheet of the 4 (which contains my dialogs) is 375KB large and 2048x2048px in the -hd variation (aka @2x).

I've been noticing memory issues in my game, so I booted up the Allocations tool in Instruments. Sure enough, the Main scene was using ~250MB of memory on retina devices (~95MB on non-retina devices). Woah! Huge! Even worse, there is fairly significant lag on older retina devices (e.g., the iPhone 4).

The first thing I did was to remove one of the sprite sheets, to see if this was the problem. Sure enough, simply not loading a single sprite sheet dropped retina memory by 60MB. Considering the sprite sheet in question is only 268KB in filesize, I'm shocked that this is such a big problem.

Here's the code I'm using to "preload" a sprite sheet (during the app startup):

- (BOOL)cacheSpriteSheet:(NSString*)sheetName 
{
    if(!sheetName) 
    {
        return NO;
    }
    NSString *fpSheet = [[AMAssetManager sharedManager] pathForSpriteSheet:sheetName];
    if(fpSheet) 
    {
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:fpSheet];
        return YES;
    }
    else 
    {
        Warn(@"NULL sheet for %@",sheetName);
        return NO;
    }
}

Question: why are my sprite sheets consuming so much memory, and how can I fix it?

Things I've tried so far:

  1. [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444] helped to reduce memory footprint by ~20%, but I can only use it with some of the sprite sheets (others have, for example, radial gradients, which don't play nice).
  2. [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames] doesn't seem to help (memory footprint remains the same)

Solution

  • The file size has nothing to do with anything when it comes to 'in core' size. A pixel is a pixel is a pixel and most likely, each and every one of those 2048x2048 pixels take 32 bits. Your options are to :

    • split your sprite sheets, and load 'just in time' when you need the sprites.
    • Dont trust instruments when running on the simulator. To be certain, use

      [[CCTextureCache sharedTextureCache] dumpCachedTextureInfo]

    to know how much memory is really consumed by your sprites.

    • Memory allocations on simulator will give you very little useful indications. Build your release version, target a device, and monitor process size. That is more likely what will be the decisive factor before getting the dreaded memory warning.
    • Dont use instruments/simulator to gain any appreciation of performance and user experience. They lie. Use the lowliest device you plan to support with your app as the litmus test
    • Even if you remove unused sprite frames, if a single sprite frame (used) refers to a texture, it will still be retained.

    Read this very interesting article to grasp textures vs memory and the required trade-offs.