為 Android 新增新的像素格式

所有新增至 Android 的新像素格式都必須包含在Android 介面定義語言 (AIDL)Android 硬體緩衝區 (AHB)中。 AIDL 和 AHB 具有嚴格的穩定性和標準化要求,因此在擴展功能時需要謹慎的流程。所有新的像素格式都必須登陸 AOSP,並且所有更新都必須由 AIDL 和 AHB 專家單獨確認。這種仔細確認的過程是標準化平台上任何新像素格式的重要因素。

本頁概述了必要的 AOSP 程式碼變更以及在 AOSP 上新增像素格式所需的流程。

在新增新的像素格式之前,請下載原始程式碼並上傳補丁,如提交補丁中所述。

在 AIDL 中新增新的像素格式

新增對新像素格式的支援需要更改位於 AIDL 中的兩個PixelFormat.aidl檔案。請參閱hardware/interfaces/graphics/common/aidl/以了解 AIDL 原始碼。

若要將新的像素形式新增至 AIDL,請執行下列步驟:

  1. 透過遵循現有程式碼約定並將條目的十六進位值設定為比前一個條目多 1,將新像素格式作為新條目附加到PixelFormat.aidlPixelFormat枚舉的末尾。將您的程式碼變更與先前的條目相符。有關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. 如果像素格式具有關聯的 OpenGL ES 格式,則將像素格式新增至 CTS 下的選用測試。為此,請將新的 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.cppPrintAhbFormat()的末尾,將新像素格式新增至適當的測試。遵循現有的代碼約定,如下所示:

    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. 將新的像素格式加入 Java 測試中,方法是將新的像素格式附加到HardwareBufferTest.javaparamsForTestCreateOptionalFormats()的末尾,如下所示:

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

為 Window 系統整合新增新的像素格式

若要使用新的像素格式作為圖形 API 中幀緩衝區的格式,請將其新增至相關圖形 API 的相應視窗系統整合 (WSI) 中。對於使用 Vulkan API 的應用程式或系統進程,請更新 Vulkan 交換鏈。對於使用 OpenGL ES API 的應用程式或系統進程,請更新EGL API。

Vulkan WSI 針對新像素格式進行了更改

如下更新 Vulkan WSI:
  1. swapchain.cpp中的GetNativePixelFormat(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,
      ...
    };
    

提交您的更新

關注供貢獻者啟動您的更改清單並與適當的團隊分享。