Cómo agregar formatos de píxeles nuevos a Android

Todos los formatos de píxeles nuevos que se agreguen a Android deben incluirse en el lenguaje de definición de la interfaz de Android (AIDL) y en el búfer de hardware de Android (AHB). El AIDL y el AHB tienen requisitos estrictos de estabilidad y estandarización que exigen un proceso cuidadoso cuando se extiende la funcionalidad. Todos los formatos de píxeles nuevos deben incluirse en el AOSP, y los expertos en AIDL y AHB deben confirmar individualmente todas las actualizaciones. Este proceso de confirmación cuidadosa es un factor importante para estandarizar cualquier formato de píxel nuevo en la plataforma.

En esta página, se describen los cambios de código necesarios en el AOSP y el proceso requerido para agregar nuevos formatos de píxeles en el AOSP.

Antes de agregar un nuevo formato de píxel, descarga el código fuente y sube los parches como se describe en Cómo enviar parches.

Agrega un nuevo formato de píxel a AIDL

Para agregar compatibilidad con un nuevo formato de píxel, se deben realizar cambios en ambos archivos PixelFormat.aidl ubicados dentro de AIDL. Consulta hardware/interfaces/graphics/common/aidl/ para ver el código fuente de AIDL.

Para agregar un nuevo formato de píxel a AIDL, sigue estos pasos:

  1. Agrega el nuevo formato de píxel como una entrada nueva al final de la enumeración PixelFormat en PixelFormat.aidl siguiendo la convención de código existente y configurando el valor hexadecimal de tu entrada para que sea uno más que la entrada anterior. Haz coincidir los cambios de código con las entradas anteriores. Consulta el siguiente ejemplo para ver la entrada del formato de píxel 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,
    

    Cuando compilas el código después de realizar cambios en PixelFormat.aidl, se muestra el siguiente mensaje de error:

    android_developer:~/android/aosp-android-latest-release: 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 borrar este error, ejecuta el siguiente comando, como se especifica en el mensaje de error, para cambiar PixelFormat.aidl en el directorio aidl_api:

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

    Ejecutar el comando anterior actualiza el archivo correcto para poder compilar normalmente.

Cómo agregar un nuevo formato de píxel al AHB

Agregar compatibilidad con un nuevo formato de píxel requiere cambios en hardware_buffer.h y AHardwareBuffer.cpp. Consulta frameworks/native/libs/nativewindow para ver el código fuente del AHB.

Para agregar un nuevo formato de píxel a AHB, sigue estos pasos:

  1. En hardware_buffer.h, agrega el nuevo formato de píxel como una entrada nueva al final de la enumeración AHardwareBuffer_Format. Sigue las convenciones de código existentes.

    Con el ejemplo del formato de píxel RGBA_8888, agrega la nueva entrada del formato de píxel de la siguiente manera:

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

    Ten en cuenta que el nuevo formato de píxel recibe un nombre en AHB, que debe comenzar con AHARDWAREBUFFER_FORMAT_, seguido de las abreviaturas de los canales y las profundidades de bits, y terminar con la codificación. Esta entrada de enumeración debe tener el mismo valor hexadecimal que el de PixelFormat.aidl.

    Se espera que el formato de píxel tenga uno o ambos formatos asociados de Vulkan o OpenGL ES. Especifica el formato asociado cuando corresponda. Si no existe ningún formato asociado, especifica N/A.

  2. Agrega el formato de píxel a las pruebas opcionales en CTS si tiene un formato de OpenGL ES asociado. Para ello, agrega el nuevo formato de GL a AHardwareBufferGLTest.cpp en AHBFormatAsString(int32_t format) con FORMAT_CASE(...) y GL_FORMAT_CASE(...) para el nuevo formato, como se muestra a continuación:

    const char* AHBFormatAsString(int32_t format) {
      switch (format) {
          ...
          FORMAT_CASE(R8G8B8A8_UNORM);
          ...
          GL_FORMAT_CASE(GL_RGB8);
      }
      return "";
    }
    
  3. A continuación, agrega una prueba nueva a AHardwareBufferGLTest.cpp, como se muestra a continuación:

    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);
    

    Especifica al menos un conjunto de valores de AHardwareBuffer_Desc. Agrega más valores si es necesario.

  4. En AHardwareBuffer.cpp, busca el final de las aserciones estáticas que se encuentran en los siguientes elementos:

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

    Agrega un nuevo static_assert para el nuevo formato de píxel, usando el enum PixelFormat:: y no la constante HAL_PIXEL_FORMAT. Con el mismo ejemplo para el formato de píxel RGBA_8888 de Cómo agregar un formato de píxel nuevo a AIDL, agrega la nueva entrada de formato de píxel de la siguiente manera:

    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. Agrega el nuevo formato de píxel a las pruebas correspondientes. Para ello, agrega el nuevo formato de píxel al final de PrintAhbFormat() en AHardwareBufferTest.cpp. Sigue la convención de código existente, como se muestra a continuación:

    void PrintAhbFormat(std::ostream& os, uint64_t format) {
        switch (format) {
            ...
            FORMAT_CASE(R8G8B8A8_UNORM);
            default: os << "unknown"; break;
        }
    }
    
  6. Agrega el nuevo formato de píxel al SDK de HardwareBuffer en HardwareBuffer.java agregando una nueva entrada a @IntDef. Por ejemplo, la entrada para el formato RGBA_8888 se muestra de la siguiente manera:

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

    Si los valores de los componentes no están normalizados sin signo, indica el valor de forma explícita en el nombre de la variable. Por ejemplo, el nombre de la variable para un formato de solo canal rojo de 16 bits de número entero sin signo debe ser R_16UI, y el mismo formato con un formato de canal verde adicional de 16 bits de número entero sin signo debe ser RG_16UI16UI.

  7. Agrega el nuevo formato de píxel como un static int en HardwareBuffer.java. Para ello, agrega una nueva variable de miembro público al final de @Format:

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

    Esta entrada de enumeración debe tener el mismo valor hexadecimal que el de PixelFormat.aidl y hardware_buffer.h. Sigue las convenciones existentes.

  8. Si intentas compilar con estos cambios de código, se generará un error de compilación:

    android_developer:~/android/aosp-android-latest-release: 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 borrar este error, ejecuta el siguiente comando, como se especifica en el mensaje de error, para cambiar current.txt:

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

    Ejecutar el comando anterior actualiza el archivo correcto para poder compilar normalmente.

  9. Agrega el nuevo formato de píxel a las pruebas de Java. Para ello, agrega el nuevo formato de píxel al final de paramsForTestCreateOptionalFormats() en HardwareBufferTest.java, como se muestra a continuación:

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

Agrega un nuevo formato de píxel a la integración del sistema de ventanas

Para usar el nuevo formato de píxel como formato para un búfer de fotogramas en una API de gráficos, agrégalo a la integración del sistema de ventanas (WSI) adecuada para la API de gráficos pertinente. En el caso de un proceso del sistema o de una app que use la API de Vulkan, actualiza el intercambio de Vulkan. Para un proceso del sistema o de la app que use la API de OpenGL ES, actualiza la API de EGL.

Cambios en la WSI de Vulkan para nuevos formatos de píxeles

Actualiza la WSI de Vulkan de la siguiente manera:
  1. Agrega un caso nuevo a la función GetNativePixelFormat(VkFormat format) en 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. Consulta la extensión de Vulkan si el formato de píxel requiere una extensión de Vulkan para funcionar. Para las extensiones del servidor de instancias, usa instance_data, como se muestra a continuación:
    bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
    

    Para las extensiones del dispositivo, usa lo siguiente:

    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;
        }
    }
    

    Google se encarga de la infraestructura necesaria para exponer una extensión de instancia o dispositivo a swapchain.cpp. No es necesario que la lista de cambios inicial tenga las extensiones configuradas correctamente desde el cargador de Vulkan.

  3. A continuación, enumera los pares de formato y espacio de color:
    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});
    }
    

    Debes conocer los pares de formato y espacio de color compatibles.

  4. Agrega el nuevo formato a dEQP-VK, que se encuentra en external/deqp.
  5. Actualiza las pruebas de cumplimiento de Vulkan en vktApiExternalMemoryTests.cpp y vktExternalMemoryUtil.cpp. Para ello, infiere los cambios necesarios a partir del código fuente existente o comunícate con el equipo de asistencia de Android para obtener información.

Cambios en EGL para nuevos formatos de píxeles

Actualiza el EGL de la siguiente manera:

  1. En la función getNativePixelFormat(), modifica el árbol if-else para que muestre el enum de AIDL para el nuevo formato de píxel. Con el ejemplo del formato de píxel 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 agregar el nuevo formato al dEQP, agrega una entrada nueva al enum androidFormats, como se muestra a continuación:
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

Envía la actualización

Sigue las instrucciones de For contributors para crear tus listas de cambios y compartirlas con el equipo correspondiente.