Hardware Composer-HAL implementieren

Der Hardware Composer (HWC) HAL fasst die von SurfaceFlinger empfangenen Ebenen zusammen, wodurch die Menge der Zusammensetzung von OpenGL ES (GLES) und der GPU reduziert wird.

Der HWC abstrahiert Objekte wie Overlays und 2D-Blitter zur Komposition von Oberflächen und kommuniziert mit spezieller Hardware für die Fensterkomposition, um Fenster zu kombinieren. Verwenden Sie HWC, um Fenster zu erstellen, anstatt SurfaceFlinger mit der GPU zusammenzusetzen. Die meisten GPUs sind nicht für die Zusammensetzung optimiert. Wenn die GPU Ebenen aus SurfaceFlinger erstellt, können Anwendungen die GPU nicht für ihr eigenes Rendering verwenden.

HWC-Implementierungen sollten Folgendes unterstützen:

  • Mindestens vier Overlays:
    • Statusleiste
    • Systemleiste
    • App
    • Hintergrund/Hintergrund
  • Ebenen, die größer als das Display sind (z. B. ein Hintergrund)
  • Gleichzeitige, vorabmultiplizierte Alpha-Blending-Funktion pro Pixel und Alpha-Überblendung auf Ebene einzelner Ebenen
  • Hardwarepfad für die Wiedergabe geschützter Videos
  • RGBA-Packungsreihenfolge, YUV-Formate sowie Tiling-, Swizzling- und Schritt-Eigenschaften

So implementieren Sie die HWC:

  1. Eine nicht operative HWC implementieren und alle Kompositionsarbeiten an GLES senden.
  2. Implementieren Sie einen Algorithmus, um die Komposition schrittweise an die HWC zu delegieren. Weisen Sie beispielsweise nur die ersten drei oder vier Oberflächen der Overlay-Hardware des HWC zu.
  3. HWC optimieren Dazu gehören unter anderem:
    • Auswahl von Oberflächen, die die Last von der GPU maximieren und an die HWC senden.
    • Erkennt, ob der Bildschirm aktualisiert wird. Andernfalls delegieren Sie die Komposition an GLES anstelle der HWC, um Strom zu sparen. Wenn sich das Display wieder aktualisiert, lade die Zusammensetzung weiterhin an die HWC aus.
    • Vorbereitung auf gängige Anwendungsfälle wie:
      • Der Startbildschirm, einschließlich Statusleiste, Systemleiste, App-Fenster und Live-Hintergründen
      • Vollbildspiele im Hoch- und Querformat
      • Vollbildvideo mit Untertiteln und Wiedergabesteuerung
      • Wiedergabe geschützter Videos
      • Splitscreen-Mehrfenstermodus

HWC-Primitive

HWC bietet zwei Primitive, Layers und Displays, um die Kompositionsarbeit und ihre Interaktion mit der Anzeigehardware darzustellen. Der HWC bietet auch eine Steuerung für VSYNC und einen Rückruf an SurfaceFlinger, um ihn über ein VSYNC-Ereignis zu benachrichtigen.

HIDL-Schnittstelle

Android 8.0 und höher verwenden eine HIDL-Schnittstelle namens Composer HAL für die bindungsorientierte IPC zwischen HWC und SurfaceFlinger. Der Composer-HAL ersetzt die alte hwcomposer2.h-Schnittstelle. Wenn Anbieter eine Composer-HAL-Implementierung der HWC bereitstellen, akzeptiert Composer HAL HIDL-Aufrufe von SurfaceFlinger direkt. Wenn Anbieter eine Legacy-Implementierung der HWC bereitstellen, lädt Composer HAL Funktionszeiger aus hwcomposer2.h und leitet HIDL-Aufrufe an Funktionszeigeraufrufe weiter.

Die HWC bietet Funktionen zum Ermitteln der Eigenschaften eines bestimmten Displays, zum Wechseln zwischen verschiedenen Displaykonfigurationen (z. B. 4K oder 1080p) und Farbmodi (z. B. native Farbe oder echtes sRGB) sowie zum Ein- und Ausschalten des Displays oder zum Aktivieren eines Energiesparmodus, sofern unterstützt.

Funktionszeiger

Wenn Anbieter Composer HAL direkt implementieren, ruft SurfaceFlinger seine Funktionen über HIDL IPC auf. Wenn beispielsweise eine Ebene erstellt werden soll, ruft SurfaceFlinger createLayer() in der Composer HAL auf.

Wenn Anbieter die hwcomposer2.h-Schnittstelle implementieren, ruft die Composer HAL hwcomposer2.h-Funktionszeigeter auf. In hwcomposer2.h-Kommentaren werden HWC-Schnittstellenfunktionen mit Namen im CamelCase-Format bezeichnet, die in der Schnittstelle nicht als benannte Felder vorhanden sind. Fast jede Funktion wird geladen, indem ein Funktionszeiger mit getFunction angefordert wird, der von hwc2_device_t bereitgestellt wird. Die Funktion createLayer ist beispielsweise ein Funktionszeiger vom Typ HWC2_PFN_CREATE_LAYER, der zurückgegeben wird, wenn der enumerierte Wert HWC2_FUNCTION_CREATE_LAYER an getFunction übergeben wird.

Eine ausführliche Dokumentation zu HAL-Funktionen von Composer und Funktionen zur Weiterleitung von HWC-Funktionen finden Sie unter composer. Eine ausführliche Dokumentation zu HWC-Funktionszeigern finden Sie unter hwcomposer2.h.

Ziehpunkte für Ebenen und Displays

Ebenen und Displays werden über vom HWC generierte Handles manipuliert. Die Handles sind für SurfaceFlinger nicht transparent.

Wenn SurfaceFlinger eine neue Ebene erstellt, wird createLayer aufgerufen, wodurch der Typ Layer für direkte Implementierungen oder hwc2_layer_t für Passthrough-Implementierungen zurückgegeben wird. Wenn SurfaceFlinger eine Eigenschaft dieser Ebene ändert, gibt SurfaceFlinger den Wert hwc2_layer_t zusammen mit allen anderen Informationen, die für die Änderung erforderlich sind, an die entsprechende Änderungsfunktion weiter. Der Typ hwc2_layer_t ist groß genug, um entweder einen Verweis oder einen Index zu enthalten.

Physische Displays werden durch Hotplugging erstellt. Wenn ein physisches Display Hot-Plugged wird, erstellt die HWC einen Handle und übergibt ihn über den Hot-Plug-Callback an SurfaceFlinger. Virtuelle Displays werden erstellt, indem SurfaceFlinger createVirtualDisplay() aufruft, um eine Anzeige anzufordern. Wenn die HWC die Zusammensetzung virtueller Bildschirme unterstützt, wird ein Handle zurückgegeben. Anschließend delegiert SurfaceFlinger die Zusammensetzung der Displays an die HWC. Wenn die HWC die Zusammensetzung von virtuellen Displays nicht unterstützt, erstellt SurfaceFlinger den Handle und setzt das Display zusammen.

Displayzusammensetzungsvorgänge

Einmal pro VSYNC wird SurfaceFlinger gestartet, wenn neue Inhalte zusammengesetzt werden müssen. Diese neuen Inhalte können neue Bildbuffer von Apps oder eine Änderung der Eigenschaften einer oder mehrerer Ebenen sein. Wenn SurfaceFlinger es aufweckt:

  1. Verarbeitet Transaktionen, falls vorhanden.
  2. Verriegelt neue Grafik-Buffer, falls vorhanden.
  3. Führt eine neue Zusammensetzung aus, wenn Schritt 1 oder 2 zu einer Änderung der angezeigten Inhalte geführt hat.

Für eine neue Komposition erstellt und zerstört SurfaceFlinger Ebenen oder ändert die Ebenenstatus. Außerdem werden Ebenen mit ihren aktuellen Inhalten aktualisiert, indem Aufrufe wie setLayerBuffer oder setLayerColor verwendet werden. Nachdem alle Ebenen aktualisiert wurden, ruft SurfaceFlinger validateDisplay auf, wodurch die HWC aufgefordert wird, den Status der Ebenen zu prüfen und zu bestimmen, wie die Zusammensetzung fortgesetzt wird. Standardmäßig versucht SurfaceFlinger, jede Ebene so zu konfigurieren, dass sie von der HWC zusammengesetzt wird. In einigen Fällen erstellt SurfaceFlinger Ebenen über das GPU-Fallback.

Nach dem Aufruf von validateDisplay ruft SurfaceFlinger getChangedCompositionTypes auf, um zu prüfen, ob die HWC vor der Ausführung der Zusammensetzung einen der Ebenenzusammensetzungstypen ändern möchte. Um die Änderungen zu akzeptieren, ruft SurfaceFlinger acceptDisplayChanges auf.

Wenn Ebenen für die SurfaceFlinger-Zusammensetzung markiert sind, werden sie von SurfaceFlinger im Zielpuffer zusammengestellt. SurfaceFlinger ruft dann setClientTarget auf, um den Zwischenspeicher an das Display zu übergeben, damit dieser auf dem Bildschirm angezeigt oder mit Ebenen zusammengefügt werden kann, die nicht für die SurfaceFlinger-Zusammensetzung markiert wurden. Wenn keine Ebenen für die SurfaceFlinger-Komposition gekennzeichnet sind, überspringt SurfaceFlinger den Kompositionschritt.

Schließlich ruft SurfaceFlinger presentDisplay auf, um dem HWC zu signalisieren, dass der Zusammensetzungsprozess abgeschlossen und das Endergebnis angezeigt werden soll.

Mehrere Displays

Android 10 unterstützt mehrere physische Displays. Bei der Entwicklung einer HWC-Implementierung für Android 7.0 und höher gelten einige Einschränkungen, die in der HWC-Definition nicht enthalten sind:

  • Es wird davon ausgegangen, dass es genau ein internes Display gibt. Die interne Anzeige ist die Anzeige, die während des Startvorgangs vom ersten Hotplug gemeldet wird. Nachdem das interne Display per Hotplug verbunden wurde, kann es nicht mehr getrennt werden.
  • Zusätzlich zum internen Display kann während des normalen Betriebs des Geräts eine beliebige Anzahl externer Displays per Hot-Plug angeschlossen werden. Das Framework geht davon aus, dass alle Hotplugs nach dem ersten internen Display externe Displays sind. Wenn also weitere interne Displays hinzugefügt werden, werden sie fälschlicherweise als Display.TYPE_HDMI statt als Display.TYPE_BUILT_IN kategorisiert.

Die oben beschriebenen SurfaceFlinger-Vorgänge werden zwar pro Display ausgeführt, aber nacheinander für alle aktiven Displays, auch wenn nur der Inhalt eines Displays aktualisiert wird.

Wenn beispielsweise das externe Display aktualisiert wird, ist die Reihenfolge so:

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

Zusammensetzung virtueller Displays

Die Zusammensetzung des virtuellen Displays ähnelt der eines externen Displays. Der Unterschied zwischen der Zusammensetzung virtueller und physischer Displays besteht darin, dass virtuelle Displays die Ausgabe an einen Gralloc-Puffer statt an das Display senden. Hardware Composer (HWC) schreibt die Ausgabe in einen Zwischenspeicher, stellt den Abschlussfence bereit und sendet den Zwischenspeicher an einen Nutzer (z. B. Videoencoder, GPU, CPU usw.). Virtuelle Displays können 2D/Blitter oder Overlays verwenden, wenn die Anzeigepipeline in den Arbeitsspeicher schreibt.

Modi

Jeder Frame befindet sich in einem der drei Modi, nachdem SurfaceFlinger die validateDisplay()-HWC-Methode aufgerufen hat:

  • GLES: Die GPU setzt alle Ebenen zusammen und schreibt direkt in den Ausgabepuffer. Die HWC ist nicht an der Komposition beteiligt.
  • MIXED: Die GPU kombiniert einige Ebenen mit dem Framebuffer und HWC kombiniert den Framebuffer mit den verbleibenden Ebenen und schreibt direkt in den Ausgabebuffer.
  • HWC: HWC kombiniert alle Ebenen und schreibt sie direkt in den Ausgabebuffer.

Ausgabeformat

Die Ausgabeformate des virtuellen Display-Buffers hängen vom Modus ab:

  • GLES-Modus: Der EGL-Treiber legt das Ausgabebufferformat in dequeueBuffer() fest, normalerweise RGBA_8888. Der Verbraucher muss das vom Treiber festgelegte Ausgabeformat akzeptieren können, da der Puffer sonst nicht gelesen werden kann.
  • MIXED- und HWC-Modi: Wenn der Verbraucher CPU-Zugriff benötigt, legt er das Format fest. Andernfalls ist das Format IMPLEMENTATION_DEFINED und Gralloc legt das beste Format basierend auf den Nutzungsflags fest. Beispielsweise legt Gralloc ein YCbCr-Format fest, wenn der Verbraucher ein Videoencoder ist und HWC das Format effizient schreiben kann.

Synchronisationsschranken

Synchronisationsschranken sind ein wichtiger Aspekt des Android-Grafiksystems. Mit Fences können CPU-Arbeiten unabhängig von gleichzeitigen GPU-Arbeiten ausgeführt werden. Sie werden nur blockiert, wenn eine echte Abhängigkeit besteht.

Wenn eine App beispielsweise einen Puffer einreicht, der auf der GPU erstellt wird, reicht sie auch ein Synchronisationsobjekt ein. Dieser Zaun signalisiert, dass die GPU mit dem Schreiben in den Puffer fertig ist.

Die HWC erfordert, dass die GPU das Schreiben von Zwischenspeichern beendet, bevor Puffer angezeigt werden. Synchronisationsschranken werden mit Buffers durch die Grafikpipeline übergeben und signalisieren, wenn Buffers geschrieben werden. Bevor ein Puffer angezeigt wird, prüft der HWC, ob der Synchronisierungs-Fence ein Signal gesendet hat. Ist das der Fall, wird der Puffer angezeigt.

Weitere Informationen zu Synchronisierungsschranken finden Sie unter Hardware Composer-Integration.