BufferQueue e Gralloc

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.