Implementar a HAL do Hardware Composer

A HAL do Hardware Composer (HWC) combina camadas recebidas do SurfaceFlinger, reduzindo a quantidade de composição OpenGL ES (GLES) e o desempenho da GPU.

O HWC abstrai objetos, como sobreposições e blitters 2D, para compor superfícies e se comunica com hardware especializado de composição de janelas para compor janelas. Use o HWC para compor janelas em vez de ter o SurfaceFlinger composto com a GPU. A maioria das GPUs não é otimizada para composição. Quando a GPU compõe camadas do SurfaceFlinger, os apps não podem usar a GPU para renderização própria.

As implementações do HWC precisam oferecer suporte a:

  • Pelo menos quatro sobreposições:
    • Barra de status
    • Barra de sistema
    • App
    • Plano de fundo
  • Camadas maiores que a tela (por exemplo, um papel de parede)
  • Mesclagem alfa pré-multiplicada simultânea por pixel e por plano
  • Caminho de hardware para reprodução de vídeo protegida
  • Ordem de empacotamento RGBA, formatos YUV e propriedades de mosaico, swizzling e stride

Para implementar o HWC:

  1. Implemente um HWC não operacional e envie todo o trabalho de composição para GLES.
  2. Implemente um algoritmo para delegar a composição ao HWC de forma incremental. Por exemplo, delegue apenas as três ou quatro primeiras superfícies ao hardware de sobreposição do HWC.
  3. Otimize o HWC. Isso pode incluir:
    • Selecionar superfícies que maximizam a redução da carga da GPU e enviá-las para o HWC.
    • Detectar se a tela está sendo atualizada. Caso contrário, delegue a composição ao GLES em vez do HWC para economizar energia. Quando a tela atualizar novamente, continue descarregando a composição para o HWC.
    • Preparação para casos de uso comuns, como:
      • A tela inicial, que inclui a barra de status, a barra de sistema, a janela do app e os planos de fundo interativos
      • Jogos em tela cheia no modo retrato e paisagem
      • Vídeo em tela cheia com legendas descritivas e controle de reprodução
      • Reprodução de vídeo protegido
      • Tela dividida em várias janelas

Primitivos HWC

O HWC oferece duas primitivas, camadas e displays, para representar o trabalho de composição e a interação dele com o hardware de exibição. O HWC também oferece controle sobre o VSync e um callback para o SurfaceFlinger para notificá-lo quando um evento VSync ocorre.

Interface HIDL

O Android 8.0 e versões mais recentes usam uma interface HIDL chamada Composer HAL para IPC binderizado entre o HWC e o SurfaceFlinger. A HAL do Composer substitui a interface hwcomposer2.h legada. Se os fornecedores disponibilizarem uma implementação da HAL do Composer do HWC, a HAL do Composer vai aceitar diretamente as chamadas HIDL do SurfaceFlinger. Se os fornecedores disponibilizarem uma implementação legada do HWC, o Composer HAL vai carregar ponteiros de função de hwcomposer2.h, encaminhando chamadas HIDL para chamadas de ponteiro de função.

O HWC fornece funções para determinar as propriedades de uma determinada tela, alternar entre diferentes configurações de tela (como resolução 4K ou 1080p) e modos de cor (como cor nativa ou sRGB verdadeiro) e ligar, desligar ou colocar a tela em um modo de baixo consumo de energia, se compatível.

Ponteiros de função

Se os fornecedores implementarem a HAL do Composer diretamente, o SurfaceFlinger vai chamar as funções dela pela IPC do HIDL. Por exemplo, para criar uma camada, o SurfaceFlinger chama createLayer() na HAL do compositor.

Se os fornecedores implementarem a interface hwcomposer2.h, o HAL do Composer fará chamadas para ponteiros de função hwcomposer2.h. Nos comentários hwcomposer2.h, as funções de interface do HWC são referidas por nomes lowerCamelCase que não existem na interface como campos nomeados. Quase todas as funções são carregadas solicitando um ponteiro de função usando getFunction fornecido por hwc2_device_t. Por exemplo, a função createLayer é um ponteiro de função do tipo HWC2_PFN_CREATE_LAYER, que é retornado quando o valor enumerado HWC2_FUNCTION_CREATE_LAYER é transmitido para getFunction.

Para documentação detalhada sobre funções do Composer HAL e funções de encaminhamento de função do HWC, consulte composer. Para ver a documentação detalhada sobre ponteiros de função HWC, consulte hwcomposer2.h.

Alças de camada e exibição

As camadas e telas são manipuladas por identificadores gerados pelo HWC. Os identificadores são opacos para o SurfaceFlinger.

Quando o SurfaceFlinger cria uma nova camada, ele chama createLayer, que retorna o tipo Layer para implementações diretas ou hwc2_layer_t para implementações de transição. Quando o SurfaceFlinger modifica uma propriedade dessa camada, ele transmite o valor hwc2_layer_t para a função de modificação adequada junto com outras informações necessárias para fazer a modificação. O tipo hwc2_layer_t é grande o suficiente para conter um ponteiro ou um índice.

Os monitores físicos são criados por hotplug. Quando um display físico é conectado a quente, o HWC cria um identificador e o transmite ao SurfaceFlinger pelo callback de conexão a quente. As telas virtuais são criadas pelo SurfaceFlinger ao chamar createVirtualDisplay() para solicitar uma tela. Se o HWC for compatível com a composição de tela virtual, ele vai retornar um identificador. Em seguida, o SurfaceFlinger delega a composição das telas ao HWC. Se o HWC não for compatível com a composição de tela virtual, o SurfaceFlinger vai criar o identificador e compor a tela.

Mostrar operações de composição

Uma vez por VSync, o SurfaceFlinger é ativado se tiver um novo conteúdo para compor. Esse novo conteúdo pode ser novos buffers de imagem de apps ou uma mudança nas propriedades de uma ou mais camadas. Quando o SurfaceFlinger o ativa:

  1. Processa transações, se houver.
  2. Trava novos buffers gráficos, se presente.
  3. Realiza uma nova composição se a etapa 1 ou 2 resultar em uma mudança no conteúdo da tela.

Para realizar uma nova composição, o SurfaceFlinger cria e destrói camadas ou modifica estados de camada, conforme aplicável. Ele também atualiza camadas com o conteúdo atual usando chamadas como setLayerBuffer ou setLayerColor. Depois que todas as camadas são atualizadas, o SurfaceFlinger chama validateDisplay, que informa ao HWC para examinar o estado das camadas e determinar como a composição vai prosseguir. Por padrão, o SurfaceFlinger tenta configurar todas as camadas para que elas sejam compostas pelo HWC. No entanto, em algumas circunstâncias, o SurfaceFlinger compõe camadas usando o fallback da GPU.

Depois da chamada para validateDisplay, o SurfaceFlinger chama getChangedCompositionTypes para ver se o HWC quer que algum dos tipos de composição de camada seja alterado antes de realizar a composição. Para aceitar as mudanças, o SurfaceFlinger chama acceptDisplayChanges.

Se alguma camada estiver marcada para composição do SurfaceFlinger, o SurfaceFlinger as compõe no buffer de destino. Em seguida, o SurfaceFlinger chama setClientTarget para dar o buffer à tela para que ele possa ser mostrado na tela ou composto com camadas que não foram marcadas para composição do SurfaceFlinger. Se nenhuma camada for marcada para composição do SurfaceFlinger, o SurfaceFlinger vai ignorar a etapa de composição.

Por fim, o SurfaceFlinger chama presentDisplay para informar ao HWC que conclua o processo de composição e mostre o resultado final.

Vários anúncios gráficos

O Android 10 oferece suporte a várias telas físicas. Ao projetar uma implementação de HWC para uso no Android 7.0 e versões mais recentes, há algumas restrições não presentes na definição de HWC:

  • Presume-se que haja exatamente uma tela interna. A tela interna é a que o hotplug inicial informa durante a inicialização. Depois que a tela interna é conectada a quente, ela não pode ser desconectada.
  • Além da tela interna, qualquer número de telas externas pode ser conectado a quente durante a operação normal do dispositivo. O framework pressupõe que todos os hotplugs após a primeira tela interna são telas externas. Portanto, se mais telas internas forem adicionadas, elas serão categorizadas incorretamente como Display.TYPE_HDMI em vez de Display.TYPE_BUILT_IN.

Embora as operações do SurfaceFlinger descritas acima sejam realizadas por tela, elas são realizadas sequencialmente para todas as telas ativas, mesmo que o conteúdo de apenas uma tela seja atualizado.

Por exemplo, se o display externo for atualizado, a sequência será:

// In Android 9 and lower:

// Update state for internal display
// Update state for external display
validateDisplay(<internal display>)
validateDisplay(<external display>)
presentDisplay(<internal display>)
presentDisplay(<external display>)

// In Android 10 and higher:

// Update state for internal display
// Update state for external display
validateInternal(<internal display>)
presentInternal(<internal display>)
validateExternal(<external display>)
presentExternal(<external display>)

Composição de tela virtual

A composição da tela virtual é semelhante à da tela externa. A diferença entre a composição de tela virtual e a física é que as telas virtuais enviam a saída para um buffer do Gralloc em vez de para a tela. O Hardware Composer (HWC) grava a saída em um buffer, fornece a barreira de conclusão e envia o buffer para um consumidor (como o codificador de vídeo, GPU, CPU etc.). As telas virtuais podem usar 2D/blitter ou sobreposições se o pipeline de exibição gravar na memória.

Modos

Cada frame está em um dos três modos depois que o SurfaceFlinger chama o método validateDisplay() HWC:

  • GLES: a GPU compõe todas as camadas, gravando diretamente no buffer de saída. O HWC não está envolvido na composição.
  • MIXED: a GPU compõe algumas camadas no framebuffer, e o HWC compõe o framebuffer e as camadas restantes, gravando diretamente no buffer de saída.
  • HWC: o HWC compõe todas as camadas e grava diretamente no buffer de saída.

Formato de saída

Os formatos de saída do buffer de exibição virtual dependem do modo:

  • Modo GLES: o driver EGL define o formato do buffer de saída em dequeueBuffer(), geralmente RGBA_8888. O consumidor precisa aceitar o formato de saída definido pelo driver, caso contrário, o buffer não poderá ser lido.
  • Modos MIXED e HWC: se o consumidor precisar de acesso à CPU, ele definirá o formato. Caso contrário, o formato é IMPLEMENTATION_DEFINED, e o Gralloc define o melhor formato com base nas flags de uso. Por exemplo, o Gralloc define um formato YCbCr se o consumidor for um codificador de vídeo e o HWC puder gravar o formato de maneira eficiente.

Barreiras de sincronização

As barreiras de sincronização são um aspecto crucial do sistema de gráficos do Android. As barreiras permitem que o trabalho da CPU prossiga de forma independente do trabalho simultâneo da GPU, bloqueando apenas quando há uma dependência real.

Por exemplo, quando um app envia um buffer que está sendo produzido na GPU, ele também envia um objeto de sincronização. Essa barreira sinaliza quando a GPU termina de gravar no buffer.

O HWC exige que a GPU termine de gravar os buffers antes que eles sejam mostrados. As barreiras de sincronização são transmitidas pelo pipeline de gráficos com buffers e sinalizam quando os buffers são gravados. Antes de um buffer ser exibido, o HWC verifica se a barreira de sincronização sinalizou e, em caso afirmativo, mostra o buffer.

Para mais informações sobre barreiras de sincronização, consulte Integração do Hardware Composer.