Livelli e display

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.