2013-11-02 4 views
7

Essi suggest:Perché Apple suggerisce di inviare comandi OpenGL in una coda di background seriale quando questo porta inevitabilmente a crash?

Quando si utilizza GCD, utilizzare una coda seriale dedicata alla spedizione comandi per OpenGL ES; questo può essere usato per sostituire il modello mutex convenzionale.

Non capisco questa raccomandazione. C'è questo conflitto che non riesco a risolvere:

Quando l'applicazione delegato di un app riceve la chiamata -applicationWillResignActive, si deve immediatamente smettere di chiamare qualsiasi funzione OpenGL.

Se l'app continua a chiamare una funzione OpenGL dopo il -applicationWillResignActive restituito, l'app si bloccherà.

Se seguo la raccomandazione di Apple per richiamare le funzioni OpenGL in una coda di sfondo di serie, mi trovo di fronte a questo problema apparentemente irrisolvibile:

1) Dopo che ricevo -applicationWillResignActive devo subito fermata chiamando eventuali ulteriori funzioni OpenGL.

2) Ma perché la coda di serie si trova nel bel mezzo di elaborazione di un blocco di codice in background, a volte il blocco di codice avrebbe finito esecuzione after-applicationWillResignActive ritorna, e l'applicazione si blocca.

Questa è un'illustrazione che mostra i "blocchi" concomitanti. Il thread principale riceve un messaggio completo e deve impedire ulteriori chiamate a OpenGL ES. Ma purtroppo questi accadere in una coda di fondo che non può essere fermato in volo di lavorare su un blocco:

|_____main thread: "STOP calling OpenGL ES!"_____| 
_____|_____drawing queue: "Draw!"_____|_____drawing queue: "Draw!"_____| 

Tecnicamente ho trovato alcun modo per fermare la coda di sfondo istantaneamente e evitare ulteriori chiamate a OpenGL nei precedenti. Un blocco di codice inviato una volta eseguito continua a essere in esecuzione.

L'unica soluzione che ho trovato era NON chiamare le funzioni OpenGL ES in background. Invece, chiamali sul thread principale per garantire che non verranno mai chiamati dopo che l'app ha perso l'accesso alla GPU.

Quindi se è bene chiamare le funzioni OpenGL ES in background, come si fa a garantire che non hanno mai vengono chiamati dopo l'applicazione è dimesso attiva?

risposta

7

È sufficiente attendere applicationWillResignActive per la coda per completare tutte le operazioni in coda utilizzando un gruppo di distribuzione o un meccanismo simile.

È possibile trovare un esempio nella documentation:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_group_t group = dispatch_group_create(); 

// Add a task to the group 
dispatch_group_async(group, queue, ^{ 
    // Some asynchronous work 
}); 

// Do some other work while the tasks execute. 

// When you cannot make any more forward progress, 
// wait on the group to block the current thread. 
dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 

// Release the group when it is no longer needed. 
dispatch_release(group); 
+0

Il gruppo di dispatch avrebbe causato l'attesa del thread principale fino al completamento del blocco di enqueued? Hai un link/esempio pratico su come è fatto? – openfrog

4

suggerimento di là di Sven di utilizzare un gruppo di spedizione, sono andato un percorso più semplice in passato utilizzando un dispaccio sincrono nella vostra coda di serie di rendering all'interno -applicationWillResignActive:

// Tell whatever is generating rendering operations to pause 

dispatch_sync(openGLESSerialQueue, ^{ 
    [EAGLContext setCurrentContext:context]; 
    glFinish(); 

    // Whatever other cleanup is required 
}); 

l'invio sincrono qui bloccherà fino a quando tutte le azioni nella coda di serie hanno finito, allora verrà eseguito il codice all'interno di quel blocco. Se hai un timer o qualche altra sorgente che sta attivando nuovi blocchi di rendering, la interromperò prima, nel caso in cui metta un ultimo blocco sulla coda.

Come ulteriore misura di sicurezza, utilizzo glFinish() che blocca fino a quando non è terminato tutto il rendering sulla GPU (le GPU PowerVR come differire il rendering il più possibile). Per i fotogrammi sottoposti a rendering estremamente lunghi ho visto occasionalmente degli arresti anomali dovuti al rendering dei fotogrammi, anche dopo che tutte le chiamate OpenGL responsabili sono finite. Questo lo impedisce.

Lo uso in alcune applicazioni diverse e praticamente elimina gli arresti anomali dovuti al rendering durante la transizione sullo sfondo. So che altri usano qualcosa di simile con la mia libreria GPUImage (che utilizza anche una coda di invio in background seriale per il rendering) e sembra funzionare bene per loro.

L'unica cosa di cui bisogna essere cauti, con questa o qualsiasi altra soluzione che blocca fino a quando non viene eseguito un processo in background, è quella di aprirsi ai deadlock se non si fa attenzione. Se si dispone di eventuali invii sincroni alla coda principale all'interno di blocchi nella coda di sfondo, è necessario rimuoverli o provare a renderli asincroni. Se stanno aspettando sulla coda principale e la coda principale è in attesa sulla coda di sfondo, ti congelerai abbastanza bene. Di solito, è abbastanza semplice evitarlo.

Problemi correlati