¿Solución para perder el contexto OpenGL cuando Android hace una pausa?

40

La documentación de Android dice:

Hay situaciones en las que se perderá el contexto de representación EGL. Esto generalmente ocurre cuando el dispositivo se despierta después de ir a dormir. Cuando se pierde el contexto EGL, todos los recursos de OpenGL (como las texturas) asociados con ese contexto se eliminarán automáticamente. Para seguir renderizando correctamente, un renderizador debe recrear los recursos perdidos que aún necesita. El método onSurfaceCreated (GL10, EGLConfig) es un lugar conveniente para hacer esto.

Pero tener que volver a cargar todas las texturas en el contexto de OpenGL es a la vez doloroso y perjudica la experiencia del juego para el usuario cuando vuelve a ingresar a la aplicación después de una pausa. Sé que "Angry Birds" de alguna manera evita esto, ¿estoy buscando sugerencias sobre cómo lograr lo mismo?

Estoy trabajando con Android NDK r5 (versión CrystaX). Encontré este posible truco para el problema, pero estoy tratando de evitar construir una versión SDK personalizada completa.

Nick Gotch
fuente
dormir y despertar se refiere al dispositivo, por lo que mientras el usuario simplemente detiene el juego o cambia los procesos, debería perder parte del contexto EGL.
Ali1S232

Respuestas:

22

Replica Island tiene una versión modificada de GLSurfaceView que trata este problema (y funciona con versiones anteriores de Android). De acuerdo con Chris Pruett :

Básicamente, pirateé el GLSurfaceView original para resolver un problema muy específico: quería ir a diferentes actividades dentro de mi aplicación sin tirar todo mi estado OpenGL. El cambio principal fue separar el EGLSurface del EGLContext y desechar el primero en Pause (), pero preservar el último hasta que el contexto se pierda explícitamente. La implementación predeterminada de GLSurfaceView (que no escribí, por cierto), arroja todo el estado GL cuando la actividad está en pausa y llama a OnSurfaceCreated () cuando se reanuda. Eso significó que, cuando apareció un cuadro de diálogo en mi juego, cerrarlo incurrió en un retraso porque todas las texturas tuvieron que volver a cargarse.

Debe usar el GLSurfaceView predeterminado. Si debe tener la misma funcionalidad que la mía, puede mirar la mía. Pero haciendo lo que hice expuso todo tipo de errores de controladores horribles en algunos teléfonos (vea los comentarios muy largos cerca del final de ese archivo), y puede evitar todo ese lío simplemente usando el predeterminado.

Editar: Me acabo de dar cuenta de que ya has publicado el enlace a un truco similar. No creo que haya una solución incorporada antes del panal. Replica Island es un juego popular que funciona en muchos dispositivos y es posible que la implementación y los comentarios de Chris sean útiles.

Firas Assaad
fuente
1
Después de probar varios enfoques para evitar esto, he decidido que la mejor solución es volver a codificar la aplicación para recrear el contexto. Este es un enfoque tan derrochador, pero no parece que haya una buena solución code.google.com/p/android/issues/detail?id=12774
Nick Gotch
9

Me gustaría agregar otra respuesta a esto que Chris Pruett me transmitió hace uno o dos años (Replica Island, Wind-Up Knight, etc.). Es especialmente útil aquí en 2013, ya que setPreserveEglContextOnPause (true) no parece funcionar en 4.3. (Podría estar equivocado al respecto, pero así es como me parece ahora que actualizo el último código del juego en 2011).

Básicamente, el truco es separar su GLSurfaceView de la jerarquía de vistas de onPause () de su Actividad. Como no está en la jerarquía de vistas en el punto en que se ejecuta onPause (), el contexto nunca se destruye.

Entonces su Actividad en onPause () debería verse así:

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

Y restaura su GLSurfaceView a la jerarquía no desde onResume () sino desde onWindowFocusChanged ():

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

Tenga en cuenta que nunca llama a onPause () y onResume () de GLSurfaceView y que este es el SDK GLSurfaceView oficial, no se requiere una versión alternativa pirateada.

Reuben Scratton
fuente
De hecho, este enfoque no parece funcionar. Chris Pruett lo usó en combinación con Unity, ¿es posible que esta solución no funcione en proyectos nativos? ¿Pero solo no llamar a GLView.onPause () parece funcionar? ¿Hay otros que puedan confirmar esto?
sjkm
Funcionó para mí dirigido a Android 2.2 OpenGl ES 2. No estoy seguro de cómo funciona en otros dispositivos. Tengo un teléfono LG G2 D802. ¿Alguien sabe si esta es una solución general?
Pixel
7

<rant> Pasé una gran cantidad de tiempo en este problema, probé muchas soluciones diferentes y ninguna funcionó hasta hoy, creo que esta es una de las decisiones de diseño más horribles que he visto, pero al provenir del equipo de Android no estoy Realmente sorprendido. </ rant>

Por lo tanto, la solución es mover el miembro eglContext hacia arriba y hacerlo estático (global) para que no se destruya, entonces simplemente debe verificar si es nulo o no antes de volver a crearlo.

Hasta ahora, esta solución parece funcionar para nosotros, y no nos importa si se rompe en los dispositivos de 2005.

Stephane Cocquereaumont
fuente
4

Use API de panal. Hay una opción para preservar su contexto OGL. De lo contrario, debe volver a cargar su contexto. No es difícil ni doloroso.

Debe comprender que hay dos casos (Android 2.1):

  • Pausa de pantalla: su aplicación siempre es la delantera => hack disponible
  • Pausa de la aplicación: hay otra aplicación frontal => Sin solución

Nota: los viejos gpus de Android no son compatibles con el contexto múltiple. Por lo tanto, el contexto opengl se pierde cuando cambia a otra aplicación => no hay solución disponible (puede piratear para preservar su contexto en la pausa de la pantalla).

Nota 2: la función HoneyComb se establece en PreserveEGLContextOnPause

Ellis
fuente
1
Estoy transfiriendo un juego C ++ al NDK de Android que no está diseñado para recargar el contexto fácilmente, por lo que al menos para mí es algo difícil. Dejando a un lado las dificultades, la recarga de texturas introducirá una demora que no está presente en nuestros otros dispositivos (iPhone, PSP, DS, etc.) Me alegra escuchar que Honeycomb corrige esto, pero desafortunadamente tenemos que admitir 2.1+
Nick Gotch
1
Esto no responde a su pregunta en absoluto.
notlesh
1
Si no entiendes, no puedes hacerlo.
Ellis
2

Algunas de las respuestas aquí son razonables para los primeros usos de OpenGL ES en Android. Los primeros dispositivos GLES solo admitían un contexto único, por lo que GLSurfaceView fue diseñado para descartar agresivamente el estado. Convencer a GLSurfaceView de hacer lo contrario no es fácil.

Para versiones más recientes de Android (probablemente cualquier cosa que use GLES 2.x), la mejor respuesta es usar un SurfaceView simple y hacer su propio EGL y administración de hilos. Puede encontrar varios ejemplos de GLES utilizados con un SurfaceView simple en Grafika , incluida una biblioteca de clases simples para crear y destruir contextos EGL.

Todavía es una buena práctica descargar el estado cuando una aplicación pasa a segundo plano, pero para el ejemplo de Chris Pruett, donde la aplicación todavía estaba en primer plano, pero la actividad que alojaba GLSurfaceView fue desactivada, no hay ningún valor en el desgarro por el contexto.

fadden
fuente