Livelli e display sono due primitive che rappresentano il lavoro di composizione e le interazioni con l'hardware del display.
Livelli
Un livello è l'unità di composizione più importante. Un livello è una combinazione di una superficie e un'istanza di SurfaceControl
.
Ogni livello ha un insieme di proprietà che definiscono il modo in cui interagisce con gli altri livelli. Le proprietà
del livello sono descritte nella tabella seguente:
Proprietà | Descrizione |
---|---|
Posizionale | Definisce dove viene visualizzato il livello sul display. Include informazioni come le posizioni dei bordi di un livello e il suo ordine Z rispetto ad altri livelli (se deve trovarsi davanti o dietro ad altri livelli). |
Contenuti | Definisce la modalità di presentazione dei contenuti visualizzati nel livello all'interno dei limiti definiti dalle proprietà di posizionamento. Include informazioni come il ritaglio (per espandere una parte dei contenuti in modo da riempire i limiti del livello) e la trasformazione (per mostrare contenuti ruotati o capovolti). |
Composizione | Definisce in che modo lo strato deve essere composto con gli altri strati. Include informazioni come la modalità di fusione e un valore alfa a livello di livello per la composizione alfa. |
Ottimizzazione | Fornisce informazioni non strettamente necessarie per comporre correttamente il livello, ma che possono essere utilizzate dal dispositivo Hardware Composer (HWC) per ottimizzare il modo in cui esegue la composizione. Include informazioni come la regione visibile del livello e la parte del livello che è stata aggiornata rispetto al frame precedente. |
Display
Un display è un'altra unità di composizione importante. Un sistema può avere più display e i display possono essere aggiunti o rimossi durante le normali operazioni del sistema. I display vengono aggiunti o rimossi su richiesta dell'HWC o del framework. Il dispositivo HWC richiede l'aggiunta o la rimozione dei display quando un display esterno viene connesso o disconnesso dal dispositivo, operazione chiamata hotplugging. I client richiedono display virtuali, i cui contenuti vengono visualizzati in un buffer fuori schermo anziché su un display fisico.
Display virtuali
SurfaceFlinger supporta un display interno (integrato nello smartphone o nel tablet), display esterni (ad esempio una televisione collegata tramite HDMI) e uno o più display virtuali che rendono disponibile l'output composito all'interno del sistema. I display virtuali possono essere utilizzati per registrare lo schermo o inviarlo tramite una rete. I frame generati per un display virtuale vengono scritti in un BufferQueue.
I display virtuali possono condividere lo stesso insieme di livelli del display principale (la pila di livelli) o avere il proprio insieme. Non esiste VSync per un display virtuale, quindi VSync per il display interno attiva la composizione per tutti i display.
Nelle implementazioni HWC che li supportano, i display virtuali possono essere composti con OpenGL ES (GLES), HWC o entrambi. Nelle implementazioni non supportate, i display virtuali vengono sempre composti utilizzando GLES.
Case study: screenrecord
Il comando screenrecord
consente all'utente di
registrare tutto ciò che appare sullo schermo come file MP4 su
disco. Per implementare questa funzionalità, il sistema riceve i frame compositi da
SurfaceFlinger, li scrive nel codificatore video e poi scrive i dati video codificati
in un file. I codec video sono gestiti da un processo separato
(mediaserver
), quindi i buffer grafici di grandi dimensioni devono spostarsi nel
sistema. Per rendere la sfida più impegnativa, l'obiettivo è registrare video a 60 fps alla massima risoluzione. La chiave per far funzionare tutto in modo efficiente è BufferQueue.
La classe MediaCodec
consente a un'app di fornire dati come byte non elaborati nei buffer
o tramite una superficie. Quando screenrecord
richiede l'accesso a un codificatore video, il processo mediaserver
crea una BufferQueue, si connette al lato consumer e poi passa il lato producer a screenrecord
come superficie.
L'utilità screenrecord
chiede quindi a SurfaceFlinger di creare un
display virtuale che rispecchi il display principale (ovvero, che abbia tutti gli stessi
livelli) e gli chiede di inviare l'output alla superficie proveniente dal
processo mediaserver
. In questo caso, SurfaceFlinger è il produttore
dei buffer anziché il consumer.
Una volta completata la configurazione, screenrecord
viene attivato quando
vengono visualizzati i dati codificati. Quando le app disegnano, i buffer vengono inviati a SurfaceFlinger, che li compone in un unico buffer inviato direttamente al codificatore video nel processo mediaserver
. I frame completi non vengono mai
visualizzati dal processo screenrecord
. Internamente, il processo mediaserver
ha un proprio modo di spostare i buffer che passa anche i dati per handle, riducendo al minimo l'overhead.
Case study: simula display secondari
WindowManager può chiedere a SurfaceFlinger di creare un livello visibile per il quale SurfaceFlinger funge da consumer BufferQueue. È anche possibile chiedere a SurfaceFlinger di creare un display virtuale, per il quale SurfaceFlinger funge da produttore di BufferQueue.
Se colleghi un display virtuale a un livello visibile, viene creato un ciclo chiuso
in cui la schermata composita viene visualizzata in una finestra. Questa finestra ora fa parte dell'output composito, quindi al successivo aggiornamento l'immagine composita all'interno della finestra mostra anche i contenuti della finestra. Per vedere questa funzionalità in azione, attiva
Opzioni sviluppatore in Impostazioni, seleziona
Simula display secondari e attiva una finestra. Per vedere
i display secondari in azione, utilizza screenrecord
per acquisire l'atto
di attivazione del display, quindi riproducilo fotogramma per fotogramma.