Implementare l'HAL del compositore hardware

I livelli compositi HAL di Hardware Composer (HWC) ricevuti da SurfaceFlinger, riducono la quantità di composizione eseguita da OpenGL ES (GLES) e dalla GPU.

L'HWC astrae oggetti, come sovrapposizioni e blitter 2D, in superfici composite e comunica con hardware specializzato per la composizione di finestre con finestre composite. Utilizza l'HWC per comporre finestre invece di utilizzare SurfaceFlinger composito con la GPU. La maggior parte delle GPU non è ottimizzata per la composizione e quando la GPU compone livelli da SurfaceFlinger, le app non possono utilizzare la GPU per il proprio rendering.

Le implementazioni HWC dovrebbero supportare:

  • Almeno quattro sovrapposizioni:
    • Barra di stato
    • Barra di sistema
    • App
    • Carta da parati/sfondo
  • Livelli più grandi del display (ad esempio uno sfondo)
  • Miscelazione alfa per pixel premoltiplicata simultanea e fusione alfa per piano
  • Percorso hardware per la riproduzione video protetta
  • Ordine di imballaggio RGBA, formati YUV e proprietà di piastrellatura, vortice e falcata

Per implementare l'HWC:

  1. Implementare un HWC non operativo e inviare tutto il lavoro di composizione a GLES.
  2. Implementare un algoritmo per delegare la composizione all'HWC in modo incrementale. Ad esempio, delegare solo le prime tre o quattro superfici all'hardware sovrapposto dell'HWC.
  3. Ottimizza l'HWC. Ciò può includere:
    • Selezione delle superfici che massimizzano il carico tolto dalla GPU e invio all'HWC.
    • Rilevare se lo schermo si sta aggiornando. In caso contrario, delegare la composizione al GLES invece che all'HWC per risparmiare energia. Quando la schermata si aggiorna nuovamente, continua a scaricare la composizione sull'HWC.
    • Preparazione per casi d'uso comuni come:
      • La schermata iniziale, che include la barra di stato, la barra di sistema, la finestra dell'app e gli sfondi animati
      • Giochi a schermo intero in modalità verticale e orizzontale
      • Video a schermo intero con sottotitoli e controllo della riproduzione
      • Riproduzione video protetta
      • Multifinestra a schermo diviso

Primitive HWC

L'HWC fornisce due primitive, layer e display , per rappresentare il lavoro di composizione e la sua interazione con l'hardware di visualizzazione. L'HWC fornisce inoltre il controllo su VSYNC e una richiamata a SurfaceFlinger per avvisarlo quando si verifica un evento VSYNC.

Interfaccia HIDL

Android 8.0 e versioni successive utilizzano un'interfaccia HIDL denominata Composer HAL per IPC binderizzato tra HWC e SurfaceFlinger. L'HAL Composer sostituisce l'interfaccia legacy hwcomposer2.h . Se i fornitori forniscono un'implementazione HAL Composer dell'HWC, Composer HAL accetta direttamente le chiamate HIDL da SurfaceFlinger. Se i fornitori forniscono un'implementazione legacy dell'HWC, Composer HAL carica i puntatori a funzione da hwcomposer2.h , inoltrando le chiamate HIDL alle chiamate dei puntatori a funzione.

L'HWC fornisce funzioni per determinare le proprietà di un determinato display; per passare tra diverse configurazioni di visualizzazione (come la risoluzione 4k o 1080p) e modalità colore (come colore nativo o true sRGB); e per accendere, spegnere o impostare la modalità di risparmio energetico, se supportata.

Puntatori di funzioni

Se i fornitori implementano direttamente Composer HAL, SurfaceFlinger chiama le sue funzioni tramite HIDL IPC. Ad esempio, per creare un livello, SurfaceFlinger chiama createLayer() sull'HAL Composer.

Se i fornitori implementano l'interfaccia hwcomposer2.h , Composer HAL richiama i puntatori alla funzione hwcomposer2.h . Nei commenti hwcomposer2.h , le funzioni dell'interfaccia HWC vengono indicate con nomi lowerCamelCase che non esistono nell'interfaccia come campi denominati. Quasi tutte le funzioni vengono caricate richiedendo un puntatore a funzione utilizzando getFunction fornito da hwc2_device_t . Ad esempio, la funzione createLayer è un puntatore a funzione di tipo HWC2_PFN_CREATE_LAYER , che viene restituito quando il valore enumerato HWC2_FUNCTION_CREATE_LAYER viene passato a getFunction .

Per la documentazione dettagliata sulle funzioni HAL di Composer e sulle funzioni passthrough delle funzioni HWC, vedere composer . Per la documentazione dettagliata sui puntatori alle funzioni HWC, vedere hwcomposer2.h .

Maniglie di visualizzazione e livello

I livelli e le visualizzazioni vengono manipolati tramite handle generati dall'HWC. Le maniglie sono opache per SurfaceFlinger.

Quando SurfaceFlinger crea un nuovo livello, chiama createLayer , che restituisce di tipo Layer per implementazioni dirette o hwc2_layer_t per implementazioni passthrough. Quando SurfaceFlinger modifica una proprietà di quel livello, SurfaceFlinger passa il valore hwc2_layer_t alla funzione di modifica appropriata insieme a qualsiasi altra informazione necessaria per apportare la modifica. Il tipo hwc2_layer_t è abbastanza grande da contenere un puntatore o un indice.

I display fisici vengono creati mediante collegamento a caldo. Quando un display fisico viene collegato a caldo, l'HWC crea un handle e passa l'handle a SurfaceFlinger tramite il callback hotplug. I display virtuali vengono creati da SurfaceFlinger chiamando createVirtualDisplay() per richiedere un display. Se l'HWC supporta la composizione del display virtuale, restituisce un handle. Quindi, SurfaceFlinger delega la composizione dei display all'HWC. Se l'HWC non supporta la composizione del display virtuale, SurfaceFlinger crea l'handle e compone il display.

Visualizzare le operazioni di composizione

Una volta per VSYNC, SurfaceFlinger si riattiva se ha nuovi contenuti da comporre. Questo nuovo contenuto può essere un nuovo buffer di immagine dalle app o una modifica nelle proprietà di uno o più livelli. Quando SurfaceFlinger lo riattiva:

  1. Gestisce le transazioni, se presenti.
  2. Blocca i nuovi buffer grafici, se presenti.
  3. Esegue una nuova composizione, se il passaggio 1 o 2 ha comportato una modifica al contenuto visualizzato.

Per eseguire una nuova composizione, SurfaceFlinger crea e distrugge i livelli o modifica gli stati dei livelli, a seconda dei casi. Aggiorna inoltre i livelli con i loro contenuti correnti, utilizzando chiamate come setLayerBuffer o setLayerColor . Dopo che tutti i livelli sono stati aggiornati, SurfaceFlinger chiama validateDisplay , che indica all'HWC di esaminare lo stato dei livelli e determinare come procederà la composizione. Per impostazione predefinita, SurfaceFlinger tenta di configurare ogni livello in modo tale che il livello sia composto dall'HWC; sebbene in alcune circostanze, SurfaceFlinger compone gli strati attraverso il fallback della GPU.

Dopo la chiamata a validateDisplay , SurfaceFlinger chiama getChangedCompositionTypes per verificare se l'HWC desidera che qualcuno dei tipi di composizione dei livelli venga modificato prima di eseguire la composizione. Per accettare le modifiche, SurfaceFlinger chiama acceptDisplayChanges .

Se qualche livello è contrassegnato per la composizione di SurfaceFlinger, SurfaceFlinger li compone nel buffer di destinazione. SurfaceFlinger chiama quindi setClientTarget per fornire il buffer al display in modo che il buffer possa essere visualizzato sullo schermo o ulteriormente composto con livelli che non sono stati contrassegnati per la composizione SurfaceFlinger. Se non sono contrassegnati livelli per la composizione di SurfaceFlinger, SurfaceFlinger ignora il passaggio di composizione.

Infine, SurfaceFlinger chiama presentDisplay per indicare all'HWC di completare il processo di composizione e visualizzare il risultato finale.

Display multipli

Android 10 supporta più display fisici. Quando si progetta un'implementazione HWC destinata all'uso su Android 7.0 e versioni successive, sono presenti alcune restrizioni non presenti nella definizione HWC:

  • Si presuppone che ci sia esattamente un display interno . Il display interno è la visualizzazione che viene visualizzata dall'hotplug iniziale durante l'avvio. Una volta collegato a caldo, il display interno non può essere disconnesso.
  • Oltre al display interno, è possibile collegare a caldo un numero qualsiasi di display esterni durante il normale funzionamento del dispositivo. Il framework presuppone che tutti gli hotplug successivi al primo display interno siano display esterni, quindi se vengono aggiunti altri display interni, verranno classificati erroneamente come Display.TYPE_HDMI anziché Display.TYPE_BUILT_IN .

Sebbene le operazioni SurfaceFlinger descritte sopra vengano eseguite per display, vengono eseguite in sequenza per tutti i display attivi, anche se vengono aggiornati i contenuti di un solo display.

Ad esempio, se viene aggiornato il display esterno, la sequenza è:

// In Android 9 and lower:

// Update state for internal display
// Update state for external display
validateDisplay(<internal display>)
validateDisplay(<external display>)
presentDisplay(<internal display>)
presentDisplay(<external display>)

// In Android 10 and higher:

// Update state for internal display
// Update state for external display
validateInternal(<internal display>)
presentInternal(<internal display>)
validateExternal(<external display>)
presentExternal(<external display>)

Composizione del display virtuale

La composizione del display virtuale è simile alla composizione del display esterno. La differenza tra la composizione del display virtuale e la composizione del display fisico è che i display virtuali inviano l'output a un buffer Gralloc anziché allo schermo. Hardware Composer (HWC) scrive l'output in un buffer, fornisce il recinto di completamento e invia il buffer a un consumatore (come il codificatore video, la GPU, la CPU e così via). I display virtuali possono utilizzare 2D/blitter o sovrapposizioni se la pipeline di visualizzazione scrive in memoria.

Modalità

Ogni frame è in una delle tre modalità dopo che SurfaceFlinger chiama il metodo HWC validateDisplay() :

  • GLES : la GPU compone tutti i livelli, scrivendo direttamente nel buffer di output. L'HWC non è coinvolto nella composizione.
  • MISTO : la GPU compone alcuni strati nel framebuffer e l'HWC compone il framebuffer e gli strati rimanenti, scrivendo direttamente nel buffer di output.
  • HWC : HWC compone tutti i livelli e scrive direttamente nel buffer di output.

Formato di output

I formati di output del buffer di visualizzazione virtuale dipendono dalla modalità:

  • Modalità GLES : il driver EGL imposta il formato del buffer di output in dequeueBuffer() , in genere RGBA_8888 . Il consumatore deve essere in grado di accettare il formato di output impostato dal driver altrimenti il ​​buffer non potrà essere letto.
  • Modalità MISTA e HWC : se il consumatore necessita dell'accesso alla CPU, imposta il formato. Altrimenti, il formato è IMPLEMENTATION_DEFINED e Gralloc imposta il formato migliore in base ai flag di utilizzo. Ad esempio, Gralloc imposta un formato YCbCr se il consumatore è un codificatore video e HWC può scrivere il formato in modo efficiente.

Recinti di sincronizzazione

I limiti di sincronizzazione (sincronizzazione) sono un aspetto cruciale del sistema grafico Android. Le recinzioni consentono al lavoro della CPU di procedere indipendentemente dal lavoro simultaneo della GPU, bloccandosi solo quando c'è una vera dipendenza.

Ad esempio, quando un'app invia un buffer prodotto sulla GPU, invia anche un oggetto di recinzione della sincronizzazione. Questo recinto segnala quando la GPU ha finito di scrivere nel buffer.

L'HWC richiede che la GPU finisca di scrivere i buffer prima che vengano visualizzati. Le recinzioni di sincronizzazione vengono passate attraverso la pipeline grafica con buffer e segnalano quando i buffer vengono scritti. Prima che venga visualizzato un buffer, l'HWC controlla se il limite di sincronizzazione ha segnalato e, in tal caso, visualizza il buffer.

Per ulteriori informazioni sui limiti di sincronizzazione, consultare Integrazione di Hardware Composer .