新しいピクセル形式を 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.aidl の PixelFormat 列挙型の最後に追加します。既存のコード規則に従い、エントリの 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.h と AHardwareBuffer.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.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. 新しいピクセル形式を HardwareBuffer.java に static int として追加するために、@Format の最後に新しいパブリック メンバー変数を追加します。

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

    この列挙型エントリは PixelFormat.aidl と hardware_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.java の paramsForTestCreateOptionalFormats() の最後に追加します。

    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.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/deqp にある dEQP-VK に追加します。
  5. 既存のソースから必要な変更を推測するか、Android サポートに問い合わせて情報を入手し、vktApiExternalMemoryTests.cpp と vktExternalMemoryUtil.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,
      ...
    };
    

アップデートを送信する

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