GLES10.glGetIntegerv devuelve 0 sólo en Lollipop

Este pedazo de código solía trabajar en mi Nexus 7 2012 KitKat:

int[] maxSize = new int[1]; GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0); 

En KitKat puedo obtener el valor máximo de píxeles correctamente, pero después de la actualización a la imagen de fábrica Lollipop este fragmento de código causa problema, ya que sólo devuelve 0. El logcat mostró esta salida cuando llegó a este método:

 E/libEGL﹕ call to OpenGL ES API with no current context (logged once per thread) 

Ya tengo android:hardwareAccelerated="true" en mi Manifest.xml. ¿Hay cambios de API que no conozco, que hacen que el código anterior no sea posible? Por favor avise.

El registro de errores señala el problema básico muy claramente:

Llamada a OpenGL ES API sin contexto actual (registrada una vez por hilo)

Necesitas un contexto OpenGL actual en tu subproceso antes de poder realizar llamadas OpenGL, que incluye tu llamada glGetIntegerv() . Esto siempre fue cierto. Pero parece que en pre-Lollipop, había un contexto OpenGL que se creó en los marcos, y que a veces (siempre?) Actual cuando se llamó el código de la aplicación.

No creo que esto haya sido nunca documentado o comportamiento previsto. Siempre se supone que las aplicaciones creaban explícitamente un contexto y lo hacían actual, si querían hacer llamadas OpenGL. Y parece que esto se aplica más estrictamente en Lollipop.

Existen dos enfoques principales para crear un contexto de OpenGL:

  • Cree un GLSurfaceView ( documentación ). Este es el enfoque más fácil y conveniente, pero sólo tiene sentido si planea realizar el renderizado de OpenGL en la pantalla.
  • Utilice EGL14 ( documentación ). Esto proporciona una interfaz de nivel inferior que le permite completar la configuración necesaria para llamadas OpenGL sin crear una vista o representación en la pantalla.

El enfoque GLSurfaceView está ampliamente documentado con ejemplos y tutoriales en todo el lugar. Así que me centraré en el enfoque EGL.

Uso de EGL14 (API nivel 17)

El siguiente código asume que le interesa ES 2.0, algunos valores de atributo tendrían que ser ajustados para otras versiones de ES.

Al inicio del archivo, importe la clase EGL14 y algunas clases relacionadas:

 import android.opengl.EGL14; import android.opengl.EGLConfig; import android.opengl.EGLContext; import android.opengl.EGLDisplay; import android.opengl.EGLSurface; import android.opengl.GLES20; 

A continuación, obtener una retención de la pantalla predeterminada, e inicializar. Esto podría ser más complejo si tienes que tratar con dispositivos que podrían tener múltiples pantallas, pero será suficiente para un típico teléfono / tablet:

 EGLDisplay dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); int[] vers = new int[2]; EGL14.eglInitialize(dpy, vers, 0, vers, 1); 

A continuación, tenemos que encontrar una configuración. Dado que no vamos a utilizar este contexto para la prestación, los atributos exactos no son muy críticos:

 int[] configAttr = { EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER, EGL14.EGL_LEVEL, 0, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT, EGL14.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] numConfig = new int[1]; EGL14.eglChooseConfig(dpy, configAttr, 0, configs, 0, 1, numConfig, 0); if (numConfig[0] == 0) { // TROUBLE! No config found. } EGLConfig config = configs[0]; 

Para hacer un contexto actual, que necesitaremos más tarde, necesitará una superficie de renderizado, incluso si realmente no planea procesar. Para satisfacer este requisito, cree una pequeña superficie fuera de pantalla (Pbuffer):

 int[] surfAttr = { EGL14.EGL_WIDTH, 64, EGL14.EGL_HEIGHT, 64, EGL14.EGL_NONE }; EGLSurface surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0); 

A continuación, cree el contexto:

 int[] ctxAttrib = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE }; EGLContext ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0); 

Listo para hacer el contexto actual:

 EGL14.eglMakeCurrent(dpy, surf, surf, ctx); 

Si todo lo anterior tuvo éxito (se omitió la comprobación de errores), puede realizar ahora sus llamadas OpenGL:

 int[] maxSize = new int[1]; GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0); 

Una vez que haya terminado, puede derribar todo:

 EGL14.eglMakeCurrent(dpy, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); EGL14.eglDestroySurface(dpy, surf); EGL14.eglDestroyContext(dpy, ctx); EGL14.eglTerminate(dpy); 

Uso de EGL10 (API nivel 1)

Si necesita algo que funcione para los niveles anteriores, puede utilizar EGL10 ( documentación ) en lugar de EGL14, que está disponible desde el nivel 1 de la API. El código anterior adoptado para 1.0 se parece a esto:

 import android.opengl.GLES10; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; EGL10 egl = (EGL10)EGLContext.getEGL(); EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] vers = new int[2]; egl.eglInitialize(dpy, vers); int[] configAttr = { EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER, EGL10.EGL_LEVEL, 0, EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] numConfig = new int[1]; egl.eglChooseConfig(dpy, configAttr, configs, 1, numConfig); if (numConfig[0] == 0) { // TROUBLE! No config found. } EGLConfig config = configs[0]; int[] surfAttr = { EGL10.EGL_WIDTH, 64, EGL10.EGL_HEIGHT, 64, EGL10.EGL_NONE }; EGLSurface surf = egl.eglCreatePbufferSurface(dpy, config, surfAttr); final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; // missing in EGL10 int[] ctxAttrib = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL10.EGL_NONE }; EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, ctxAttrib); egl.eglMakeCurrent(dpy, surf, surf, ctx); int[] maxSize = new int[1]; GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0); egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl.eglDestroySurface(dpy, surf); egl.eglDestroyContext(dpy, ctx); egl.eglTerminate(dpy); 

Tenga en cuenta que esta versión del código utiliza un contexto ES 1.x. El tamaño de textura máximo reportado puede ser diferente para ES 1.x y ES 2.0.

El mensaje de error indica que está llamando a la función GLES antes de que exista el contexto de OpenGL ES. He encontrado que KitKat es más estricto acerca de la corrección en varias áreas por lo que puede ser la razón del problema que aparece ahora, o puede haber alguna diferencia en el orden en que está iniciando la aplicación que está causando. Si publicó más de su código de inicialización, la razón puede ser más clara.

Normalmente tiene una clase que implementa GLSurfaceView.Renderer que tiene una función:

 public void onSurfaceCreated(GL10 gl, EGLConfig config) 

En esta función, deberías poder llamar a gl.glGetIntegerv forma segura ya que en este momento sabes que se ha creado el contexto de OpenGL ES. Si lo está llamando antes que esto, entonces eso explicaría el error que está viendo.

  • 'Texture2D': No se encontró ninguna función sobrecargada equivalente OpenGL ES2 Android (JAVA)
  • ¿Hay una solución de reproducción de video más rápida que las funciones de AndroidBitmap_xxx?
  • Android - OpenGL ES 2.0: Emulador (Works) - Dispositivo (no)
  • GLSurfaceView onDrawFrame comportamiento de borrado
  • ¿Cómo acceder a OpenGL ES 2 a través de C ++ / NDK si EGL_NATIVE_RENDERABLE no es compatible?
  • Android Canvas o Open GL ES para el juego 2d?
  • OpenGL en Android versus iOS: optimizaciones, y donde difieren
  • ¿Es posible deformar la imagen subyacente con OpenGL ES 2.0?
  • Texturas OpenGL de Android: crearlas y borrarlas al vuelo
  • Simple particle system en Android usando OpenGL ES 1.0
  • Error de procesamiento de instancia de OpenGL ES 3, pero funciona en escritorio
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.