Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Mockup & CodeGen, iPhone & iPad
($9.99)

Make your own iPhone apps
and run them live!
(free)

Manu
($0.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 12-03-2009, 06:32 AM   #1 (permalink)
Divine avenger
 
Johanovski's Avatar
 
Join Date: Nov 2009
Location: Vic, Catalunya (Spain)
Posts: 320
Default Unable to solve critical memory problem!

Hi there!

I'm getting mad trying to solve a memory problem of my app... First of all I've allocated all the objects at the EAGLView's initWithCoder functions (I'm working in an OpenGL project) but this causes my app to start really slowly and with 20 MB of living memory just at starting. Then, I've split the allocations and divided them in different game moments. The game started then better, but when all the objects are allocated (which is necessary to play) memory comes again up to 20 living MB, which causes continuous memory warning and, then, my app is killed. I'm wondering what can be happening and how can I solve this, I've also tried to alloc and release objects during gameplay, but releasing objects doesn't seems to free real memory... Here I've made a list of what objects I need to alloc and the files' size:

----------------------------------------------------------------------------------
AVAudioPlayer --> 4 allocs (.wav files that goes from 5.2 to 1.8 MB)
Image --> nearly 120 allocs (.png files that goes from 356 to 4 KB)
Proper Objects --> At least 20 allocs at the beginning, and then up to 150
Other Objects (NSString, NSMutableArray, ...) --> nearly 50 allocs
----------------------------------------------------------------------------------

And, trying to keep free as much memory as I can, I've tried doing this:

The game has a variety of "fly" types. Each type has it's own texture array, and then every instance takes it's texture from that array. What I've tried to do is to alloc this texture array only if exists at least one instance of that fly type, and if not, release the texture array. The methods are the following:

************************************************** ***********
// For example, allocations of the "normal fly" texture array...
// This is done the first time a "normal fly" is created
(...some code...)
texMoscaNormal[0][0] = [[Image alloc] initWithImage:[UIImage imageNamed:@"sangnormal01.png"]];
texMoscaNormal[0][1] = [[Image alloc] initWithImage:[UIImage imageNamed:@"sangnormal02.png"]];
texMoscaNormal[0][2] = [[Image alloc] initWithImage:[UIImage imageNamed:@"sangnormal03.png"]];
texMoscaNormal[1][0] = [[Image alloc] initWithImage:[UIImage imageNamed:@"gana.png"]];
texMoscaNormal[1][1] = [[Image alloc] initWithImage:[UIImage imageNamed:@"mnormalenrampa01.png"]];
texMoscaNormal[1][2] = [[Image alloc] initWithImage:[UIImage imageNamed:@"mnormalenrampa02.png"]];
texMoscaNormal[2][0] = [[Image alloc] initWithImage:[UIImage imageNamed:@"mnormal01.png"]];
texMoscaNormal[2][1] = [[Image alloc] initWithImage:[UIImage imageNamed:@"mnormal02.png"]];
texMoscaNormal[2][2] = [[Image alloc] initWithImage:[UIImage imageNamed:@"mnormal03.png"]];
(...some code...)
---------------------------------------------------------------------------------
// And this function releases the "normal fly" texture array
// It is called when no "normal flies" are in the game scene
// Then, if a "normal fly" is created, the upper function is called
// (only once until the array is released again)
(...some code...)
[texMoscaNormal[0][0] release];
[texMoscaNormal[0][1] release];
[texMoscaNormal[0][2] release];
[texMoscaNormal[1][0] release];
[texMoscaNormal[1][1] release];
[texMoscaNormal[1][2] release];
[texMoscaNormal[2][0] release];
[texMoscaNormal[2][1] release];
[texMoscaNormal[2][2] release];
moscaNormal = FALSE;
(...some code...)
************************************************** ***********

Can anyone help me with this? I'm not sure if releasing the texture array is really freeing memory, so it doesn't seems to do in Instruments... And is possible that I've reached the maximum memory available on the iPhone with this images, just 20MB?
Any help with this will be really appreciated...

Thanks in advance!
Johanovski is offline   Reply With Quote
Old 12-03-2009, 12:04 PM   #2 (permalink)
Divine avenger
 
Johanovski's Avatar
 
Join Date: Nov 2009
Location: Vic, Catalunya (Spain)
Posts: 320
Default

Hi again!

I don't know if it's important, but a lot of allocations are mallocs that come from OpenGL...

Thanks!
Johanovski is offline   Reply With Quote
Old 12-03-2009, 01:04 PM   #3 (permalink)
Senior Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
Default

You said you're using OpenGL. Do you have a method that loads these images into textures? You should not load all of the images at once and then convert them into textures.

You should keep an array of filenames instead, and pass them into the loadTexture function. The loadTexture function can create the image, copy it to a texture, and then destroy the image. That way you have only one image in memory at a time, instead of all of the images in memory at one time. You may need to add an autorelease pool in your texture load method to make sure the images get destroyed, not just queud for autorelease.

So instead of:
(load images) * repeat 20 times
(copy image to texture) * repeat 20 times
(destroy images) * repeat 20 times

you'll do:
(load one image, copy to texture, destroy image) * repeat 20 times.

So you only have one image in memory at a time. Oh, and use imageWithContentsOfFile: , not imageNamed: , in this situation. imageNamed: keeps an extra copy in memory for faster loading next time, but you don't want that here since you only use the image once.
__________________

Free Games!
smasher is offline   Reply With Quote
Old 12-04-2009, 02:18 AM   #4 (permalink)
Divine avenger
 
Johanovski's Avatar
 
Join Date: Nov 2009
Location: Vic, Catalunya (Spain)
Posts: 320
Default

Hi Smasher!

Now I have a class named "Image" and a class named "Texture2D" (Image uses Texture2D), and the method for init an image is the following:

************************************************** *************
// Of course located in "Image.m"
- (id)initWithImageUIImage *)image {
self = [super init];
if (self != nil) {
// By default set the scale to 1.0f and the filtering to GL_NEAREST
//texture = [[Texture2D alloc] initWithImage:image filter:GL_NEAREST];
texture = [[Texture2D alloc] initWithImage:image];
//[texture initWithImage:image];
scale = 1.0f;
[self initImpl];
}
return self;
}
-------------------------------------------------------------------
// This calls a Texture2D method, which is the following:
// (I'm sorry so it's really a big method...)
- (id) initWithImageUIImage *)uiImage
{
NSUInteger width,
height,
i;
CGContextRef context = nil;
void* data = nil;;
CGColorSpaceRef colorSpace;
void* tempData;
unsigned int* inPixel32;
unsigned short* outPixel16;
BOOL hasAlpha;
CGImageAlphaInfo info;
CGAffineTransform transform;
CGSize imageSize;
Texture2DPixelFormat pixelFormat;
CGImageRef image;
UIImageOrientation orientation;
BOOL sizeToFit = NO;


image = [uiImage CGImage];
orientation = [uiImage imageOrientation];

if(image == NULL) {
[self release];
NSLog(@"Image is Null");
return nil;
}


info = CGImageGetAlphaInfo(image);
hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO);
if(CGImageGetColorSpace(image)) {
if(hasAlpha)
pixelFormat = kTexture2DPixelFormat_RGBA8888;
else
pixelFormat = kTexture2DPixelFormat_RGB565;
} else //NOTE: No colorspace means a mask image
pixelFormat = kTexture2DPixelFormat_A8;


imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
transform = CGAffineTransformIdentity;

width = imageSize.width;

if((width != 1) && (width & (width - 1))) {
i = 1;
while((sizeToFit ? 2 * i : i) < width)
i *= 2;
width = i;
}
height = imageSize.height;
if((height != 1) && (height & (height - 1))) {
i = 1;
while((sizeToFit ? 2 * i : i) < height)
i *= 2;
height = i;
}
while((width > kMaxTextureSize) || (height > kMaxTextureSize)) {
width /= 2;
height /= 2;
transform = CGAffineTransformScale(transform, 0.5, 0.5);
imageSize.width *= 0.5;
imageSize.height *= 0.5;
}

switch(pixelFormat) {
case kTexture2DPixelFormat_RGBA8888:
colorSpace = CGColorSpaceCreateDeviceRGB();
data = malloc(height * width * 4);
context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
break;
case kTexture2DPixelFormat_RGB565:
colorSpace = CGColorSpaceCreateDeviceRGB();
data = malloc(height * width * 4);
context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
break;

case kTexture2DPixelFormat_A8:
data = malloc(height * width);
context = CGBitmapContextCreate(data, width, height, 8, width, NULL, kCGImageAlphaOnly);
break;
default:
[NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
}


CGContextClearRect(context, CGRectMake(0, 0, width, height));
CGContextTranslateCTM(context, 0, height - imageSize.height);

if(!CGAffineTransformIsIdentity(transform))
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
if(pixelFormat == kTexture2DPixelFormat_RGB565) {
tempData = malloc(height * width * 2);
inPixel32 = (unsigned int*)data;
outPixel16 = (unsigned short*)tempData;
for(i = 0; i < width * height; ++i, ++inPixel32)
*outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
free(data);
data = tempData;

}
self = [self initWithData:data pixelFormatixelFormat pixelsWide:width pixelsHigh:height contentSize:imageSize];

CGContextRelease(context);
free(data);

return self;
}
************************************************** ******

As you've said now I'm loading all necessary images and then using during gameplay (each image needs to be used many times each time I draw the screen and needs to be rotated, scaled or colored just before printing), if it's possible to use what you're saying (I'm praying for it ^_^) what I have to do, modify the "Image.m" method? Or perhaps the "Texture2D.m" method? I'm not sure if I understand what you're meaning, but thanks for replaying because I really need help with this!

Thanks!
Johanovski is offline   Reply With Quote
Old 12-04-2009, 02:25 AM   #5 (permalink)
Divine avenger
 
Johanovski's Avatar
 
Join Date: Nov 2009
Location: Vic, Catalunya (Spain)
Posts: 320
Default

Hi again Smasher!

I've started trying to change the imageNamed: method for the imageWithContentsOfFile: method, and seems that no image is loaded with this change... With the imageNamed: method I just pass the image name (for example "moscanormal01.png"), it's the same I have to pass with imageWithContentsOfFile: method? Perhaps is needed to specify also the path (however, images are in the my project's root folder)?

Thanks again! ^_^
Johanovski is offline   Reply With Quote
Old 12-04-2009, 02:46 AM   #6 (permalink)
Senior Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
Default

Yes, imageWithContentsOfFile takes a path - you want something like this to get the path of the image in your bundle:

Code:
NSString *path = [[NSBundle mainBundle] pathForResource:@"ball" ofType:@"png"];
Once you create your texture (your Texture2D object) you don't need the original image in memory any more. You use and draw the texture object (Texture2D object) instead. That will cut your memory a lot.
__________________

Free Games!
smasher is offline   Reply With Quote
Old 12-04-2009, 04:32 AM   #7 (permalink)
Divine avenger
 
Johanovski's Avatar
 
Join Date: Nov 2009
Location: Vic, Catalunya (Spain)
Posts: 320
Default

Hi again!

Wow, I've changed successfully the "imageNamed" method by the "imageWithContentsOfFile" method and the result is astonishing! Thank you!

Now, if it's possible to change what you've said about loading the image, generating the texture and then deleting the image I want to do it too, but I don't understand how can I do this... I have to modify the "Image" or "Texture2D" method, or it's just how I call it from my main function? I'm so confused about this...

But thanks!
Johanovski is offline   Reply With Quote
Old 12-04-2009, 11:23 AM   #8 (permalink)
Senior Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
Default

Wherever you are calling initWithImage right now. You should make a loop that calls initWithImage repeatedly, for all the filenames in your array. Inside that loop you can create an autorelease pool, create the the image, call initWithImage, and then drain the autorelease pool.

If you don't use the autorelease pool, then the images are not autoreleased until the end of the current event - which causes a much bigger memory spike because they're all in memory at the same time.
__________________

Free Games!
smasher is offline   Reply With Quote
Reply

Bookmarks

Tags
alloc, crash, images, memory

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 259
17 members and 242 guests
ADY, Alsahir, dacapo, Dani77, Desert Diva, djohnson, F_Bryant, Grinarn, HemiMG, jansan, linkmx, M@realobjects, macquitzon216, prchn4christ, smethorst, spiderguy84
Most users ever online was 1,187, 10-11-2011 at 08:09 AM.
» Stats
Members: 158,882
Threads: 89,228
Posts: 380,762
Top Poster: BrianSlick (7,129)
Welcome to our newest member, jansan
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 01:49 PM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0