Implémenter le HAL Hardware Composer

La HAL Hardware Composer (HWC) combine les calques reçus de SurfaceFlinger, ce qui réduit la quantité de composition OpenGL ES (GLES) et le travail effectué par le GPU.

La HWC abstrait les objets, tels que les superpositions et les blitters 2D, pour combiner les surfaces et communiquer avec le matériel de composition de fenêtres spécialisé afin de combiner les fenêtres. Utilisez la HWC pour combiner les fenêtres au lieu de laisser SurfaceFlinger effectuer la combinaison avec le GPU. La plupart des GPU ne sont pas optimisés pour la composition. Lorsque le GPU combine des calques à partir de SurfaceFlinger, les applications ne peuvent pas utiliser le GPU pour leur propre rendu.

Les implémentations HWC doivent être compatibles avec les éléments suivants :

  • Au moins quatre superpositions :
    • Barre d'état
    • Barre système
    • Application
    • Fond d'écran
  • Calques plus grands que l'écran (par exemple, un fond d'écran)
  • Mélange alpha prémultiplié par pixel et mélange alpha par plan simultanés
  • 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 swizzling et de pas propriétés

Pour implémenter la HWC :

  1. Implémentez une HWC non opérationnelle et envoyez tout le travail de composition à GLES.
  2. Implémentez un algorithme pour déléguer la composition à la HWC de manière incrémentielle. Par exemple, déléguez uniquement les trois ou quatre premières surfaces au matériel de superposition de la HWC.
  3. Optimisez la HWC. Cela peut inclure les éléments suivants :
    • Sélectionner les surfaces qui maximisent la charge retirée du GPU et envoyer à la 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 de la HWC pour économiser de l'énergie. Lorsque l'écran est à nouveau mis à jour, continuez à décharger la composition sur la HWC.
    • Préparer les 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
      • Les jeux en plein écran en mode Portrait et Paysage
      • Les vidéos en plein écran avec sous-titres et commandes de lecture
      • La lecture de vidéos protégées
      • Le mode multifenêtre en écran partagé

Primitives HWC

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

Interface HIDL

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

La HWC fournit des fonctions permettant de déterminer les propriétés d'un écran donné, de basculer entre différentes configurations d'écran (telles que la résolution 4K ou 1080p) et les modes de couleur (tels que la couleur native ou la sRGB réelle), et d'activer, de désactiver ou de mettre l'écran en mode basse consommation s'il est compatible.

Pointeurs de fonction

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

Si les fournisseurs implémentent l'interface hwcomposer2.h, la HAL Composer appelle les pointeurs de fonction hwcomposer2.h. Dans les commentaires hwcomposer2.h, les fonctions d'interface HWC sont appelées par des noms 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 de la HAL Composer et les fonctions de transfert de fonction HWC fonctions, consultez composer. Pour obtenir une documentation détaillée sur les pointeurs de fonction HWC, consultez les hwcomposer2.h.

Identifiants de calque et d'écran

Les calques et les écrans sont manipulés par des identifiants générés par la HWC. Les identifiants 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 de transfert. Lorsque SurfaceFlinger modifie une propriété de ce calque, il transmet la valeur hwc2_layer_t à la fonction de modification appropriée, ainsi que toute autre information nécessaire pour effectuer la modification. Le type hwc2_layer_t est suffisamment grand pour contenir un pointeur ou un index.

Les écrans physiques sont créés par connexion à chaud. Lorsqu'un écran physique est connecté à chaud, la HWC crée un identifiant et le transmet à SurfaceFlinger via le rappel de connexion à chaud. Les écrans virtuels sont créés par SurfaceFlinger qui appelle createVirtualDisplay() pour demander un écran. Si la HWC est compatible avec la composition d'écran virtuel, elle renvoie un identifiant. Ensuite, SurfaceFlinger délègue la composition des écrans à la HWC. Si la HWC n'est pas compatible avec la composition d'écran virtuel, SurfaceFlinger crée l'identifiant et combine l'écran.

Opérations de composition d'écran

Une fois par synchronisation verticale, SurfaceFlinger se réactive s'il a du nouveau contenu à combiner. Ce nouveau contenu peut être de nouveaux tampons d'image provenant d'applications ou une modification des propriétés d'un ou de plusieurs calques. Lorsque SurfaceFlinger se réactive :

  1. Il gère les transactions, le cas échéant.
  2. Il verrouille les nouveaux tampons graphiques, le cas échéant.
  3. Il 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, le cas échéant. Il met également à jour les calques avec leur contenu actuel, à l'aide d'appels tels que setLayerBuffer ou setLayerColor. Une fois tous les calques mis à jour, SurfaceFlinger appelle validateDisplay, ce qui indique à la HWC d'examiner l'état des calques 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 combiné par la HWC. Toutefois, dans certaines circonstances, SurfaceFlinger combine les calques via le GPU de secours.

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

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

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

Plusieurs écrans

Android 10 est compatible avec plusieurs écrans physiques. Lors de la conception d'une implémentation HWC destinée à être utilisée sur Android 7.0 et versions ultérieures, certaines restrictions ne sont pas présentes dans la définition HWC :

  • On suppose qu'il n'y a qu'un seul écran interne. L'écran interne est celui que le rapport de connexion à chaud initial indique au démarrage. Une fois l'écran interne connecté à chaud, il ne peut plus être déconnecté.
  • En plus de l'écran interne, un nombre quelconque d'écrans externes peut être connecté à chaud pendant le fonctionnement normal de l'appareil. Le framework suppose que toutes les connexions à 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 d'écran virtuel

La composition d'écran virtuel est semblable à la composition d'écran externe. La différence entre la composition d'écran virtuel et la composition d'écran physique est que les écrans virtuels envoient la sortie à un tampon Gralloc au lieu de l'envoyer à l'écran. La HAL Hardware Composer (HWC) écrit la sortie dans un tampon, fournit la barrière de fin et envoie le tampon à un consommateur (tel que l'encodeur vidéo, le GPU, le processeur, etc.). Les écrans virtuels peuvent utiliser des blitters 2D ou des superpositions si le pipeline d'affichage écrit dans la mémoire.

Modes

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

  • GLES : le GPU combine tous les calques en écrivant directement dans le tampon de sortie. La HWC n'est pas impliquée dans la composition.
  • MIXED : le GPU combine certains calques dans le framebuffer, et la HWC combine le framebuffer et les calques restants en écrivant directement dans le tampon de sortie.
  • HWC : la HWC combine tous les calques et écrit directement dans le tampon de sortie.

Format de sortie

Les formats de sortie du tampon d'écran 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 la HWC peut écrire le format de manière efficace.

Barrières de synchronisation

Les barrières de synchronisation sont un aspect essentiel du système graphique Android. Les barrières permettent au processeur de fonctionner indépendamment du travail simultané du GPU, 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 barrière signale quand le GPU a terminé d'écrire dans le tampon.

La 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 les tampons et signalent quand les tampons sont écrits. Avant qu'un tampon ne soit affiché, la HWC vérifie si la barrière de synchronisation a signalé. Si c'est le cas, elle affiche le tampon.

Pour en savoir plus sur les barrières de synchronisation, consultez Intégration de Hardware Composer.