Camadas e telas

As 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 a interação com outras camadas. As propriedades da camada são descritas na tabela abaixo.

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 em relação a outras camadas (se ela precisa estar na frente ou atrás de outras camadas).
Conteúdo Define como o conteúdo exibido na camada precisa 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 precisa ser composta com outras camadas. Inclui informações como modo de mesclagem e um valor alfa em toda a camada para composição alfa.
Otimização Fornece informações que não são estritamente necessárias para compor corretamente a camada, mas que podem ser usadas pelo dispositivo Hardware Composer (HWC) para otimizar a composição. Inclui informações como a região visível da camada e qual parte da camada foi atualizada desde o frame anterior.

Telas

Um display é 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. As telas são adicionadas/removidas a pedido do HWC ou a solicitação do framework. As solicitações do dispositivo HWC são adicionadas ou removidas quando uma tela externa é conectada ou desconectada 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 oferece suporte a 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 a saída composta no sistema. As telas virtuais podem ser usadas 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, então o VSYNC para a tela interna aciona a composição para todas as telas.

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

Estudo de caso: screenrecord

O comando screenrecord permite que o usuário registre tudo o que aparece na tela como um arquivo .mp4 no disco. Para implementar isso, o sistema recebe frames compostos do SurfaceFlinger, os grava 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 grandes buffers gráficos precisam se mover pelo sistema. Para aumentar o desafio, o objetivo é gravar vídeos de 60 qps na resolução máxima. A chave para fazer isso funcionar de maneira eficiente é a 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 para screenrecord como uma superfície.

O utilitário screenrecord solicita que o SurfaceFlinger crie uma tela virtual que espelha a tela principal (ou seja, tenha todas as mesmas camadas) e a direciona para enviar a saída para a superfície proveniente do processo mediaserver. Nesse caso, o SurfaceFlinger é o produtor de buffers, e não o consumidor.

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

Estudo de caso: simular telas secundárias

O WindowManager pode solicitar que o SurfaceFlinger crie uma camada visível para que o SurfaceFlinger atue como consumidor da BufferQueue. Também é possível pedir ao SurfaceFlinger para criar uma tela virtual, para a qual o SurfaceFlinger atua como produtor da BufferQueue.

Se você conectar uma tela virtual a uma camada visível, um loop fechado será criado, em que a tela composta aparece em uma janela. Essa janela agora faz parte da saída composta. Assim, na próxima atualização, a imagem composta dentro da janela também mostra o conteúdo da janela. Para conferir isso em ação, ative as Opções do desenvolvedor nas 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 reproduzi-la quadro por quadro.