Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Android EGL / OpenGL ES Frecuencia de fotogramas tartamudeando

TL, DR

Incluso cuando no se hace ningún dibujo, parece imposible mantener una tasa de actualización de 60Hz en un subproceso de renderizado de OpenGL ES en un dispositivo Android. Picos misteriosos con frecuencia aparecen (demostrado en el código en la parte inferior), y todos los esfuerzos que he hecho para averiguar por qué o cómo ha llevado a un callejón sin salida. La sincronización en ejemplos más complicados con un hilo de renderizado personalizado ha demostrado consistentemente que eglSwapBuffers () es el culpable, llegando frecuentemente en más de 17ms-32ms. ¿Ayuda?

Más detalles

Esto es especialmente perjudicial porque los requisitos de renderizado para nuestro proyecto son elementos alineados con la pantalla que se desplazan suavemente horizontalmente a una velocidad fija y alta de un lado a otro de la pantalla. En otras palabras, un juego de plataformas. Las frecuentes descensos desde 60Hz resultan en un notable desplome y balanceo, tanto con y sin movimiento basado en el tiempo. Renderización a 30Hz no es una opción debido a la alta velocidad de desplazamiento, que es una parte no negociable del diseño.

Nuestro proyecto está basado en Java para maximizar la compatibilidad y usa OpenGL ES 2.0. Sólo bajamos al NDK para el procesamiento de OpenGL ES 2.0 en dispositivos API 7-8 y al soporte ETC1 en dispositivos API 7. Tanto en el código de prueba como en el siguiente, he verificado que no hay asignaciones / eventos de GC, excepto para la impresión de registro y los subprocesos automáticos que están fuera de mi control.

He recreado el problema en un solo archivo que utiliza acciones de clases de Android y no NDK. El código de abajo se puede pegar en un nuevo proyecto de Android creado en Eclipse y debería funcionar prácticamente fuera de la caja siempre y cuando elija el nivel de API 8 o superior.

La prueba se ha reproducido en una variedad de dispositivos con una gama de GPUs y versiones del sistema operativo:

  • Galaxy Tab 10.1 (Android 3.1)
  • Nexus S (Android 2.3.4)
  • Galaxy S II (Android 2.3.3)
  • XPERIA Play (Android 2.3.2)
  • Droid Incredible (Android 2.2)
  • Galaxy S (Android 2.1-update1) (al bajar los requisitos de la API hasta el nivel 7)

Salida de muestra (recogida de menos de 1 segundo de tiempo de ejecución):

Spike: 0.017554 Spike: 0.017767 Spike: 0.018017 Spike: 0.016855 Spike: 0.016759 Spike: 0.016669 Spike: 0.024925 Spike: 0.017083999 Spike: 0.032984 Spike: 0.026052998 Spike: 0.017372 

He estado persiguiendo a éste por un rato y tengo sobre golpe una pared de ladrillo. Si una corrección no está disponible, por lo menos una explicación acerca de por qué esto sucede y consejos sobre cómo se ha superado esto en proyectos con requisitos similares sería muy apreciada.

Ejemplo de código

 package com.test.spikeglsurfview; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManager; import android.widget.LinearLayout; /** * A simple Activity that demonstrates frequent frame rate dips from 60Hz, * even when doing no rendering at all. * * This class targets API level 8 and is meant to be drop-in compatible with a * fresh auto-generated Android project in Eclipse. * * This example uses stock Android classes whenever possible. * * @author Bill Roeske */ public class SpikeActivity extends Activity { @Override public void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); // Make the activity fill the screen. requestWindowFeature( Window.FEATURE_NO_TITLE ); getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN ); // Get a reference to the default layout. final LayoutInflater factory = getLayoutInflater(); final LinearLayout layout = (LinearLayout)factory.inflate( R.layout.main, null ); // Clear the layout to remove the default "Hello World" TextView. layout.removeAllViews(); // Create a GLSurfaceView and add it to the layout. GLSurfaceView glView = new GLSurfaceView( getApplicationContext() ); layout.addView( glView ); // Configure the GLSurfaceView for OpenGL ES 2.0 rendering with the test renderer. glView.setEGLContextClientVersion( 2 ); glView.setRenderer( new SpikeRenderer() ); // Apply the modified layout to this activity's UI. setContentView( layout ); } } class SpikeRenderer implements GLSurfaceView.Renderer { @Override public void onDrawFrame( GL10 gl ) { // Update base time values. final long timeCurrentNS = System.nanoTime(); final long timeDeltaNS = timeCurrentNS - timePreviousNS; timePreviousNS = timeCurrentNS; // Determine time since last frame in seconds. final float timeDeltaS = timeDeltaNS * 1.0e-9f; // Print a notice if rendering falls behind 60Hz. if( timeDeltaS > (1.0f / 60.0f) ) { Log.d( "SpikeTest", "Spike: " + timeDeltaS ); } /*// Clear the screen. gl.glClear( GLES20.GL_COLOR_BUFFER_BIT );*/ } @Override public void onSurfaceChanged( GL10 gl, int width, int height ) { } @Override public void onSurfaceCreated( GL10 gl, EGLConfig config ) { // Set clear color to purple. gl.glClearColor( 0.5f, 0.0f, 0.5f, 1.0f ); } private long timePreviousNS = System.nanoTime(); } 

  • Android OpenGL ES 2, Dibujo de cuadrados
  • Dibuja una imagen 2D con OpenGL ES 2.0
  • Cómo renderizar la imagen de la cámara YUV-NV21 de Android en el fondo en libgdx con OpenGLES 2.0 en tiempo real?
  • Bajo rendimiento al ejecutar eglSwapBuffer y eglMakeCurrent
  • Android Openg GL ES 2 dibujo grandes texturas lento
  • Cómo depurar el error de OPENGL ES 2.0 en Nexus 4 sin tener el dispositivo?
  • ¿Qué versiones de GLSL puedo usar en OpenGL ES 2.0?
  • ¿Hay alguna forma de comprobar si el dispositivo Android es compatible con openGL ES 2.0?
  • 2 Solutions collect form web for “Android EGL / OpenGL ES Frecuencia de fotogramas tartamudeando”

    No estoy seguro de si esta es la respuesta, pero tenga en cuenta que la llamada a eglSwapBuffers () bloquea durante al menos 16 ms, incluso si no hay nada que dibujar.

    Ejecutar la lógica del juego en un hilo separado podría ganar atrás algún tiempo.

    Echa un vistazo a la entrada del blog en el juego de plataformas de código abierto Isla Relica. La lógica del juego es pesada, pero el framrate es suave debido a la solución de pipeline de los autores / doble buffer.

    http://replicaisland.blogspot.com/2009/10/rendering-with-two-threads.html

    Usted no está obligando la framerate usted mismo. Así que está limitado por la CPU.

    Lo que significa que se ejecutará rápido cuando la CPU no tiene nada que hacer y más lento cuando está haciendo otras cosas.

    Otras cosas se están ejecutando en su teléfono, que a veces toma tiempo de CPU de su juego. Garbage Collector también a esto, a pesar de no congelar su juego como solía hacer (ya que ahora se ejecuta en un hilo separado)

    Usted vería que esto suceda en cualquier sistema operativo multiprogramación que estaba en uso normal. No es sólo culpa de Java.

    Mi sugerencia: enlazar el framerate a un valor más bajo si se requiere un bitrate constante. Sugerencia: use una mezcla de Thread.sleep () y busy waiting (while loop), ya que el tiempo de espera puede exceder el tiempo de espera requerido.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.