richard
richard

Reputation: 547

Memory leak in drawing OpenGL framebuffer to UIImage

I am using OpenGL to do my drawing on screen and want to draw a portion of the screen to a UIImage. This is code I've cobbled together looking at examples, which works, but produces a memory leak every time it's called. I've tested this on device and the leak still persists and eventually causes a crash. The returned UIImage is saved to an instance variable and is later definitely set to nil again. What is causing the memory leak here?

- (UIImage *)renderToImageWithContentFrame:(CGRect)contentFrame
                               exportFrame:(CGRect)exportFrame
                                     scale:(float)scale {

    float screenWidth = contentFrame.size.width * scale;
    float screenHeight = contentFrame.size.height * scale;

    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    GLuint renderTex;
    glGenTextures(1, &renderTex);
    glBindTexture(GL_TEXTURE_2D, renderTex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenWidth, screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTex, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    glViewport(0, 0, screenWidth, screenHeight);

    [self _renderImageGL];

    // grabbing image from FBO

    NSInteger dataLength = screenWidth * screenHeight * 4;
    GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));

    glPixelStorei(GL_PACK_ALIGNMENT, 4);
    glReadPixels(0, 0, screenWidth, screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);

    glDeleteFramebuffers(1, &framebuffer);

    CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGImageRef iref = CGImageCreate(screenWidth, screenHeight, 8, 32, screenWidth * 4, colorspace,
                                    kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
                                    ref, NULL, true, kCGRenderingIntentDefault);

    float x = (exportFrame.origin.x - contentFrame.origin.x) * scale;
    float y = (exportFrame.origin.y - contentFrame.origin.y) * scale;
    float width = exportFrame.size.width * scale;
    float height = exportFrame.size.height * scale;
    CGImageRef cropped = CGImageCreateWithImageInRect(iref, (CGRect){x, y, width, height});

    UIGraphicsBeginImageContext((CGSize){width, height});
    CGContextRef cgcontext = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
    CGContextDrawImage(cgcontext, (CGRect){0, 0, width, height}, cropped);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    free(data);
    CFRelease(ref);
    CFRelease(colorspace);
    CGImageRelease(iref);
    CGImageRelease(cropped);

    return image;

}

Upvotes: 0

Views: 753

Answers (1)

user1118321
user1118321

Reputation: 26395

The variable renderTex is not being freed (via a call to glDeleteTextures(1, &renderTex);). My guess is that Instruments may not know about OpenGL-allocated memory because that memory may reside on a graphics card and may be harder (or impossible) to track. (Though that's mainly a guess.)

Upvotes: 3

Related Questions