Camadas e telas

Camadas e telas são duas primitivas que representam o trabalho de composição e as interações com o hardware de exibição.

Camadas

Uma camada é a unidade de composição mais importante. Uma camada é uma combinação de uma superfície e uma instância de SurfaceControl. Cada camada tem um conjunto de propriedades que definem como ela interage com outras camadas. As propriedades da camada estão descritas na tabela a seguir:

Propriedade Descrição
Posicional Define onde a camada aparece na tela. Inclui informações como as posições das bordas de uma camada e a ordem Z dela em relação a outras camadas (se ela deve ficar na frente ou atrás de outras camadas).
Conteúdo Define como o conteúdo mostrado na camada deve ser apresentado dentro dos limites definidos pelas propriedades posicionais. Inclui informações como corte (para expandir uma parte do conteúdo e preencher os limites da camada) e transformação (para mostrar conteúdo girado ou invertido).
Composição Define como a camada deve ser combinada com outras camadas. Inclui informações como modo de fusão e um valor alfa em toda a camada para composição alfa.
Otimização Fornece informações não estritamente necessárias para compor corretamente a camada, mas que podem ser usadas pelo dispositivo Hardware Composer (HWC) para otimizar a forma como ele realiza a composição. Inclui informações como a região visível da camada e qual parte dela foi atualizada desde o frame anterior.

Telas

Uma tela é outra unidade importante de composição. Um sistema pode ter várias telas, e elas podem ser adicionadas ou removidas durante as operações normais do sistema. Os anúncios são adicionados ou removidos a pedido do HWC ou da estrutura. O dispositivo HWC solicita que as telas sejam adicionadas ou removidas quando um monitor externo é conectado ou desconectado do dispositivo, o que é chamado de hotplugging. Os clientes solicitam telas virtuais, cujo conteúdo é renderizado em um buffer fora da tela em vez de em uma tela física.

Telas virtuais

O SurfaceFlinger é compatível com uma tela interna (integrada ao smartphone ou tablet), telas externas (como uma televisão conectada por HDMI) e uma ou mais telas virtuais que disponibilizam saída combinada no sistema. Os displays virtuais podem ser usados para gravar a tela ou enviá-la por uma rede. Os frames gerados para uma tela virtual são gravados em uma BufferQueue.

As telas virtuais podem compartilhar o mesmo conjunto de camadas da tela principal (a pilha de camadas) ou ter um conjunto próprio. Não há VSync para uma tela virtual. Portanto, o VSync da tela interna aciona a composição de todas as telas.

Em implementações de HWC que oferecem suporte a eles, telas virtuais podem ser compostas com OpenGL ES (GLES), HWC ou ambos. Em implementações sem suporte, as telas virtuais são sempre compostas usando GLES.

Estudo de caso: screenrecord

O comando screenrecord permite que o usuário grave tudo o que aparece na tela como um arquivo MP4 no disco. Para implementar isso, o sistema recebe frames compostos do SurfaceFlinger, grava-os no codificador de vídeo e, em seguida, grava os dados de vídeo codificados em um arquivo. Os codecs de vídeo são gerenciados por um processo separado (mediaserver), então buffers gráficos grandes precisam se mover pelo sistema. Para tornar o desafio ainda maior, a meta é gravar um vídeo de 60 fps em resolução total. A chave para fazer isso funcionar de maneira eficiente é o BufferQueue.

A classe MediaCodec permite que um app forneça dados como bytes brutos em buffers ou por uma superfície. Quando o screenrecord solicita acesso a um codificador de vídeo, o processo mediaserver cria uma BufferQueue, se conecta ao lado do consumidor e transmite o lado do produtor de volta ao screenrecord como uma superfície.

A utilidade screenrecord pede ao SurfaceFlinger para criar uma tela virtual que espelha a tela principal (ou seja, tem todas as mesmas camadas) e direciona a saída para a superfície que veio do processo mediaserver. Nesse caso, o SurfaceFlinger é o produtor de buffers, e não o consumidor.

Depois que a configuração for concluída, o screenrecord será acionado quando os dados codificados aparecerem. À medida que os apps são renderizados, os buffers viajam para o SurfaceFlinger, que os compõe em um único buffer enviado diretamente ao codificador de vídeo no processo mediaserver. Os frames completos nunca são vistos pelo processo screenrecord. Internamente, o processo mediaserver tem uma maneira própria de mover buffers que também transmite dados por identificador, minimizando a sobrecarga.

Estudo de caso: simular telas secundárias

O WindowManager pode pedir ao SurfaceFlinger para criar uma camada visível em que o SurfaceFlinger atua como o consumidor do BufferQueue. Também é possível pedir ao SurfaceFlinger para criar uma tela virtual, em que ele atua como produtor de BufferQueue.

Se você conectar uma tela virtual a uma camada visível, um loop fechado será criado, e a tela combinada vai aparecer em uma janela. Essa janela agora faz parte da saída combinada. Portanto, na próxima atualização, a imagem combinada dentro da janela também vai mostrar o conteúdo dela. Para ver isso em ação, ative as Opções do desenvolvedor em Configurações, selecione Simular telas secundárias e ative uma janela. Para ver telas secundárias em ação, use screenrecord para capturar o ato de ativar a tela e reproduzir quadro a quadro.