Synchronisations-Framework

Das Synchronisationsframework beschreibt explizit Abhängigkeiten zwischen verschiedenen asynchronen Operationen im Android-Grafiksystem. Das Framework stellt eine API bereit, die es Komponenten ermöglicht, anzuzeigen, wann Puffer freigegeben werden. Das Framework ermöglicht auch die Weitergabe von Synchronisationsprimitiven zwischen Treibern vom Kernel zum Userspace und zwischen Userspace-Prozessen selbst.

Zum Beispiel kann eine Anwendung Arbeit in die Warteschlange stellen, die in der GPU ausgeführt werden soll. Die GPU beginnt mit dem Zeichnen dieses Bildes. Obwohl das Bild noch nicht in den Speicher gezeichnet wurde, wird der Pufferzeiger zusammen mit einem Zaun, der angibt, wann die GPU-Arbeit beendet ist, an den Fenster-Kompositor übergeben. Der Window-Compositor startet die Verarbeitung vorzeitig und übergibt die Arbeit an den Display-Controller. Auf ähnliche Weise wird die CPU-Arbeit im Voraus erledigt. Sobald die GPU fertig ist, zeigt der Display-Controller sofort das Bild an.

Das Synchronisationsframework ermöglicht es Implementierern auch, Synchronisationsressourcen in ihren eigenen Hardwarekomponenten zu nutzen. Schließlich bietet das Framework Einblick in die Grafikpipeline, um beim Debuggen zu helfen.

Explizite Synchronisation

Die explizite Synchronisierung ermöglicht es Produzenten und Konsumenten von Grafikpuffern, zu signalisieren, wenn sie mit der Verwendung eines Puffers fertig sind. Die explizite Synchronisation ist im Kernel-Space implementiert.

Zu den Vorteilen der expliziten Synchronisierung gehören:

  • Weniger Verhaltensunterschiede zwischen Geräten
  • Bessere Debugging-Unterstützung
  • Verbesserte Testmetriken

Das Synchronisierungsframework hat drei Objekttypen:

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline eine monoton wachsende Zeitleiste, die Anbieter für jeden Treiber - Instanz, wie zum Beispiel ein GL Zusammenhang Anzeigesteuerung oder 2D Blitter implementieren soll. sync_timeline zählt Arbeitsplätze für ein bestimmtes Stück Hardware an den Kernel eingereicht. sync_timeline bietet Garantien über die Reihenfolge der Operationen und ermöglicht hardwarespezifische Implementierungen.

Befolgen Sie diese Richtlinien bei der Umsetzung sync_timeline :

  • Geben Sie nützliche Namen für alle Treiber, Zeitachsen und Zäune an, um das Debuggen zu vereinfachen.
  • Implementieren Sie die timeline_value_str und pt_value_str Operatoren in Zeitrahmen Debugging - Ausgabe besser lesbar zu machen.
  • Implementieren Sie die Füllung driver_data zu geben , Userspace - Bibliotheken, wie die GL - Bibliothek, den Zugang zu privaten Timeline - Daten, falls gewünscht. data_driver können Anbieter übergeben Informationen über die unveränderliche sync_fence und sync_pts zu bauen Befehlszeilen auf ihnen basieren.
  • Erlauben Sie dem Userspace nicht, explizit einen Zaun zu erstellen oder zu signalisieren. Das explizite Erstellen von Signalen/Zäunen führt zu einem Denial-of-Service-Angriff, der die Pipeline-Funktionalität stoppt.
  • Kein Zugang sync_timeline , sync_pt oder sync_fence Elemente explizit. Die API stellt alle erforderlichen Funktionen bereit.

sync_pt

sync_pt ist ein einzelner Wert oder Punkt auf einem sync_timeline . Ein Punkt hat drei Zustände: aktiv, signalisiert und Fehler. Punkte beginnen im aktiven Zustand und gehen in den signalisierten oder Fehlerzustand über. Zum Beispiel, wenn ein Bild Verbraucher nicht mehr einen Puffer benötigt, ein sync_pt signalisiert so ein Bild Produzent weiß , dass es okay ist , wieder in die Puffer zu schreiben.

sync_fence

sync_fence ist eine Sammlung von sync_pt Werte , die oft unterschiedliche haben sync_timeline Eltern (wie beispielsweise für die Anzeigesteuerung und GPU). sync_fence , sync_pt und sync_timeline sind die Hauptgrundelemente, die Treiber und Anwenderseite Gebrauch ihre Abhängigkeiten zu kommunizieren. Wenn ein Fence signalisiert wird, sind alle Befehle, die vor dem Fence ausgegeben wurden, garantiert vollständig, da der Kernel-Treiber oder der Hardware-Block die Befehle der Reihe nach ausführt.

Das Sync-Framework ermöglicht es mehreren Consumern oder Producern, mithilfe eines Puffers zu signalisieren, wenn sie fertig sind, indem die Abhängigkeitsinformationen mit einem Funktionsparameter kommuniziert werden. Fences werden durch einen Dateideskriptor unterstützt und vom Kernelspace an den Userspace weitergegeben. Zum Beispiel kann ein Zaun zwei enthält sync_pt Werte , die bedeuten , wenn zwei separate Bild Verbraucher getan werden einen Puffer zu lesen. Wenn der Zaun signalisiert wird, wissen die Bildproduzenten, dass beide Konsumenten mit dem Konsum fertig sind.

Zäune, wie sync_pt Werte, starten Sie aktiv und Änderungszustand basierend auf dem Zustand ihrer Punkte. Wenn alle sync_pt Werte signalisiert werden, wird die sync_fence wird signalisiert. Wenn man sync_pt in einen Fehlerzustand fällt, wird die gesamte sync_fence hat einen Fehlerzustand.

Die Mitgliedschaft in einer sync_fence ist unveränderlich , nachdem der Zaun erstellt wird. Um mehr als einen Punkt in einem Zaun zu erhalten, wird eine Zusammenführung durchgeführt, bei der Punkte von zwei verschiedenen Zauns zu einem dritten Zaun hinzugefügt werden. Wenn einer dieser Punkte im Ursprungszaun signalisiert wurde und der andere nicht, befindet sich auch der dritte Zaun nicht in einem signalisierten Zustand.

Geben Sie Folgendes an, um die explizite Synchronisierung zu implementieren:

  • Ein Kernel-Space-Subsystem, das das Synchronisierungsframework für einen bestimmten Hardwaretreiber implementiert. Treiber, die Fence-fähig sein müssen, sind im Allgemeinen alles, was auf den Hardware Composer zugreift oder mit ihm kommuniziert. Zu den wichtigsten Dateien gehören:
    • Kernimplementierung:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • Dokumentation im kernel/common/Documentation/sync.txt
    • Bibliothek mit dem Kernel - Raum in Kommunikation platform/system/core/libsync
  • Der Hersteller muss die entsprechenden Synchronisations Zäunen als Parameter an die bieten validateDisplay() und presentDisplay() Funktionen in der HAL.
  • Zwei Zaunbezogene GL - Erweiterungen ( EGL_ANDROID_native_fence_sync und EGL_ANDROID_wait_sync ) und Zaun Unterstützung in Grafiktreiber.

Fallstudie: Implementierung eines Bildschirmtreibers

Um die API zu verwenden, die die Synchronisationsfunktion unterstützt, entwickeln Sie einen Anzeigetreiber, der über eine Anzeigepufferfunktion verfügt. Bevor die Synchronisierungs Rahmen gäbe, würde diese Funktion erhalten dma-buf Objekte, diese Puffer auf dem Display gesetzt und Block während der Puffer sichtbar war. Zum Beispiel:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

Mit dem Synchronisationsrahmen, die display_buffer ist Funktion komplexer. Während ein Puffer angezeigt wird, ist der Puffer mit einem Zaun verbunden, der anzeigt, wann der Puffer bereit ist. Sie können sich anstellen und die Arbeit einleiten, nachdem der Zaun gelöscht wurde.

Das Anstehen und Einleiten der Arbeit nach dem Aufräumen des Zauns blockiert nichts. Sie geben sofort Ihren eigenen Zaun zurück, der garantiert, wenn der Puffer aus dem Display ist. Während Sie Puffer in die Warteschlange stellen, listet der Kernel Abhängigkeiten mit dem Synchronisationsframework auf:

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

Synchronisierungsintegration

In diesem Abschnitt wird erläutert, wie Sie das Kernel-Space-Synchronisierungs-Framework in Userspace-Teile des Android-Frameworks und die Treiber integrieren, die miteinander kommunizieren müssen. Kernel-Space-Objekte werden im Userspace als Dateideskriptoren dargestellt.

Integrationskonventionen

Befolgen Sie die Konventionen der Android HAL-Schnittstelle:

  • Wenn die API einen Dateideskriptor, der mit einem bezieht liefert sync_pt , der Hersteller-Treiber oder der HAL die API verwendet , muss den Dateideskriptor schließen.
  • Wenn der Verkäufer oder der Treiber HAL gibt einen Dateideskriptor, die A enthält sync_pt zu einer API - Funktion, die Verkäufer - Treiber oder den HAL muss nicht in der Nähe des Dateideskriptor.
  • Um den Fencing-Dateideskriptor weiterhin verwenden zu können, muss der Herstellertreiber oder die HAL den Deskriptor duplizieren.

Ein Zaunobjekt wird jedes Mal umbenannt, wenn es BufferQueue passiert. Kernel Zaun Unterstützung ermöglicht Zäune Saiten für Namen haben, so dass der Sync Framework die Fensternamen verwendet und Pufferindex, der die Warteschlange gestellt ist wird , um den Zaun zu nennen, wie SurfaceView:0 . Dies ist hilfreich bei der Fehlersuche der Quelle eines Deadlock zu identifizieren , wie die Namen in der Ausgabe erscheinen /d/sync und Fehlerberichte.

ANativeWindow-Integration

ANativeWindow kennt den Zaun. dequeueBuffer , queueBuffer und cancelBuffer haben Zaun Parameter.

OpenGL ES-Integration

Die OpenGL ES-Synchronisierungsintegration basiert auf zwei EGL-Erweiterungen:

  • EGL_ANDROID_native_fence_sync bietet eine Möglichkeit , nativen Android - Deskriptoren Zaun Datei in einzuwickeln oder erstellen EGLSyncKHR Objekte.
  • EGL_ANDROID_wait_sync ermöglicht GPU-Seite Ständen statt CPU-Seite, so dass die GPU warten EGLSyncKHR . Die EGL_ANDROID_wait_sync Erweiterung ist das gleiche wie die EGL_KHR_wait_sync Erweiterung.

Um diese Erweiterungen unabhängig zu nutzen, implementieren die EGL_ANDROID_native_fence_sync Erweiterung zusammen mit der dazugehörigen Kernel - Unterstützung. Als nächstes aktivieren Sie die EGL_ANDROID_wait_sync Erweiterung in Ihrem Fahrer. Die EGL_ANDROID_native_fence_sync Erweiterung besteht aus einem bestimmten nativen Zaun EGLSyncKHR Objekttypen. Als Ergebnis Erweiterungen , die auf bestehende anwenden EGLSyncKHR Objekttypen gelten nicht unbedingt zu EGL_ANDROID_native_fence Objekte, die Vermeidung unerwünschter Wechselwirkungen.

Die EGL_ANDROID_native_fence_sync Erweiterung setzt ein entsprechendes native Zaun Dateideskriptors Attribut , das nur bei der Erstellung festgelegt werden kann und kann nicht direkt weiter aus einem vorhandenen Synchronisations Objekt abgefragt werden. Dieses Attribut kann auf einen von zwei Modi eingestellt werden:

  • Ein gültiger Zaun Dateideskriptors hüllt einen vorhandenen nativen Android Zaun Dateideskriptor in einem EGLSyncKHR Objekt.
  • -1 schafft eine native Descriptor Android Zaun Datei von einem EGLSyncKHR Objekt.

Verwenden Sie die DupNativeFenceFD() Funktionsaufruf das extrahieren EGLSyncKHR Objekt aus dem nativen Android Zaun Dateideskriptor. Dies hat das gleiche Ergebnis wie die Abfrage des set-Attributs, hält sich jedoch an die Konvention, dass der Empfänger den Zaun schließt (daher die Duplizierungsoperation). Schließlich ist die Zerstörung EGLSyncKHR schließt Attribut des internen fence - Objekts.

Hardware Composer-Integration

Der Hardware Composer verarbeitet drei Arten von Synchronisierungszäunen:

  • Acquire Zäune werden zusammen mit Eingangspuffern zu den übergebenen setLayerBuffer und setClientTarget Anrufe. Diese stellen einen anstehenden Schreibvorgang in den Puffer dar und müssen signalisieren, bevor der SurfaceFlinger oder der HWC versucht, aus dem zugehörigen Puffer zu lesen, um eine Komposition durchzuführen.
  • Release Zäune sind nach dem Aufruf abgerufen presentDisplay mit dem getReleaseFences Anruf. Diese stellen einen ausstehenden Lesevorgang aus dem vorherigen Puffer auf derselben Ebene dar. Ein Freigabezaun signalisiert, wenn der HWC den vorherigen Puffer nicht mehr verwendet, weil der aktuelle Puffer den vorherigen Puffer auf dem Display ersetzt hat. Freigabezäune werden zusammen mit den vorherigen Puffern, die während der aktuellen Komposition ersetzt werden, an die App zurückgegeben. Die App muss warten, bis ein Freigabezaunsignal signalisiert wird, bevor neue Inhalte in den Puffer geschrieben werden, der an sie zurückgegeben wurde.
  • Zäune vorliegenden zurückgegeben werden , eine pro Rahmen, als Teil des Anrufs zu presentDisplay . Gegenwärtige Zäune stellen dar, wann die Komposition dieses Frames abgeschlossen ist, oder alternativ, wenn das Kompositionsergebnis des vorherigen Frames nicht mehr benötigt wird. Für physikalische Displays, presentDisplay gibt vorhanden Zäunen , wenn der aktuelle Frame auf dem Bildschirm erscheint. Nachdem die aktuellen Fences zurückgegeben wurden, können Sie ggf. erneut in den SurfaceFlinger-Zielpuffer schreiben. Bei virtuellen Displays werden vorhandene Fences zurückgegeben, wenn das Lesen aus dem Ausgabepuffer sicher ist.