SpriteKit SKTextureAtlas, Terminated Due to Memory Pressure while loading texture

1

Similar to the SpriteKit Featured Game "Adventure" from WWDC, I am try to load my background image via tiles. I have created a Texture Atlas that contains 6,300 "tiles" that are each 100x100 pixels in size. The complete background image is a total of 30,000x2048 (for retina displays). The idea is that the background will move from right to left (side-scroller). The first column and the last column match so that they seem continuous.

When the application runs, it loads my initial loading screen and title images and spikes to 54MB in my memory tab with a CPU usage of 16%. This stays the same as I navigate through the menus until I choose my level, which tells a background thread to load the level assets (of which contains the aforementioned background image). The entire .atlas folder shows to be only 35.4MB. I don't believe that this is a problem since the Adventure .atlas folder (from WWDC) shows to be only 32.7MB.

Once I select the level, it loads approximately 20 of the textures in the .atlas folder before I start receiving memory warnings and it crashes the application. I've checked in Instruments for leaks and it doesn't show any memory leaks. I don't receive any compiler errors (not even the EXC_BAD_ACCESS one). I've looked at my device console and have found a few lines of where the application crashes, but it doesn't look to make much sense to me. I've also checked for Zombies, but haven't seemed to find any.

CoreLevel.m

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    // Used to determine time spent to load
    NSDate *startDate = [NSDate date];

    // Atlas to load
    SKTextureAtlas *tileAtlas = [SKTextureAtlas atlasNamed:@"Day"];

    // Make sure the array is empty, before storing the tiles
    sBackgroundTiles = nil;
    sBackgroundTiles = [[NSMutableArray alloc] initWithCapacity:6300];

    // For each row (21 Totals Rows)
    for (int y = 0; y < 21; y++) {

        // For each Column (100 Total Columns)
        for (int x = 1; x <= 100; x++) {

            // Get the tile number (0 * 32) + 0;
            int tileNumber = (y * 300) + x;

            // Create a SpriteNode of that tile
            SKSpriteNode *tileNode = [SKSpriteNode spriteNodeWithTexture:[tileAtlas               textureNamed:[NSString stringWithFormat:@"tile_%d.png", tileNumber]]];

            // Position the SpriteNode
            CGPoint position = CGPointMake((x * 100), (y * 100));
            tileNode.position = position;

            // At layer
            tileNode.zPosition = -1.0f;
            tileNode.blendMode = SKBlendModeReplace;

            // Add to array
            [(NSMutableArray *)sBackgroundTiles addObject:tileNode];
        }
    }
    NSLog(@"Loaded all world tiles in %f seconds", [[NSDate date] timeIntervalSinceDate:startDate]);
});

This is what seems to pertain to the crash from the Debug console:

com.apple.debugserver-300.2[9438] <Warning>: 1 +0.000000 sec [24de/1807]: error: ::read ( -1, 0x4069ec, 18446744069414585344 ) => -1 err = Bad file descriptor (0x00000009)
com.apple.debugserver-300.2[9438] <Warning>: Exiting.
com.apple.launchd[1] (UIKitApplication:tv.thebasement.Coin-Voyage[0x641d][9441]) <Notice>:     (UIKitApplication:tv.thebasement.Coin-Voyage[0x641d]) Exited: Killed: 9

I don't have enough reputation to post images so here is a link to a screenshot screenshot of my allocations in Instruments:

http://postimg.org/image/j17xl39et/

Any help and advice is much appreciated! If I've left out some pertinent information, I'm glad to update.

ios
objective-c
memory
sprite-kit
sktextureatlas
asked on Stack Overflow Feb 13, 2014 by Matt Wagner

1 Answer

3

The file size of an image file (PNG, JPG, atlas folder, etc) tells you nothing about the memory usage.

Instead you have to calculate the texture memory usage using the formula:

width * height * (color bit depth / 8) = texture size in bytes

For example an image with dimensions 4096x4096 pixels and 32 bits color depth (4 bytes) uses this much memory when loaded as a texture (uncompressed):

4096 * 4096 * 4 = 67108864 bytes (64 Megabytes)

According to your specs (6,300 tiles, each 100x100 pixels, assuming they're all unique) you're way, wayyyyyy above any reasonable limit for texture memory usage (about 1.5 Gigabytes!). Considering the atlas size of 35 Megabytes (which is huge for an atlas btw) and assuming a mere 10:1 compression ratio you may still be looking at 350+ Megabytes of texture memory usage.

answered on Stack Overflow Feb 13, 2014 by LearnCocos2D

User contributions licensed under CC BY-SA 3.0