A classe BufferQueue conecta componentes que geram buffers de dados gráficos (produtores) a componentes que aceitam os dados para exibição ou processamento posterior (consumidores). Quase tudo que move buffers de dados gráficos pelo sistema depende do BufferQueue.
O alocador de memória Gralloc executa alocações de buffer e é
implementado por duas interfaces HIDL específicas do fornecedor (consulte
hardware/interfaces/graphics/allocator/
e
hardware/interfaces/graphics/mapper/
). A
função allocate()
recebe argumentos esperados (largura, altura, formato
de pixel) e um conjunto de flags de uso.
Produtores e consumidores de BufferQueue
Os consumidores criam e são proprietários da estrutura de dados BufferQueue e podem existir em
processos diferentes dos produtores. Quando um produtor precisa de um buffer, ele
solicita um buffer livre da BufferQueue chamando
dequeueBuffer()
, especificando a largura, a altura,
o formato de pixel e as flags de uso dos buffers. O produtor preenche o buffer e
o retorna à fila chamando queueBuffer()
. Em seguida,
o consumidor adquire o buffer com acquireBuffer()
e faz
uso do conteúdo do buffer. Quando o consumidor termina, ele retorna o buffer para
a fila chamando releaseBuffer()
. O framework de sincronização controla
como os buffers se movem pelo pipeline gráfico do Android.
Algumas características da BufferQueue, como o número máximo de buffers que ela pode conter, são determinadas em conjunto pelo produtor e pelo consumidor. No entanto, a BufferQueue aloca buffers conforme necessário. Os buffers são mantidos, a menos que as características mudem. Por exemplo, se um produtor solicitar buffers com um tamanho diferente, os buffers antigos serão liberados e os novos serão alocados sob demanda.
O conteúdo do buffer nunca é copiado pelo BufferQueue, porque mover tantos dados é ineficiente. Em vez disso, os buffers são sempre transmitidos por um identificador.
Rastrear BufferQueue com o Systrace
Para entender como os buffers gráficos se movem, use o Systrace, uma ferramenta que registra a atividade do dispositivo por um curto período. O código de gráficos no nível do sistema é bem instrumentado, assim como grande parte do código relevante do framework do app.
Para usar o Systrace, ative as tags gfx
, view
e
sched
. Os objetos BufferQueue são mostrados no rastro.
Por exemplo, se você fizer um rastreamento enquanto o
vídeo do Play
da Grafika (SurfaceView) estiver em execução, a linha SurfaceView informará
quantos buffers foram enfileirados em um determinado momento.
O valor é incrementado enquanto o app está ativo, o que aciona a renderização de frames pelo decodificador MediaCodec. O valor diminui enquanto o SurfaceFlinger está trabalhando e consumindo buffers. Ao mostrar vídeos a 30 qps, o valor da fila varia de 0 a 1 porque a exibição de aproximadamente 60 qps pode acompanhar a origem. O SurfaceFlinger é ativado apenas quando há trabalho a ser feito, não 60 vezes por segundo. O sistema tenta evitar o trabalho e desativa a VSYNC se nada estiver atualizando a tela.
Se você mudar para o vídeo do Play da Grafika (TextureView) e extrair um novo rastro,
uma linha será mostrada com o nome
com.android.grafika
 / com.android.grafika.PlayMovieActivity
.
Essa é a camada principal da interface, que é outra BufferQueue. Como TextureView
renderiza na camada da interface, e não em uma camada separada, todas as
atualizações geradas por vídeo são mostradas aqui.
Gralloc
O alocador HAL Gralloc
hardware/libhardware/include/hardware/gralloc.h
executa alocações de buffer usando flags de uso. As flags de uso incluem atributos
como:
- Com que frequência a memória será acessada pelo software (CPU)
- Com que frequência a memória será acessada pelo hardware (GPU)
- Indica se a memória será usada como uma textura OpenGL ES (GLES)
- Se a memória será usada por um codificador de vídeo
Por exemplo, se o formato de buffer de um produtor especificar pixels RGBA_8888
e
o produtor indicar que o buffer será acessado pelo software (ou seja, um app
tocará pixels na CPU), o Gralloc criará um buffer com 4 bytes por pixel
na ordem R-G-B-A. Se um produtor especificar que o buffer será acessado apenas
pelo hardware e como uma textura GLES, o Gralloc poderá fazer tudo o que o driver
GLES quiser, como ordenação BGRA, layouts não lineares e formatos de cores
alternativos. Permitir que o hardware use o formato preferido pode
melhorar o desempenho.
Alguns valores não podem ser combinados em determinadas plataformas. Por exemplo, a flag do codificador
de vídeo pode exigir pixels YUV. Portanto, adicionar o acesso ao software e especificar
RGBA_8888
falha.
O identificador retornado pelo Gralloc pode ser transmitido entre processos pelo Binder.
Buffers protegidos
A flag de uso do Gralloc GRALLOC_USAGE_PROTECTED
permite que o
buffer gráfico seja mostrado apenas por um caminho protegido por hardware. Esses
planos de sobreposição são a única maneira de exibir conteúdo DRM. Os buffers protegidos por DRM
não podem ser acessados pelo SurfaceFlinger ou pelo driver OpenGL ES.
O vídeo protegido por DRM só pode ser apresentado em um plano de sobreposição. Os players de vídeo que oferecem suporte a conteúdo protegido precisam ser implementados com o SurfaceView. Softwares executados em hardware não protegido não podem ler nem gravar o buffer. Os caminhos protegidos por hardware precisam aparecer na sobreposição do compositor de hardware. Ou seja, vídeos protegidos desaparecem da tela se o compositor de hardware mudar para a composição OpenGL ES.
Para mais detalhes sobre conteúdo protegido, consulte DRM.