The iPhone has an extremely limited amount of RAM (especially for doing 3D work). During development of the latest 1.1.0 release of StarSmasher I ran up against the glass ceiling on the RAM quite a lot. StarSmasher was only using about 20 MB of RAM at peak levels but this was enough for the app to receive low memory warnings from the OS. I don’t load that many textures, but the menu screen textures where full resolution 512×512 jpegs and I wasn’t doing any kind of unloading when they weren’t being used. Ultimately, this caused the app to crash after a couple of levels. I had written it this way initially to make the app feel snappier (because I figured 20 megs wasn’t that bad) and because I’m learning OpenGL as I go and didn’t fully understand the texture management.
Now I’ve implemented just in time texture loading for the menu screens that checks if the texture is loaded on draw and loads it if necessary. When a menu screen is exited (for instance by tapping the “done” button) I unload the texture (with glDeleteTextures). Below is the TextureLoader class I wrote to manage my textures.
I’m not sure if this is or isn’t a good way to do this, but you are welcome to use it and/or make suggestions. Also, because of the NDA, there is one line that is SDK specific that I have removed. It is:
textureImage = getCGImageForImageNamed(name);
in TextureLoader.m. You will need to replace this line with the way to get a CGImage for whichever platform you are using. For Mac OS X look at this page. For the iPhone SDK refer to Apple’s documentation and example code. Also you will need to #import the appropriate libraries in TextureImage.h to load textureImage.
Enjoy… and again, I welcome suggestions, I’ve still got loads to learn about graphics development.
This stuff is licensed by:

TextureLoader.m by John Bowers, espressoSoft.com is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.
TextureLoader.h
//
// TextureLoader.h
// Starfield
//
// Created by John Bowers on 8/3/08.
// Copyright 2008 John Bowers. Some rights reserved.
//
@interface TextureLoader : NSObject {
}
+ (void) initialize;
+ (GLuint) textureWithName:(NSString*)name;
+ (BOOL) textureIsLoaded:(NSString*)name;
+ (void) unloadTexture:(NSString*)name;
@end
TextureLoader.m
//
// TextureLoader.m
// Starfield
//
// Created by John Bowers on 8/3/08.
// Copyright 2008 John Bowers. All rights reserved.
//
#import "TextureLoader.h"
static NSMutableDictionary* gameTextures;
@implementation TextureLoader
+ (void) initialize {
gameTextures = [NSMutableDictionary new];
}
+ (BOOL) textureIsLoaded:(NSString*)name {
return [gameTextures objectForKey:name] != nil;
}
+ (GLuint) textureWithName:(NSString*)name {
NSNumber *textureNumber = [gameTextures objectForKey:name];
if (textureNumber) {
return [textureNumber unsignedIntValue];
} else {
//Load the texture:
GLuint texture[1];
CGImageRef textureImage;
CGContextRef textureContext;
GLubyte *textureData;
size_t textureWidth, textureHeight;
textureImage = getCGImageForImageNamed(name);
textureWidth = CGImageGetWidth(textureImage);
textureHeight = CGImageGetHeight(textureImage);
if (textureImage) {
textureData = (GLubyte*) malloc(textureWidth*textureHeight*4);
textureContext = CGBitmapContextCreate(textureData, textureWidth, textureHeight, 8, textureWidth*4, CGImageGetColorSpace(textureImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(textureContext, CGRectMake(0.0, 0.0, (CGFloat)textureWidth, (CGFloat)textureHeight), textureImage);
CGContextRelease(textureContext);
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
free(textureData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
[gameTextures setObject:[NSNumber numberWithUnsignedInt:texture[0]] forKey:name];
return texture[0];
}
}
+ (void) unloadTexture:(NSString*)name {
NSNumber *textureNumber = [gameTextures objectForKey:name];
if (textureNumber) {
[gameTextures removeObjectForKey:name];
GLuint texture[1];
texture[0] = [textureNumber unsignedIntValue];
glDeleteTextures(1, texture);
}
}
@end
Posted on August 30th, 2008 | filed under code::, iPhone dev | Trackback |