如何在应用程序进入后台时停止并发的OpenGL绘制?

6
一旦应用程序进入后台,它必须停止调用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来更新runloop:
CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(update)];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
self.displayLink = displayLink; 

更新方法在串行渲染队列上异步执行绘图计算,并使用信号量等待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);
  });
}

更新方法会在主线程上检查是否可以进行渲染。检查的代码如下:
- (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返回YES。然后我调用了[sprite drawState:state];这个方法调用了很多gl函数。我认为这是根本问题。因为即使我的异步块在主线程上检查应用程序是否仍在前台,当我在3纳秒后继续异步进行复杂的绘制时,应用程序可能已经处于后台状态并且崩溃。

因此,即使我在每次gl函数调用之前都在主线程上检查-canRender,也可能会出现应用程序在后台崩溃的情况。

有没有一种从内部关闭OpenGL的方法,使其忽略对其函数的调用?我听说过一个finishing方法。我将不得不关闭它。但是如何操作呢?

1个回答

1
您可以考虑在应用程序进入后台之前使用 glFinish (...)。它会阻塞直到管道中的每个命令完成。这可能需要一段时间,可能需要几毫秒。
同样地,如果您只想让OpenGL忽略API调用,直到满足某些条件(在本例中为直到您的应用程序不再处于后台),最简单的方法是将给定线程的活动上下文设置为NULL。这将使每个调用成为无操作,以至于在此状态下进行任何GL API调用时都不会生成错误。当您的应用程序被带回前台时,可以恢复活动上下文。
从您的问题描述中,听起来您可能需要同时使用这两种方法。但是,glFlush (...) 可能已经足够了 - 这将告诉OpenGL刷新缓冲区中的所有命令(基本上开始执行排队的任何内容)。它不会等待命令在返回之前完成,但它保证它们在GL执行其他操作之前完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接