Livelli e visualizzazioni

I livelli e i 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 determinano il modo in cui interagisce con gli altri livelli. Le proprietà dei livelli sono descritte nella tabella seguente.

Proprietà Descrizione
Posizionale Definisce dove viene visualizzato il livello. Include informazioni come le posizioni dei bordi di un livello e il suo ordine Z rispetto agli altri livelli (se devono essere davanti o dietro altri livelli).
Contenuti Definisce come devono essere presentati i contenuti visualizzati sul livello all'interno dei limiti definiti dalle proprietà di posizione. Sono incluse 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 il livello deve essere composto con altri livelli. Sono incluse informazioni come la modalità di miscelazione e un valore alfa a livello di livello per il compositing alpha.
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. Sono incluse informazioni quali la regione visibile del livello e la parte del livello aggiornata dal frame precedente.

Display

Un'altra unità di composizione importante è la visualizzazione. Un sistema può avere più display, che possono essere aggiunti o rimossi durante il normale funzionamento del sistema. I display vengono aggiunti/rimossi su richiesta dell'HWC o del framework. Il dispositivo HWC richiede che i display vengano aggiunti o rimossi quando un display esterno viene connesso o disconnesso dal dispositivo, ovvero il hotplugging. I client richiedono display virtuali, i cui contenuti vengono visualizzati in un buffer off-screen anziché su un display fisico.

Display virtuali

SurfaceFlinger supporta un display interno (integrato nello smartphone o nel tablet), display esterni (ad esempio una TV 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 su una rete. I frame generati per un display virtuale vengono scritti in una BufferQueue.

I display virtuali possono condividere lo stesso insieme di livelli del display principale (la serie di livelli) o avere un 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 con entrambi GLES e HWC. 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 viene visualizzato sullo schermo come file .mp4 sul disco. Per implementare questa funzionalità, il sistema riceve i frame composti 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 distinto (mediaserver), pertanto i buffer grafici di grandi dimensioni devono spostarsi all'interno del sistema. Per rendere la sfida più impegnativa, l'obiettivo è registrare video a 60 f/s con risoluzione completa. La chiave per farlo funzionare in modo efficiente è BufferQueue.

La classe MediaCodec consente a un'app di fornire dati come byte non elaborati nei buffer o attraverso una piattaforma. Quando screenrecord richiede l'accesso a un codificatore video, il processo screenrecord crea una BufferQueue, si connette al lato consumer e poi passa il lato producer a screenrecord come superficie.mediaserver

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 lo incarica di inviare l'output alla superficie proveniente dal processo mediaserver. In questo caso, SurfaceFlinger è il produttore di buffer anziché il consumatore.

Al termine della configurazione, screenrecord si attiva quando vengono visualizzati i dati codificati. Quando le app vengono disegnate, i relativi buffer vengono inviati a SurfaceFlinger, che li compone in un unico buffer inviato direttamente all'encoder video nel processo mediaserver. I frame completi non vengono mai visti dal processo screenrecord. All'interno, il processo mediaserver ha il proprio modo di spostare i buffer, nonché di trasmettere i dati tramite handle, riducendo al minimo il sovraccarico.

Case study: simulare display secondari

WindowManager può chiedere a SurfaceFlinger di creare un livello visibile per il quale SurfaceFlinger agisce come consumatore di BufferQueue. È anche possibile chiedere a SurfaceFlinger di creare un display virtuale per il quale SurfaceFlinger agisce come producer di BufferQueue.

Se colleghi un display virtuale a un livello visibile, viene creato un loop chiuso in cui la schermata composita viene visualizzata in una finestra. Questa finestra fa ora 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 le Opzioni sviluppatore in Impostazioni, seleziona Simula display secondari e attiva una finestra. Per vedere i display secondari in azione, usa screenrecord per acquisire l'atto di attivare il display e riprodurlo fotogramma per fotogramma.