Implementando o Hardware Composer HAL

O Hardware Composer (HWC) HAL compõe as camadas recebidas do SurfaceFlinger, reduzindo a quantidade de composição que o OpenGL ES (GLES) e a GPU executam.

O HWC abstrai objetos, como sobreposições e blitters 2D, para superfícies compostas e se comunica com hardware especializado de composição de janelas para janelas compostas. 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 e, quando a GPU compõe camadas do SurfaceFlinger, os aplicativos não podem usar a GPU para sua própria renderização.

As implementações de HWC devem suportar:

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

Para implementar o HWC:

  1. Implemente um HWC não operacional e envie todo o trabalho de composição para o GLES.
  2. Implemente um algoritmo para delegar a composição ao HWC de forma incremental. Por exemplo, delegue apenas as primeiras três ou quatro superfícies ao hardware de sobreposição do HWC.
  3. Otimize o HWC. Isso pode incluir:
    • Selecionando superfícies que maximizam a carga retirada da GPU e enviando-as para o HWC.
    • Detectando se a tela está atualizando. Se não for, delegue a composição ao GLES em vez do HWC para economizar energia. Quando a tela for atualizada novamente, continue a descarregar a composição para o HWC.
    • Preparando-se para casos de uso comuns, como:
      • A tela inicial, que inclui a barra de status, barra do sistema, janela do aplicativo e papéis de parede ao vivo
      • Jogos em tela cheia no modo retrato e paisagem
      • Vídeo em tela cheia com legendas fechadas e controle de reprodução
      • Reprodução de vídeo protegida
      • Tela dividida em várias janelas

Primitivas HWC

O HWC fornece duas primitivas, layers e displays , para representar o trabalho de composição e sua interação com o hardware do display. O HWC também fornece controle sobre VSYNC e um retorno de chamada para SurfaceFlinger para notificá-lo quando ocorrer um evento VSYNC.

Interface HIDL

O Android 8.0 e superior usa uma interface HIDL chamada Composer HAL para IPC vinculado entre o HWC e o SurfaceFlinger. O Composer HAL substitui a interface herdada hwcomposer2.h . Se os fornecedores fornecerem uma implementação do Composer HAL do HWC, o Composer HAL aceitará diretamente as chamadas HIDL do SurfaceFlinger. Se os fornecedores fornecerem uma implementação herdada do HWC, o Composer HAL 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 um determinado display; para alternar entre diferentes configurações de exibição (como resolução 4k ou 1080p) e modos de cor (como cor nativa ou sRGB verdadeiro); e para ligar, desligar ou colocar o monitor no modo de baixo consumo de energia, se suportado.

Ponteiros de função

Se os fornecedores implementarem o Composer HAL diretamente, o SurfaceFlinger chama suas funções por meio do HIDL IPC. Por exemplo, para criar uma camada, SurfaceFlinger chama createLayer() no Composer HAL.

Se os fornecedores implementarem a interface hwcomposer2.h , o Composer HAL chamará ponteiros de função hwcomposer2.h . Nos comentários hwcomposer2.h , as funções da interface 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 é passado para getFunction .

Para obter documentação detalhada sobre as funções do Composer HAL e as funções de passagem da função HWC, consulte composer . Para documentação detalhada sobre ponteiros de função HWC, consulte hwcomposer2.h .

Camadas e alças de exibição

Camadas e displays são manipulados por handles gerados pelo HWC. As alças são opacas para SurfaceFlinger.

Quando o SurfaceFlinger cria uma nova camada, ele chama createLayer , que retorna do tipo Layer para implementações diretas ou hwc2_layer_t para implementações de passagem. Quando SurfaceFlinger modifica uma propriedade dessa camada, SurfaceFlinger passa o valor hwc2_layer_t para a função de modificação apropriada junto com qualquer outra informação necessária para fazer a modificação. O tipo hwc2_layer_t é grande o suficiente para conter um ponteiro ou um índice.

Exibições físicas são criadas por hotplug. Quando uma exibição física é hotplug, o HWC cria um identificador e passa o identificador para SurfaceFlinger por meio do retorno de chamada hotplug. As telas virtuais são criadas pelo SurfaceFlinger chamando createVirtualDisplay() para solicitar uma tela. Se o HWC oferecer suporte à composição de exibição virtual, ele retornará um identificador. Em seguida, o SurfaceFlinger delega a composição das telas ao HWC. Se o HWC não oferecer suporte à composição de exibição virtual, o SurfaceFlinger cria a alça e compõe a exibição.

Operações de composição de exibição

Uma vez por VSYNC, o SurfaceFlinger é ativado se tiver novo conteúdo para compor. Esse novo conteúdo pode ser novos buffers de imagem de aplicativos ou uma alteração nas propriedades de uma ou mais camadas. Quando o SurfaceFlinger o desperta:

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

Para realizar uma nova composição, o SurfaceFlinger cria e destrói camadas ou modifica os estados das camadas, conforme aplicável. Ele também atualiza as camadas com seu conteúdo atual, usando chamadas como setLayerBuffer ou setLayerColor . Depois que todas as camadas são atualizadas, SurfaceFlinger chama validateDisplay , que informa ao HWC para examinar o estado das camadas e determinar como a composição prosseguirá. Por padrão, o SurfaceFlinger tenta configurar cada camada de forma que a camada seja composta pelo HWC; embora, em algumas circunstâncias, o SurfaceFlinger componha camadas por meio do fallback da GPU.

Após a chamada para validateDisplay , SurfaceFlinger chama getChangedCompositionTypes para ver se o HWC deseja que algum dos tipos de composição de camada seja alterado antes de executar a composição. Para aceitar as alterações, SurfaceFlinger chama acceptDisplayChanges .

Se alguma camada estiver marcada para composição do SurfaceFlinger, o SurfaceFlinger as compõe no buffer de destino. SurfaceFlinger então chama setClientTarget para fornecer o buffer para a exibição para que o buffer possa ser exibido na tela ou ainda composto com camadas que não foram marcadas para composição SurfaceFlinger. Se nenhuma camada estiver marcada para a composição do SurfaceFlinger, o SurfaceFlinger ignora a etapa de composição.

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

Vários monitores

O Android 10 é compatível com vários monitores físicos. Ao projetar uma implementação de HWC destinada ao uso no Android 7.0 e superior, existem algumas restrições que não estão presentes na definição de HWC:

  • Supõe-se que haja exatamente uma tela interna . A tela interna é a tela que o hotplug inicial relata durante a inicialização. Depois que o display interno é conectado a quente, ele não pode ser desconectado.
  • Além do display interno, qualquer número de displays externos pode ser conectado durante a operação normal do dispositivo. A estrutura assume que todos os hotplugs após o primeiro monitor interno são monitores externos, portanto, se mais monitores internos forem adicionados, eles serão categorizados incorretamente como Display.TYPE_HDMI em vez de Display.TYPE_BUILT_IN .

Embora as operações do SurfaceFlinger descritas acima sejam executadas por tela, elas são executadas 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 é:

// 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 exibição virtual

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

Modos

Cada quadro está em um dos três modos após SurfaceFlinger chamar o método HWC validateDisplay() :

  • 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 — 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 de seu modo:

  • Modo GLES — O driver EGL define o formato do buffer de saída em dequeueBuffer() , normalmente RGBA_8888 . O consumidor deve ser capaz de aceitar o formato de saída definido pelo driver ou o buffer não poderá ser lido.
  • Modos MIXED e HWC — Se o consumidor precisar de acesso à CPU, o consumidor define o formato. Caso contrário, o formato é IMPLEMENTATION_DEFINED e Gralloc define o melhor formato com base nos sinalizadores de uso. Por exemplo, Gralloc define um formato YCbCr se o consumidor for um codificador de vídeo e o HWC puder gravar o formato com eficiência.

Cercas de sincronização

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

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

O HWC requer que a GPU termine de gravar os buffers antes que os buffers sejam exibidos. As cercas de sincronização são passadas pelo pipeline gráfico com buffers e sinalizam quando os buffers são gravados. Antes de um buffer ser exibido, o HWC verifica se o sync fence foi sinalizado e, se tiver, ele exibe o buffer.

Para obter mais informações sobre as cercas de sincronização, consulte Integração do Compositor de Hardware .