Synchronisierungs-Framework

Das Synchronisierungsframework beschreibt explizit Abhängigkeiten zwischen verschiedenen asynchronen Vorgängen im Android-Grafiksystem. Das Framework bietet eine API, mit der Komponenten angeben können, wann Buffers freigegeben werden. Außerdem ermöglicht das Framework die Weitergabe von Synchronisierungsprimitiven zwischen Treibern vom Kernel an den Userspace und zwischen 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. Der Fenster-Compositor beginnt die Verarbeitung im Voraus und übergibt die Arbeit an den Displaycontroller. Analog dazu arbeitet die CPU mit im Voraus erfolgt. Sobald die GPU fertig ist, zeigt der Displaycontroller das Bild sofort an.

Mit dem Synchronisierungsframework können Implementierer auch Synchronisierungsressourcen in ihren eigenen Hardwarekomponenten nutzen. Schließlich bietet das Framework Einblicke in die Grafikpipeline, um das Debuggen zu erleichtern.

Explizite Synchronisierung

Durch die explizite Synchronisierung können Ersteller und Nutzer von Grafikpuffern signalisieren, wenn sie mit der Verwendung eines Puffers fertig sind. Explizite Synchronisierung im Kernel-Space implementiert.

Zu den Vorteilen der expliziten Synchronisierung gehören:

  • Weniger Verhaltensabweichungen zwischen Geräten
  • 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 eine monoton steigende Zeitachse, die Anbieter für jede Treiberinstanz implementieren sollten, z. B. für einen GL-Kontext, einen Displaycontroller oder einen 2D-Blitter. sync_timeline zählt die Jobs, die für eine bestimmte Hardware an den Kernel gesendet wurden. sync_timeline bietet Garantien für 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.
  • Implementieren Sie die Operatoren timeline_value_str und pt_value_str in Zeitleisten, um die Ausgabe der Fehlerbehebung übersichtlicher zu gestalten.
  • 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 zu den unveränderlichen sync_fence und sync_pts übergeben, um darauf basierende Befehlszeilen zu erstellen.
  • 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.
  • Greifen Sie nicht explizit auf sync_timeline-, sync_pt- oder sync_fence-Elemente zu. Die API stellt alle erforderlichen Funktionen.

Synchronisierungsschritt

sync_pt ist ein einzelner Wert oder Punkt auf einer sync_timeline. Ein Punkt hat drei Status: „aktiv“, „signalisiert“ und „Fehler“. Punkte beginnen im aktiven Status und wechseln in den Status „Signalisiert“ oder „Fehler“. Wenn ein Bildverbraucher beispielsweise keinen Puffer mehr benötigt, wird ein sync_pt signalisiert, damit ein Bilderzeuger weiß, dass er wieder in den Puffer schreiben kann.

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 Primitiven, mit denen Treiber und Userspace ihre Abhängigkeiten kommunizieren. Wenn ein Zaun signalisiert wird, sind alle Befehle, die vor dem Zaun ausgegeben wurden, garantiert abgeschlossen, da der Kernel-Treiber oder Hardwareblock Befehle in der richtigen Reihenfolge ausführt.

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 vom Kernel-Speicherplatz zum Userspace. Ein Zaun kann beispielsweise zwei sync_pt-Werte enthalten, die angeben, wann zwei separate Bildverbraucher mit dem Lesen eines Buffers fertig sind. Wenn die Begrenzung signalisiert wird, wissen die Bildproduzenten, dass beide Verbraucher fertig sind.

Einschlüsse sind wie sync_pt-Werte standardmäßig aktiv und ändern ihren Status je nach Status ihrer Punkte. Wenn alle sync_pt-Werte signalisiert werden, wird auch sync_fence 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. Wenn Sie mehr als einen Punkt in einem Zaun haben möchten, wird ein Zusammenführen durchgeführt, bei dem Punkte aus zwei verschiedenen Zäunen zu einem dritten Zaun hinzugefügt werden. Wenn einer dieser Punkte im ursprünglichen Zaun signalisiert wurde und der andere nicht, ist auch der dritte Zaun nicht signalisiert.

Wenn Sie eine explizite Synchronisierung implementieren möchten, geben Sie Folgendes an:

  • Ein Kernel-Subsystem, das das Synchronisierungsframework für einen bestimmten Hardwaretreiber implementiert. Fahrer, die sicherheitshalber sein müssen, im Allgemeinen alles, was auf den Hardware Composer zugreift oder mit ihm kommuniziert. Zu den wichtigsten Dateien gehören:
    • Grundlegende Implementierung:
      • 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 grenzwertbezogene GL-Erweiterungen (EGL_ANDROID_native_fence_sync und EGL_ANDROID_wait_sync) sowie Grenzwertunterstützung im Grafiktreiber.

Fallstudie: Treiber für Display-Werbung implementieren

Um die API zu verwenden, die die Synchronisierungsfunktion unterstützt, entwickeln einen Anzeigetreiber mit einer Anzeigepufferfunktion. Vor der Einführung des Synchronization Framework empfing diese Funktion dma-buf-Objekte, platzierte diese Buffers auf dem Display und blockierte, während der Buffer 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 Puffer angezeigt wird, wird er mit einem Zaun verknüpft, 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 Aufträge in die Warteschlange stellen und starten, nachdem der Begrenzungsbereich aufgehoben 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);

Synchronisierungsintegration

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

Beachten Sie die Konventionen für die Android HAL-Schnittstelle:

  • Wenn die API einen Dateideskriptor bereitstellt, der sich auf eine sync_pt bezieht, muss der Treiber des Anbieters oder die HAL, die die API verwendet, den Dateideskriptor 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.
  • Damit der Dateideskriptor für die Umzäunung weiter verwendet werden kann, muss der Anbietertreiber oder die HAL den Deskriptor duplizieren.

Ein Zaunobjekt wird jedes Mal umbenannt, wenn es die BufferQueue durchläuft. 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. Dies ist hilfreich bei der Fehlerbehebung, um die Ursache eines Deadlocks zu ermitteln, da die Namen in der Ausgabe von /d/sync und in Fehlerberichten erscheinen.

ANativeWindow-Integration

ANativeWindow ist zaunfähig. dequeueBuffer, queueBuffer und cancelBuffer haben Zaunparameter.

OpenGL ES-Integration

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

  • EGL_ANDROID_native_fence_sync bietet eine Möglichkeit, native Android-Fence-Dateibeschreibungen in EGLSyncKHR-Objekten zu verpacken oder zu erstellen.
  • EGL_ANDROID_wait_sync ermöglicht GPU-seitige Aussetzer anstelle von CPU-seitigen Aussetzern, sodass die GPU auf EGLSyncKHR wartet. 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. Die EGL_ANDROID_native_fence_sync-Erweiterung besteht aus einem separaten nativen Zaunobjekttyp EGLSyncKHR. 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 Grenzwertdateideskriptor umschließt einen vorhandenen nativen Android-Grenzwertdateideskriptor 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 das Attribut „internal_fence“ geschlossen.

Hardware Composer-Integration

Der Hardware Composer verarbeitet drei Arten von Synchronisationssperren:

  • Acquire-Grenzen werden zusammen mit Eingabepuffern an die setLayerBuffer- und setClientTarget-Aufrufe übergeben. 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. Present-Grenzen geben an, wann die Komposition dieses Frames abgeschlossen ist oder wann das Kompositionierungsergebnis des vorherigen Frames nicht mehr benötigt wird. Bei physischen Displays gibt presentDisplay aktuelle Begrenzungen zurück, wenn der aktuelle Frame auf dem Bildschirm erscheint. 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.