As soon as the application goes into the background, it stops calling OpenGL functions. But somehow, all attempts to stop OpenGL fail, and my application often (but not always) crashes when the user clicks the home button.
Crash with
Exception Type: EXC_BAD_ACCESS Code: KERN_INVALID_ADDRESS at 0x1 libGPUSupportMercury.dylib gpus_ReturnNotPermittedKillClient
As far as I understand, if you call OpenGL functions after the application is not in the foreground, the application will stop working because the GPU is not available for the application.
The view that displays with OpenGL has a submit queue
self.drawingQueue = dispatch_queue_create("openglRenderQueue", DISPATCH_QUEUE_SERIAL);
It should be rendered in parallel with the UIScrollView. Thus, there is a generated GCD semaphore so that the queue expects a UIScrollView.
self.renderSemaphore = dispatch_semaphore_create(1);
I create CADisplayLink to update runloop:
CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(update)]; [dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; self.displayLink = displayLink;
The update method performs async calculations on a sequential render queue and uses the semaphore to wait for a UIScrollView. It finally synchronizes the main thread.
- (void)update { dispatch_async(drawingQueue, ^{ if (dispatch_semaphore_wait(renderSemaphore, DISPATCH_TIME_NOW) != 0) { return; } @autoreleasepool { [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer); glViewport(0, 0, width, height); glMatrixMode(GL_MODELVIEW); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); // Quickly grab the target game state for rendering GameState state = targetState; [sprite drawState:state]; dispatch_sync(dispatch_get_main_queue(), ^{ if ([self canRender]) { BOOL isAnimating = [self isAnimating]; if (isAnimating && displayLink) { [EAGLContext setCurrentContext:context]; glBindRenderbufferOES(GL_RENDERBUFFER_OES, renderbuffer); if (displayLink != nil) { [context presentRenderbuffer:GL_RENDERBUFFER_OES]; } } } }); } dispatch_semaphore_signal(renderSemaphore); }); }
The update method checks the main thread if it can display. The check is as follows:
- (BOOL)canRender { UIApplicationState appState = [[UIApplication sharedApplication] applicationState]; return (appState != UIApplicationStateBackground && appState != UIApplicationStateInactive); }
The only thing I suspect the most is the concurrency problem to stop the queue. Since it is asynchronous, the block is blocked even after I stopped the link to the image.
So, I think that what is missing is that I also have to check -canRender before I call any OpenGL function in this async block:
__block BOOL canRender = YES; dispatch_sync(dispatch_get_main_queue(), ^{ canRender = [self canRender]; }); if (!canRender) { return; }
OK, but now imagine that I am doing this check at the beginning of the rendering cycle. -canRender says YES. Then I call [sprite drawState:state]; . This method calls many gl functions. I think this is the main problem. Because even if my asynchronous blocks check the main thread, if the application is still in the foreground, when I continue asynchronous execution of complex Figure 3 nanoseconds, the application can later be in the background and CRASH.
That way, even if I check -canRender for the main thread before each call to the gl function, perhaps this is an earlier application with nanoseconds in the background and it crashes.
Is there a way to close OpenGL from within, so it ignores calls to its functions. I heard about the finish method. I would have to close it. But how?