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
undpt_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. Mitdata_driver
können Anbieter Informationen zu den unveränderlichensync_fence
undsync_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
- odersync_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
- Grundlegende Implementierung:
- Der Anbieter muss die entsprechende Synchronisierung
fences als Parameter für
validateDisplay()
undpresentDisplay()
-Funktionen im HAL. - Zwei grenzwertbezogene GL-Erweiterungen (
EGL_ANDROID_native_fence_sync
undEGL_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 inEGLSyncKHR
-Objekten zu verpacken oder zu erstellen.EGL_ANDROID_wait_sync
ermöglicht GPU-seitige Aussetzer anstelle von CPU-seitigen Aussetzern, sodass die GPU aufEGLSyncKHR
wartet. Die Die ErweiterungEGL_ANDROID_wait_sync
ist mit der ErweiterungEGL_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
- undsetClientTarget
-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 dengetReleaseFences
-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 gibtpresentDisplay
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.