Soluzione alternativa alla perdita del contesto OpenGL quando Android fa una pausa?


40

La documentazione di Android dice:

Vi sono situazioni in cui il contesto di rendering EGL andrà perso. Questo in genere accade quando il dispositivo si sveglia dopo essere andato a dormire. Quando il contesto EGL viene perso, tutte le risorse OpenGL (come le trame) associate a quel contesto verranno automaticamente eliminate. Per mantenere il rendering correttamente, un renderer deve ricreare tutte le risorse perse di cui ha ancora bisogno. Il metodo onSurfaceCreated (GL10, EGLConfig) è un posto conveniente per farlo.

Ma dover ricaricare tutte le trame nel contesto OpenGL è sia una seccatura che ferisce l'esperienza di gioco per l'utente quando rientra nell'app dopo una pausa. So che "Angry Birds" in qualche modo evita questo, sto cercando suggerimenti su come realizzare lo stesso?

Sto lavorando con Android NDK r5 (versione CrystaX.) Ho trovato questo possibile hack per il problema, ma sto cercando di evitare di creare un'intera versione SDK personalizzata.


dormire e svegliarsi si riferisce al dispositivo, quindi mentre l'utente mette semplicemente in pausa il gioco o cambia processo, dovrebbe perdere qualsiasi contesto EGL.
Ali1S232,

Risposte:


22

Replica Island ha una versione modificata di GLSurfaceView che risolve questo problema (e funziona con le versioni precedenti di Android). Secondo Chris Pruett :

Fondamentalmente, ho hackerato l'originale GLSurfaceView per risolvere un problema molto specifico: volevo andare a diverse attività all'interno della mia app senza buttare via tutto il mio stato OpenGL. La principale modifica è stata quella di separare la superficie EGL da EGLContext e di gettare via la prima suPause (), ma preservare la seconda fino a quando il contesto è esplicitamente perso. L'implementazione predefinita di GLSurfaceView (che non ho scritto, a proposito), elimina tutto lo stato GL quando l'attività viene messa in pausa e chiama onSurfaceCreated () quando viene ripresa. Ciò significa che, quando una finestra di dialogo è spuntata nel mio gioco, la chiusura ha comportato un ritardo perché tutte le trame dovevano essere ricaricate.

È necessario utilizzare GLSurfaceView predefinito. Se devi avere la stessa funzionalità della mia, puoi guardare la mia. Ma facendo quello che ho fatto ho scoperto tutti i tipi di terribili bug del driver in alcuni portatili (vedi i commenti molto lunghi verso la fine di quel file), e puoi evitare tutto quel casino semplicemente usando quello predefinito.

Modifica: ho appena realizzato che hai già pubblicato il link a un hack simile. Non credo che ci sia una soluzione integrata prima del nido d'ape. Replica Island è un gioco popolare che funziona su molti dispositivi e potresti trovare utili l'implementazione e i commenti di Chris.


1
Dopo aver provato vari approcci per aggirare questo, ho deciso che la soluzione migliore è semplicemente ricodificare l'app per ricreare il contesto. Questo è un approccio così dispendioso ma non sembra che ci sia una buona soluzione code.google.com/p/android/issues/detail?id=12774
Nick Gotch,

9

Vorrei aggiungere un'altra risposta a questa cosa che mi è stata tramandata un paio di anni fa da Chris Pruett (Replica Island, Wind-Up Knight, ecc.). È particolarmente utile qui nel 2013 poiché setPreserveEglContextOnPause (true) sembra non funzionare su 4.3. (Potrei sbagliarmi su questo, ma è così che mi sembra adesso mentre aggiorno il codice di gioco toccato l'ultima volta nel 2011).

Fondamentalmente il trucco è quello di staccare GLSurfaceView dalla gerarchia della vista da onPause () della tua attività. Dal momento che non è nella gerarchia della vista nel momento in cui viene eseguito OnPause (), il contesto non viene mai distrutto.

Quindi la tua attività su onPause () dovrebbe apparire così:

@Override
public void onPause() {
    view.setVisibility(View.GONE);
    super.onPause();
    ...
}

E ripristini GLSurfaceView nella gerarchia non da onResume () ma da onWindowFocusChanged ():

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && view.getVisibility() == View.GONE) {
         view.setVisibility(View.VISIBLE);
    }
    ...
}

Si noti che non si chiama mai onPause () e onResume () di GLSurfaceView e che questo è l'SDK GLSurfaceView ufficiale, non è richiesta alcuna versione alternativa compromessa.


In realtà questo approccio non sembra funzionare. Chris Pruett lo ha utilizzato in combinazione con Unity, è possibile che questa soluzione alternativa non funzioni nei progetti nativi? Ma solo non chiamare GLView.onPause () sembra funzionare !? Ci sono altri che possono confermarlo?
sjkm

Ha funzionato per me su Android 2.2 OpenGl ES 2. Non sono sicuro di come si comporta su altri dispositivi. Ho un telefono LG G2 D802. Qualcuno sa se questa è una soluzione generale?
Pixel,

7

<rant> Ho passato molto tempo su questo problema, ho provato molte soluzioni diverse e nessuna ha funzionato fino ad oggi, penso che questa sia una delle decisioni di design più terribili che abbia mai visto, ma non provengo dal team Android davvero sorpreso. </ rant>

Quindi, la soluzione è spostare il membro eglContext verso l'alto e renderlo statico (globale) in modo che non venga distrutto, quindi devi semplicemente verificare se è nullo o meno prima di crearlo nuovamente.

Finora questa soluzione sembra funzionare per noi e non ci importa se si rompe sui dispositivi del 2005.


4

Usa API a nido d'ape. C'è un'opzione per preservare il tuo contesto OGL. Altrimenti, devi ricaricare il tuo contesto. Non è difficile né doloroso.

Devi capire che esistono due casi (Android 2.1):

  • Pausa dello schermo: l'applicazione è sempre quella iniziale => hack disponibile
  • Pausa applicazione: esiste un'altra applicazione frontale => Nessuna soluzione

Nota: il vecchio gpus Android non supporta il multi contesto. Quindi il contesto aperto viene perso quando si passa a un'altra applicazione => nessuna soluzione disponibile (è possibile hackerare per preservare il contesto in pausa dello schermo).

Nota 2: la funzione HoneyComb è setPreserveEGLContextOnPause


1
Sto trasferendo un gioco C ++ su Android NDK che non è progettato per ricaricare facilmente il contesto, quindi almeno per me è un po 'difficile. Difficoltà a parte, ricaricare le trame introdurrà un ritardo che non è presente sui nostri altri dispositivi (iPhone, PSP, DS, ecc.) Lieto di sentire correzioni a nido d'ape, ma purtroppo dobbiamo supportare 2.1+
Nick Gotch,

1
Questo non risponde affatto alla sua domanda.
notlesh,

1
Se non capisci, non puoi farlo.
Ellis,

2

Alcune delle risposte qui sono ragionevoli per i primi usi di OpenGL ES su Android. I primi dispositivi GLES supportavano solo un singolo contesto, quindi GLSurfaceView è stato progettato per eliminare in modo aggressivo lo stato. Convincere GLSurfaceView a fare altrimenti non è facile.

Per le versioni più recenti di Android (probabilmente qualsiasi cosa che utilizza GLES 2.x), la risposta migliore è utilizzare un semplice SurfaceView e fare la propria gestione EGL e thread. Puoi trovare più esempi di GLES usati con un semplice SurfaceView in Grafika , inclusa una libreria di classi semplici per creare e distruggere contesti EGL.

È comunque buona norma scaricare lo stato quando un'app passa in background, ma per esempio da Chris Pruett - dove l'app era ancora in primo piano, ma l'attività che ospita GLSurfaceView è stata spostata - non ha alcun valore nel strappare nel contesto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.