6

アプリがバックグラウンドになるとすぐに、OpenGL 関数の呼び出しを停止する必要があります。しかし、どういうわけか、OpenGL を停止するすべての試みが失敗し、ユーザーがホーム ボタンを押すと、アプリが頻繁に (常にではありませんが) クラッシュします。

でクラッシュします

例外の種類: EXC_BAD_ACCESS コード: 0x1 の KERN_INVALID_ADDRESS libGPUSupportMercury.dylib gpus_ReturnNotPermittedKillClient

私の理解では、アプリがフォアグラウンドになくなった後に OpenGL 関数を呼び出すと、GPU がアプリで使用できないため、アプリがクラッシュします。

OpenGL でレンダリングするビューにはディスパッチ キューがあります

self.drawingQueue = dispatch_queue_create("openglRenderQueue", DISPATCH_QUEUE_SERIAL);

UIScrollView と並行してレンダリングする必要があります。そのため、キューが UIScrollView を待機できるようにする GCDセマフォがあります。

self.renderSemaphore = dispatch_semaphore_create(1);

runloop を更新するために 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);
}

私が最も疑っている 1 つのことは、キューを停止する同時実行性の問題です。非同期であるため、表示リンクを停止した後でもブロックが起動します。

したがって、不足しているのは、この非同期ブロックで 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 を裏返しにシャットダウンして、関数の呼び出しを無視する方法はありますか。仕上げ方法について聞いたことがあります。私はそれをシャットダウンする必要があります。しかし、どのように?

4

1 に答える 1