2013-10-06 41 views
6

只要應用程序轉到後臺,它必須停止調用OpenGL函數。但不知何故,當用戶按下主頁按鈕時,所有阻止OpenGL失敗的努力都失敗了,我的應用程序經常(但並非總是)崩潰。如何在應用程序後臺中暫停併發OpenGL繪圖?

它崩潰,

異常類型:EXC_BAD_ACCESS代碼:KERN_INVALID_ADDRESS在爲0x1 libGPUSupportMercury.dylib gpus_ReturnNotPermittedKillClient

我的理解是,如果你的應用程序之後,調用OpenGL函數是不是在前臺了應用程序將因爲GPU不適用於應用程序,因此崩潰。

與OpenGL的呈現視圖具有調度隊列

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

它具有與並行的UIScrollView來呈現。所以有一個GCD semaphore讓隊列等待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); 
} 

我最懷疑的一件事是停止隊列的併發問題。因爲即使在停止顯示鏈接之後,該塊也是異步觸發。

所以我覺得現在缺少的是,我還必須檢查-canRender之前我所說的任何OpenGL的功能在這個異步塊:

__block BOOL canRender = YES; 
dispatch_sync(dispatch_get_main_queue(), ^{ 
    canRender = [self canRender]; 
}); 
if (!canRender) { 
    return; 
} 

OK,但現在我想象使這個檢查在渲染運行開始循環。 -canRender說是的。然後我打電話給[sprite drawState:state];。這個方法調用了很多gl函數。我認爲這是根本問題。因爲即使我的異步程序塊在主線程中檢查應用程序是否仍在前臺,當我繼續異步執行復雜繪圖時,3納秒以後,應用程序可能處於後臺狀態,並且出現了CRASH。

因此,即使我在每個單獨的gl函數調用之前在主線程上檢查了canRender,它可能是一個分裂納秒後的應用程序在後臺並崩潰。

有沒有辦法從內部關閉OpenGL,所以它忽略了對其功能的調用。我聽說過一種整理方法。我將不得不關閉它。但是如何?

回答

1

在允許您的應用程序進入後臺之前,您可以考慮glFinish (...)。它將阻塞,直到管道中的每個命令結束。這可能需要一段時間,可能需要幾ms。同樣的道理,如果你真正想做的只是讓OpenGL忽略API調用,直到滿足一些條件(在這種情況下,直到你的應用程序不再處於後臺)爲止,最簡單的方法是將給定線程的活動上下文設置爲NULL。這將使每個調用都成爲空操作,以至於在此狀態下進行任何GL API調用時都不會產生錯誤。當您的應用程序返回到前臺時,您可以恢復活動的上下文。

從你的問題描述來看,這聽起來像你可能需要這些東西的組合。然而,glFlush (...)可能就足夠了 - 這將告訴OpenGL刷新緩衝區中的所有命令(基本上開始執行正在排隊的任何事情)。它不會等待命令在返回之前完成,但它確保它們將在GL執行任何其他操作之前完成。

相關問題