2013-10-06 11 views
6

Non appena l'app passa allo sfondo, deve smettere di chiamare le funzioni OpenGL. Ma in qualche modo tutti gli sforzi per fermare OpenGL falliscono e la mia app spesso (ma non sempre) si blocca quando l'utente preme il pulsante home.Come interrompere il disegno OpenGL simultaneo durante lo sfondo dell'applicazione?

Si blocca con il tipo

eccezione: Codice EXC_BAD_ACCESS: KERN_INVALID_ADDRESS a 0x1 libGPUSupportMercury.dylib gpus_ReturnNotPermittedKillClient

Per la mia comprensione se si chiama funzioni OpenGL dopo applicazione non è in primo piano più app arresto anomalo perché GPU non è disponibile per l'app.

La vista che rende con OpenGL ha una coda di invio

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

Deve rendering in parallelo con UIScrollView. Quindi esiste un GCD semaphore per consentire alla coda di attendere UIScrollView.

self.renderSemaphore = dispatch_semaphore_create(1); 

Creo CADisplayLink aggiornare runloop:

CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(update)]; 
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 
self.displayLink = displayLink; 

Il metodo di aggiornamento esegue calcoli disegno asincrona su seriale rendono coda e utilizza il semaforo aspettare UIScrollView. Alla fine esegue il commit della sincronizzazione sul thread principale.

- (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); 
    }); 
} 

il metodo di aggiornamento di controllo dei thread principale se può rendere. Il controllo è simile al seguente:

- (BOOL)canRender { 
    UIApplicationState appState = [[UIApplication sharedApplication] applicationState]; 
    return (appState != UIApplicationStateBackground && appState != UIApplicationStateInactive); 
} 

L'unica cosa che ho il sospetto che il più è un problema di concorrenza per fermare la coda. Poiché è asincrono, il blocco si attiva anche dopo aver interrotto il collegamento del display.

Quindi penso ciò che manca è che io devo anche controllare -canRender prima che chiami qualsiasi funzione OpenGL in questo blocco asincrona:

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

OK ma ora immagino faccio questo controllo all'inizio del render corsa ciclo continuo. -canRender dice SÌ. Quindi chiamo [sprite drawState:state];. Questo metodo richiede molte funzioni gl. Penso che questo sia il problema alla radice. Perché anche se i miei blocchi asincroni controllano il thread principale se l'app è ancora in primo piano, quando continuo asincrono a fare il disegno complesso 3 nanosecondi più tardi l'app potrebbe trovarsi nello stato di background e nel CRASH.

Quindi, anche se controllo -canRender sul thread principale prima di ogni singola funzione gl chiamarlo potrebbe essere che un nanosecondo spaccatura tardi applicazione è in background e crash.

C'è un modo per arrestare OpenGL dall'interno, in modo che ignori le chiamate alle sue funzioni. Avevo sentito parlare di un metodo di finitura. Dovrei spegnerlo. Ma come?

risposta

1

Si potrebbe considerare glFinish (...) prima di consentire all'applicazione di andare in secondo piano. Si bloccherà fino al termine di ogni comando nella pipeline. Questo potrebbe richiedere un po 'di tempo, potenzialmente diversi ms.

Per lo stesso motivo, se tutto ciò che si vuole veramente fare è far sì che OpenGL ignori le chiamate API fino a quando non viene soddisfatta una condizione (in questo caso, fino a quando l'applicazione non è più in background) il modo più semplice per farlo è imposta il contesto attivo per un dato thread su NULL. Questo renderà ogni chiamata no-op, così tanto che non verrà generato alcun errore quando si effettua una chiamata API GL in questo stato. È possibile ripristinare il contesto attivo quando l'applicazione viene riportata in primo piano.

Dalla descrizione del problema, sembra che potresti aver bisogno di una combinazione di entrambe queste cose. Tuttavia, glFlush (...) potrebbe essere sufficiente - questo dirà a OpenGL di svuotare tutti i comandi nel buffer (in pratica inizia l'esecuzione di tutto ciò che era in coda). Non sarà attendere per i comandi da finire prima di tornare, ma garantisce che finiranno prima che GL faccia qualcos'altro.

Problemi correlati