La clase BufferQueue conecta componentes que generan búferes de datos gráficos (productores) a componentes que aceptan los datos para su visualización o procesamiento adicional (consumidores). Casi todo lo que mueve búferes de datos gráficos a través del sistema depende de BufferQueue.
El asignador de memoria de Gralloc realiza asignaciones de búfer y se implementa a través de dos interfaces HIDL específicas del proveedor (consulta hardware/interfaces/graphics/allocator/
y hardware/interfaces/graphics/mapper/
). La función allocate()
toma los argumentos esperados (ancho, alto, formato de píxeles) y un conjunto de marcas de uso.
Productores y consumidores de BufferQueue
Los consumidores crean y son propietarios de la estructura de datos de BufferQueue y pueden existir en
procesos diferentes a los de sus productores. Cuando un productor necesita un búfer, llama a dequeueBuffer()
para solicitar un búfer libre de BufferQueue y especifica el ancho, la altura, el formato de píxeles y las marcas de uso de los búferes. Luego, el productor propaga el búfer y lo muestra a la cola llamando a queueBuffer()
. A continuación, el consumidor adquiere el búfer con acquireBuffer()
y usa el contenido del búfer. Cuando el consumidor termina, llama a releaseBuffer()
para mostrar el búfer a la fila. El framework de sincronización controla cómo se mueven los búferes a través de la canalización de gráficos de Android.
El productor y el consumidor determinan conjuntamente algunas características de BufferQueue, como la cantidad máxima de búferes que puede contener. Sin embargo, BufferQueue asigna búferes según los necesite. Los búferes se retienen, a menos que cambien las características. Por ejemplo, si un productor solicita búferes con un tamaño diferente, se liberan los búferes anteriores y se asignan búferes nuevos a pedido.
BufferQueue nunca copia el contenido del búfer, ya que mover tanta cantidad de datos es ineficiente. En su lugar, los búferes siempre se pasan con un identificador.
Realiza un seguimiento de BufferQueue con Systrace
Para comprender cómo se mueven los búferes de gráficos, usa Systrace, una herramienta que registra la actividad del dispositivo durante un período breve. El código de gráficos a nivel del sistema está bien instrumentado, al igual que gran parte del código relevante del framework de la app.
Para usar Systrace, habilita las etiquetas gfx
, view
y sched
. Los objetos BufferQueue se muestran en el seguimiento.
A modo de ejemplo, si realizas un seguimiento mientras se ejecuta el video de Play de Grafika (SurfaceView), la fila etiquetada como SurfaceView te indica cuántos búferes se pusieron en cola en un momento determinado.
El valor aumenta mientras la app está activa, lo que activa la renderización de fotogramas por parte del decodificador MediaCodec. El valor disminuye mientras SurfaceFlinger está trabajando y consumiendo búferes. Cuando se muestra un video a 30 fps, el valor de la fila varía de 0 a 1 porque la pantalla de ~60 fps puede seguir el ritmo de la fuente. SurfaceFlinger se activa solo cuando hay trabajo por hacer, no 60 veces por segundo. El sistema intenta evitar el trabajo y, si nada actualiza la pantalla, inhabilita VSYNC.
Si cambias al video de Play de Grafika (TextureView) y obtienes un nuevo registro, verás una fila etiquetada como com.android.grafika
 / com.android.grafika.PlayMovieActivity
.
Esta es la capa principal de la IU, que es otro BufferQueue. Debido a que TextureView se renderiza en la capa de la IU en lugar de una capa independiente, todas las actualizaciones impulsadas por video se muestran aquí.
Gralloc
El HAL del asignador de Gralloc hardware/libhardware/include/hardware/gralloc.h
realiza asignaciones de búfer a través de marcas de uso. Las marcas de uso incluyen atributos como los siguientes:
- La frecuencia con la que se accederá a la memoria desde el software (CPU)
- La frecuencia con la que se accederá a la memoria desde el hardware (GPU)
- Indica si la memoria se usará como una textura de OpenGL ES (GLES)
- Indica si un codificador de video usará la memoria
Por ejemplo, si el formato del búfer de un productor especifica RGBA_8888
píxeles y el productor indica que se accederá al búfer desde el software (es decir, una app tocará píxeles en la CPU), Gralloc crea un búfer con 4 bytes por píxel en orden R-G-B-A. En cambio, si un productor especifica que solo se accederá a su búfer desde el hardware y como una textura GLES, Gralloc puede hacer lo que desee el controlador GLES, como el orden BGRA, los diseños de intercambio no lineal y los formatos de color alternativos. Permitir que el hardware use su formato preferido puede mejorar el rendimiento.
Algunos valores no se pueden combinar en ciertas plataformas. Por ejemplo, la marca del codificador de video puede requerir píxeles YUV, por lo que se produce un error cuando se agrega acceso de software y se especifica RGBA_8888
.
El identificador que devuelve Gralloc se puede pasar entre procesos a través de Binder.
Búferes protegidos
La marca de uso de Gralloc GRALLOC_USAGE_PROTECTED
permite que el búfer de gráficos se muestre solo a través de una ruta de acceso protegida por hardware. Estos planos superpuestos son la única forma de mostrar contenido de DRM (SurfaceFlinger o el controlador de OpenGL ES no pueden acceder a los búferes protegidos por DRM).
Los videos protegidos por DRM solo se pueden presentar en un plano superpuesto. Los reproductores de video que admiten contenido protegido deben implementarse con SurfaceView. El software que se ejecuta en hardware no protegido no puede leer ni escribir el búfer. Las rutas de acceso protegidas por hardware deben aparecer en la superposición de Hardware Composer (es decir, los videos protegidos desaparecen de la pantalla si Hardware Composer cambia a la composición de OpenGL ES).
Para obtener detalles sobre el contenido protegido, consulta DRM.