アプリがバックグラウンドになるとすぐに、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 を裏返しにシャットダウンして、関数の呼び出しを無視する方法はありますか。仕上げ方法について聞いたことがあります。私はそれをシャットダウンする必要があります。しかし、どのように?