向 Android 添加新像素格式

添加到 Android 的所有新像素格式都必须包含在 Android 接口定义语言 (AIDL)Android 硬件缓冲区 (AHB) 中。AIDL 和 AHB 具有严格的稳定性和标准化要求,因此在扩展功能时需要有谨慎的流程。所有新像素格式都必须在 AOSP 中推出,并且所有更新都必须由 AIDL 和 AHB 专家单独确认。这个谨慎确认的流程是对平台上任何新像素格式进行标准化时的重要因素。

本页概述了必要的 AOSP 代码更改以及在 AOSP 上添加新像素格式所需的流程。

在添加新像素格式之前,请按照提交补丁中的概述下载源代码并上传补丁。

向 AIDL 添加新像素格式

若要添加对新像素格式的支持,需要更改 AIDL 中的两个 PixelFormat.aidl 文件。如需了解 AIDL 源代码,请参阅 hardware/interfaces/graphics/common/aidl/

若要向 AIDL 添加新像素格式,请按以下步骤操作:

  1. 遵循现有的代码规范,将您条目的十六进制值设为比上一个条目多 1,以便将新像素格式作为新条目附加到 PixelFormat.aidl 中的 PixelFormat 枚举的末尾。使代码更改与之前的条目匹配。有关 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,
    

    如果您在对 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. 为了清除此错误,请按照错误消息中的说明运行以下命令,以更改 aidl_api 目录中的 PixelFormat.aidl

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

    运行上述命令会更新正确的文件,以便能够正常构建。

向 AHB 添加新像素格式

若要添加对新像素格式的支持,需要更改 hardware_buffer.hAHardwareBuffer.cpp。如需了解 AHB 源代码,请参阅 frameworks/native/libs/nativewindow

若要向 AHB 添加新像素格式,请按以下步骤操作:

  1. hardware_buffer.h 中,将新像素格式作为新条目附加到 AHardwareBuffer_Format 枚举的末尾。遵循现有的代码规范。

    RGBA_8888 像素格式为例,如下所示添加新像素格式条目:

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

    请注意,在 AHB 中,系统会给新像素格式命名,该名称必须以 AHARDWAREBUFFER_FORMAT_ 开头,后跟通道缩写和位深度,最后以编码结尾。此枚举条目必须具有与 PixelFormat.aidl 中相同的十六进制值。

    像素格式应具有相关联的 VulkanOpenGL ES 格式之一或二者皆有。在适当情况下指定关联的格式。如果不存在关联的格式,请指定 N/A

  2. 将像素格式添加到 CTS 下的可选测试,如果它具有相关联的 OpenGL ES 格式。为此,请将新的 GL 格式添加到 AHBFormatAsString(int32_t format) 中的 AHardwareBufferGLTest.cpp,并为新格式使用 FORMAT_CASE(...)GL_FORMAT_CASE(...),如下所示:

    const char* AHBFormatAsString(int32_t format) {
      switch (format) {
          ...
          FORMAT_CASE(R8G8B8A8_UNORM);
          ...
          GL_FORMAT_CASE(GL_RGB8);
      }
      return "";
    }
    
  3. 接下来,向 AHardwareBufferGLTest.cpp 添加新测试,如下所示:

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

    指定至少一组 AHardwareBuffer_Desc 值。根据需要添加更多值。

  4. AHardwareBuffer.cpp 中,找到在以下内容中发现的静态断言的末尾:

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

    使用 PixelFormat:: 枚举而非 HAL_PIXEL_FORMAT 常量为新像素格式附加新的 static_assert。使用向 AIDL 添加新像素格式RGBA_8888 像素格式的相同示例,添加新像素格式条目,如下所示:

    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. 通过将新像素格式附加到 AHardwareBufferTest.cpp 中的 PrintAhbFormat() 末尾,向相应的测试添加新像素格式。遵循现有的代码规范,如下所示:

    void PrintAhbFormat(std::ostream& os, uint64_t format) {
        switch (format) {
            ...
            FORMAT_CASE(R8G8B8A8_UNORM);
            default: os << "unknown"; break;
        }
    }
    
  6. 将新像素格式添加到 HardwareBuffer.java 中的 HardwareBuffer SDK,方法是向 @IntDef 附加一个新条目。例如,RGBA_8888 格式的条目如下所示:

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

    如果组件值未经过无符号归一化,则在变量名称中明确指出该值。例如,仅无符号整数 16 位红色通道格式的变量名称必须为 R_16UI,具有附加无符号整数 16 位绿色通道格式的相同格式必须为 RG_16UI16UI

  7. 通过在 @Format 末尾附加新的公开成员变量,在 HardwareBuffer.java 中将新像素格式添加为 static int

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

    此枚举条目必须具有与 PixelFormat.aidlhardware_buffer.h 中相同的十六进制值。遵循现有规范。

  8. 尝试使用以下代码更改进行构建会产生构建错误:

    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.
    ******************************
    ...
    

    为了清除此错误,请按照错误消息中的说明运行以下命令,以更改 current.txt

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

    运行上述命令会更新正确的文件,以便能够正常构建。

  9. 通过将新像素格式附加到 HardwareBufferTest.java 中的 paramsForTestCreateOptionalFormats() 末尾,向 Java 测试中添加新像素格式,如下所示:

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

向窗口系统集成添加新像素格式

若要将新像素格式用作图形 API 中某个帧缓冲区的格式,请将此格式添加到相关图形 API 的相应窗口系统集成 (WSI) 中。对于使用 Vulkan API 的应用或系统进程,请更新 Vulkan 交换链。对于使用 OpenGL ES API 的应用或系统进程,请更新 EGL API。

针对新像素格式的 Vulkan WSI 更改

请按如下所示更新 Vulkan WSI:
  1. 将新用例添加到 swapchain.cppGetNativePixelFormat(VkFormat format) 函数中:

    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. 如果像素格式需要 Vulkan 扩展才能正常运行,则查询 Vulkan 扩展。对于实例端扩展,请使用 instance_data,如下所示:
    bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
    

    对于设备端扩展,请使用以下内容:

    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 负责处理向 swapchain.cpp 公开实例扩展或设备扩展所需的基础架构。无需用初始更改列表,即可从 Vulkan 加载器正确设置扩展。

  3. 接下来,枚举格式和颜色空间对:
    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});
    }
    

    您必须了解兼容的格式和颜色空间对。

  4. 将新格式添加到位于 external/deqpdEQP-VK
  5. 通过从现有来源推理所需的更改或向 Android 支持团队索要相关信息,更新 vktApiExternalMemoryTests.cppvktExternalMemoryUtil.cpp 中的 Vulkan 一致性测试。

针对新像素格式的 EGL 更改

按如下所示更新 EGL:

  1. getNativePixelFormat() 函数中,修改 if-else 树以返回新像素格式的 AIDL 枚举。使用 RGBA_8888 像素格式示例:
    if (a == 0) {
      ...
    } else {
      if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
          if (colorDepth > 24) {
              ...
          } else {
              *format = PixelFormat::RGBA_8888;
          }
      } else {
        ...
      }
    }
    
  2. 若要将新格式添加到 dEQP,请向 androidFormats 枚举添加新条目,如下所示:
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

提交您的更新

请按照贡献者须知来启动您的更改列表,并将列表与相关团队分享。