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 realizará en la GPU. La GPU comienza 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á hasta el final. 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 termina la GPU, el controlador de pantalla muestra la imagen de inmediato.
El framework de sincronización también permite que los implementadores aprovechen los recursos 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.
Estos son algunos de los beneficios de la sincronización explícita:
- 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 que los proveedores deben implementar para cada instancia de controlador, como un contexto de GL, un controlador de pantalla o un 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
ypt_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 lossync_fence
ysync_pts
inmutables para compilar líneas de comandos basadas en ellos. - No permitas que el espacio de usuario cree o señale una cerca de forma explícita. 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
nisync_fence
de forma explícita. La API proporciona todas las funciones necesarias.
sync_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 un consumidor de imágenes ya no necesita un búfer, se indica un sync_pt
para que un productor de imágenes sepa que está bien 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 primitivas principales que usan los controladores y el espacio de usuario 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 framework de sincronización permite que varios consumidores o productores indiquen cuando terminan de usar un búfer y comunicar la información de dependencia con un parámetro de función. 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 un sync_pt
entra
en un estado de error, todo el 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 cerca, se realiza una combinación en la que se agregan puntos de dos cercas 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 en particular. Los controladores que deben ser compatibles con la cerca suelen ser todo lo que accede o se comunica con el 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
- Implementación principal:
- El proveedor debe proporcionar la sincronización adecuada
vallas como parámetros a
validateDisplay()
y FuncionespresentDisplay()
en la HAL - Dos extensiones de GL relacionadas con la cerca (
EGL_ANDROID_native_fence_sync
yEGL_ANDROID_wait_sync
) y compatibilidad con la cerca en el controlador de gráficos
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 compleja. Mientras se muestra un búfer, este se asocia con una cerca que indica cuándo estará listo. 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. A medida que pones en cola los búferes, el kernel enumera las 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
En esta sección, se explica cómo integrar el framework de sincronización del espacio del kernel con las partes del espacio de 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 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 de proveedor o el HAL pasan un descriptor de archivo que contiene un
sync_pt
a una función de API, el controlador de proveedor o el HAL no deben cerrar 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.
El nombre de un objeto de cerca se cambia 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 sincronización de OpenGL ES se basa en dos extensiones de EGL:
EGL_ANDROID_native_fence_sync
proporciona una forma de unir o crear descriptores de archivos de valla nativos de Android enEGLSyncKHR
.EGL_ANDROID_wait_sync
permite los bloqueos en la GPU. en lugar de en la CPU, lo que hace que la GPU espereEGLSyncKHR
. El La extensiónEGL_ANDROID_wait_sync
es igual a laEGL_KHR_wait_sync
.
Para usar estas extensiones de forma independiente, implementa la extensión EGL_ANDROID_native_fence_sync
junto con la compatibilidad con el kernel asociada. 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. Como resultado, las extensiones que se aplican a los tipos de objetos EGLSyncKHR
existentes no se aplican necesariamente a los objetos EGL_ANDROID_native_fence
, lo que evita interacciones no deseadas.
La extensión EGL_ANDROID_native_fence_sync
emplea un atributo descriptor de archivo de cerca nativo correspondiente que se puede establecer solo en el momento de la creación y que no se puede consultar directamente desde un objeto de sincronización existente. Este atributo
se puede establecer en uno de dos modos:
- Un descriptor de archivo de zona válida une un descriptor de archivo de zona nativo de Android existente en un objeto
EGLSyncKHR
. - -1 crea un descriptor de archivo de cerca 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 tiene el mismo resultado que consultar el atributo set, pero se adhiere a la convención de que el destinatario cierra la cerca (de ahí la operación duplicada). Por último, destruir el objeto EGLSyncKHR
cierra el atributo de cerca interna.
Integración de Hardware Composer
El Hardware Composer controla tres tipos de cercas de sincronización:
- Los cercados de adquisición se pasan junto con los búferes de entrada a las llamadas
setLayerBuffer
ysetClientTarget
. Estos representan una operación de escritura pendiente en el búfer y deben indicarse antes de que SurfaceFlinger o HWC intenten leer del búfer asociado para realizar la composición. - Las vallas de liberación se recuperan después de la llamada a
presentDisplay
mediante la llamadagetReleaseFences
Estas representan una lectura pendiente del búfer anterior en la misma capa. Una valla de liberación indica cuando el HWC ya no usa el búfer anterior porque el búfer actual reemplazó el búfer anterior en la pantalla. Los límites de lanzamiento se devuelven a la app junto con los búferes anteriores que se reemplazarán 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 se les devolvió la cantidad total de tiempo. - Se muestran cercos actuales, uno por fotograma, como parte
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. En el caso de las pantallas físicas,presentDisplay
muestra los límites presentes cuando el fotograma actual aparece en la pantalla. Después de que se devuelven las cercas presentes, es seguro volver a escribir en el búfer de destino de SurfaceFlinger, si corresponde. Para las pantallas virtuales, se devuelven las vallas actuales cuando se y seguro leer desde el búfer de salida.