新しいピクセル形式を 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. 新しいピクセル形式を新規エントリとして PixelFormat.aidlPixelFormat 列挙型の最後に追加します。既存のコード規則に従い、エントリの 16 進数値をその前のエントリより 1 つ増やして設定します。コード変更をその前のエントリと一致させます。以下の 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 にあるものと同じ 16 進数値である必要があります。

    ピクセル形式は関連する Vulkan または OpenGL 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 値のセットを 1 つ以上指定します。必要に応じてさらに値を追加します。

  4. AHardwareBuffer.cpp 内にある静的アサートの最後の部分を見つけます。

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

    新しいピクセル形式の新しい static_assert を追加します。PixelFormat:: 列挙型を使用し、HAL_PIXEL_FORMAT 定数は使用しません。新しいピクセル形式を 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.javaHardwareBuffer 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. 新しいピクセル形式を HardwareBuffer.javastatic int として追加するために、@Format の最後に新しいパブリック メンバー変数を追加します。

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

    この列挙型エントリは PixelFormat.aidlhardware_buffer.h にあるものと同じ 16 進数値である必要があります。既存の規則に従います。

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

新しいピクセル形式をウィンドウ システム統合に追加する

新しいピクセル形式を Graphics API でフレームバッファの形式として使用するには、関連する Graphics 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/deqp にある dEQP-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,
      ...
    };
    

アップデートを送信する

コントリビューターの手順に沿って、変更リストをスピンアップし、適切なチームと共有します。