Framework de sincronización

El framework de sincronización describe de forma explícita las dependencias entre diferentes operaciones asíncronas en el sistema de gráficos de Android. El marco de trabajo proporciona una API que habilita a los componentes para indicar cuándo se liberan los búferes. El framework también permite pasar las primitivas de sincronización entre los controladores del kernel al espacio del usuario y entre los procesos de este espacio.

Por ejemplo, una aplicación puede poner en cola el trabajo que se ejecutará en la GPU. La GPU comenzará a dibujar esa imagen. Aunque la imagen no se dibujó en la memoria, el puntero del búfer se pasa a la ventana junto con una valla que indique cuándo se realizará para finalizar. El compositor de ventanas comienza a procesar con anticipación pasa el trabajo al controlador de la pantalla. De manera similar, la CPU funciona se hace con anticipación. Una vez que la GPU finaliza, el controlador de pantalla muestra la imagen de inmediato.

El framework de sincronización también permite a los implementadores aprovechar de sincronización en sus propios componentes de hardware. Por último, la proporciona visibilidad de la canalización de los gráficos para ayudar con la depuración.

Sincronización explícita

La sincronización explícita permite que los productores y consumidores de búferes de gráficos para indicar que terminan de usar un búfer. La sincronización explícita es implementadas en el espacio del kernel.

Entre los beneficios de la sincronización explícita, se incluyen los siguientes:

  • Menos variación de comportamiento entre dispositivos
  • Mejor compatibilidad con la depuración
  • Métricas de prueba mejoradas

El framework de sincronización tiene tres tipos de objetos:

  • sync_timeline
  • sync_pt
  • sync_fence

sincronización_línea

sync_timeline es un cronograma que aumenta monótonamente y que los proveedores deben implementar para cada instancia de controlador, como un contexto de GL, controlador de pantalla o blitter 2D. sync_timeline recuentos trabajos enviados al kernel para una pieza concreta de hardware. sync_timeline ofrece garantías sobre el orden de las operaciones. y habilita implementaciones específicas de hardware.

Sigue estos lineamientos cuando implementes sync_timeline:

  • Proporciona nombres útiles para todos los conductores, los cronogramas y las cercas para simplificar el proceso la depuración.
  • Implementa timeline_value_str y pt_value_str. en cronogramas para que los resultados de depuración sean más legibles.
  • Implementa el driver_data de relleno para otorgar bibliotecas de espacio de usuario. como la biblioteca GL, y acceso a datos privados de la línea de tiempo, si así lo desean. data_driver permite que los proveedores pasen información sobre el modelo inmutable sync_fence y sync_pts para compilar líneas de comandos en función de ellos.
  • No permitas que el espacio del usuario cree explícitamente una valla ni la indique. Explícitamente crear señales o vallas da como resultado un ataque de denegación del servicio detiene la funcionalidad de canalización.
  • No acceder a sync_timeline, sync_pt ni sync_fence de forma explícita. La API proporciona todos los funciones.

sincronización_pt

sync_pt es un valor o punto único en un sync_timeline Un punto tiene tres estados: activo, señalizado y error. Los puntos comienzan en el estado activo y hacer la transición a los estados indicado o de error. Por ejemplo, cuando una imagen el consumidor ya no necesita un búfer, se indica sync_pt para que el productor de imágenes sepa que puede volver a escribir en el búfer.

protección_sincronización

sync_fence es una colección de valores sync_pt que a menudo tener diferentes sync_timeline superiores (por ejemplo, para la pantalla controlador y GPU). sync_fence, sync_pt y sync_timeline son las principales primitivas que usan los controladores y el espacio del usuario usan para comunicar sus dependencias. Cuando se señala una cerca, todo comandos emitidos antes de la valla están garantizados, ya que el el controlador de kernel o el bloque de hardware ejecutan comandos en orden.

El marco de sincronización permite que varios consumidores o productores Terminé de usar un búfer, se comunica la información de la dependencia con una función parámetro. Las vallas están respaldadas por un descriptor de archivo y se pasan desde espacio de kernel al espacio del usuario. Por ejemplo, una valla puede contener dos Valores sync_pt que indican cuando dos consumidores de imágenes diferentes terminan leer un búfer. Cuando se indica el cerco, los productores de imágenes saben que ambos los consumidores terminan de consumir.

Las cercas, como los valores de sync_pt, comienzan activas y cambian el estado según el estado de sus puntos. Si se indican todos los valores de sync_pt, el Se indicará sync_fence. Si cae un sync_pt a un estado de error, toda la sync_fence tiene un estado de error.

La membresía en un sync_fence es inmutable después de que se establece la valla. crear. Para obtener más de un punto en una valla, se debe realizar una combinación donde se agregan puntos de dos vallas distintas a una tercera. Si uno de esos puntos se señaló en la cerca de origen y el otro no, la tercera barrera tampoco estará en estado indicado.

Para implementar la sincronización explícita, proporciona lo siguiente:

  • Un subsistema de espacio de kernel que implementa el framework de sincronización. para un controlador de hardware específico. Los conductores que deben estar atentos generalmente cualquier cosa que acceda o se comunique con Hardware Composer. Entre los archivos clave, se incluyen los siguientes:
    • Implementación principal:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • Documentación en kernel/common/Documentation/sync.txt
    • para comunicarse con el espacio del kernel en platform/system/core/libsync
  • El proveedor debe proporcionar la sincronización adecuada vallas como parámetros a validateDisplay() y Funciones presentDisplay() en la HAL
  • Dos extensiones de GL relacionadas con vallas (EGL_ANDROID_native_fence_sync) y EGL_ANDROID_wait_sync) y vallas en los gráficos controlador.

Caso de éxito: Implementa un controlador de pantalla

Para usar la API que admite la función de sincronización, desarrollar un controlador de pantalla que tenga una función de búfer de pantalla Antes del de sincronización existente, esta función recibiría dma-buf coloca esos búferes en la pantalla y realiza el bloqueo mientras el búfer estaba visible. Por ejemplo:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

Con el framework de sincronización, la función display_buffer es más complejo. Cuando se muestra un búfer en pantalla, este se asocia con una valla que indique cuándo estará listo el búfer. Puedes agregar a la fila e iniciar el trabajo después de que se despegue la cerca.

Iniciar el trabajo y poner en cola el trabajo después de que se despeja la barrera no bloquea nada. Debes devolver inmediatamente tu propia valla, que garantiza que el búfer quedará fuera de la pantalla. Cuando pones en cola los búferes, el kernel enumera dependencias con el framework de sincronización:

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

Integración de sincronización

Esta sección explica cómo integrar el framework de sincronización del espacio del kernel con del espacio del usuario del framework de Android y los controladores que se deben comunicar entre sí. Los objetos de espacio de kernel se representan como descriptores de archivos en el espacio de usuario.

Convenciones de integración

Sigue las convenciones de la interfaz de la HAL de Android:

  • Si la API proporciona un descriptor de archivo que hace referencia a un sync_pt, el controlador del proveedor o la HAL que usa la API debe cerrar el descriptor de archivos.
  • Si el controlador del proveedor o la HAL pasan un descriptor de archivos que contiene un sync_pt a una función de API, el controlador del proveedor o la HAL no deben cierra el descriptor de archivo.
  • Para seguir usando el descriptor de archivos de valla, el controlador del proveedor o el La HAL debe duplicar el descriptor.

Se cambia el nombre de un objeto de valla cada vez que pasa por BufferQueue. La compatibilidad con vallas de kernel permite que las vallas tengan cadenas para nombres. usa el nombre de la ventana y el índice del búfer que se pone en cola para nombrar la valla, como SurfaceView:0. Esta es útil en la depuración para identificar la fuente de un interbloqueo cuando aparecen los nombres en el resultado de /d/sync y los informes de errores

Integración de ANativeWindow

ANativeWindow admite vallas. dequeueBuffer, queueBuffer y cancelBuffer tienen parámetros de valla.

Integración de OpenGL ES

La integración de la sincronización de OpenGL ES se basa en dos extensiones EGL:

  • EGL_ANDROID_native_fence_sync proporciona una forma de unir o crear descriptores de archivos de valla nativos de Android en EGLSyncKHR.
  • EGL_ANDROID_wait_sync permite los bloqueos en la GPU. en lugar de en la CPU, lo que hace que la GPU espere EGLSyncKHR. El La extensión EGL_ANDROID_wait_sync es igual a la EGL_KHR_wait_sync.

Para usar estas extensiones de forma independiente, implementa la EGL_ANDROID_native_fence_sync junto con la extensión compatibilidad con el kernel. A continuación, habilita el EGL_ANDROID_wait_sync en tu controlador. El EGL_ANDROID_native_fence_sync La extensión consta de un objeto EGLSyncKHR de valla nativo distinto el tipo de letra. Por lo tanto, las extensiones que se aplican a EGLSyncKHR existentes los tipos de objeto no se aplican necesariamente a EGL_ANDROID_native_fence de objetos, lo que evita interacciones no deseadas.

La extensión EGL_ANDROID_native_fence_sync usa un código nativo correspondiente del descriptor de archivo de valla que se puede configurar solo en el momento de la creación y no se pueden consultar en adelante directamente desde un objeto de sincronización existente. Este atributo se puede establecer en uno de dos modos:

  • Un descriptor de archivo de valla válido une un nativo existente Descriptor de archivos de valla de Android en un objeto EGLSyncKHR.
  • -1 crea un descriptor de archivo de valla nativo de Android a partir de un Objeto EGLSyncKHR.

Usa la llamada a función DupNativeFenceFD() para extraer el Es un objeto EGLSyncKHR del descriptor de archivos de valla nativo de Android. Esto produce el mismo resultado que la consulta del atributo set, pero cumple con la convención de que el destinatario cierra la barrera (de ahí el una sola operación). Por último, la destrucción del objeto EGLSyncKHR se cierra el atributo de valla interna.

Integración de Hardware Composer

Hardware Composer controla tres tipos de vallas de sincronización:

  • Las vallas de adquisición se pasan junto con los búferes de entrada para a las llamadas setLayerBuffer y setClientTarget. Representan una escritura pendiente en el búfer y deben indicarse antes de la SurfaceFlinger o HWC intenta leer desde el búfer asociado para realizar una composición.
  • Las vallas de liberación se recuperan después de la llamada a presentDisplay mediante la llamada getReleaseFences Estas representan una lectura pendiente del búfer anterior en la misma capa. R La valla de liberación señala cuando el HWC ya no usa el búfer anterior porque el búfer actual reemplazó al anterior en la pantalla. Las vallas de liberación se devuelven a la app junto con los búferes anteriores se reemplazará durante la composición actual. La aplicación debe esperar hasta que de protección y liberación de vallas antes de escribir contenido nuevo en el búfer que y cómo se les devolvió.
  • Se muestran cercos actuales, uno por fotograma, como parte del la llamada a presentDisplay. Las vallas de presencia representan cuándo se la composición de este marco se complete, o alternativamente, cuando la el resultado de la composición del fotograma anterior ya no es necesario. Para dispositivos físicos presentDisplay muestra las vallas actuales cuando la el marco actual en la pantalla. Después de que se devuelvan las vallas actuales, es seguro volver a escribir en el búfer de destino que corresponda. Para las pantallas virtuales, se devuelven las vallas actuales cuando se y seguro leer desde el búfer de salida.