Synchronisierungs-Framework

Das Synchronisierungs-Framework beschreibt explizit Abhängigkeiten zwischen verschiedene asynchrone Vorgänge im Android-Grafiksystem. Das Framework stellt eine API bereit, mit der Komponenten anzeigen können, wann Puffer freigegeben werden. Das Framework ermöglicht die Übergabe von Synchronisierungsprimitiven zwischen Treibern vom Kernel aus. und zwischen den Userspace-Prozessen selbst.

Eine Anwendung kann beispielsweise die Arbeit in der GPU in die Warteschlange stellen. Die GPU beginnt mit dem Zeichnen dieses Bildes. Obwohl das Bild nicht gezeichnet wurde, im Arbeitsspeicher gespeichert, wird der Pufferzeiger an das Fenster übergeben, Compositor zusammen mit einem Zaun, der angibt, wann die GPU Fertigstellen. Die Window Compositor-Verarbeitung beginnt im Voraus und übergibt die Arbeit an den Display-Controller. Analog dazu arbeitet die CPU mit im Voraus erfolgt. Sobald die GPU fertig ist, das Bild sofort angezeigt wird.

Das Synchronisierungs-Framework ermöglicht Implementierern außerdem, in ihren eigenen Hardwarekomponenten. Die Funktion bietet einen Einblick in die Grafikpipeline, Debugging.

Explizite Synchronisierung

Die explizite Synchronisierung ermöglicht Produzenten und Nutzern von Grafikpuffern um zu signalisieren, dass sie die Verwendung eines Puffers beendet haben. Explizite Synchronisierung im Kernel-Space implementiert.

Zu den Vorteilen der expliziten Synchronisierung gehören:

  • Geringere Unterschiede im Verhalten der Geräte
  • Bessere Unterstützung bei der Fehlerbehebung
  • Verbesserte Testmesswerte

Das Synchronisierungs-Framework umfasst drei Objekttypen:

  • sync_timeline
  • sync_pt
  • sync_fence

Zeitachse_synchronisieren

sync_timeline ist ein kontinuierlich ansteigender Zeitplan, die Anbieter für jede Treiberinstanz implementieren sollten, z. B. einen GL-Kontext, Display-Controllers oder 2D-Effekt. sync_timeline Zähler Jobs, die für eine bestimmte Hardware an den Kernel gesendet werden. sync_timeline garantiert die Reihenfolge der Vorgänge. und ermöglicht hardwarespezifische Implementierungen.

Beachte bei der Implementierung von sync_timeline die folgenden Richtlinien:

  • Nützliche Namen für Fahrer, Fahrpläne und Zäune angeben Debugging.
  • timeline_value_str und pt_value_str implementieren in Zeitachsen, um die Debugging-Ausgabe besser lesbar zu machen.
  • Implementiere die Füllung driver_data, um Userspace-Bibliotheken bereitzustellen, wie die GL-Bibliothek, Zugriff auf private Zeitachsendaten. Mit data_driver können Anbieter Informationen über die unveränderlichen sync_fence und sync_pts zum Erstellen von Befehlszeilen basierend auf ihnen.
  • Nutzerbereich darf nicht explizit einen Zaun erstellen oder signalisieren. Ausdrücklich die Erstellung von Signalen/Zäunen zu einem Denial-of-Service-Angriff führt, stoppt die Pipelinefunktionalität.
  • Du solltest nicht auf sync_timeline, sync_pt oder sync_fence-Elementen explizit. Die API stellt alle erforderlichen Funktionen.

Synchronisierungsschritt

sync_pt ist ein einzelner Wert oder Punkt auf einem sync_timeline. Ein Punkt hat drei Status: aktiv, signalisiert und Fehler. Punkte beginnen im Status „Aktiv“ und in den Signal- oder Fehlerstatus übergehen. Wenn beispielsweise ein Bild Verbraucher keinen Puffer mehr benötigt, wird ein sync_pt signalisiert sodass ein Bildersteller weiß, dass er wieder in den Zwischenspeicher schreiben darf.

fechten

sync_fence ist eine Sammlung von sync_pt-Werten. so oft haben unterschiedliche übergeordnete sync_timeline-Elemente, z. B. für das Controller und GPU). sync_fence, sync_pt und sync_timeline sind die wichtigsten Primitive, die Treiber und Userspace um ihre Abhängigkeiten zu kommunizieren. Wenn ein Zaun signalisiert wird, Befehle, die vor dem Zaun ausgegeben wurden, sind garantiert vollständig, einen Kernel-Treiber oder einen Hardwareblock, führt Befehle der Reihe nach aus.

Mit dem Synchronisierungs-Framework können mehrere Nutzer die Verwendung eines Puffers abgeschlossen und die Abhängigkeitsinformationen mit einer Funktion übermittelt haben. . Zäune werden von einem Dateideskriptor gestützt und vom den Kernel-Bereich zum Userspace. Ein Zaun kann beispielsweise zwei sync_pt-Werte, die angeben, wann zwei separate Bildnutzer fertig sind Puffer zu lesen. Wenn der Zaun signalisiert wird, wissen die Bildersteller, dass sowohl mit dem Konsumieren fertig.

Zäune wie sync_pt-Werte starten in den aktiven Status und ändern den Status basierend auf den Zustand ihrer Standpunkte. Wenn alle sync_pt-Werte signalisiert werden, wird der Wert sync_fence wird signalisiert. Wenn ein sync_pt fällt in einen Fehlerstatus versetzt, hat der gesamte sync_fence einen Fehlerstatus.

Die Mitgliedschaft in einer sync_fence ist nach dem Zaun nicht mehr veränderbar erstellt. Um mehr als einen Punkt in einem Zaun zu erhalten, bei denen Punkte aus zwei unterschiedlichen Zäunen zu einem dritten Zaun hinzugefügt werden. Wenn einer dieser Punkte im ursprünglichen Zaun signalisiert wurde und der andere nicht, ist der dritte Zaun nicht signalisiert.

Um eine explizite Synchronisierung zu implementieren, geben Sie Folgendes an:

  • Ein Kernel-Bereich-Subsystem, das das Synchronisierungs-Framework implementiert für einen bestimmten Hardwaretreiber. Fahrer, die fechten müssen, im Allgemeinen alles, was auf den Hardware Composer zugreift oder mit ihm kommuniziert. Zu den Schlüsseldateien gehören: <ph type="x-smartling-placeholder">
      </ph>
    • Grundlegende Implementierung: <ph type="x-smartling-placeholder">
        </ph>
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • Dokumentation unter kernel/common/Documentation/sync.txt
    • Bibliothek zur Kommunikation mit dem Kernelbereich in platform/system/core/libsync
  • Der Anbieter muss die entsprechende Synchronisierung fences als Parameter für validateDisplay() und presentDisplay()-Funktionen im HAL.
  • Zwei zaunbezogene GL-Erweiterungen (EGL_ANDROID_native_fence_sync) und EGL_ANDROID_wait_sync) und Zaununterstützung in Grafiken .

Fallstudie: Treiber für Display-Werbung implementieren

Um die API zu verwenden, die die Synchronisierungsfunktion unterstützt, entwickeln einen Anzeigetreiber mit einer Anzeigepufferfunktion. Vor dem Synchronisierungs-Framework vorhanden ist, würde diese Funktion dma-buf empfangen. Objekte, diese Puffer auf dem Bildschirm platziert und ihn blockiert, solange der Puffer sichtbar war. Für 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 Synchronisierungs-Framework kann die Funktion display_buffer ist komplexer. Wenn ein Zwischenspeicher angezeigt wird, durch einen Zaun, der angibt, wann der Puffer bereit ist. Du kannst dich in die Warteschlange stellen und beginnen mit der Arbeit, nachdem der Zaun sich geräumt hat.

Wenn Sie die Warteschlange stellen und mit der Arbeit beginnen, nachdem der Zaun geräumt wurde, wird nichts blockiert. Sie stellen sofort Ihren eigenen Zaun um. So wird sichergestellt, dass der Puffer nicht mehr auf dem Display. Wenn Sie Puffer in die Warteschlange stellen, listet der Kernel Abhängigkeiten mit dem Synchronisierungs-Framework:

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

Integration synchronisieren

In diesem Abschnitt wird erläutert, wie Sie das Framework für die Kernel-Space-Synchronisierung mit des Android-Frameworks und die Treiber, die kommunizieren müssen, miteinander kommunizieren. Kernel-Space-Objekte werden als Dateideskriptoren dargestellt, Userspace.

Integrationskonventionen

Halten Sie sich an die Android-HAL-Schnittstellenkonventionen:

  • Wenn die API einen Dateideskriptor bereitstellt, der auf eine sync_pt verweist, Der Anbietertreiber oder der HAL, der die API verwendet, muss die Dateibeschreibung schließen.
  • Wenn der Anbietertreiber oder der HAL einen Dateideskriptor übergibt, der sync_pt einer API-Funktion hinzugefügt haben, darf der Anbietertreiber oder der HAL Schließen Sie den Dateideskriptor.
  • Um den Fence-Dateideskriptor weiterhin zu verwenden, müssen Sie den Anbietertreiber oder den HAL muss den Deskriptor duplizieren.

Ein Fence-Objekt wird jedes Mal umbenannt, wenn es die BufferQueue passiert. Dank der Kernel-Fence-Unterstützung können Fences Strings für Namen haben, sodass die Synchronisierung verwendet das Framework den Fensternamen und den Zwischenspeicherindex, den Zaun, z. B. SurfaceView:0. Dieses ist hilfreich bei der Fehlerbehebung, um die Ursache eines Deadlocks zu identifizieren, wenn die Namen angezeigt werden. in der Ausgabe von /d/sync und Fehlerberichten.

ANativeWindow-Integration

ANativeWindow ist zaunfähig. dequeueBuffer, queueBuffer und cancelBuffer haben Fence-Parameter.

OpenGL ES-Integration

Die Integration von OpenGL ES-Synchronisierungen stützt sich auf zwei EGL-Erweiterungen:

  • Mit EGL_ANDROID_native_fence_sync können Sie native Android-Fence-Dateideskriptoren in EGLSyncKHR-Objekte.
  • EGL_ANDROID_wait_sync ermöglicht GPU-seitige Ladevorgänge anstatt auf CPU-Seite. Dadurch wartet die GPU auf EGLSyncKHR. Die Die Erweiterung EGL_ANDROID_wait_sync ist mit der Erweiterung EGL_KHR_wait_sync.

Um diese Erweiterungen unabhängig zu verwenden, implementieren Sie den EGL_ANDROID_native_fence_sync-Erweiterung mit dem zugehörigen Kernel-Unterstützung. Aktivieren Sie als Nächstes die EGL_ANDROID_wait_sync in Ihrem Treiber installiert haben. Das EGL_ANDROID_native_fence_sync Die Erweiterung besteht aus einem speziellen nativen Zaun EGLSyncKHR-Objekt. Typ. Daher werden Erweiterungen, die für bestehende EGLSyncKHR gelten, Objekttypen gelten nicht unbedingt für EGL_ANDROID_native_fence Objekte und vermeidet unerwünschte Interaktionen.

In der Erweiterung EGL_ANDROID_native_fence_sync wird eine entsprechende native Fence-Datei-Deskriptor-Attribut, das nur bei der Erstellung festgelegt werden kann, und können nicht direkt von einem vorhandenen Synchronisierungsobjekt direkt abgefragt werden. Dieses Attribut kann auf einen von zwei Modi eingestellt werden:

  • Ein gültiger Fence-Dateideskriptor umschließt eine vorhandene native Android-Fence-Dateideskriptor in einem EGLSyncKHR-Objekt.
  • -1 erstellt einen nativen Android-Fence-Dateideskriptor aus einer EGLSyncKHR-Objekt.

Verwenden Sie den Funktionsaufruf DupNativeFenceFD(), um die EGLSyncKHR-Objekt aus dem nativen Android-Fence-Dateideskriptor. Dies hat das gleiche Ergebnis wie die Abfrage des Attributs set, wird jedoch gemäß den die Konvention, dass der Empfänger die Abgrenzung schließt (damit das Duplikat Vorgang). Durch das Löschen des EGLSyncKHR-Objekts wird schließlich das Attribut „interner Zaun“.

Hardware Composer-Integration

Der Hardware Composer verarbeitet drei Arten von Synchronisierungszäunen:

  • Zäune erfassen werden zusammen mit Eingabepuffern übergeben, die Aufrufe setLayerBuffer und setClientTarget. Diese stehen für einen ausstehenden Schreibvorgang in den Zwischenspeicher und müssen vor dem SurfaceFlinger oder die HWC versuchen, aus dem zugehörigen Zwischenspeicher zu lesen, Kompositionen durchzuführen.
  • Freigabe-Zäune werden nach dem Aufruf von presentDisplay über den getReleaseFences-Aufruf. Diese stehen für einen ausstehenden Lesevorgang aus dem vorherigen Zwischenspeicher auf derselben Ebene. A Zaunsignale freigeben, wenn die HWC den vorherigen Zwischenspeicher nicht mehr verwendet da der aktuelle Puffer den vorherigen in der Anzeige ersetzt hat. Release-Fences werden zusammen mit den vorherigen Puffern an die App zurückgegeben, wird während der aktuellen Komposition ersetzt. Die App muss warten, bis loslassen, bevor neue Inhalte in den Puffer geschrieben werden, zurückgegeben wurde.
  • Aktuelle Zäune werden zurückgegeben, einer pro Frame. den Aufruf von presentDisplay. Aktuelle Zäune stellen dar, wann die die Zusammensetzung dieses Frames abgeschlossen ist, oder alternativ, des vorherigen Frames nicht mehr benötigt. Für physische angezeigt wird, gibt presentDisplay vorhandene Zäune zurück, wenn der auf dem Bildschirm angezeigt wird. Nach der Rückgabe der aktuellen Zäune wieder in den SurfaceFlinger-Zielpuffer, zutreffend sind. Für virtuelle Anzeigen werden die aktuellen Zäune zurückgegeben, wenn aus dem Ausgabepuffer gelesen werden kann.