GlClear () toma demasiado tiempo – Android OpenGL ES 2

Estoy desarrollando una aplicación para Android con OpenGL ES 2. El problema que estoy encontrando es que la función glClear() está tomando tanto tiempo para procesar que el juego aparece nervioso cuando los fotogramas se retrasan. La salida de una ejecución del programa con sondas de temporización muestra que mientras que la configuración de todos los vértices y las imágenes del atlas sólo toma menos de 1 milisegundo, glClear() toma entre 10 y 20 milisegundos. De hecho, la compensación a menudo toma hasta el 95% del tiempo total de representación. Mi código se basa en tutoriales comunes, y la función Render es la siguiente:

 private void Render(float[] m, short[] indices) { Log.d("time", "--START RENDER--"); // get handle to vertex shader's vPosition member int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition"); // Enable generic vertex attribute array GLES20.glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, true, 0, vertexBuffer); // Get handle to texture coordinates location int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord" ); // Enable generic vertex attribute array GLES20.glEnableVertexAttribArray ( mTexCoordLoc ); // Prepare the texturecoordinates GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, uvBuffer); // Get handle to shape's transformation matrix int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix"); // Apply the projection and view transformation GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, m, 0); // Get handle to textures locations int mSamplerLoc = GLES20.glGetUniformLocation (riGraphicTools.sp_Image, "s_texture" ); // Set the sampler texture unit to 0, where we have saved the texture. GLES20.glUniform1i ( mSamplerLoc, 0); long clearTime = System.nanoTime(); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); Log.d("time", "Clear time is " + (System.nanoTime() - clearTime)); // Draw the triangles GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); GLES20.glDisableVertexAttribArray(mTexCoordLoc); Log.d("time", "--END RENDER--"); } 

He intentado mover el png atlas a /drawable-nodpi pero no tuvo ningún efecto.

He intentado usar las glFlush() y de glFinish() también. Curiosamente, si no llamo glClear() entonces se debe llamar automáticamente. Esto se debe a que el tiempo total de procesamiento sigue siendo tan alto como cuando se llamó, y no hay restos del marco anterior en pantalla. Sólo la primera llamada a glClear() lleva mucho tiempo. Si se llama de nuevo, las llamadas posteriores son sólo 1 o 2 milisegundos.

También he intentado combinaciones diferentes de parámetros (tales como GLES20.GL_DEPTH_BUFFER_BIT ), y usando glClearColor() . El tiempo claro sigue siendo alto.

Gracias de antemano.

No estás midiendo lo que crees que eres. Medir el tiempo transcurrido de una llamada de OpenGL API es en su mayoría sin sentido.

Asincronicidad

El aspecto clave a entender es que OpenGL es una API para pasar el trabajo a una GPU. El modelo mental más fácil (que en gran medida corresponde a la realidad) es que cuando se hacen llamadas OpenGL API, se pone en cola el trabajo que luego se enviará a la GPU. Por ejemplo, si realiza una glDraw*() , glDraw*() la llamada creando un elemento de trabajo que se pone en cola y, en algún momento posterior, se enviará a la GPU para su ejecución.

En otras palabras, el API es altamente asíncrono. El trabajo que solicita al realizar llamadas API no se completa en el momento en que se devuelve la llamada. En la mayoría de los casos, ni siquiera se presenta a la GPU para su ejecución. Es sólo en cola, y se presentará en algún momento después, la mayoría fuera de su control.

Una consecuencia de este enfoque general es que el tiempo que se mide para realizar una llamada glClear() tiene prácticamente nada que ver con cuánto tiempo se tarda en borrar el framebuffer.

Sincronización

Ahora que hemos establecido cómo el OpenGL API es asíncrono, el siguiente concepto a entender es que un cierto nivel de sincronización es necesario.

Veamos una carga de trabajo en la que el rendimiento global está limitado por la GPU (ya sea por el rendimiento de la GPU o porque la velocidad de fotogramas está limitada por la actualización de la pantalla). Si mantenemos todo el sistema totalmente asíncrono, y la CPU puede producir comandos de GPU más rápido de lo que la GPU puede procesar, estaríamos haciendo cola una cantidad cada vez mayor de trabajo. Esto es indeseable por un par de razones:

  • En el caso extremo, la cantidad de trabajo en cola crecería hacia el infinito y nos quedaría sin memoria sólo almacenando los comandos de la GPU en cola.
  • En las aplicaciones que necesitan responder a la entrada del usuario, como los juegos, obtendríamos una latencia creciente entre la entrada y la representación del usuario.

Para evitar esto, los conductores utilizan mecanismos de estrangulamiento para evitar que la CPU avance demasiado. Los detalles de cómo exactamente esto se maneja pueden ser bastante complejos. Pero como un modelo simple, podría ser algo así como bloquear la CPU cuando se obtiene más de 1-2 fotogramas por delante de lo que la GPU ha terminado de representación. Lo ideal es que siempre quiera un poco de trabajo en la cola para que la GPU nunca vaya a la inactividad de las aplicaciones de gráficos limitados, pero desea mantener la cantidad de trabajo en cola tan pequeña como sea posible para minimizar el uso de memoria y la latencia.

Significado de su medida

Con toda esta información de fondo explicada, sus mediciones deben ser mucho menos sorprendentes. Con mucho, el escenario más probable es que su llamada glClear() desencadena una sincronización, y el tiempo que mide es el tiempo que tarda la GPU en ponerse al día suficientemente, hasta que tenga sentido enviar más trabajo.

Tenga en cuenta que esto no significa que todo el trabajo enviado anteriormente debe completarse. Veamos una secuencia que es algo hipotética, pero bastante realista para ilustrar lo que puede suceder:

  • Digamos que haces la llamada glClear() que forma el inicio de rendering frame n .
  • En este momento, el marco n - 3 está en la pantalla, y la GPU está ocupada procesando los comandos de renderizado para el marco n - 2 .
  • El conductor decide que usted realmente no debe estar recibiendo más de 2 fotogramas por delante. Por lo tanto, bloquea en su llamada glClear() hasta que la GPU terminó los comandos de representación para el marco n - 2 .
  • También puede decidir que tiene que esperar hasta que se muestre la imagen n - 2 en la pantalla, lo que significa esperar la siguiente sincronización de haz.
  • Ahora que la trama n - 2 está en la pantalla, el búfer que anteriormente contenía la trama n - 3 ya no se usa. Ahora está listo para ser utilizado para frame n , lo que significa que ahora se puede enviar el comando glClear() para frame n .

Tenga en cuenta que mientras su llamada glClear() hizo todo tipo de espera en este escenario, que se mide como parte del tiempo transcurrido en la llamada a la API, nada de este tiempo se utilizó para borrar el framebuffer de su marco. Usted probablemente estaba sentado en algún tipo de semáforo (o mecanismo de sincronización similar), esperando a que la GPU para completar el trabajo presentado anteriormente.

Conclusión

Considerando que su medición no es directamente útil después de todo, ¿qué puede aprender de ella? Desafortunadamente no mucho.

Si observas que tu velocidad de fotogramas no alcanza tu objetivo, por ejemplo, porque observas la tartamudez, o incluso mejor, porque mides el framerate durante un cierto período de tiempo, lo único que sabes con certeza es que tu renderizado es demasiado lento. Entrar en los detalles del análisis de rendimiento es un tema que es demasiado grande para este formato. Sólo para darle una visión general de los pasos que podría tomar:

  • Mida / perfile el uso de su CPU para verificar que realmente está limitado a GPU.
  • Utilice las herramientas de creación de perfiles de GPU que a menudo están disponibles en los proveedores de GPU.
  • Simplifique su procesamiento, o omita partes de él, y vea cómo cambia el rendimiento. Por ejemplo, ¿se hace más rápido si se simplifica la geometría? Usted puede estar limitado por el procesamiento de vértices. ¿Se vuelve más rápido si reduce el tamaño de framebuffer? ¿O si simplificas tus shaders de fragmentos? Probablemente estás limitado por el procesamiento de fragmentos.
  • NVidia PerfHUD ES Tegra en Google Nexus 7
  • Android OpenGL texturas se ven terrible en Sprint Samsung Galaxy s2 Epic Touch 4g
  • GLSurfaceView dentro del fragmento que no se representa cuando se reinicia
  • Android: 2D. ¿OpenGl o android.graphics?
  • ¿Mejor velocidad de cuadro que dibuja los mapas de bits en una lona?
  • Android: el renderizado de OpenGL se detiene cuando se ejecuta una gran tarea de fondo
  • Threading proceso de carga de texturas para el juego opengl android
  • GLES10.glGetIntegerv devuelve 0 sólo en Lollipop
  • Simple particle system en Android usando OpenGL ES 1.0
  • Recorte de vídeo antes de la codificación con MediaCodec para Grafika "Captura continua" Actividad
  • Transformaciones de matrices Android de OpenGL ES
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.