Ajouter de nouveaux formats de pixel à Android

Tous les nouveaux formats de pixels ajoutés à Android doivent être inclus dans le langage de définition d'interface Android (AIDL) et dans le tampon matériel Android (AHB). L'AIDL et l'AHB ont des exigences strictes en matière de stabilité et de normalisation qui nécessitent un processus minutieux lors de l'extension des fonctionnalités. Tous les nouveaux formats de pixels doivent être intégrés à AOSP, et toutes les mises à jour doivent être confirmées individuellement par des experts AIDL et AHB. Ce processus de confirmation minutieux est un facteur important pour standardiser les nouveaux formats de pixels sur la plate-forme.

Cette page décrit les modifications de code AOSP nécessaires et la procédure à suivre pour ajouter de nouveaux formats de pixels sur AOSP.

Avant d'ajouter un format de pixel, téléchargez la source et importez les correctifs comme indiqué dans Envoyer des correctifs.

Ajouter un format de pixel à AIDL

Pour ajouter la prise en charge d'un nouveau format de pixel, vous devez modifier les deux fichiers PixelFormat.aidl situés dans AIDL. Consultez hardware/interfaces/graphics/common/aidl/ pour obtenir le code source AIDL.

Pour ajouter un nouveau format de pixel à AIDL, procédez comme suit :

  1. Ajoutez le nouveau format de pixel en tant que nouvelle entrée à la fin de l'énumération PixelFormat dans PixelFormat.aidl en suivant la convention de code existante et en définissant la valeur hexadécimale de votre entrée sur une unité de plus que l'entrée précédente. Faites correspondre vos modifications de code aux entrées précédentes. Consultez l'exemple suivant pour l'entrée du format de pixel 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,
    

    Le message d'erreur suivant s'affiche lorsque vous compilez le code après avoir apporté des modifications à 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. Pour corriger cette erreur, exécutez la commande suivante, comme indiqué dans le message d'erreur, afin de modifier PixelFormat.aidl dans le répertoire aidl_api :

    m android.hardware.graphics.common-update-api
    

    L'exécution de la commande ci-dessus met à jour le fichier approprié pour permettre une compilation normale.

Ajouter un format de pixel à AHB

L'ajout de la prise en charge d'un nouveau format de pixel nécessite des modifications dans hardware_buffer.h et AHardwareBuffer.cpp. Consultez frameworks/native/libs/nativewindow pour obtenir le code source AHB.

Pour ajouter un format de pixel à AHB, procédez comme suit :

  1. Dans hardware_buffer.h, ajoutez le nouveau format de pixel en tant que nouvelle entrée à la fin de l'énumération AHardwareBuffer_Format. Respectez les conventions de code existantes.

    En utilisant l'exemple de format de pixel RGBA_8888, ajoutez la nouvelle entrée de format de pixel comme suit :

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

    Notez que le nouveau format de pixel reçoit un nom dans AHB, qui doit commencer par AHARDWAREBUFFER_FORMAT_, suivi des abréviations des canaux et des profondeurs de bits, et se terminer par l'encodage. Cette entrée d'énumération doit avoir la même valeur hexadécimale que celle de PixelFormat.aidl.

    Le format de pixel doit être associé à un format Vulkan ou OpenGL ES. Spécifiez le format associé, le cas échéant. Si aucun format associé n'existe, spécifiez N/A.

  2. Ajoutez le format de pixel aux tests facultatifs sous CTS, s'il est associé à un format OpenGL ES. Pour ce faire, ajoutez le nouveau format GL à AHardwareBufferGLTest.cpp dans AHBFormatAsString(int32_t format) avec FORMAT_CASE(...) et GL_FORMAT_CASE(...) pour le nouveau format, comme indiqué ci-dessous :

    const char* AHBFormatAsString(int32_t format) {
      switch (format) {
          ...
          FORMAT_CASE(R8G8B8A8_UNORM);
          ...
          GL_FORMAT_CASE(GL_RGB8);
      }
      return "";
    }
    
  3. Ensuite, ajoutez un nouveau test à AHardwareBufferGLTest.cpp, comme suit :

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

    Spécifiez au moins un ensemble de valeurs AHardwareBuffer_Desc. Ajoutez d'autres valeurs si nécessaire.

  4. Dans AHardwareBuffer.cpp, recherchez la fin des assertions statiques trouvées dans :

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

    Ajoutez un static_assert pour le nouveau format de pixel, en utilisant l'énumération PixelFormat:: et non la constante HAL_PIXEL_FORMAT. En utilisant le même exemple pour le format de pixel RGBA_8888 à partir de Ajouter un nouveau format de pixel à AIDL, ajoutez la nouvelle entrée de format de pixel comme suit :

    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. Ajoutez le nouveau format de pixel aux tests appropriés en l'ajoutant à la fin de PrintAhbFormat() dans AHardwareBufferTest.cpp. Suivez la convention de code existante, comme indiqué ci-dessous :

    void PrintAhbFormat(std::ostream& os, uint64_t format) {
        switch (format) {
            ...
            FORMAT_CASE(R8G8B8A8_UNORM);
            default: os << "unknown"; break;
        }
    }
    
  6. Ajoutez le nouveau format de pixel au SDK HardwareBuffer dans HardwareBuffer.java en ajoutant une entrée à @IntDef. Par exemple, l'entrée pour le format RGBA_8888 est la suivante :

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

    Si les valeurs des composants ne sont pas normalisées non signées, indiquez explicitement la valeur dans le nom de la variable. Par exemple, le nom de variable pour un format de canal rouge de 16 bits non signé uniquement doit être R_16UI, et le même format avec un format de canal vert de 16 bits non signé supplémentaire doit être RG_16UI16UI.

  7. Ajoutez le nouveau format de pixel en tant que static int dans HardwareBuffer.java, en ajoutant une nouvelle variable de membre public à la fin de @Format :

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

    Cette entrée d'énumération doit avoir la même valeur hexadécimale que celle de PixelFormat.aidl et hardware_buffer.h. Respectez les conventions existantes.

  8. Toute tentative de compilation avec ces modifications de code génère une erreur de compilation :

    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.
    ******************************
    ...
    

    Pour corriger cette erreur, exécutez la commande suivante, comme indiqué dans le message d'erreur, afin de modifier current.txt :

    m api-stubs-docs-non-updatable-update-current-api
    

    L'exécution de la commande ci-dessus met à jour le fichier approprié pour permettre une compilation normale.

  9. Ajoutez le nouveau format de pixel aux tests Java en l'ajoutant à la fin de paramsForTestCreateOptionalFormats() dans HardwareBufferTest.java, comme suit :

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

Ajouter un nouveau format de pixel à l'intégration du système de fenêtres

Pour utiliser le nouveau format de pixel comme format pour un tampon de frame dans une API graphique, ajoutez-le à l'intégration du système de fenêtres (WSI) appropriée pour l'API graphique concernée. Pour une application ou un processus système utilisant l'API Vulkan, mettez à jour la chaîne d'échange Vulkan. Pour un processus d'application ou système utilisant l'API OpenGL ES, mettez à jour l'API EGL.

Modifications apportées à Vulkan WSI pour les nouveaux formats Pixel

Mettez à jour le WSI Vulkan comme suit :
  1. Ajoutez un cas à la fonction GetNativePixelFormat(VkFormat format) dans 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. Interrogez l'extension Vulkan si le format de pixel nécessite une extension Vulkan pour fonctionner. Pour les extensions côté instance, utilisez instance_data, comme suit :
    bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
    

    Pour les extensions côté appareil, utilisez les éléments suivants :

    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 gère l'infrastructure requise pour exposer une extension d'instance ou d'appareil à swapchain.cpp. La liste de modifications initiale n'est pas nécessaire pour que les extensions soient correctement configurées à partir du chargeur Vulkan.

  3. Ensuite, énumérez les paires de format et d'espace colorimétrique :
    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});
    }
    

    Vous devez connaître les paires de formats et d'espaces colorimétriques compatibles.

  4. Ajoutez le nouveau format à dEQP-VK situé à l'adresse external/deqp.
  5. Mettez à jour les tests de conformité Vulkan dans vktApiExternalMemoryTests.cpp et vktExternalMemoryUtil.cpp en déduisant les modifications requises à partir de la source existante ou en contactant l'assistance Android pour obtenir des informations.

Modifications apportées à EGL pour les nouveaux formats de pixels

Mettez à jour l'EGL comme suit :

  1. Dans la fonction getNativePixelFormat(), modifiez l'arborescence if-else pour renvoyer l'énumération AIDL pour le nouveau format de pixel. En utilisant l'exemple du format de pixel RGBA_8888 :
    if (a == 0) {
      ...
    } else {
      if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
          if (colorDepth > 24) {
              ...
          } else {
              *format = PixelFormat::RGBA_8888;
          }
      } else {
        ...
      }
    }
    
  2. Pour ajouter le nouveau format à dEQP, ajoutez une entrée à l'énumération androidFormats, comme suit :
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

Envoyer votre mise à jour

Suivez les instructions de la section Pour les contributeurs afin de générer vos listes de modifications et de les partager avec l'équipe appropriée.