Question

I'm currently using GLKit to do some OpenGL drawing. I created a normal UIViewController and then added a GLKViewController subclass inside a container to do my drawing. While everything runs fine initially, if I let my program run for a period of time (perhaps 15-30 minutes), eventually it crashes and gives me the following error.

malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

So I turned on the malloc_error_break breakpoint, and the stack trace points to the following code.

-(NSArray*)meshes:(NSArray *)meshes sortedFromFrontToBack:(BOOL)sortedFromFrontToBack
{
    NSMutableArray *sortedMeshes = meshes.mutableCopy;
    [sortedMeshes sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        DSNode *mesh1 = obj1;
        DSNode *mesh2 = obj2;
        GLKVector3 depth1 = isnan(mesh1.boundingSphere.radius) ? mesh1.transformationState.position : mesh1.boundingSphere.center;
        GLKVector3 depth2 = isnan(mesh2.boundingSphere.radius) ? mesh2.transformationState.position : mesh2.boundingSphere.center;

        GLKMatrix4 mesh1ToCameraSpace = [mesh1 nodeToOtherNodeTransform:self];
        GLKMatrix4 mesh2ToCameraSpace = [mesh2 nodeToOtherNodeTransform:self];

        GLKVector3 depth1InCameraSpace = GLKMatrix4MultiplyVector3WithTranslation(mesh1ToCameraSpace, depth1);
        GLKVector3 depth2InCameraSpace = GLKMatrix4MultiplyVector3WithTranslation(mesh2ToCameraSpace, depth2);


        NSNumber *n1 = [NSNumber numberWithFloat:depth1InCameraSpace.z];
        NSNumber *n2 = [NSNumber numberWithFloat:depth2InCameraSpace.z]; /* Breakpoint triggered here */

        if(sortedFromFrontToBack)
        {
            return [n2 compare:n1];
        }
        return [n1 compare:n2];
    }];

    return sortedMeshes;
}

As I commented, the [NSNumber numberWithFloat:] call throws the malloc error. This method gets called once each frame from my GLKViewController's drawInRect method. Essentially, I have a class which keeps track of my cameras and the meshes which are going to be drawn by OpenGL, and it sorts them in camera space from either front to back for opaque meshes or back to front for transparent before drawing them.

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(self.clearColor.r, self.clearColor.g, self.clearColor.b, self.clearColor.a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    DSDirector *director = [DSDirector sharedDirector];

    for(DSMesh *mesh in director.opaqueMeshes)
    {
        [mesh draw];
    }

    /* The director class keeps track of my scene's cameras and meshes and calls the above method to return the scene's transparent meshes properly sorted */
    for(DSMesh *mesh in director.transparentMeshes) 
    {
        [mesh draw];
    }
}

From what I've read, the autorelease pool should drain at the end of each run loop, so I wouldn't think that creating a bunch of autoreleased objects every frame is an issue, they should all get flushed each frame. I've profiled my program and checked for any leaks and can't find any, and I'm using ARC as well, which should minimize the risk. When I profile it, the live bytes total never budges, although the overall bytes rises quite quickly, and no leaks are found. In addition, didReceiveMemoryWarning never fires. I'm stumped.

Was it helpful?

Solution 2

So, I think I've figured out what was going on. At some prior point I'd enabled NSZombies and then forgot about it, and so all of my objects that I expected to be deallocated were actually still hanging around. When profiling in Instruments, zombies must not be counted as live, which is why I couldn't figure out why I was seemingly running out of memory when the number of live bytes wasn't climbing. However, when I upgraded to XCode 5, the new memory gauge showed that I was quickly racking up memory, and then when I dropped into Instruments, Leaks was covered up by a warning that said that it would not work properly since NSZombies were enabled. So, I disabled zombies, and now my memory usage is staying constant, just like I expected it to.

OTHER TIPS

Something smells wrong about the NSNumber creation being the cause of your malloc error. Testing with Instruments might help find the real culprit.

However, you're doing two things you don't need to, so there's some chance that eliminating them might help with your problem.

First, you don't need to wrap floats in an NSNumber to compare them. The basic comparison and mathematical operators work just fine, and don't require extra time or memory for object creation:

if (depth1InCameraSpace.z < depth2InCameraSpace.z)
    return NSOrderedAscending;
else if (depth1InCameraSpace.z > depth2InCameraSpace.z)
    return NSOrderedDescending;
else
    return NSOrderedSame;

Second, the Tile-Based Deferred Rendering strategy implemented by the GPU hardware on iOS devices does its own hidden surface removal optimizations -- sorting opaque geometry front to back is redundant, so all it does is waste CPU time. (There's a decent explanation of this is in the OpenGL ES Hardware Platform Guide for iOS.) You should still sort translucent geometry back to front (and draw it after opaque geometry) for proper blending, though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top