How to stop simultaneous OpenGL drawing while app background setting?

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?

+6
source share
1 answer

You can consider glFinish (...) before letting your application go into the background. It will be blocked until each command in the pipeline ends. This may take some time, although perhaps a few ms.

Similarly, if all you really want to do is make OpenGL ignore API calls until a condition is met (in this case, until your application is no longer in the background), the easiest way To do this, set the active context for the given stream to NULL. This will cause every call to be absent, so no error will be generated if you make any GL API call in this state. You can restore the active context when the application is brought back to the fore.

From the description of the problem, it looks like you might need a combination of both of these things. However, glFlush (...) may be sufficient - this will tell OpenGL to clear all the commands in the buffer (basically run everything that was in the queue). He will not wait to complete the commands before returning, but he ensures that they finish before GL does anything else.

+1
source

Source: https://habr.com/ru/post/955373/


All Articles