Добавление новых форматов пикселей в Android

Все новые форматы пикселей, добавленные в Android, должны быть включены в язык описания интерфейсов Android (AIDL) и в аппаратный буфер Android (AHB). К AIDL и AHB предъявляются строгие требования в отношении стабильности и стандартизации, поэтому расширение их функциональности требует тщательного подхода. Все новые форматы пикселей должны быть добавлены в AOSP, а все обновления должны быть подтверждены экспертами по AIDL и AHB. Этот процесс тщательной проверки является важным фактором стандартизации любых новых форматов пикселей на платформе.

На этой странице описаны необходимые изменения кода AOSP и процесс добавления новых форматов пикселей в AOSP.

Прежде чем добавлять новый формат пикселей, скачайте исходный код и загрузите исправления, как описано в разделе Отправка исправлений.

Добавьте новый формат пикселей в AIDL

Чтобы добавить поддержку нового формата пикселей, необходимо внести изменения в оба файла PixelFormat.aidl, расположенные в AIDL. Исходный код AIDL можно найти на странице hardware/interfaces/graphics/common/aidl/.

Чтобы добавить новый формат пикселей в AIDL, выполните следующие действия:

  1. Добавьте новый формат пикселей в конец перечисления PixelFormat в файле PixelFormat.aidl, следуя существующим правилам написания кода и задав для нового элемента шестнадцатеричное значение, на единицу больше, чем у предыдущего элемента. Сопоставьте изменения в коде с предыдущими записями. Ниже приведен пример записи для формата пикселей 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-android-latest-release: 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. Чтобы устранить эту ошибку, выполните следующую команду, как указано в сообщении об ошибке, чтобы изменить PixelFormat.aidl в каталоге aidl_api:

    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.

    Формат пикселей должен быть связан с форматом Vulkan или OpenGL ES. При необходимости укажите связанный формат. Если связанного формата нет, укажите N/A.

  2. Добавьте формат пикселей в необязательное тестирование в CTS, если у него есть связанный формат OpenGL ES. Для этого добавьте новый формат GL в AHardwareBufferGLTest.cpp в AHBFormatAsString(int32_t format) с 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
    // ----------------------------------------------------------------------------
    

    Добавьте новый элемент static_assert для нового формата пикселей, используя перечисление PixelFormat::, а не константу HAL_PIXEL_FORMAT. Используя тот же пример для формата пикселей RGBA_8888 из раздела Как добавить новый формат пикселей в AIDL, добавьте запись о новом формате пикселей следующим образом:

    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. Добавьте новый формат пикселей в конец PrintAhbFormat() в AHardwareBufferTest.cpp. Соблюдайте существующие правила написания кода, как показано ниже:

    void PrintAhbFormat(std::ostream& os, uint64_t format) {
        switch (format) {
            ...
            FORMAT_CASE(R8G8B8A8_UNORM);
            default: os << "unknown"; break;
        }
    }
    
  6. Добавьте новый формат пикселей в SDK HardwareBuffer в файле HardwareBuffer.java: добавив новую запись в @IntDef. Например, запись для формата RGBA_8888 выглядит следующим образом:

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

    Если значения компонентов не являются нормализованными без знака, явно укажите значение в названии переменной. Например, название переменной для 16-битного красного канала без знака должно быть R_16UI, а для того же формата с дополнительным 16-битным зеленым каналом без знака – RG_16UI16UI.

  7. Добавьте новый формат пикселя как static int в HardwareBuffer.java, добавив новую общедоступную переменную в конец @Format:

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

    Шестнадцатеричное значение этого элемента перечисления должно совпадать со значениями из PixelFormat.aidl и hardware_buffer.h. Следуйте существующим соглашениям.

  8. При попытке сборки с этими изменениями кода возникает ошибка:

    android_developer:~/android/aosp-android-latest-release: 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, добавив его в конец paramsForTestCreateOptionalFormats() в HardwareBufferTest.java, как показано ниже:

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

Как добавить новый формат пикселей в интеграцию с системным окном

Чтобы использовать новый формат пикселей в качестве формата для буфера кадра в графическом API, добавьте его в подходящую интеграцию с оконной системой (WSI) для нужного графического API. Для приложения или системного процесса, использующего Vulkan API, обновите Vulkan Swapchain. Для приложения или системного процесса, использующего OpenGL ES API, обновите EGL API.

Изменения в Vulkan WSI для новых форматов пикселей

Обновите WSI Vulkan следующим образом:

  1. Добавьте в функцию GetNativePixelFormat(VkFormat format) новый вариант в файле swapchain.cpp:

    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. Добавьте новый формат в файл dEQP-VK, который находится в каталоге external/deqp.
  5. Обновите тесты на соответствие Vulkan в vktApiExternalMemoryTests.cpp и vktExternalMemoryUtil.cpp. Для этого определите необходимые изменения на основе существующего источника или обратитесь в службу поддержки Android.

Изменения 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,
      ...
    };
    

Как отправить запрос на обновление

Следуйте инструкциям в разделе Для авторов, чтобы создать списки изменений и поделиться ими с нужной командой.