Ajouter de nouveaux formats de pixels à Android

Tous les nouveaux formats de pixels ajoutés à Android doivent être inclus dans le langage AIDL (Android Interface Definition Language) 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 atterrir dans AOSP et toutes les mises à jour doivent être confirmées individuellement par les experts AIDL et AHB. Ce processus de confirmation minutieuse est un facteur important dans la normalisation de tout nouveau format de pixel sur la plateforme.

Cette page décrit les modifications nécessaires du code AOSP et le processus requis pour ajouter de nouveaux formats de pixels sur AOSP.

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

Ajouter un nouveau format de pixel à AIDL

L'ajout de la prise en charge d'un nouveau format de pixel nécessite des modifications des deux fichiers PixelFormat.aidl situés dans AIDL. Voir hardware/interfaces/graphics/common/aidl/ pour le code source AIDL.

Pour ajouter un nouveau pixel formel à 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 de plus que l'entrée précédente. Faites correspondre vos modifications de code aux entrées précédentes. Voir l'exemple suivant pour l'entrée de 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 créez le code après avoir apporté des modifications à 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. Pour effacer cette erreur, exécutez la commande suivante, comme spécifié dans le message d'erreur, pour 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 correct pour pouvoir construire normalement.

Ajouter un nouveau format de pixel à AHB

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

Pour ajouter un nouveau pixel formel à 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 . Suivez les conventions de code existantes.

    À l'aide de 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 en AHB, qui doit commencer par AHARDWAREBUFFER_FORMAT_ , suivi des abréviations de canal 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 devrait avoir l'un ou les deux formats associés Vulkan ou OpenGL ES . Précisez le format associé le cas échéant. Si aucun format associé n'existe, précisez 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, affiché comme suit :

    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 , affiché 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 plus de 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 nouveau static_assert pour le nouveau format de pixel, en utilisant l'énumération PixelFormat:: et non avec la constante HAL_PIXEL_FORMAT . En utilisant le même exemple pour le format de pixel RGBA_8888 de Add a new pixel format to 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 ajoutant le nouveau format de pixel à 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 nouvelle entrée à @IntDef . Par exemple, l'entrée pour le format RGBA_8888 s'affiche comme suit :

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

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

  7. Ajoutez le nouveau format de pixel en tant static int dans HardwareBuffer.java , en ajoutant une nouvelle variable membre publique à 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 . Suivez les conventions existantes.

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

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

    Pour effacer cette erreur, exécutez la commande suivante, comme spécifié dans le message d'erreur, pour modifier current.txt :

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

    L'exécution de la commande ci-dessus met à jour le fichier correct pour pouvoir construire normalement.

  9. Ajoutez le nouveau format de pixel aux tests Java, en ajoutant le nouveau format de pixel à 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 Windows

Pour utiliser le nouveau format de pixel comme format d'un framebuffer dans une API graphique, ajoutez-le au Window System Integration (WSI) approprié pour l'API graphique appropriée. Pour une application ou un processus système utilisant l'API Vulkan, mettez à jour la Vulkan Swapchain. Pour une application ou un processus système utilisant l’API OpenGL ES, mettez à jour l’API EGL .

Modifications de Vulkan WSI pour les nouveaux formats de pixels

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

    Pour les extensions côté appareil, utilisez ce qui suit :

    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 instance ou une extension de périphérique à 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é dans 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 votre support Android pour obtenir des informations.

Modifications 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 nouvelle entrée à l'énumération androidFormats , affichée comme suit :
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

Soumettez votre mise à jour

Suivez Pour les contributeurs pour faire tourner vos listes de modifications et les partager avec l'équipe appropriée.