Implementare Hardware Composer HAL

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 esegue l'astrazione di oggetti, come overlay e blitter 2D, per le superfici composite e comunica con l'hardware specializzato per la composizione delle finestre per comporre le finestre. Utilizza l'HWC per comporre le finestre anziché avere la composizione di 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 dello schermo (ad esempio, uno sfondo)
  • Combinazione alfa per pixel premoltiplicata simultanea e fusione alfa per piano
  • Percorso hardware per la riproduzione di video protetti
  • Ordine di imballaggio RGBA, formati YUV e proprietà di ricomposizione, scambio e larghezza pixel

Per implementare l'HWC:

  1. Implementa un HWC non operativo e invia tutto il lavoro di composizione a GLES.
  2. Implementare un algoritmo per delegare la composizione all'HWC in modo incrementale. Ad esempio, delega solo le prime tre o quattro piattaforme all'hardware di overlay del set di conversione hardware.
  3. Ottimizza l'HWC. ad esempio:
    • Selezione delle piattaforme che massimizzano il carico tolto dalla GPU e invio a HWC.
    • Rilevamento dell'aggiornamento dello schermo in corso. In caso contrario, delega la composizione a GLES anziché all'HWC per risparmiare energia. Quando la schermata viene nuovamente aggiornata, continua a trasferire la composizione all'HWC.
    • Preparazione per casi d'uso comuni come:
      • La schermata Home, che include la barra di stato, la barra di sistema, la finestra delle 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
      • Multi-finestra 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 notificare quando si verifica un evento VSYNC.

Interfaccia HIDL

Android 8.0 e versioni successive utilizzano un'interfaccia HIDL chiamata HAL Composer per l'IPC con binder tra HWC e SurfaceFlinger. L'HAL di Composer sostituisce l'interfaccia hwcomposer2.h precedente. Se i fornitori forniscono un'implementazione dell'HWC per Composer HAL, quest'ultimo accetta direttamente le chiamate HIDL da SurfaceFlinger. Se i fornitori forniscono un'implementazione precedente dell'HWC, Composer HAL carica i puntatori funzione da hwcomposer2.h, inoltra le chiamate HIDL alle chiamate dei puntatori funzione.

L'HWC fornisce funzioni per determinare le proprietà di un determinato display, per passare tra diverse configurazioni del display (ad esempio la risoluzione 4K o 1080p) e le modalità a colori (come colore nativo o true sRGB) e per accendere, spegnere il display o passare alla modalità a basso consumo, se supportata.

Puntatori di funzione

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

Se i fornitori implementano l'interfaccia hwcomposer2.h, HAL di Composer richiama i puntatori di funzione hwcomposer2.h. Nei commenti hwcomposer2.h, si fa riferimento alle funzioni dell'interfaccia HWC con nomi in minuscoloCamelCase 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 di funzione di tipo HWC2_PFN_CREATE_LAYER, che viene restituito quando il valore enumerato HWC2_FUNCTION_CREATE_LAYER viene passato in getFunction.

Per una documentazione dettagliata sulle funzioni HAL di Composer e sulle funzioni di passaggio delle funzioni HWC, consulta composer. Per una documentazione dettagliata sui puntatori di funzione HWC, consulta il hwcomposer2.h.

Maniglie di livello e visualizzazione

I livelli e le visualizzazioni vengono manipolati da handle 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à del livello, passa il valore hwc2_layer_t alla funzione di modifica appropriata insieme a tutte le altre informazioni necessarie per apportare la modifica. Il tipo hwc2_layer_t è abbastanza grande da contenere un puntatore o un indice.

I display fisici vengono creati tramite il collegamento a caldo. Quando un display fisico viene collegato tramite hotplug, l'HWC crea un handle e lo passa 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.

Operazioni di composizione della visualizzazione

Una volta per VSYNC, SurfaceFlinger si riattiva se ha nuovi contenuti da compositare. Questi nuovi contenuti possono essere nuovi buffer di immagini delle app o una modifica delle proprietà di uno o più livelli. Quando SurfaceFlinger lo attiva:

  1. Gestisce le transazioni, se presenti.
  2. Blocca i nuovi buffer grafici, se presenti.
  3. Esegui una nuova composizione se il passaggio 1 o 2 ha comportato una modifica ai contenuti visualizzati.

Per eseguire una nuova composizione, SurfaceFlinger crea e distrugge i livelli o ne modifica gli stati, a seconda dei casi. Aggiorna inoltre i livelli con i relativi contenuti attuali, 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 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 modificare uno dei tipi di composizione degli strati prima di eseguire la composizione. Per accettare le modifiche, SurfaceFlinger chiama acceptDisplayChanges.

Se alcuni livelli sono contrassegnati per la composizione di SurfaceFlinger, quest'ultimo li compone nel buffer di destinazione. SurfaceFlinger chiama quindi setClientTarget per assegnare il buffer al display in modo che possa essere visualizzato sullo schermo o ulteriormente composto con i 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 indicare all'HWC di completare la procedura di composizione e visualizzare il risultato finale.

Più display

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

  • Si presume che sia presente un solo display interno. Il display interno è quello segnalato dal hotplug iniziale durante l'avvio. Una volta collegato il display interno, non può essere disconnesso.
  • Oltre al display interno, è possibile collegare tramite hot-plug un numero qualsiasi di display esterni durante il normale funzionamento del dispositivo. Il framework presuppone che tutti gli hot plug dopo il primo display interno siano display esterni, pertanto se vengono aggiunti altri display interni, questi vengono classificati erroneamente 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 i contenuti di un solo 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 del display virtuale e la composizione del display fisico è che i display virtuali inviano l'output a un buffer Gralloc anziché allo schermo. Il compositore hardware (HWC) scrive l'output in un buffer, fornisce la recinzione di completamento e invia il buffer a un consumatore (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 in memoria.

Modalità

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

  • GLES: la GPU compone tutti i livelli, scrivendo direttamente nel buffer di output. Il Centro per la salute e il benessere non è coinvolto nella composizione.
  • MISTO: la GPU compone alcuni livelli nel framebuffer e l'HWC compone il framebuffer 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 su dequeueBuffer(), in genere RGBA_8888. Il consumer deve essere in grado di accettare il formato di output impostato dal driver, altrimenti il buffer non può essere letto.
  • Modalità MIXED e HWC: se il consumatore ha bisogno di accedere alla CPU, viene impostato 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 consumatore è un codificatore video e HWC può scrivere il formato in modo efficiente.

Barriere di sincronizzazione

I recinti di sincronizzazione (sync) sono un aspetto fondamentale del sistema grafico Android. Le recinti consentono al lavoro della CPU di procedere indipendentemente dal lavoro con GPU concorrente, bloccandosi solo quando esiste una vera dipendenza.

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

L'HWC richiede che la GPU completi la scrittura dei buffer prima che questi vengano visualizzati. Le barriere di sincronizzazione vengono trasmesse tramite la pipeline grafica con i buffer e segnalano quando vengono scritti. Prima che venga visualizzato un buffer, l'HWC controlla se la recinzione di sincronizzazione ha segnalato un evento e, in questo caso, mostra il buffer.

Per ulteriori informazioni sulle barriere di sincronizzazione, consulta Integrazione di Hardware Composer.