L'HAL Hardware Composer (HWC) compone i livelli ricevuti da SurfaceFlinger, riducendo la quantità di composizione OpenGL ES (GLES) e le prestazioni della GPU.
L'HWC astrae gli oggetti, come le sovrapposizioni e i blitter 2D, per comporre le superfici e comunica con l'hardware di composizione delle finestre specializzato per comporre le finestre. Utilizza HWC per comporre le finestre anziché SurfaceFlinger con la GPU. La maggior parte delle GPU non è ottimizzata per la composizione e, quando la GPU compone i livelli da SurfaceFlinger, le app non possono utilizzare la GPU per il proprio rendering.
Le implementazioni HWC devono supportare:
- Almeno quattro overlay:
- Barra di stato
- Barra di sistema
- App
- Sfondo
- Livelli più grandi del display (ad esempio, uno sfondo)
- Fusione alfa per pixel e per piano premoltiplicata simultanea
- Percorso hardware per la riproduzione di video protetti
- Ordine di pacchettizzazione RGBA, formati YUV e proprietà di tiling, swizzling e stride
Per implementare l'HWC:
- Implementa un HWC non operativo e invia tutto il lavoro di composizione a GLES.
- Implementa un algoritmo per delegare la composizione all'HWC in modo incrementale. Ad esempio, delega solo le prime tre o quattro superfici all'hardware di sovrapposizione dell'HWC.
- Ottimizza l'HWC. Potrebbe includere:
- Selezionando le superfici che massimizzano il carico della GPU e inviandole all'HWC.
- Rilevamento dell'aggiornamento dello schermo. In caso contrario, delega la composizione a GLES anziché a HWC per risparmiare energia. Quando lo schermo si aggiorna di nuovo, continua a scaricare la composizione sull'HWC.
- Prepararsi per casi d'uso comuni, ad esempio:
- La schermata Home, 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 codificati e controllo della riproduzione
- Riproduzione di video protetti
- Multifinestra con schermo diviso
Primitive HWC
L'HWC fornisce due primitive, layers e displays, per rappresentare il lavoro di composizione e la sua interazione con l'hardware del display. L'HWC fornisce anche il controllo su VSync e un callback a SurfaceFlinger per comunicare quando si verifica un evento VSync.
Interfaccia HIDL
Android 8.0 e versioni successive utilizzano un'interfaccia
HIDL chiamata Composer HAL per
IPC binderizzato tra HWC e SurfaceFlinger. L'HAL di Composer sostituisce l'interfaccia hwcomposer2.h
legacy. Se i fornitori forniscono un'implementazione di Composer HAL
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 di funzione da hwcomposer2.h
,
inoltrando le chiamate HIDL alle chiamate dei puntatori di funzione.
L'HWC fornisce funzioni per determinare le proprietà di un determinato display, per passare da una configurazione del display all'altra (ad esempio risoluzione 4K o 1080p) e da una modalità colore all'altra (ad esempio colore nativo o sRGB reale) e per accendere, spegnere o impostare la modalità a basso consumo del display, se supportata.
Puntatori a funzione
Se i fornitori implementano direttamente Composer HAL, SurfaceFlinger chiama le sue funzioni
tramite HIDL IPC. Ad esempio, per creare un livello, SurfaceFlinger chiama
createLayer()
su Composer HAL.
Se i fornitori implementano l'interfaccia hwcomposer2.h
, Composer HAL
chiama i puntatori di funzione hwcomposer2.h
. Nei commenti hwcomposer2.h
,
le funzioni dell'interfaccia HWC
sono indicate con nomi in formato lowerCamelCase che non esistono nell'interfaccia
come campi denominati. Quasi tutte le funzioni vengono caricate richiedendo un puntatore di 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
trasmesso a getFunction
.
Per la documentazione dettagliata sulle funzioni HAL di Composer e sulle funzioni di passthrough di HWC, consulta composer
. Per la documentazione dettagliata sui puntatori di funzione HWC, consulta hwcomposer2.h
.
Maniglie dei livelli e del display
I livelli e i display vengono manipolati tramite i punti di manipolazione generati dall'HWC. Gli handle sono opachi per SurfaceFlinger.
Quando SurfaceFlinger crea un nuovo livello, chiama createLayer
,
che restituisce il tipo Layer
per le implementazioni dirette o hwc2_layer_t
per le implementazioni passthrough. Quando
SurfaceFlinger modifica una proprietà di questo 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 tramite hotplug. Quando un display fisico viene
inserito a caldo, HWC crea un handle e lo passa a SurfaceFlinger
tramite il callback hotplug. I display virtuali vengono creati da SurfaceFlinger
che chiama createVirtualDisplay()
per richiedere un display. Se HWC
supporta la composizione del display virtuale, restituisce un handle. Poi, SurfaceFlinger
delega la composizione dei display all'HWC. Se HWC non supporta la composizione
dello schermo virtuale, SurfaceFlinger crea l'handle e compone lo schermo.
Visualizza le operazioni di composizione
Una volta per VSync, SurfaceFlinger si riattiva se ha nuovi contenuti da comporre. Questi nuovi contenuti possono essere nuovi buffer di immagini delle app o una modifica delle proprietà di uno o più livelli. Quando SurfaceFlinger lo riattiva:
- Gestisce le transazioni, se presenti.
- Blocca i nuovi buffer grafici, se presenti.
- Esegue una nuova composizione, se il passaggio 1 o 2 ha comportato una modifica ai contenuti visualizzati.
Per eseguire una nuova composizione, SurfaceFlinger crea ed elimina i livelli o modifica gli stati dei livelli, a seconda dei casi. Aggiorna anche
i livelli con i loro contenuti attuali, utilizzando chiamate come
setLayerBuffer
o setLayerColor
. Dopo l'aggiornamento di tutti i livelli, SurfaceFlinger chiama validateDisplay
, che indica all'HWC di esaminare lo stato dei livelli e determinare come procedere con la composizione. Per impostazione predefinita, SurfaceFlinger tenta di configurare ogni livello
in modo che venga composto dall'HWC; tuttavia, in alcune
circostanze, SurfaceFlinger compone i livelli tramite il fallback della GPU.
Dopo la chiamata a validateDisplay
, SurfaceFlinger chiama
getChangedCompositionTypes
per verificare se l'HWC
vuole che venga modificato uno dei tipi di composizione dei livelli prima di eseguire la
composizione. Per accettare le modifiche, SurfaceFlinger chiama
acceptDisplayChanges
.
Se alcuni livelli sono contrassegnati per la composizione di SurfaceFlinger, SurfaceFlinger
li compone nel buffer di destinazione. SurfaceFlinger chiama quindi
setClientTarget
per dare il buffer al display in modo che
possa essere visualizzato sullo schermo o ulteriormente composto con livelli che
non sono stati contrassegnati per la composizione di SurfaceFlinger. Se non sono contrassegnati livelli per la composizione di SurfaceFlinger, SurfaceFlinger ignora il passaggio di composizione.
Infine, SurfaceFlinger chiama presentDisplay
per comunicare
all'HWC di completare la procedura di composizione e visualizzare il risultato finale.
Più annunci display
Android 10 supporta più display fisici. Quando progetti un'implementazione HWC destinata all'uso su Android 7.0 e versioni successive, esistono alcune limitazioni non presenti nella definizione HWC:
- Si presume che esista esattamente un display interno. Il display interno è quello che viene segnalato 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 dopo il primo display interno siano display esterni, quindi se vengono aggiunti altri
display interni, vengono classificati in modo errato come
Display.TYPE_HDMI
anzichéDisplay.TYPE_BUILT_IN
.
Sebbene le operazioni di SurfaceFlinger descritte sopra vengano eseguite per display, vengono eseguite in sequenza per tutti i display attivi, anche se vengono aggiornati solo i contenuti di un display.
Ad esempio, se il display esterno viene aggiornato, 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 a quella del display esterno. La differenza tra la composizione dello schermo virtuale e quella dello schermo fisico è che gli schermi virtuali inviano l'output a un buffer Gralloc anziché allo schermo. Hardware Composer (HWC) scrive l'output in un buffer, fornisce la barriera di completamento e invia il buffer a un consumer (ad esempio il codificatore video, la GPU, la CPU e così via). I display virtuali possono utilizzare 2D/blitter o overlay se la pipeline di visualizzazione scrive nella memoria.
Modalità
Ogni frame si trova 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 è coinvolta nella composizione.
- MISTO: la GPU compone alcuni livelli nel frame buffer e HWC compone il frame buffer e i livelli 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 del display virtuale dipendono dalla modalità:
- Modalità GLES: il driver EGL imposta il formato del buffer di output in
dequeueBuffer()
, in genereRGBA_8888
. Il consumatore deve essere in grado di accettare il formato di output impostato dal driver, altrimenti il buffer non può essere letto. - Modalità MISTA e HWC: se il consumatore ha bisogno dell'accesso alla CPU, imposta il formato. In caso contrario, 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 consumer è un codificatore video e HWC può scrivere il formato in modo efficiente.
Barriere di sincronizzazione
Le recinzioni di sincronizzazione sono un aspetto fondamentale del sistema grafico di Android. Le barriere consentono alla CPU di funzionare indipendentemente dal lavoro simultaneo della GPU, bloccando solo in caso di una vera dipendenza.
Ad esempio, quando un'app invia un buffer in fase di produzione sulla GPU, invia anche un oggetto barriera di sincronizzazione. Questa barriera 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 barriere di sincronizzazione vengono trasmesse tramite la pipeline grafica con buffer e segnalano quando i buffer vengono scritti. Prima che venga visualizzato un buffer, HWC controlla se la barriera di sincronizzazione ha segnalato e, in caso affermativo, visualizza il buffer.
Per saperne di più sulle barriere di sincronizzazione, consulta Integrazione di Hardware Composer.