views:

193

answers:

3

Hello,

My app is running out of memory. To resolve this, I free up two very large arrays used in a function that writes a framebuffer to an image. The method looks like this:

-(UIImage *) glToUIImage {
    NSInteger myDataLength = 768 * 1024 * 4;
    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 768, 1024, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y <1024; y++)
    {
            for(int x = 0; x <768 * 4; x++)
            {
                    buffer2[(1023 - y) * 768 * 4 + x] = buffer[y * 4 * 768 + x];
            }
    }

     // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);

    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * 768;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    // make the cgimage
    CGImageRef imageRef = CGImageCreate(768, 1024, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];

    //free(buffer);
    //free(buffer2);

    return myImage;
}

Note the two called to free(buffer) and free(buffer2) at the end there? Those work fine on the iPad simulator, removing the memory problem and allowing me to generate with impudence. However, they kill the iPad instantly. Like, the first time it executes it. If I remove the free() calls it runs fine, just runs out of memory after a minute or two. So why is the free() call crashing the device?

Note - it's not the call to free() that explicitly crashes the device, it crashes later. But that seems to be the root cause/..

EDIT - Someone's asked about where it exactly crashes. This flow goes on to return the image to another object, which writes it to a file. When calling the 'UIImageJPEGRepresentation' method, it generates an EXT_BAD_ACCESS message. I assume this is because the UIImage I'm passing it to write to the file is corrupt, null or something else. But this only happens when I free those two buffers.

I'd understand if the memory was somehow related to the UIIMage, but it really shouldn't be, especially as it works on the simulator. I wondered if it is down to how iPad handles 'free' calls...

+1  A: 

First things first, you really should check if malloc failed and returned NULL. However, if that doesn't solve your problem, use a debugger and step through your programm to see exactly where it fails (or at least to get a stack trace). From my experience, odd failures like crashing somewhere in unsuspected areas almost always are buffer overflows corrupting arbitrary data some time before.

RWS
I know where it fails (sort of) but the trace is quite a long one. I'll edit it in now.
mtc06
You may be right about the overflow. If I don't free buffer2, it works fine. Even if I free buffer.
mtc06
+5  A: 

From reading the docs, I believe CGDataProviderCreateWithData will only reference the memory pointed to by buffer2, not copy it. You should keep it allocated until the image is released.

Try this:

static void _glToUIImageRelease (void *info, const void *data, size_t size) {
    free(data);
}

-(UIImage *) glToUIImage {
    NSInteger myDataLength = 768 * 1024 * 4;
    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 768, 1024, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y 
Krumelur
I just cottoned onto that too! Excellent spot, my good man. Thanks! :)
mtc06
A: 

Buffer(s) are undersized? Look at the loops.

for(int y = 0; y <1024; y++)
{
     for(int x = 0; x <768 * 4; x++)
     {
          buffer2[(1023 - y) * 768 * 4 + x] = buffer[y * 4 * 768 + x];
     }
}

let y == 0 and x == (768*4)-1, the index of buffer2 exceeds allocated size. Probably outside range before that?

Bruce