Framework di sincronizzazione

Il framework di sincronizzazione descrive in modo esplicito le dipendenze tra le diverse operazioni asincrone nel sistema grafico Android. Il framework fornisce un'API che consente ai componenti di indicare quando vengono rilasciati i buffer. Il framework consente anche il passaggio di primitive di sincronizzazione tra i driver dal kernel allo spazio utente e tra i processi dello spazio utente stessi.

Ad esempio, un'applicazione può accodare il lavoro da eseguire nella GPU. La GPU inizia a disegnare quell'immagine. Sebbene l'immagine non sia stata ancora disegnata in memoria, il puntatore del buffer viene passato al compositore di finestre insieme a una recinzione che indica quando terminerà il lavoro della GPU. Il compositore di finestre avvia l'elaborazione in anticipo e passa il lavoro al controller del display. In modo simile, il lavoro della CPU viene svolto in anticipo. Al termine della GPU, il controller del display visualizza immediatamente l'immagine.

Il framework di sincronizzazione consente inoltre agli implementatori di sfruttare le risorse di sincronizzazione nei propri componenti hardware. Infine, il framework fornisce visibilità nella pipeline grafica per facilitare il debug.

Sincronizzazione esplicita

La sincronizzazione esplicita consente ai produttori e ai consumatori di buffer grafici di segnalare quando hanno finito di utilizzare un buffer. La sincronizzazione esplicita è implementata nello spazio del kernel.

I vantaggi della sincronizzazione esplicita includono:

  • Minore variazione di comportamento tra i dispositivi
  • Migliore supporto per il debug
  • Metriche di test migliorate

Il framework di sincronizzazione ha tre tipi di oggetti:

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline una cronologia monotona crescente che i fornitori dovrebbero attuare per ciascuna istanza del driver, come ad esempio un contesto GL, scheda video, o blitter 2D. sync_timeline lavori conta presentate al kernel per un particolare pezzo di hardware. sync_timeline fornisce garanzie circa l'ordine delle operazioni e permette implementazioni hardware specifici.

Seguire queste linee guida in sede di attuazione sync_timeline :

  • Fornisci nomi utili per tutti i driver, le tempistiche e le barriere per semplificare il debug.
  • Implementare il timeline_value_str e pt_value_str operatori nelle timeline per fare il debug di uscita più leggibile.
  • Implementare il riempimento driver_data per dare spazio utente librerie, come ad esempio la libreria GL, l'accesso ai dati della timeline privati, se lo si desidera. data_driver lascia fornitori passano informazioni sulla immutabile sync_fence e sync_pts a righe di comando di generazione basati su di essi.
  • Non consentire allo spazio utente di creare o segnalare esplicitamente una recinzione. La creazione esplicita di segnali/recinzioni provoca un attacco denial-of-service che interrompe la funzionalità della pipeline.
  • Non accesso sync_timeline , sync_pt , o sync_fence elementi in modo esplicito. L'API fornisce tutte le funzioni richieste.

sync_pt

sync_pt è un valore singolo o un punto su una sync_timeline . Un punto ha tre stati: attivo, segnalato ed errore. I punti iniziano nello stato attivo e passano allo stato segnalato o di errore. Ad esempio, quando un consumatore un'immagine non ha più bisogno di un buffer, uno sync_pt viene segnalato in modo che un produttore di immagini sa che è bene scrivere nel buffer di nuovo.

sync_fence

sync_fence è una raccolta di sync_pt valori che spesso hanno differenti sync_timeline genitori (come per la scheda video e GPU). sync_fence , sync_pt , e sync_timeline sono i principali primitive che i driver e l'uso userspace per comunicare le loro dipendenze. Quando una recinzione viene segnalata, tutti i comandi emessi prima della recinzione sono garantiti per essere completati perché il driver del kernel o il blocco hardware esegue i comandi in ordine.

Il framework di sincronizzazione consente a più consumatori o produttori di segnalare quando hanno finito di usare un buffer, comunicando le informazioni sulla dipendenza con un parametro di funzione. I recinti sono supportati da un descrittore di file e vengono passati dallo spazio del kernel allo spazio utente. Ad esempio, una recinzione può contenere due sync_pt valori che indicano quando due consumatori di immagine separati realizza la lettura di un buffer. Quando viene segnalato il recinto, i produttori di immagini sanno che entrambi i consumatori hanno finito di consumare.

Recinzioni, come sync_pt valori, iniziano stato attivo e il cambiamento in base allo stato dei loro punti. Se tutti i sync_pt valori diventano segnalati, lo sync_fence diventa segnalato. Se uno sync_pt cade in uno stato di errore, l'intero sync_fence ha uno stato di errore.

L'appartenenza a uno sync_fence è immutabile dopo la creazione del recinto. Per ottenere più di un punto in una recinzione, viene eseguita un'unione in cui i punti di due barriere distinte vengono aggiunti a una terza recinzione. Se uno di questi punti è stato segnalato nel recinto di origine e l'altro no, anche il terzo recinto non sarà in uno stato segnalato.

Per implementare la sincronizzazione esplicita, fornire quanto segue:

  • Un sottosistema kernel-space che implementa il framework di sincronizzazione per un particolare driver hardware. I driver che devono essere consapevoli della recinzione sono generalmente tutto ciò che accede o comunica con Hardware Composer. I file chiave includono:
    • Implementazione di base:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • La documentazione in kernel/common/Documentation/sync.txt
    • Libreria per comunicare con lo spazio kernel in platform/system/core/libsync
  • Il fornitore deve fornire le recinzioni sincronizzazione appropriate come parametri al validateDisplay() e presentDisplay() funzioni nel HAL.
  • Due estensioni recinzione legati GL ( EGL_ANDROID_native_fence_sync e EGL_ANDROID_wait_sync ) e il supporto recinto nel driver grafico.

Caso di studio: implementazione di un driver video

Per utilizzare l'API che supporta la funzione di sincronizzazione, sviluppare un driver video con una funzione di buffer di visualizzazione. Prima esisteva il quadro di sincronizzazione, questa funzione riceverebbe dma-buf oggetti, mettere quelle buffer sul display, e il blocco mentre il buffer era visibile. Per esempio:

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

Con il quadro di sincronizzazione, la display_buffer funzione è più complessa. Durante la visualizzazione di un buffer, il buffer è associato a una recinzione che indica quando il buffer sarà pronto. Puoi metterti in coda e iniziare il lavoro dopo che la recinzione è stata cancellata.

L'accodamento e l'avvio del lavoro dopo che la recinzione è stata rimossa non blocca nulla. Restituisci immediatamente il tuo recinto, che garantisce quando il buffer sarà spento dal display. Mentre metti in coda i buffer, il kernel elenca le dipendenze con il framework di sincronizzazione:

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

Integrazione sincronizzazione

Questa sezione spiega come integrare il framework di sincronizzazione dello spazio kernel con le parti dello spazio utente del framework Android e i driver che devono comunicare tra loro. Gli oggetti nello spazio del kernel sono rappresentati come descrittori di file nello spazio utente.

Convenzioni di integrazione

Segui le convenzioni dell'interfaccia HAL di Android:

  • Se l'API fornisce un descrittore di file che si riferisce ad una sync_pt , il driver del venditore o il HAL utilizzando l'API devono chiudere il descrittore di file.
  • Se il conducente o il fornitore HAL passa un descrittore di file che contiene uno sync_pt ad una funzione API, il fornitore del driver o l'HAL non deve chiudere il descrittore di file.
  • Per continuare a utilizzare il descrittore di file di recinzione, il driver del fornitore o l'HAL deve duplicare il descrittore.

Un oggetto recinzione viene rinominato ogni volta che passa attraverso BufferQueue. Supporto recinzione kernel permette recinzioni di avere le stringhe per i nomi, in modo che il quadro Sync utilizza il nome della finestra e index buffer che viene in coda per il nome del recinto, come ad esempio SurfaceView:0 . Questo è utile per il debug per identificare la fonte di una situazione di stallo come i nomi appaiono nella produzione di /d/sync e bug report.

Integrazione con ANativeWindow

ANativeWindow è a conoscenza della recinzione. dequeueBuffer , queueBuffer , e cancelBuffer hanno parametri di recinzione.

Integrazione OpenGL ES

L'integrazione della sincronizzazione OpenGL ES si basa su due estensioni EGL:

  • EGL_ANDROID_native_fence_sync fornisce un modo per avvolgere o creare nativi descrittori di file recinzione Android in EGLSyncKHR oggetti.
  • EGL_ANDROID_wait_sync permette GPU-side bancarelle piuttosto che CPU-side, rendendo la GPU attesa per EGLSyncKHR . EGL_ANDROID_wait_sync estensione è lo stesso del EGL_KHR_wait_sync estensione.

Per utilizzare queste estensioni indipendente, attuare EGL_ANDROID_native_fence_sync estensione lungo con il supporto del kernel associato. Avanti, abilitare EGL_ANDROID_wait_sync estensione nel driver. EGL_ANDROID_native_fence_sync estensione consiste in un distinto recinzione nativo EGLSyncKHR tipo di oggetto. Di conseguenza, le estensioni esistenti che si applicano a EGLSyncKHR tipi di oggetti non necessariamente si applicano a EGL_ANDROID_native_fence oggetti, evitando interazioni indesiderate.

EGL_ANDROID_native_fence_sync estensione impiega un corrispondente attributo descrittore di file recinzione nativo che può essere impostata solo in fase di creazione e non può essere direttamente interrogato in avanti da un oggetto di sincronizzazione esistente. Questo attributo può essere impostato su una delle due modalità:

  • Un descrittore di file recinzione valida avvolge un nativo Android descrittore di file recinzione esistente in un EGLSyncKHR oggetto.
  • -1 crea un nativo Android descrittore di file da un recinto EGLSyncKHR oggetto.

Utilizzare la DupNativeFenceFD() chiamata di funzione per estrarre EGLSyncKHR oggetto dal descrittore di file recinzione nativo Android. Questo ha lo stesso risultato dell'interrogazione dell'attributo set, ma aderisce alla convenzione secondo cui il destinatario chiude la recinzione (da qui l'operazione di duplicazione). Infine, distruggendo EGLSyncKHR oggetto chiude l'attributo internal recinzione.

Integrazione del compositore hardware

Hardware Composer gestisce tre tipi di barriere di sincronizzazione:

  • Recinzioni acquisire vengono passati ai buffer di ingresso ai setLayerBuffer e setClientTarget chiamate. Questi rappresentano una scrittura in sospeso nel buffer e devono segnalare prima che SurfaceFlinger o l'HWC tenti di leggere dal buffer associato per eseguire la composizione.
  • Recinzioni di uscita vengono recuperati dopo la chiamata al presentDisplay utilizzando il getReleaseFences chiamata. Questi rappresentano una lettura in sospeso dal buffer precedente sullo stesso livello. Un blocco di rilascio segnala quando l'HWC non utilizza più il buffer precedente perché il buffer corrente ha sostituito il buffer precedente sul display. I limiti di rilascio vengono restituiti all'app insieme ai buffer precedenti che verranno sostituiti durante la composizione corrente. L'app deve attendere fino a quando non viene segnalato un limite di rilascio prima di scrivere nuovi contenuti nel buffer che gli è stato restituito.
  • Recinzioni presenti vengono restituiti, uno per ogni fotogramma, come parte della chiamata a presentDisplay . Le recinzioni presenti rappresentano quando la composizione di questa cornice è stata completata, o in alternativa, quando il risultato della composizione della cornice precedente non è più necessario. Per display fisici, presentDisplay restituisce recinzioni presenti quando compare il fotogramma corrente sullo schermo. Dopo aver restituito le barriere presenti, è possibile scrivere nuovamente nel buffer di destinazione di SurfaceFlinger, se applicabile. Per i display virtuali, i recinti presenti vengono restituiti quando è possibile leggere dal buffer di output.