Implémenter le HAL Hardware Composer

Le HAL Hardware Composer (HWC) compose les calques reçus de SurfaceFlinger, ce qui réduit la quantité de composition OpenGL ES (GLES) et les performances du GPU.

Le HWC fait abstraction des objets, tels que les calques et les blitters 2D, pour composer des surfaces et communique avec le matériel de composition de fenêtres spécialisé pour composer les fenêtres. Utilisez le HWC pour composer des fenêtres au lieu de faire composer SurfaceFlinger avec le GPU. La plupart des GPU ne sont pas optimisés pour la composition. Lorsque le GPU compose des calques à partir de SurfaceFlinger, les applications ne peuvent pas l'utiliser pour leur propre rendu.

Les implémentations HWC doivent prendre en charge les éléments suivants :

  • Au moins quatre calques :
    • Barre d'état
    • Barre système
    • Application
    • Fond d'écran/arrière-plan
  • Calques plus grands que l'écran (par exemple, un fond d'écran)
  • Mélange alpha simultané par pixel et par plan
  • Chemin matériel pour la lecture de vidéos protégées
  • Ordre d'empaquetage RGBA, formats YUV et propriétés de mosaïque, de permutation et de foulée

Pour implémenter le HWC :

  1. Implémentez un HWC non opérationnel et envoyez tout le travail de composition à GLES.
  2. Implémentez un algorithme pour déléguer la composition au HWC de manière incrémentielle. Par exemple, déléguez uniquement les trois ou quatre premières surfaces au matériel de superposition du HWC.
  3. Optimisez le HWC. Cela peut inclure :
    • Sélectionner les surfaces qui maximisent la décharge du GPU et les envoyer au HWC.
    • Détecter si l'écran est en cours de mise à jour. Si ce n'est pas le cas, déléguez la composition à GLES au lieu du HWC pour économiser de l'énergie. Lorsque l'écran se met à jour à nouveau, continuez à décharger la composition sur le HWC.
    • Préparez-vous à des cas d'utilisation courants, tels que :
      • L'écran d'accueil, qui inclut la barre d'état, la barre système, la fenêtre de l'application et les fonds d'écran animés
      • Jeux en plein écran en mode Portrait et Paysage
      • Vidéo en plein écran avec sous-titres et commandes de lecture
      • Lecture de vidéos protégées
      • Écran partagé multifenêtre

Primitives HWC

Le HWC fournit deux primitives, layers et displays, pour représenter le travail de composition et son interaction avec le matériel d'affichage. Le HWC contrôle également la synchronisation verticale et fournit un rappel à SurfaceFlinger pour l'avertir lorsqu'un événement VSync se produit.

Interface HIDL

Android 8.0 et versions ultérieures utilisent une interface HIDL appelée Composer HAL pour l'IPC binderisé entre HWC et SurfaceFlinger. Le HAL Composer remplace l'ancienne interface hwcomposer2.h. Si les fournisseurs fournissent une implémentation Composer HAL du HWC, Composer HAL accepte directement les appels HIDL de SurfaceFlinger. Si les fournisseurs proposent une ancienne implémentation du HWC, Composer HAL charge les pointeurs de fonction à partir de hwcomposer2.h, en transférant les appels HIDL vers les appels de pointeur de fonction.

Le HWC fournit des fonctions permettant de déterminer les propriétés d'un écran donné, de basculer entre différentes configurations d'écran (résolution 4K ou 1080p, par exemple) et modes de couleur (couleur native ou sRGB véritable, par exemple), et d'allumer, d'éteindre ou de mettre l'écran en mode basse consommation, le cas échéant.

Pointeurs de fonction

Si les fournisseurs implémentent Composer HAL directement, SurfaceFlinger appelle ses fonctions via HIDL IPC. Par exemple, pour créer un calque, SurfaceFlinger appelle createLayer() sur le HAL du compositeur.

Si les fournisseurs implémentent l'interface hwcomposer2.h, Composer HAL appelle les pointeurs de fonction hwcomposer2.h. Dans les commentaires hwcomposer2.h, les fonctions d'interface HWC sont désignées par des noms en lowerCamelCase qui n'existent pas dans l'interface en tant que champs nommés. Presque toutes les fonctions sont chargées en demandant un pointeur de fonction à l'aide de getFunction fourni par hwc2_device_t. Par exemple, la fonction createLayer est un pointeur de fonction de type HWC2_PFN_CREATE_LAYER, qui est renvoyé lorsque la valeur énumérée HWC2_FUNCTION_CREATE_LAYER est transmise à getFunction.

Pour obtenir une documentation détaillée sur les fonctions Composer HAL et les fonctions de transfert de fonctions HWC, consultez composer. Pour obtenir une documentation détaillée sur les pointeurs de fonction HWC, consultez hwcomposer2.h.

Poignées de calque et d'affichage

Les calques et les affichages sont manipulés par des poignées générées par le HWC. Les handles sont opaques pour SurfaceFlinger.

Lorsque SurfaceFlinger crée un calque, il appelle createLayer, qui renvoie le type Layer pour les implémentations directes ou hwc2_layer_t pour les implémentations directes. Lorsque SurfaceFlinger modifie une propriété de cette couche, il transmet la valeur hwc2_layer_t à la fonction de modification appropriée, ainsi que toute autre information nécessaire à la modification. Le type hwc2_layer_t est suffisamment grand pour contenir un pointeur ou un index.

Les écrans physiques sont créés par branchement à chaud. Lorsqu'un écran physique est branché à chaud, le HWC crée un handle et le transmet à SurfaceFlinger via le rappel de branchement à chaud. Les écrans virtuels sont créés par SurfaceFlinger qui appelle createVirtualDisplay() pour demander un écran. Si le HWC est compatible avec la composition d'affichage virtuel, il renvoie un handle. SurfaceFlinger délègue ensuite la composition des écrans au HWC. Si le HWC n'est pas compatible avec la composition d'affichage virtuel, SurfaceFlinger crée le handle et compose l'affichage.

Opérations de composition de l'affichage

Une fois par VSync, SurfaceFlinger se réveille s'il a de nouveaux contenus à composer. Ces nouveaux contenus peuvent être de nouveaux tampons d'image provenant d'applications ou une modification des propriétés d'une ou plusieurs couches. Lorsque SurfaceFlinger le réactive :

  1. Gère les transactions, le cas échéant.
  2. Ajoute de nouveaux tampons graphiques, s'ils sont présents.
  3. Effectue une nouvelle composition si l'étape 1 ou 2 a entraîné une modification du contenu de l'écran.

Pour effectuer une nouvelle composition, SurfaceFlinger crée et détruit des calques ou modifie les états des calques, selon le cas. Il met également à jour les calques avec leur contenu actuel, à l'aide d'appels tels que setLayerBuffer ou setLayerColor. Une fois toutes les couches mises à jour, SurfaceFlinger appelle validateDisplay, qui indique au HWC d'examiner l'état des couches et de déterminer comment la composition va se dérouler. Par défaut, SurfaceFlinger tente de configurer chaque calque de sorte qu'il soit composé par le HWC. Toutefois, dans certains cas, SurfaceFlinger compose les calques via le GPU de secours.

Après l'appel à validateDisplay, SurfaceFlinger appelle getChangedCompositionTypes pour voir si le HWC souhaite modifier l'un des types de composition de calques avant d'effectuer la composition. Pour accepter les modifications, SurfaceFlinger appelle acceptDisplayChanges.

Si des calques sont marqués pour la composition SurfaceFlinger, SurfaceFlinger les compose dans le tampon cible. SurfaceFlinger appelle ensuite setClientTarget pour donner le tampon à l'écran afin qu'il puisse être affiché ou composé avec des calques qui n'ont pas été marqués pour la composition SurfaceFlinger. Si aucune couche n'est marquée pour la composition SurfaceFlinger, SurfaceFlinger ignore l'étape de composition.

Enfin, SurfaceFlinger appelle presentDisplay pour indiquer au HWC de terminer le processus de composition et d'afficher le résultat final.

Plusieurs écrans

Android 10 est compatible avec plusieurs écrans physiques. Lorsque vous concevez une implémentation HWC destinée à être utilisée sur Android 7.0 ou version ultérieure, certaines restrictions ne sont pas présentes dans la définition HWC :

  • Il est supposé qu'il existe exactement un écran interne. L'écran interne est celui que le premier branchement à chaud signale au démarrage. Une fois l'écran interne branché à chaud, il ne peut plus être déconnecté.
  • En plus de l'écran interne, n'importe quel nombre d'écrans externes peuvent être branchés à chaud pendant le fonctionnement normal de l'appareil. Le framework suppose que tous les branchements à chaud après le premier écran interne sont des écrans externes. Par conséquent, si d'autres écrans internes sont ajoutés, ils sont classés de manière incorrecte comme Display.TYPE_HDMI au lieu de Display.TYPE_BUILT_IN.

Bien que les opérations SurfaceFlinger décrites ci-dessus soient effectuées par écran, elles sont effectuées de manière séquentielle pour tous les écrans actifs, même si le contenu d'un seul écran est mis à jour.

Par exemple, si l'écran externe est mis à jour, la séquence est la suivante :

// In Android 9 and lower:

// Update state for internal display
// Update state for external display
validateDisplay(<internal display>)
validateDisplay(<external display>)
presentDisplay(<internal display>)
presentDisplay(<external display>)

// In Android 10 and higher:

// Update state for internal display
// Update state for external display
validateInternal(<internal display>)
presentInternal(<internal display>)
validateExternal(<external display>)
presentExternal(<external display>)

Composition de l'écran virtuel

La composition de l'affichage virtuel est semblable à celle de l'affichage externe. La différence entre la composition d'un écran virtuel et celle d'un écran physique réside dans le fait que les écrans virtuels envoient la sortie à un tampon Gralloc au lieu de l'envoyer à l'écran. Hardware Composer (HWC) écrit la sortie dans un tampon, fournit la clôture d'achèvement et envoie le tampon à un consommateur (tel que l'encodeur vidéo, le GPU, le CPU, etc.). Les affichages virtuels peuvent utiliser des blitters 2D ou des calques si le pipeline d'affichage écrit dans la mémoire.

Modes

Chaque frame est dans l'un des trois modes suivants après que SurfaceFlinger a appelé la méthode validateDisplay() HWC :

  • GLES : le GPU compose toutes les couches en écrivant directement dans le tampon de sortie. Le HWC n'est pas impliqué dans la composition.
  • MIXED : le GPU compose certaines couches dans le framebuffer, et HWC compose le framebuffer et les couches restantes, en écrivant directement dans le tampon de sortie.
  • HWC : HWC compose toutes les couches et écrit directement dans le tampon de sortie.

Format de sortie

Les formats de sortie du tampon d'affichage virtuel dépendent de leur mode :

  • Mode GLES : le pilote EGL définit le format du tampon de sortie dans dequeueBuffer(), généralement RGBA_8888. Le consommateur doit pouvoir accepter le format de sortie défini par le pilote, sinon le tampon ne peut pas être lu.
  • Modes MIXED et HWC : si le consommateur a besoin d'accéder au processeur, il définit le format. Sinon, le format est IMPLEMENTATION_DEFINED et Gralloc définit le meilleur format en fonction des indicateurs d'utilisation. Par exemple, Gralloc définit un format YCbCr si le consommateur est un encodeur vidéo et que HWC peut écrire le format de manière efficace.

Barrières de synchronisation

Les clôtures de synchronisation sont un aspect essentiel du système graphique Android. Les clôtures permettent au processeur de fonctionner indépendamment du processeur graphique simultané, en bloquant uniquement en cas de véritable dépendance.

Par exemple, lorsqu'une application envoie un tampon en cours de production sur le GPU, elle envoie également un objet de barrière de synchronisation. Cette clôture indique quand le GPU a fini d'écrire dans le tampon.

Le HWC exige que le GPU termine d'écrire les tampons avant qu'ils ne soient affichés. Les barrières de synchronisation sont transmises via le pipeline graphique avec des tampons et signalent quand les tampons sont écrits. Avant qu'un tampon ne soit affiché, le HWC vérifie si la barrière de synchronisation a signalé un événement. Si c'est le cas, il affiche le tampon.

Pour en savoir plus sur les barrières de synchronisation, consultez Intégration du compositeur matériel.