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.