Solution de rechange pour perdre le contexte OpenGL lorsque Android se met en pause?

40

La documentation Android dit:

Il y a des situations où le contexte de rendu EGL sera perdu. Cela se produit généralement lorsque l'appareil se réveille après s'être endormi. Lorsque le contexte EGL est perdu, toutes les ressources OpenGL (telles que les textures) associées à ce contexte sont automatiquement supprimées. Afin de continuer à rendre le rendu correctement, un moteur de rendu doit recréer toutes les ressources perdues dont il a encore besoin. La méthode onSurfaceCreated (GL10, EGLConfig) est un moyen pratique de le faire.

Mais devoir recharger toutes les textures dans le contexte OpenGL est à la fois pénible et nuit à l'expérience de jeu de l'utilisateur lorsqu'il ressaisit l'application après une pause. Je sais que "Angry Birds" évite en quelque sorte cela, je cherche des suggestions sur la façon d'accomplir la même chose?

Je travaille avec l’Android NDK r5 (version CrystaX.) J'ai trouvé le piratage possible, mais j’essaie d’éviter de créer une version complète du SDK personnalisé.

Nick Gotch
la source
dormir et se réveiller est considéré comme un appareil. Ainsi, lorsqu'un utilisateur met juste en pause votre jeu ou change de processus, il perd tout le contexte EGL.
Ali1S232

Réponses:

22

L'île de réplication dispose d'une version modifiée de GLSurfaceView qui traite ce problème (et fonctionne avec les versions antérieures d'Android). Selon Chris Pruett :

Fondamentalement, j'ai piraté le GLSurfaceView d'origine pour résoudre un problème très spécifique: je voulais accéder à différentes activités au sein de mon application sans jeter tous mes états OpenGL. Le changement majeur consistait à séparer la surface EGLS du contexte EGLContext et à jeter le premier onPause (), tout en préservant le dernier jusqu'à ce que le contexte soit explicitement perdu. L'implémentation par défaut de GLSurfaceView (que je n'ai pas écrite, d'ailleurs), supprime tous les états du GL lorsque l'activité est suspendue et appelle onSurfaceCreated () à la reprise. Cela signifiait que, lorsqu'une boîte de dialogue apparaissait dans mon jeu, la fermer entraînait un retard car toutes les textures devaient être rechargées.

Vous devez utiliser le GLSurfaceView par défaut. Si vous devez avoir la même fonctionnalité que la mienne, vous pouvez regarder la mienne. Mais ce que j'ai fait a exposé toutes sortes de bogues de pilotes affreux dans certains combinés (voir les très longs commentaires vers la fin de ce fichier), et vous pouvez éviter tout ce désordre en utilisant simplement celui par défaut.

Edit: Je viens de réaliser que vous avez déjà posté le lien vers un hack similaire. Je ne pense pas qu'il existe une solution intégrée avant le nid d'abeilles. Replica Island est un jeu populaire fonctionnant sur de nombreux appareils. L'implémentation et les commentaires de Chris peuvent être utiles.

Firas Assaad
la source
1
Après avoir essayé diverses approches pour contourner cela, j'ai décidé que la meilleure solution consistait simplement à recoder l'application pour recréer le contexte. C'est une approche tellement inutile, mais il ne semble pas y avoir de bonne solution. Code.google.com/p/android/issues/detail?id=12774
Nick Gotch
9

J'aimerais ajouter une autre réponse à cela qui m'a été transmise un an ou deux en arrière par Chris Pruett (Replica Island, Wind-Up Knight, etc.). C'est particulièrement utile ici en 2013, car setPreserveEglContextOnPause (true) ne semble pas fonctionner avec la version 4.3. (Je peux me tromper à ce sujet mais c'est ainsi que je vois les choses lorsque je met à jour le code du jeu pour la dernière fois en 2011).

L'essentiel est de détacher votre GLSurfaceView de la hiérarchie des vues de votre activité onPause (). Etant donné que ce n'est pas dans la hiérarchie de la vue au moment où onPause () est exécuté, le contexte n'est jamais détruit.

Donc, votre activité onPause () devrait ressembler à ceci:

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

Et vous restaurez votre GLSurfaceView dans la hiérarchie non pas de onResume () mais de onWindowFocusChanged ():

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

Notez que vous n’appelez jamais onPause () et onResume () de GLSurfaceView et qu’il s’agit du SDK GLSurfaceView officiel, aucune version alternative piratée n’est requise.

Reuben Scratton
la source
En fait, cette approche ne semble pas fonctionner. Chris Pruett l'a utilisé en association avec Unity. Est-il possible que cette solution de contournement ne fonctionne pas dans les projets natifs? Mais ne pas appeler GLView.onPause () semble fonctionner!? Y a-t-il d'autres personnes qui peuvent le confirmer?
sjkm
Cela a fonctionné pour moi ciblé sur Android 2.2 OpenGl ES 2. Je ne sais pas comment cela fonctionne sur d'autres appareils. J'ai un téléphone LG G2 D802. Est-ce que quelqu'un sait s'il s'agit d'une solution générale?
Pixel
7

<rant> J'ai passé énormément de temps sur ce problème, j'ai essayé de nombreuses solutions différentes et aucune ne fonctionnait jusqu'à aujourd'hui, je pense que c'est l'une des décisions de conception les plus terribles que j'ai jamais vues, mais venant de l'équipe Android, je ne suis pas vraiment surpris. </ rant>

La solution consiste donc à déplacer le membre eglContext vers le haut et à le rendre statique (global) afin qu'il ne soit pas détruit. Il vous suffit ensuite de vérifier s'il est nul ou non avant de le recréer.

Jusqu'à présent, cette solution semble fonctionner pour nous, et peu importe si elle tombe en panne sur les appareils 2005.

Stéphane Cocquereaumont
la source
4

Utilisez l'API en nid d'abeille. Il existe une option qui préserve votre contexte OGL. Sinon, vous devez recharger votre contexte. Ce n'est pas difficile ni douloureux.

Vous devez comprendre qu'il existe deux cas (Android 2.1):

  • Screen Pause: votre application est toujours la première => bidouille disponible
  • Application Pause: il y a une autre application frontale => No Solution

Remarque: les anciens gpus android ne prennent pas en charge le contexte multiple. Ainsi, le contexte opengl est perdu lorsque vous passez à une autre application => aucune solution disponible (vous pouvez bidouiller pour préserver votre contexte lors d'une pause à l'écran).

Remarque 2: la fonction HoneyComb est setPreserveEGLContextOnPause

Ellis
la source
1
Je porte un jeu C ++ sur le NDK Android qui n'est pas conçu pour recharger facilement le contexte. C'est donc un peu difficile pour moi. Les difficultés mises à part, le rechargement des textures introduira un délai qui n'est pas présent sur nos autres appareils (iPhone, PSP, DS, etc.). Nous sommes ravis d'entendre alvéoler résoudre ce problème, mais malheureusement, nous devons prendre en charge la version 2.1
Nick Gotch,
1
Cela ne répond pas du tout à sa question.
Notlesh
1
Si vous ne comprenez pas, vous ne pouvez pas le faire.
Ellis
2

Certaines des réponses fournies ici sont raisonnables pour les premières utilisations d’OpenGL ES sur Android. Les premiers appareils GLES ne prenaient en charge qu'un seul contexte. GLSurfaceView a donc été conçu pour éliminer de manière agressive l'état. Convaincre GLSurfaceView de faire autrement n'est pas facile.

Pour les versions plus récentes d'Android (probablement tout ce qui utilise GLES 2.x), la meilleure solution consiste à utiliser un SurfaceView standard et à gérer vous-même vos EGL et vos threads. Vous pouvez trouver plusieurs exemples de GLES utilisés avec un SurfaceView brut dans Grafika , notamment une bibliothèque de classes simples pour la création et la destruction de contextes EGL.

C'est toujours une bonne pratique de décharger l'état lorsqu'une application passe en arrière-plan, mais pour l'exemple de Chris Pruett - où l'application était toujours au premier plan, mais où l'activité hébergeant GLSurfaceView a été modifiée - rien ne sert de rien de se déchirer dans le contexte.

fadden
la source