Representación de alta frecuencia de actualización en Android

Publicado por Ady Abraham, ingeniero de software

Durante mucho tiempo, los teléfonos tenían una pantalla que se actualiza a 60Hz. Los desarrolladores de aplicaciones y juegos simplemente podrían suponer que la frecuencia de actualización es de 60 Hz, la caducidad de la trama es de 16,6 ms y las cosas funcionarían. Este ya no es el caso. Los nuevos dispositivos insignia están hechos con pantallas de mayor frecuencia de actualización, que ofrecen animaciones más suaves, menos latencia y una mejor experiencia general para el usuario. También hay dispositivos que admiten múltiples frecuencias de actualización, como Pixel 4, que admite 60Hz y 90Hz.

Una pantalla de 60Hz actualiza el contenido de la pantalla cada 16.6 ms. Esto significa que se mostrará una imagen para la duración de un múltiplo de 16.6ms (16.6ms, 33.3ms, 50ms, etc.). Una pantalla que admite múltiples frecuencias de actualización, ofrece múltiples opciones para renderizar a diferentes velocidades sin fluctuaciones. Por ejemplo, un juego que no es compatible con la representación de 60 fps debe descender a 30 fps en una pantalla de 60 Hz para permanecer fluido y sin clics (ya que la pantalla está limitada a imágenes en un múltiplo de 16 , 6 ms, la siguiente velocidad de cuadros disponible es un cuadro cada 33.3ms o 30 fps). En un dispositivo de 90Hz, el mismo juego puede caer a 45 fps (22.2 ms para cada cuadro), ofreciendo una experiencia de usuario mucho más fluida. Un dispositivo que admita 90Hz y 120Hz puede presentar sin problemas contenidos en 120, 90, 60 (120/2), 45 (90/2), 40 (120/3), 30 (90/3), 24 (120/5 ), etc. cuadros por segundo.

Renderizado a altas velocidades

Cuanto mayor sea la velocidad de renderizado, más difícil será mantener la velocidad de fotogramas, simplemente porque hay menos tiempo disponible para la misma cantidad de trabajo. Para renderizar a 90Hz, las aplicaciones solo tienen 11.1ms para producir un cuadro en lugar de 16.6ms a 60Hz.

Para probar esto, echemos un vistazo a la canalización de representación de la interfaz de usuario de Android. La representación de trama se puede dividir en aproximadamente cinco etapas de canalización:

  1. El subproceso de interfaz de usuario de la aplicación procesa los eventos de entrada, llama a las devoluciones de llamada de la aplicación y actualiza la lista de comandos de dibujo de jerarquía registrados
  2. RenderThread de la aplicación envía los comandos registrados a la GPU
  3. La GPU diseña el marco
  4. SurfaceFlinger, que es el servicio del sistema encargado de mostrar las diferentes ventanas de la aplicación en la pantalla, compone la pantalla y envía el marco a la pantalla HAL
  5. La pantalla muestra el marco

Toda la tubería está controlada por Android Choreographer. Choreographer se basa en los eventos de sincronización de pantalla vertical (vsync), que indican el momento en que la pantalla comienza a escanear la imagen y a actualizar los píxeles de la pantalla. El coreógrafo se basa en eventos vsync pero tiene diferentes compensaciones de reactivación para la aplicación y para SurfaceFlinger. El siguiente diagrama muestra la canalización en un dispositivo Pixel 4 que funciona a 60Hz, donde la aplicación se despierta 2 ms después del evento vsync y SurfaceFlinger se despierta 6 ms después del evento vsync. Esto proporciona 20 ms para que una aplicación produzca un marco y 10 ms para que SurfaceFlinger componga la pantalla.

  Diagrama que muestra la canalización en un dispositivo Pixel 4

Cuando se ejecuta a 90 Hz, la aplicación aún se despierta 2 ms después del evento vsync. Sin embargo, SurfaceFlinger se despierta 1 ms después del evento vsync para tener los mismos 10 ms para la composición de la pantalla. La aplicación, por otro lado, solo tiene 10 ms para renderizar un marco, que es muy corto.

  Diagrama de ejecución en un dispositivo de 90Hz

Para mitigarlo, el subsistema de interfaz de usuario en Android está utilizando "renderizar por adelantado" (que retrasa la presentación de un marco al iniciarlo simultáneamente) para profundizar la tubería y posponer el Presentación del marco de una vsync. Esto le da a la aplicación 21 ms para producir un marco, manteniendo el rendimiento a 90Hz.

  Aplicación de diagrama de 21 ms para producir un marco

Algunas aplicaciones, incluida la mayoría de los juegos, tienen sus propios canales de representación personalizados. Estas tuberías pueden tener más o menos etapas, dependiendo de lo que estén tratando de lograr. En general, a medida que la tubería se vuelve más profunda, se pueden ejecutar varias etapas en paralelo, lo que aumenta la productividad general. Por otro lado, esto puede aumentar la latencia de un solo cuadro (la latencia será number_of_pipeline_stages x longest_pipeline_stage ). Este compromiso debe ser considerado cuidadosamente.

Al aprovechar las tasas de actualización múltiples

Como se mencionó anteriormente, las tasas de actualización múltiples le permiten utilizar un rango más amplio de frecuencias de renderizado disponibles. Esto es especialmente útil para juegos que pueden controlar la velocidad de renderizado y para reproductores de video que necesitan presentar contenido a una velocidad determinada. Por ejemplo, para reproducir video de 24 fps en una pantalla de 60 Hz, debe usar un algoritmo de pulldown 3: 2, que crea jitter. Sin embargo, si el dispositivo tiene una pantalla capaz de presentar contenido de 24 fps de forma nativa (24/48/72 / 120Hz), eliminará la necesidad de desplegar y el jitter asociado.

La plataforma de Android controla la frecuencia de actualización en la que opera el dispositivo. Las aplicaciones y los juegos pueden influir en la frecuencia de actualización a través de varios métodos (explicados a continuación), pero la plataforma toma la decisión final. Esto es crucial cuando hay más de una aplicación en la pantalla y la plataforma debe satisfacerlas a todas. Un buen ejemplo es un reproductor de video de 24 fps. 24Hz puede ser excelente para reproducir videos, pero es terrible para la interfaz de usuario receptiva. Una notificación que cobra vida a solo 24Hz parece extraña. En situaciones como esta, la plataforma establecerá la frecuencia de actualización para garantizar que el contenido en la pantalla sea hermoso.

Por esta razón, las aplicaciones pueden necesitar conocer la tasa de actualización actual del dispositivo. Esto se puede hacer de las siguientes maneras:

Las aplicaciones pueden influir en la frecuencia de actualización del dispositivo estableciendo una frecuencia de fotogramas en la ventana o superficie. Esta es una nueva característica introducida en Android 11 y permite a la plataforma conocer las intenciones de representación de la aplicación de llamada. Las aplicaciones pueden llamar a uno de los siguientes métodos:

Consulte la guía de velocidad de fotogramas sobre cómo usar estas API.

El sistema elegirá la frecuencia de actualización más adecuada en función de la frecuencia de fotogramas programada en la ventana o superficie.

En versiones anteriores de Android (antes de Android 11) donde la API setFrameRate no existe, las aplicaciones aún pueden influir en la frecuencia de actualización configurando directamente WindowManager.LayoutParams.preferredDisplayModeId en uno de los modos disponibles en Display.getSupportedModes . Se desaconseja este enfoque a partir de Android 11, ya que la plataforma no conoce la intención de representación de la aplicación. Por ejemplo, si un dispositivo admite 48Hz, 60Hz y 120Hz y hay dos aplicaciones en la pantalla que llaman respectivamente setFrameRate (60, …) y setFrameRate (24, …), la plataforma puede elegir 120Hz y hacer felices a ambos aplicaciones. Por otro lado, si esas aplicaciones usaran PreferredModisplayId, probablemente habrían configurado el modo a 60Hz y 48Hz respectivamente, dejando la plataforma sin posibilidad de configurar 120Hz. La plataforma elegirá 60Hz o 48Hz, por lo que es una aplicación infeliz.

Para llevar

La frecuencia de actualización no siempre es de 60Hz, no asumas 60Hz y no asumas códigos rígidos basados ​​en ese artefacto histórico.

La frecuencia de actualización no siempre es constante: si está interesado en la frecuencia de actualización, debe registrar una devolución de llamada para saber cuándo cambia la frecuencia de actualización y actualizar sus datos internos en consecuencia.

Si no usa el kit de herramientas de la interfaz de usuario de Android y tiene su propio renderizador personalizado, puede editar la canalización de renderización en función de la frecuencia de actualización actual. La profundización de la tubería se puede hacer estableciendo una marca de tiempo de presentación usando eglPresentationTimeANDROID en OpenGL o VkPresentTimesInfoGOOGLE en Vulkan. Establecer una marca de tiempo de presentación le indica a SurfaceFlinger cuándo presentar la imagen. Si en el futuro se establece en algunos cuadros, profundizará la tubería del número de cuadros en los que se establece. La interfaz de usuario de Android en el ejemplo anterior establece la hora actual en frameTimeNanos 1 + 2 * vsyncPeriod 2

Indica la plataforma sus intenciones de renderizado utilizando la API setFrameRate. La plataforma combinará varias solicitudes seleccionando la frecuencia de actualización adecuada.

Use PreferredDisplayModeId solo cuando sea necesario, cuando la API setFrameRate no esté disponible o cuando necesite usar un modo muy específico.

Finalmente, familiarícese con la biblioteca Android Frame Pacing. Esta biblioteca gestiona el ritmo correcto de los marcos para su juego y utiliza los métodos descritos anteriormente para gestionar múltiples frecuencias de actualización.

Notas

Compruebe también

en vivo desde Droidcon, incluida la mayor actualización de Gemini en Android Studio y más lanzamientos del SDK de Android.

Acabamos de lanzar nuestro episodio de otoño de #TheAndroidShow en YouTube etcétera desarrollador.android.comy esta vez …

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *