Adicionar novos formatos de pixel ao Android

Todos os novos formatos de pixel adicionados ao Android precisam ser incluídos na Linguagem de definição de interface do Android (AIDL) e no Buffer de hardware do Android (AHB). O AIDL e o AHB têm requisitos rigorosos de estabilidade e padronização que exigem um processo cuidadoso ao estender a funcionalidade. Todos os novos formatos de pixel precisam ser lançados no AOSP e todas as atualizações precisam ser confirmadas individualmente por especialistas em AIDL e AHB. Esse processo de confirmação cuidadosa é um fator importante na padronização de novos formatos de pixel na plataforma.

Esta página descreve as mudanças necessárias no código do AOSP e o processo necessário para adicionar novos formatos de pixel no AOSP.

Antes de adicionar um novo formato de pixel, faça o download da origem e envie os patches conforme descrito em Enviar patches.

Adicionar um novo formato de pixel ao AIDL

Adicionar suporte a um novo formato de pixel exige mudanças nos dois arquivos PixelFormat.aidl localizados no AIDL. Consulte hardware/interfaces/graphics/common/aidl/ para conferir o código-fonte do AIDL.

Para adicionar um novo pixel formal à AIDL, siga estas etapas:

  1. Anexe o novo formato de pixel como uma nova entrada ao final do tipo enumerado PixelFormat em PixelFormat.aidl, seguindo a convenção de código atual e definindo o valor hexadecimal da entrada como um número maior do que a anterior. Corresponda as alterações de código às entradas anteriores. Confira o exemplo abaixo para a entrada de formato de pixel RGBA_8888:
    /**
     * 32-bit format that has 8-bit R, G, B, and A components, in that order,
     * from the lowest memory address to the highest memory address.
     *
     * The component values are unsigned normalized to the range [0, 1], whose
     * interpretation is defined by the dataspace.
     */
    RGBA_8888 = 0x1,
    

    A seguinte mensagem de erro aparece quando você cria o código depois de fazer mudanças em PixelFormat.aidl:

    android_developer:~/android/aosp-main: m
    ...
    ###############################################################################
    # ERROR: AIDL API change detected                                             #
    ###############################################################################
    Above AIDL file(s) has changed. Run `m android.hardware.graphics.common-update-api` to reflect the changes
    to the current version so that it is reviewed by
    android-aidl-api-council@google.com
    And then you need to change dependency on android.hardware.graphics.common-V(n)-* to android.hardware.graphics.common-V(n+1)-* to use
    new APIs.
    
  2. Para limpar esse erro, execute o comando a seguir, conforme especificado na mensagem de erro, para mudar PixelFormat.aidl no diretório aidl_api:

    m android.hardware.graphics.common-update-api
    

    A execução do comando acima atualiza o arquivo correto para que ele possa ser criado normalmente.

Adicionar um novo formato de pixel ao AHB

Adicionar suporte a um novo formato de pixel exige mudanças em hardware_buffer.h e AHardwareBuffer.cpp. Consulte frameworks/native/libs/nativewindow para conferir o código-fonte do AHB.

Para adicionar um novo pixel formal ao AHB, siga estas etapas:

  1. Em hardware_buffer.h, anexe o novo formato de pixel como uma nova entrada ao final da enumeração AHardwareBuffer_Format. Siga as convenções de código atuais.

    Usando o exemplo de formato de pixel RGBA_8888, adicione a nova entrada de formato de pixel desta maneira:

    /**
     * Corresponding formats:
     *   Vulkan: VK_FORMAT_R8G8B8A8_UNORM
     *   OpenGL ES: GL_RGBA8
     */
    AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1,
    

    O novo formato de pixel recebe um nome em AHB, que precisa começar com AHARDWAREBUFFER_FORMAT_, seguido pelas abreviações do canal e pela profundidade de bits e terminar com a codificação. Essa entrada de tipo enumerado precisa ter o mesmo valor hexadecimal que o de PixelFormat.aidl.

    Espera-se que o formato de pixel tenha um ou ambos os formatos associados Vulkan ou OpenGL ES. Especifique o formato associado, quando apropriado. Se nenhum formato associado existir, especifique N/A.

  2. Adicione o formato de pixel a testes opcionais no CTS se ele tiver um formato OpenGL ES associado. Para fazer isso, adicione o novo formato GL ao AHardwareBufferGLTest.cpp em AHBFormatAsString(int32_t format) com FORMAT_CASE(...) e GL_FORMAT_CASE(...) para o novo formato, mostrado da seguinte maneira:

    const char* AHBFormatAsString(int32_t format) {
      switch (format) {
          ...
          FORMAT_CASE(R8G8B8A8_UNORM);
          ...
          GL_FORMAT_CASE(GL_RGB8);
      }
      return "";
    }
    
  3. Em seguida, adicione um novo teste a AHardwareBufferGLTest.cpp, mostrado da seguinte maneira:

    class RGBA8Test : public AHardwareBufferGLTest {};
    
    // Verify that if we can allocate an RGBA8 AHB we can render to it.
    TEST_P(RGBA8Test, Write) {
        AHardwareBuffer_Desc desc = GetParam();
        desc.usage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
        if (!SetUpBuffer(desc)) {
            return;
        }
    
        ASSERT_NO_FATAL_FAILURE(SetUpFramebuffer(desc.width, desc.height, 0, kBufferAsRenderbuffer));
        ASSERT_NO_FATAL_FAILURE(
            SetUpProgram(kVertexShader, kColorFragmentShader, kPyramidPositions, 0.5f));
    
        glDrawArrays(GL_TRIANGLES, 0, kPyramidVertexCount);
        ASSERT_EQ(GLenum{GL_NO_ERROR}, glGetError());
    }
    
    INSTANTIATE_TEST_CASE_P(
        SingleLayer, RGBA8Test,
        ::testing::Values(
            AHardwareBuffer_Desc{57, 33, 1, AHARDWAREBUFFER_FORMAT_R16G16_UINT, 0, 0, 0, 0}),
        &GetTestName);
    

    Especifique pelo menos um conjunto de valores AHardwareBuffer_Desc. Adicione mais valores, se necessário.

  4. Em AHardwareBuffer.cpp, encontre o fim das declarações estáticas encontradas:

    // ----------------------------------------------------------------------------
    // Validate hardware_buffer.h and PixelFormat.aidl agree
    // ----------------------------------------------------------------------------
    

    Anexe um novo static_assert para o novo formato de pixel, usando o enum PixelFormat:: e não a constante HAL_PIXEL_FORMAT. Usando o mesmo exemplo para o formato de pixel RGBA_8888 de Adicionar um novo formato de pixel ao AIDL, adicione a nova entrada de formato de pixel da seguinte maneira:

    static_assert(static_cast(aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888) ==
      AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
    "HAL and AHardwareBuffer pixel format don't match");
    
  5. Adicione o novo formato de pixel aos testes apropriados anexando-o ao final de PrintAhbFormat() em AHardwareBufferTest.cpp. Siga a convenção de código atual, conforme mostrado abaixo:

    void PrintAhbFormat(std::ostream& os, uint64_t format) {
        switch (format) {
            ...
            FORMAT_CASE(R8G8B8A8_UNORM);
            default: os << "unknown"; break;
        }
    }
    
  6. Adicione o novo formato de pixel ao SDK HardwareBuffer em HardwareBuffer.java: anexando uma nova entrada a @IntDef. Por exemplo, a entrada para o formato RGBA_8888 é mostrada da seguinte maneira:

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "RGB", "BLOB", "YCBCR_", "D_", "DS_", "S_" }, value = {
      ...
      RGBA_8888,
    })
    

    Se os valores dos componentes não forem normalizados sem sinal, indique o valor explicitamente no nome da variável. Por exemplo, o nome da variável para um formato de canal vermelho de número inteiro não assinado de 16 bits precisa ser R_16UI, e o mesmo formato com um formato de canal verde de número inteiro não assinado de 16 bits precisa ser RG_16UI16UI.

  7. Adicione o novo formato de pixel como static int em HardwareBuffer.java, anexando uma nova variável de membro público ao final de @Format:

    @Format
    ...
    /** Format: 8 bits each red, green, blue, alpha */
    public static final int RGBA_8888 = 0x1;
    

    Essa entrada de enumeração precisa ter o mesmo valor hexadecimal que o de PixelFormat.aidl e hardware_buffer.h. Siga as convenções atuais.

  8. A tentativa de criar com essas mudanças no código gera um erro de build:

    android_developer:~/android/aosp-main: m
    ...
    ******************************
    You have tried to change the API from what has been previously approved.
    
    To make these errors go away, you have two choices:
       1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)
          to the new methods, etc. shown in the above diff.
    
       2. You can update current.txt and/or removed.txt by executing the following command:
             m api-stubs-docs-non-updatable-update-current-api
    
          To submit the revised current.txt to the main Android repository,
          you will need approval.
    ******************************
    ...
    

    Para limpar esse erro, execute o seguinte comando, conforme especificado na mensagem de erro, para alterar current.txt:

    m api-stubs-docs-non-updatable-update-current-api
    

    A execução do comando acima atualiza o arquivo correto para que seja possível compilar normalmente.

  9. Adicione o novo formato de pixel aos testes de Java anexando o novo formato de pixel ao final de paramsForTestCreateOptionalFormats() em HardwareBufferTest.java, conforme mostrado a seguir:

    private static Object[] paramsForTestCreateOptionalFormats() {
      return new Integer[]{
          HardwareBuffer.RGBA_8888
      };
    

Adicionamos um novo formato de pixel à integração do sistema de janelas

Para usar o novo formato de pixel como o formato de um framebuffer em uma API de gráficos, adicione-o à integração do sistema de janelas (WSI, na sigla em inglês) adequada para a API de gráficos relevante. Para um app ou processo do sistema que usa a API Vulkan, atualize a Swapchain do Vulkan. Para um processo de app ou sistema que usa a API OpenGL ES, atualize a API EGL.

Mudanças no WSI do Vulkan para novos formatos de pixels

Atualize o WSI do Vulkan da seguinte maneira:
  1. Adicione um novo caso à função GetNativePixelFormat(VkFormat format) em swapchain.cpp:

    android::PixelFormat GetNativePixelFormat(VkFormat format) {
      ...
      switch (format) {
          ...
          case VK_FORMAT_R8G8B8A8_UNORM:
              native_format = PixelFormat::RGBA_8888;
              break;
          ...
          default:
              ALOGV("unsupported swapchain format %d", format);
              break;
      }
      return native_format;
    }
    
  2. Consultar a extensão do Vulkan se o formato de pixel exigir uma extensão do Vulkan para funcionar. Para extensões secundárias da instância, use instance_data, mostrado da seguinte maneira:
    bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
    

    Para extensões no lado do dispositivo, use o seguinte:

    bool rgba10x6_formats_ext = false;
    uint32_t exts_count;
    const auto& driver = GetData(pdev).driver;
    driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
                                              nullptr);
    std::vector props(exts_count);
    driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
                                              props.data());
    for (uint32_t i = 0; i < exts_count; i++) {
        VkExtensionProperties prop = props[i];
        if (strcmp(prop.extensionName,
                   VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME) == 0) {
            rgba10x6_formats_ext = true;
        }
    }
    

    O Google processa a infraestrutura necessária para expor uma instância ou extensão de dispositivo ao swapchain.cpp. A lista de mudanças inicial não é necessária para configurar as extensões corretamente no carregador do Vulkan.

  3. Em seguida, enumere os pares de formato e espaço de cores:
    desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
    if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) {
      all_formats.emplace_back(
          VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                             VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
      if (colorspace_ext) {
        all_formats.emplace_back(
            VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                               VK_COLOR_SPACE_PASS_THROUGH_EXT});
        all_formats.emplace_back(
            VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                               VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
    }
    

    Você deve ter conhecimento do formato compatível e dos pares de espaços de cores.

  4. Adicione o novo formato a dEQP-VK, localizado em external/deqp.
  5. Atualize os testes de conformidade do Vulkan em vktApiExternalMemoryTests.cpp e vktExternalMemoryUtil.cpp inferindo as mudanças necessárias da fonte atual ou entrando em contato com o suporte do Android para mais informações.

Mudanças na EGL para novos formatos de pixel

Atualize o EGL da seguinte maneira:

  1. Na função getNativePixelFormat(), modifique a árvore if-else para retornar a enumeração AIDL para o novo formato de pixel. Usando o exemplo do formato de pixel RGBA_8888:
    if (a == 0) {
      ...
    } else {
      if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
          if (colorDepth > 24) {
              ...
          } else {
              *format = PixelFormat::RGBA_8888;
          }
      } else {
        ...
      }
    }
    
  2. Para adicionar o novo formato ao dEQP, adicione uma nova entrada ao tipo enumerado androidFormats, conforme mostrado abaixo:
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

Enviar a atualização

Siga Para colaboradores para criar suas listas de mudanças e compartilhá-las com a equipe apropriada.