Mapeamento de tons da luminância HDR em um intervalo compatível com SDR

O Android 13 introduz uma biblioteca estática configurável pelo fornecedor chamada libtonemap, que define operações de mapeamento de tons e é compartilhada com as implementações do processo SurfaceFlinger e do Hardware Composer (HWC). Esse recurso permite que os OEMs definam e compartilhem os algoritmos do mapeamento de tons de telas entre o framework e os fornecedores, o que diminui a incompatibilidade no mapeamento de tons.

Antes do Android 13, as operações de mapeamento de tons de tela específicos não eram compartilhadas entre o HWC, o SurfaceFlinger e os apps. Dependendo do caminho de renderização, para conteúdo HDR, isso levou a incompatibilidades na qualidade da imagem, em que o conteúdo HDR foi mapeado para um espaço de saída de maneiras diferentes. Isso foi perceptível em cenários como rotação de tela, em que a estratégia de composição muda entre a GPU e a DPU, e em diferenças no comportamento de renderização entre TextureView e SurfaceView.

Esta página descreve a interface, personalização e detalhes de validação da biblioteca libtonemap.

Interface para a biblioteca de mapeamento de tons

A biblioteca libtonemap contém implementações apoiadas pela CPU e sombreadores de SkSL, que podem ser conectados pelo SurfaceFlinger para composição de back-end de GPU e pelo HWC para gerar uma tabela de pesquisa de mapeamento de tons (LUT, na sigla em inglês). O ponto de entrada para libtonemap é android::tonemap::getToneMapper(), que retorna um objeto que implementa a interface ToneMapper.

A interface ToneMapper é compatível com os seguintes recursos:

  • Gerar uma LUT de mapeamento de tons

    A interface ToneMapper::lookupTonemapGain é uma implementação de CPU do sombreador definido em libtonemap_LookupTonemapGain(). Ele é usado por testes de unidade no framework e pode ser usado por parceiros para ajudar na geração de uma LUT de mapeamento de tons no pipeline de cores.

    libtonemap_LookupTonemapGain() recebe valores de cor em um espaço linear absoluto não normalizado, tanto em RGB linear quanto em XYZ, e retorna um número flutuante que descreve quanto multiplicar as cores de entrada no espaço linear.

  • Gerar um sombreador da SkSL

    A interface ToneMapper::generateTonemapGainShaderSkSL() retorna uma string de sombreador da SkSL de acordo com um espaço de dados de origem e de destino. O sombreador SkSL está conectado à implementação do Skia para RenderEngine, o componente de composição acelerado por GPU para o SurfaceFlinger. O sombreador também é conectado a libhwui, para que o mapeamento de tons HDR para SDR possa ser realizado de maneira eficiente para TextureView. Como a string gerada é alinhada a outros sombreadores SkSL usados pelo Skia, o sombreador precisa obedecer às seguintes regras:

    • A string do sombreador precisa ter um ponto de entrada com a assinatura float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz), em que linearRGB é o valor dos nits absolutos dos pixels RGB no espaço linear e xyz é linearRGB convertido em XYZ.
    • Todos os métodos auxiliares usados pela string do sombreador precisam ter o prefixo libtonemap_ para que as definições do sombreador do framework não entrem em conflito. Da mesma forma, os uniformes de entrada precisam ter o prefixo in_libtonemap_.
  • Gerar uniformes do SkSL

    A interface ToneMapper::generateShaderSkSLUniforms() retorna o seguinte, considerando um struct de metadados que descreve metadados de diferentes padrões HDR e condições de exibição:

    • Uma lista de uniformes vinculados por um sombreador da SkSL.

    • Os valores uniformes in_libtonemap_displayMaxLuminance e in_libtonemap_inputMaxLuminance. Esses valores são usados pelos sombreadores do framework ao dimensionar a entrada para libtonemap e normalizar a saída, conforme aplicável.

    Atualmente, o processo de geração de uniformes não depende do espaço de dados de entrada e saída.

Personalização

A implementação de referência da biblioteca libtonemap produz resultados aceitáveis. No entanto, como o algoritmo de mapeamento de tons usado pela composição da GPU pode ser diferente daquele usado pela composição da DPU, o uso da implementação de referência pode causar oscilação em alguns cenários, como a animação de rotação. A personalização pode resolver esses problemas de qualidade de imagem específicos do fornecedor.

É altamente recomendável que os OEMs substituam a implementação de libtonemap para definir a própria subclasse ToneMapper, que é retornada por getToneMapper(). Ao personalizar a implementação, os parceiros precisam:

  • Modificar a implementação de libtonemap diretamente.
  • Definir a própria biblioteca estática, compilar a biblioteca como independente e substituir o arquivo .a da biblioteca libtonemap pelo gerado da biblioteca personalizada.

Os fornecedores não precisam modificar nenhum código do kernel, mas vários fornecedores precisam comunicar detalhes sobre os algoritmos de mapeamento de tons da DPU para implementação adequada.

Validação

Siga estas etapas para validar sua implementação:

  1. Reproduza vídeos em HDR na tela de qualquer padrão HDR com suporte do seu sistema de exibição, como HLG, HDR10, HDR10+ ou DolbyVision.

  2. Alterne a composição da GPU para garantir que não haja cintilação perceptível pelo usuário.

    Use o comando adb abaixo para alternar a composição da GPU:

    adb shell service call SurfaceFlinger 1008 i32 <0 to enable HWC composition,
    1 to force GPU composition>
    
    

Problemas comuns

Os seguintes problemas podem ocorrer com essa implementação:

  • As faixas são causadas quando o destino de renderização usado pela composição da GPU tem uma precisão menor do que o valor típico para conteúdo HDR. Por exemplo, a mudança brusca de tom pode ocorrer quando uma implementação de HWC oferece suporte a formatos opacos de 10 bits para HDR, como RGBA1010102 ou P010, mas exige que a composição da GPU seja gravada em um formato de 8 bits como RGBA8888 para oferecer suporte a Alfa.

  • Uma mudança de cor sutil será causada por diferenças de quantização se a DPU operar com uma precisão diferente da GPU.

Cada um desses problemas está relacionado às diferenças de precisão relativa do hardware. Uma solução alternativa típica é garantir que haja uma etapa de dithering nos caminhos de precisão mais baixa, tornando as diferenças de precisão menos perceptíveis pelo ser humano.