Nexus 10 – renderizar a rendertarget externo sólo funciona en paisaje

Estamos desarrollando fondos de pantalla en vivo con OpenGL ES 2.0 en Nexus 10.

Live wallpaper utiliza 2 pequeños (128×128) framebuffers externos para hacer render de ping-pong entre ellos para desenfocar la imagen.

Si bien esto funciona perfectamente bien en cualquier dispositivo (incluso en los antiguos Motorola Milestone) hay un extraño problema con Nexus 10. Esto sólo funciona si el dispositivo está en orientación horizontal. Si el dispositivo se gira en cualquier otra posición (90, 180 o 270 grados) framebuffers tiene sólo color claro. He fijado glClearColor al rojo así que es claramente visible que estos framebuffers se despejan pero nada se rinde en ellos.

Lo he probado en Tegra 2, Tegra 3, Adreno 200, Adreno 320, 2 PowerVR GPUs y funciona muy bien.

Esto parece un error de controlador extraño, pero también podría ser algunos detalles del controlador de Malí. Por favor aconséjame.

Extractos de código.

Init framebuffers:

 private void initBloomStuff() { mBloomTextureID = loadTexture("textures/empty128.png"); mBloomVertTextureID = loadTexture("textures/empty128.png"); mBloomFBHeight = 128; mBloomFBWidth = 128; float blurSize = 1.0f; // Texel offset for blur filter kernel m_fTexelOffset = 1.0f / mBloomFBWidth / blurSize; ByteBuffer tmpFB, tmpRB; IntBuffer handle, renderbuffers; int result; tmpFB = ByteBuffer.allocateDirect(4); tmpFB.order(ByteOrder.nativeOrder()); handle = tmpFB.asIntBuffer(); GLES20.glGenFramebuffers(1, handle); framebufferHandle = handle.get(0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomTextureID); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferHandle); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mBloomTextureID, 0); checkGlError("FB 1"); tmpRB = ByteBuffer.allocateDirect(4); renderbuffers = tmpRB.asIntBuffer(); GLES20.glGenRenderbuffers(1, renderbuffers); checkGlError("FB 1 - glGenRenderbuffers"); depthbufferHandle = renderbuffers.get(0); GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthbufferHandle); checkGlError("FB 1 - glBindRenderbuffer"); GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, mBloomFBWidth, mBloomFBHeight); checkGlError("FB 1 - glRenderbufferStorage"); GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthbufferHandle); checkGlError("FB 1 - glFramebufferRenderbuffer"); result = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); if (result != GLES20.GL_FRAMEBUFFER_COMPLETE) { Log.d(TAG, "Error creating framebufer 1: " + result); } else { Log.d(TAG, "Created framebufer 1: " + result); } GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); tmpFB = ByteBuffer.allocateDirect(4); tmpFB.order(ByteOrder.nativeOrder()); handle = tmpFB.asIntBuffer(); GLES20.glGenFramebuffers(1, handle); framebufferVertHandle = handle.get(0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomVertTextureID); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferVertHandle); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mBloomVertTextureID, 0); checkGlError("FB 2"); tmpRB = ByteBuffer.allocateDirect(4); renderbuffers = tmpRB.asIntBuffer(); GLES20.glGenRenderbuffers(1, renderbuffers); checkGlError("FB 2 - glGenRenderbuffers"); depthbufferVertHandle = renderbuffers.get(0); GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthbufferVertHandle); checkGlError("FB 2 - glBindRenderbuffer"); GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, mBloomFBWidth, mBloomFBHeight); checkGlError("FB 2 - glRenderbufferStorage"); GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthbufferVertHandle); checkGlError("FB 2 - glFramebufferRenderbuffer"); result = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); if (result != GLES20.GL_FRAMEBUFFER_COMPLETE) { Log.d(TAG, "Error creating framebufer 2: " + result); } else { Log.d(TAG, "Created framebufer 2: " + result); } GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); mTriangleVerticesVignette = ByteBuffer.allocateDirect(mQuadTriangles.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); mTriangleVerticesVignette.put(mQuadTriangles).position(0); } 

Render a FB:

  GLES20.glUseProgram(mProgram); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mStemTextureID); drawBird(); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mSphereTextureID); drawSphere(); GLES20.glViewport(0, 0, mBloomFBWidth, mBloomFBHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferHandle); // GLES20.glBindRenderbuffer(GLES20.GL_FRAMEBUFFER, depthbufferHandle); GLES20.glClearColor(1.0f, 0.5f, 0.5f, 1.0f); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mStemTextureID); drawBird(); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mSphereTextureID); drawSphere(); GLES20.glViewport(0, 0, screenWidth, screenHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); // GLES20.glBindRenderbuffer(GLES20.GL_FRAMEBUFFER, 0); 

Rendimiento de Ping-pong entre 2 FBs para desenfocar la imagen:

  GLES20.glUseProgram(mBloomProgram); GLES20.glUniform1i(mBloom_sTexture, 0); GLES20.glUniform1f(mBloom_bloomFactor, 0.8f); GLES20.glActiveTexture(GL10.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomTextureID); GLES20.glViewport(0, 0, mBloomFBWidth, mBloomFBHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferVertHandle); GLES20.glUniform1f(mBloom_TexelOffsetX, m_fTexelOffset); GLES20.glUniform1f(mBloom_TexelOffsetY, 0.0f); GLES20.glActiveTexture(GL10.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomTextureID); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); drawBloom(); GLES20.glViewport(0, 0, screenWidth, screenHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glActiveTexture(GL10.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomVertTextureID); GLES20.glViewport(0, 0, mBloomFBWidth, mBloomFBHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferHandle); GLES20.glUniform1f(mBloom_TexelOffsetX, 0.0f); GLES20.glUniform1f(mBloom_TexelOffsetY, m_fTexelOffset); GLES20.glActiveTexture(GL10.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomVertTextureID); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); drawBloom(); GLES20.glViewport(0, 0, screenWidth, screenHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glActiveTexture(GL10.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomTextureID); GLES20.glViewport(0, 0, mBloomFBWidth, mBloomFBHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferVertHandle); GLES20.glUniform1f(mBloom_TexelOffsetX, m_fTexelOffset / 2); GLES20.glUniform1f(mBloom_TexelOffsetY, m_fTexelOffset / 2); GLES20.glActiveTexture(GL10.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mBloomTextureID); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); drawBloom(); GLES20.glViewport(0, 0, screenWidth, screenHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 

Aplicación de prueba para reproducir errores

Puede descargar la prueba de APK aquí: https://dl.dropboxusercontent.com/u/7197208/LiveWallpaperAnimTest.apk Es una aplicación de fondo de pantalla en vivo, instálela y seleccione 'Prueba' de fondo de pantalla en vivo (tiene un icono con rosa).

Como se puede ver, en la orientación horizontal predeterminada verá un efecto "flor" alrededor de pájaro que se implementa mediante la representación de ping-pong entre 2 framebuffers. En cualquier otra orientación del dispositivo no funciona y llena el FB con un color claro (rojo).

Enlaces adicionales

También he publicado este problema en Mali Developer Center y Google Code :

Http://forums.arm.com/index.php?/topic/16894-nexus-10-render-to-external-rendertarget-works-only-in-landscape/

Http://code.google.com/p/android/issues/detail?id=57391

El equipo de soporte del conductor de Mali confirma que se trata de un error de controlador y lo arreglará en la próxima versión del controlador. Como una solución temporal, se proponen llamar a glViewport después de glBindFramebuffer . Esta solución funciona – después de cambiar el orden de las llamadas, logré obtener la representación correcta.

He propuesto a Google que contacte a ARM para incluir un controlador OpenGL ES fijo para Nexus 10 tan pronto como el controlador fijo esté disponible. Si alguien está dispuesto a que esto suceda más rápido, puede marcar un problema en el rastreador de errores de Android: http://code.google.com/p/android/issues/detail?id=57391

Enlace a los foros de Mali Developer Center donde he presentado este problema: http://forums.arm.com/index.php?/topic/16894-nexus-10-render-to-external-rendertarget-works-only-in -paisaje/

A medida que este error del controlador es verificado y confirmado por ARM, por favor vaya y lance la edición de Android # 57391 en vez de votar por esta pregunta de SO. Esperemos que esto obligará a Google a prestar atención a la cuestión ya liberar una actualización de firmware, haciendo Nexus 10 mejor. Esta es la razón por la que he asignado recompensa para esta pregunta.

EDITAR. Este error finalmente se soluciona en Android 4.4.

  • Android OpenGL ES 2,0 coordenadas de la pantalla a las coordenadas del mundo
  • Gestión adecuada de la memoria en OpenGL en dispositivos Android
  • Android OpenGL2.0 que muestra texturas negras
  • Transparencia de OpenGL ES 2.0 mediante pruebas alfa en shader
  • Varias cámaras en libgdx (probablemente similar en otros marcos)
  • EGL vs GLES 2.0 en Android (por ejemplo, Java)
  • Alternativa para glBlitFrameBuffer () en OpenGL ES 2.0
  • OpenGL ES 2.0 texture2D sesgo / lod
  • ¿Cuántos dispositivos Android soportan GLSurfaceView.setPreserveEGLContextOnPause hoy?
  • Cómo depurar el error de OPENGL ES 2.0 en Nexus 4 sin tener el dispositivo?
  • Cómo utilizar correctamente glDiscardFramebufferEXT
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.