Minimiza el retraso de Android GLSurfaceView

Después de algunas otras preguntas sobre Stack Overflow, he leído la guía de las partes internas de Android Surfaces, SurfaceViews, etc. desde aquí:

https://source.android.com/devices/graphics/architecture.html

Esa guía me ha dado una comprensión mucho mejor de cómo encajan todas las diferentes piezas en Android. Cubre cómo eglSwapBuffers simplemente empuja el marco renderizado a una cola que luego será consumida por SurfaceFlinger cuando prepare el siguiente marco para mostrar. Si la cola está llena, esperará hasta que esté disponible un búfer para el siguiente marco antes de regresar. El documento anterior describe esto como "rellenar la cola" y confiar en la "contrapresión" de los buffers de intercambio para limitar el renderizado a la vsync de la pantalla. Esto es lo que sucede usando el modo de renderizado continuo predeterminado de GLSurfaceView.

Si su renderizado es simple y se completa en mucho menos que el período del cuadro, el efecto negativo de esto es un retraso adicional causado por el BufferQueue, ya que la espera en SwapBuffers no ocurre hasta que la cola esté llena, y por lo tanto, el cuadro nosotros ' re rendering siempre está destinado a estar en la parte posterior de la cola, por lo que no se mostrará de inmediato en la próxima vsync, ya que es probable que haya buffers antes en la cola.

Por el contrario, el renderizado a pedido generalmente ocurre con mucha menos frecuencia que la velocidad de actualización de la pantalla, por lo que generalmente las BufferQueues para esas vistas están vacías y, por lo tanto, SurfaceFlinger tomará las actualizaciones introducidas en esas colas en la próxima sincronización virtual.

Así que aquí está la pregunta: ¿cómo puedo configurar un renderizador continuo, pero con un retraso mínimo? El objetivo es que la cola del búfer esté vacía al comienzo de cada vsync, represento mi contenido en menos de 16 ms, lo empujo a la cola (conteo del búfer = 1) y SurfaceFlinger lo consume en el siguiente vsync (conteo del búfer = 0), repetir. El número de Buffers en la cola se puede ver en systrace, por lo que el objetivo es tener esta alternativa entre 0 y 1.

El documento que menciono anteriormente presenta a Choreographer como una forma de obtener devoluciones de llamada en cada vsync. Sin embargo, no estoy convencido de que eso sea suficiente para poder lograr el comportamiento de retraso mínimo que busco. He probado hacer un requestRender () en una devolución de llamada vsync con un onDrawFrame () muy mínimo y, de hecho, muestra el comportamiento de conteo de búfer 0/1. Sin embargo, ¿qué sucede si SurfaceFlinger no puede hacer todo su trabajo dentro de un solo período de fotograma (tal vez aparece una notificación o lo que sea)? En ese caso, espero que mi renderizador esté felizmente produciendo 1 cuadro por vsync, pero el consumidor final de ese BufferQueue ha dejado caer un cuadro. Resultado: ahora estamos alternando entre 1 y 2 buffers en nuestra cola, y hemos ganado un marco de retraso entre hacer el renderizado y ver el marco.

El documento parece sugerir mirar el desplazamiento de tiempo entre el tiempo de sincronización sincronizado y el momento en que se ejecuta la devolución de llamada. Puedo ver cómo eso puede ayudar si su devolución de llamada se entrega tarde debido a su hilo principal debido a un pase de diseño o algo así. Sin embargo, no creo que eso permita detectar que SurfaceFlinger se salte un latido y no consuma un marco. ¿Hay alguna forma de que la aplicación pueda resolver que SurfaceFlinger ha dejado caer un marco? También parece que la incapacidad de decir la longitud de la cola rompe la idea de usar el tiempo de vsync para las actualizaciones del estado del juego, ya que hay una cantidad desconocida de fotogramas en la cola antes de que se muestre el que estás procesando.

Reducir la longitud máxima de la cola y confiar en la contrapresión sería una forma de lograr esto, pero no creo que haya una API para establecer el número máximo de buffers en el GLSurfaceView BufferQueue.

Respuestas a la pregunta(1)

Su respuesta a la pregunta