一旦应用程序进入后台,它必须停止调用 OpenGL 函数。但不知何故,阻止 OpenGL 的所有努力都失败了,当用户按下主页按钮时,我的应用程序经常(但不总是)崩溃。
它崩溃了
异常类型:EXC_BAD_ACCESS 代码:KERN_INVALID_ADDRESS at 0x1 libGPUSupportMercury.dylib gpus_ReturnNotPermittedKillClient
据我了解,如果您在应用程序不在前台之后调用 OpenGL 函数,应用程序将崩溃,因为 GPU 对应用程序不可用。
用 OpenGL 渲染的视图有一个调度队列
self.drawingQueue = dispatch_queue_create("openglRenderQueue", DISPATCH_QUEUE_SERIAL);
它必须与 UIScrollView 并行渲染。所以有一个 GCD信号量让队列等待 UIScrollView。
self.renderSemaphore = dispatch_semaphore_create(1);
我创建了一个 CADisplayLink 来更新运行循环:
CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(update)];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
self.displayLink = displayLink;
update 方法在串行渲染队列上异步执行绘图计算,并使用信号量等待 UIScrollView。它最终在主线程上提交同步。
- (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);
});
}
update 方法检查主线程是否可以渲染。检查如下所示:
- (BOOL)canRender {
UIApplicationState appState = [[UIApplication sharedApplication] applicationState];
return (appState != UIApplicationStateBackground && appState != UIApplicationStateInactive);
}
我最怀疑的一件事是暂停队列的并发问题。因为它是异步的,所以即使在我停止显示链接之后也会触发该块。
所以我认为缺少的是,在我调用这个异步块中的任何 OpenGL 函数之前,我还必须检查 -canRender:
__block BOOL canRender = YES;
dispatch_sync(dispatch_get_main_queue(), ^{
canRender = [self canRender];
});
if (!canRender) {
return;
}
好的,但现在想象我在渲染运行循环开始时进行此检查。-canRender
说是的。然后我打电话[sprite drawState:state];
。这个方法调用了很多 gl 函数。我认为这是根本问题。因为即使我的异步阻塞检查主线程是否应用程序仍在前台,当我继续异步执行复杂绘图 3 纳秒后应用程序可能处于后台状态和崩溃。
因此,即使我在每个 gl 函数调用之前检查主线程上的 -canRender ,也可能是一个拆分纳秒后的应用程序处于后台并崩溃。
有没有办法从内到外关闭 OpenGL,以便它忽略对其函数的调用。我听说过一种整理方法。我将不得不关闭它。但是怎么做?