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.
- GLES2.0: Usar GL_TEXTURE_EXTERNAL_OES a través de glEGLImageTargetTexture2DOES
- Intermitentes marcos lentos en Android OpenGL ES 2.0
- Optimización de un dibujo VBO enorme en dispositivos Android / iOS
- Multiplicación apropiada de matrices para rotación / traducción
- ¿Por qué mi programa de shader openGL para puntos tiene anillos de artefactos?
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
- ¿Es posible establecer un valor inicial para un uniforme de sombreado? (Android, OpenGL ES 2.0)
- Actualización de la textura con glTexImage2D (..) en OpenglES2
- ¿Cómo se consigue rajawali para trabajar (tutorial 1 en git)
- Solución para escribir el valor del búfer de profundidad / profundidad en OpenGL ES 2.0
- Google Maps Android API V2 no muestra mapas en Emulator
- Android 3D objeto con la física
- OpenGL ES 2.0 - Texturas siempre negras
- OpenGL ES 2 - un gran vs múltiples texturas pequeñas atlas. ¿Cuál es el mejor rendimiento?
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.
- Cómo crear una nueva hoja de cálculo mediante programación en Android / Java?
- Compilar la herramienta linux perf para android