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. 기존 코드 규칙을 따르고 항목의 16진수 값을 이전 항목보다 1 크게 설정하여 PixelFormat.aidlPixelFormat enum 끝에 새 픽셀 형식을 새로운 항목으로 추가합니다. 코드 변경사항을 이전 항목과 일치시킵니다. 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 enum 끝에 새 픽셀 형식을 새로운 항목으로 추가합니다. 기존 코드 규칙을 따릅니다.

    RGBA_8888 픽셀 형식 예를 사용하여 새 픽셀 형식 항목을 다음과 같이 추가하세요.

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

    새 픽셀 형식은 AHB에서 이름이 지정되며 AHARDWAREBUFFER_FORMAT_으로 시작하고 그 뒤에 채널 약어와 비트 심도가 오고 인코딩으로 끝나야 합니다. 이 enum 항목에는 PixelFormat.aidl의 값과 동일한 16진수 값이 있어야 합니다.

    픽셀 형식은 연결된 Vulkan이나 OpenGL ES 형식 중 하나 또는 둘 다를 보유할 것으로 예상됩니다. 적절한 경우 연결된 형식을 지정하세요. 연결된 형식이 없으면 N/A를 지정합니다.

  2. 연결된 OpenGL ES 형식이 있다면 CTS 아래 선택적 테스트에 픽셀 형식을 추가합니다. 이렇게 하려면 다음과 같이 새 형식의 FORMAT_CASE(...)GL_FORMAT_CASE(...)를 사용하여 새 GL 형식을 AHBFormatAsString(int32_t format)AHardwareBufferGLTest.cpp에 추가합니다.

    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
    // ----------------------------------------------------------------------------
    

    HAL_PIXEL_FORMAT 상수가 아닌 PixelFormat:: enum을 사용하여 새 픽셀 형식의 새 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. 새 항목을 @IntDef에 추가하여 새 픽셀 형식을 HardwareBuffer.javaHardwareBuffer SDK에 추가합니다. 예를 들어 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.javastatic int로 추가합니다.

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

    이 enum 항목에는 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. 다음과 같이 새 픽셀 형식을 HardwareBufferTest.javaparamsForTestCreateOptionalFormats() 끝에 추가하여 Java 테스트에 새 픽셀 형식을 추가합니다.

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

Window System Integration에 새 픽셀 형식 추가

그래픽 API에서 프레임 버퍼의 형식으로 새 픽셀 형식을 사용하려면 관련 그래픽 API의 적절한 Window System Integration(WSI)에 추가하세요. Vulkan API를 사용하는 앱 또는 시스템 프로세스의 경우 Vulkan Swapchain을 업데이트합니다. 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() 함수에서 새 픽셀 형식의 AIDL enum을 반환하도록 if-else 트리를 수정합니다. 다음과 같이 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 enum에 새 항목을 추가합니다.
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

업데이트 제출

기여자를 따라 변경 목록을 작성하고 담당팀과 공유하세요.