Qui scoprirai come vengono configurati i componenti di RenderingNG e come la pipeline di rendering li attraversa.
A partire dal livello più alto, le attività di rendering sono:
- Esegui il rendering dei contenuti in pixel sullo schermo.
- Anima gli effetti visivi sui contenuti da uno stato all'altro.
- Scorri in risposta all'input.
- Inoltra l'input in modo efficiente nei posti giusti in modo che gli script per sviluppatori e altri sottosistemi possano rispondere.
I contenuti da visualizzare sono un albero di frame per ogni scheda del browser, oltre all'interfaccia del browser. Inoltre, uno stream di eventi di input non elaborati da touchscreen, mouse, tastiere e altri dispositivi hardware.
Ogni frame include:
- Stato DOM
- CSS
- Canvas
- Risorse esterne, come immagini, video, caratteri e SVG
Un frame è un documento HTML, oltre al relativo URL. Una pagina web caricata in una scheda del browser ha un frame di primo livello, frame secondari per ogni iframe incluso nel documento di primo livello e i relativi discendenti iframe ricorsivi.
Un effetto visivo è un'operazione grafica applicata a una bitmap, come scorrimento, trasformazione, clip, filtro, opacità o fusione.
Componenti dell'architettura
In RenderingNG, queste attività sono suddivise in modo logico in più fasi e componenti di codice. I componenti finiscono in vari processi, thread e subcomponenti della CPU all'interno di questi thread. Ognuna gioca un ruolo importante per raggiungere affidabilità, prestazioni scalabili ed estensibilità di tutti i contenuti web.
Struttura della pipeline di rendering
Il rendering procede in una pipeline con una serie di fasi e artefatti creati lungo il percorso. Ogni fase rappresenta un codice che esegue un'attività ben definita all'interno del rendering. Gli elementi sono strutture di dati che rappresentano input o output delle fasi.
Le fasi sono:
- Anima:modifica gli stili calcolati e muta gli alberi di proprietà nel tempo in base a schemi temporali dichiarativi.
- Stile:applica CSS al DOM e crea stili calcolati.
- Layout: determina le dimensioni e la posizione degli elementi DOM sullo schermo e crea la struttura ad albero dei frammenti immutabili.
- Pre-paint: calcola le strutture ad albero delle proprietà e invalidate eventuali elenchi di visualizzazione esistenti e riquadri di texture della GPU, a seconda dei casi.
- Scorrimento:aggiorna l'offset di scorrimento dei documenti e degli elementi DOM scorrevoli modificando le strutture ad albero delle proprietà.
- Paint:calcola un elenco di visualizzazione che descrive come rasterizzare i riquadri delle texture della GPU dal DOM.
- Commit: copia gli alberi di proprietà e l'elenco di visualizzazione nel thread del compositore.
- Esegui la suddivisione in livelli:suddividi l'elenco di visualizzazione in un elenco di livelli compositi per la rasterizzazione e l'animazione indipendenti.
- Raster, decodifica e colora i worklet:trasforma, rispettivamente, gli elenchi di visualizzazione, le immagini codificate e il codice del worklet di colorazione in riquadro di texture GPU.
- Attiva:crea un frame del compositore che rappresenti come disegnare e posizionare i riquadri della GPU sullo schermo, insieme a eventuali effetti visivi.
- Aggregate:combina i frame compositor di tutti i frame compositor visibili in un unico frame compositor globale.
- Disegna:esegui il frame del compositore aggregato sulla GPU per creare pixel sullo schermo.
Se non sono necessarie, le fasi della pipeline di rendering possono essere ignorate. Ad esempio, le animazioni di effetti visivi e lo scorrimento possono saltare il layout, pre-colorare e colorare. Questo è il motivo per cui l'animazione e lo scorrimento sono contrassegnati da punti gialli e verdi nel diagramma. Se il layout, la pre-tinta e la tinta possono essere ignorati per gli effetti visivi, possono essere eseguiti interamente nel thread del compositore e ignorare il thread principale.
Il rendering dell'interfaccia utente del browser non è illustrato direttamente qui, ma può essere considerato come una versione semplificata di questa stessa pipeline (in effetti la sua implementazione condivide gran parte del codice). Il video (anche se non mostrato direttamente) viene generalmente visualizzato con codice indipendente che decodifica i frame in riquadri di texture GPU che vengono poi collegati ai frame del compositore e al passaggio di disegno.
Struttura del processo e del thread
Processi della CPU
L'utilizzo di più processi della CPU consente di ottenere l'isolamento delle prestazioni e della sicurezza tra i siti e dallo stato del browser, nonché l'isolamento della stabilità e della sicurezza dall'hardware della GPU.
- La procedura di rendering esegue il rendering, l'animazione, lo scorrimento e inoltra l'input per una singola combinazione di sito e scheda. Esistono diversi processi di rendering.
- Il processo del browser esegue il rendering, l'animazione e inoltra l'input per l'interfaccia utente del browser (inclusa la barra degli indirizzi, i titoli delle schede e le icone) e inoltra tutto l'input rimanente al processo di rendering appropriato. Esiste un solo processo del browser.
- Il processo di visualizzazione aggrega il compositing di più processi di rendering più il processo del browser. Esegue la rasterizzazione e il disegno utilizzando la GPU. Esiste un solo processo di Viz.
Siti diversi finiscono sempre in processi di rendering diversi.
Più finestre o schede del browser dello stesso sito vengono solitamente eseguite in diversi processi di rendering, a meno che le schede non siano correlate, ad esempio se una apre l'altra. In caso di forte utilizzo della memoria su computer, Chromium potrebbe inserire più schede dello stesso sito nella stessa procedura di rendering anche se non sono correlate.
All'interno di una singola scheda del browser, i frame di siti diversi sono sempre in processi di rendering diversi tra loro, ma i frame dello stesso sito sono sempre nello stesso processo di rendering. Dal punto di vista del rendering, l'importante vantaggio di più processi di rendering è che gli iframe e le schede cross-site ottengono isolamento del rendimento tra loro. Inoltre, le origini possono attivare un isolamento ancora maggiore.
Esiste esattamente un processo Viz per tutto Chromium, poiché in genere esistono solo una GPU e uno schermo su cui disegnare.
Separare Viz in un processo dedicato è utile per la stabilità in caso di bug nei driver o nell'hardware della GPU. È inoltre utile per l'isolamento di sicurezza, il che è importante per le API GPU come Vulkan e per la sicurezza in generale.
Poiché il browser può avere molte schede e finestre, tutte con pixel dell'interfaccia utente del browser da disegnare, potresti chiederti: perché esiste esattamente un processo del browser? Il motivo è che solo una di queste è attiva alla volta; infatti, le schede del browser non visibili sono per lo più disattivate e liberano tutta la memoria della GPU. Tuttavia, le funzionalità di rendering dell'interfaccia utente del browser complesse vengono sempre più implementate anche nelle procedure di rendering (note come WebUI). Non per motivi di isolamento delle prestazioni, ma per sfruttare la facilità d'uso del motore di rendering web di Chromium.
Sui dispositivi Android meno recenti, il processo di rendering e del browser vengono condivisi quando vengono utilizzati in un componente WebView (questo non vale per Chromium su Android in generale, ma solo per WebView). In WebView, il processo del browser viene condiviso anche con l'app di incorporamento e WebView ha un solo processo di rendering.
A volte è presente anche un processo di utilità per la decodifica dei contenuti video protetti. Questa procedura non è illustrata nei diagrammi precedenti.
Thread
I thread consentono di ottenere isolamento delle prestazioni e reattività nonostante le attività lente, la parallellizzazione della pipeline e il buffering multiplo.
- Il thread principale esegue gli script, il loop di eventi di rendering, il ciclo di vita del documento, il test di hit, l'invio di eventi script e l'analisi di HTML, CSS e altri formati di dati.
- Gli helper del thread principale eseguono attività come la creazione di bitmap e blob di immagini che richiedono la codifica o la decodifica.
- Worker web esegui lo script e un loop di eventi di rendering per OffscreenCanvas.
- Il thread del compositore elabora gli eventi di input,
esegue lo scorrimento e le animazioni dei contenuti web,
calcola la stratificazione ottimale dei contenuti web,
e coordina le decodificazioni delle immagini, i worklet di pittura e le attività di rasterizzazione.
- Gli helper per i thread del compositore coordinano le attività di rasterizzazione di Viz, eseguono attività di decodifica delle immagini, worklet di pittura e raster di riserva.
- I thread di output audio, demuxer o multimediali decodificano, elaborano e sincronizzano gli stream video e audio. Ricorda che il video viene eseguito in parallelo con la pipeline di rendering principale.
La separazione del thread principale da quello del compositore è di fondamentale importanza per l'isolamento delle prestazioni dell'animazione e dello scorrimento dal lavoro del thread principale.
Esiste un solo thread principale per processo di rendering, anche se più schede o frame dello stesso sito possono finire nello stesso processo. Tuttavia, il rendimento è isolato dal lavoro svolto nelle varie API del browser. Ad esempio, la generazione di bitmap e blob di immagini nell'API Canvas viene eseguita in un thread di supporto del thread principale.
Allo stesso modo, esiste un solo thread del compositore per processo di rendering. Di solito non è un problema se ne esiste solo una, perché tutte le operazioni molto costose sul thread del compositore sono delegate ai thread worker del compositore o al processo di Viz e questo lavoro può essere fatto in parallelo con il routing dell'input, lo scorrimento o l'animazione. I thread di lavoro del compositore coordinano le attività eseguite nel processo Viz, ma l'accelerazione GPU ovunque può non riuscire per motivi non controllati da Chromium, ad esempio bug dei driver. In queste situazioni, il thread di lavoro eseguirà il lavoro in una modalità di riserva sulla CPU.
Il numero di thread worker del compositore dipende dalle funzionalità del dispositivo. Ad esempio, i computer in genere utilizzano più thread, poiché hanno più core della CPU e sono meno soggetti a limitazioni della batteria rispetto ai dispositivi mobili. Questo è un esempio di scaling up e scaling down.
L'architettura di threading del processo di rendering è un'applicazione di tre diversi modelli di ottimizzazione:
- Thread di supporto: invia le attività secondarie che richiedono molto tempo a thread aggiuntivi per mantenere il thread principale reattivo ad altre richieste simultanee. I thread di supporto del thread principale e del compositore sono ottimi esempi di questa tecnica.
- Buffer multiplo: mostra i contenuti sottoposti a rendering in precedenza durante il rendering di nuovi contenuti, per nascondere la latenza del rendering. Il thread del compositore utilizza questa tecnica.
- Parallelizzazione della pipeline:esegui la pipeline di rendering in più punti contemporaneamente. In questo modo, lo scorrimento e l'animazione possono essere rapidi; anche se è in corso un aggiornamento del rendering del thread principale, lo scorrimento e l'animazione possono essere eseguiti in parallelo.
Processo del browser
- Il thread di rendering e composizione risponde agli input nell'interfaccia utente del browser, indirizza gli altri input al processo di rendering corretto, e gestisce il layout e la visualizzazione dell'interfaccia utente del browser.
- Gli helper per i thread di rendering e composizione eseguono attività di decodifica delle immagini e raster o decodifica di riserva.
I thread di rendering e composizione del processo del browser sono simili al codice e alla funzionalità di un processo di rendering, ad eccezione del fatto che il thread principale e il thread del compositore sono combinati in uno solo. In questo caso è necessario un solo thread perché non è necessario l'isolamento del rendimento dalle attività lunghe nel thread principale, poiché non ce ne sono per progettazione.
Processo di visualizzazione
- Il thread principale GPU esegue il raster degli elenchi di visualizzazione e dei frame video nei riquadri di texture GPU, e traccia i frame del compositore sullo schermo.
- Il thread del compositore della visualizzazione aggrega e ottimizza il compositing di ogni processo di rendering, oltre al processo del browser, in un unico frame del compositore per la presentazione sullo schermo.
Raster e disegno in genere avvengono sullo stesso thread, perché entrambi si basano sulle risorse GPU ed è difficile utilizzare in modo affidabile la GPU in multi-thread (l'accesso multi-thread alla GPU più semplice è uno dei motivi per lo sviluppo del nuovo standard Vulkan). In Android WebView è presente un thread di rendering separato a livello di sistema operativo per il disegno a causa del modo in cui le visualizzazioni WebView sono incorporate in un'app nativa. È probabile che altre piattaforme avranno un thread simile in futuro.
Il compositore display si trova su un thread diverso perché deve essere sempre reattivo e non bloccarsi su qualsiasi possibile origine di rallentamento sul thread principale della GPU. Una causa del rallentamento nel thread principale della GPU sono le chiamate a codice non Chromium, ad esempio driver GPU specifici del fornitore, che potrebbero essere lenti in modi difficili da prevedere.
Struttura del componente
All'interno di ogni thread principale o compositore del processo di rendering, esistono componenti software logici che interagiscono tra loro in modo strutturato.
Componenti del thread principale del processo di rendering
Nel Renderer Blink:
- Il frammento ad albero di frame locali rappresenta l'albero dei frame locali e il DOM all'interno dei frame.
- Il componente API DOM e Canvas contiene le implementazioni di tutte queste API.
- Il runner del ciclo di vita del documento esegue i passaggi della pipeline di rendering fino al passaggio di commit incluso.
- Il componente Test di hit e invio di eventi di input esegue test di hit per scoprire quale elemento DOM è scelto come target da un evento ed esegue gli algoritmi di invio degli eventi di input e i comportamenti predefiniti.
Lo scheduler e runner del loop degli eventi di rendering decidono cosa eseguire nel loop degli eventi e quando. Pianifica il rendering in modo che avvenga a una cadenza corrispondente a quella del display del dispositivo.
I frammenti dell'albero del frame locale sono un po' complicati. Ricorda che una struttura di frame è la pagina principale e i suoi iframe secondari, in modo ricorsivo. Un frame è locale per un processo di rendering se viene visualizzato in quel processo, in caso contrario è remoto.
Puoi immaginare di colorare i fotogrammi in base al processo di rendering. Nell'immagine precedente, i cerchi verdi sono tutti i frame di un processo di rendering; quelli arancioni sono in un secondo e quello blu in un terzo.
Un frammento di albero di frame locale è un componente connesso dello stesso colore in un albero di frame. Nell'immagine sono presenti quattro strutture di frame locali: due per il sito A, uno per il sito B e uno per il sito C. Ogni albero di frame locale ha il proprio componente di rendering Blink. Il renderer Blink di una struttura ad albero di frame locale può o meno trovarsi nella stessa procedura di rendering di altre strutture ad albero di frame locali. Viene determinato dal modo in cui vengono selezionate le procedure di rendering, come descritto in precedenza.
Struttura dei thread del compositore del processo di rendering
I componenti del compositore del processo di rendering includono:
- Un gestore dei dati che gestisce un elenco di livelli compositi, elenchi di visualizzazione e alberi di proprietà.
- Un runner del ciclo di vita che esegue le animazioni, lo scorrimento, la composizione, il rasterizzazione, la decodifica e l'attivazione dei passaggi della pipeline di rendering. Ricorda che l'animazione e lo scorrimento possono verificarsi sia nel thread principale che nel compositore.
- Un handler di test di input e hit esegue l'elaborazione dell'input e i test di hit alla risoluzione dei livelli compositi per determinare se è possibile eseguire gesti di scorrimento nel thread del compositore e quale processo di rendering deve essere scelto come target per i test di hit.
Architettura di esempio nella pratica
In questo esempio sono presenti tre schede:
Scheda 1: foo.com
<html>
<iframe id=one src="foo.com/other-url"></iframe>
<iframe id=two src="bar.com"></iframe>
</html>
Scheda 2: bar.com
<html>
…
</html>
Scheda 3: baz.com
html
<html>
…
</html>
La struttura dei processi, dei thread e dei componenti per queste schede è la seguente:
Vediamo un esempio per ognuna delle quattro attività principali del rendering. Un promemoria:
- Eseguire il rendering dei contenuti in pixel sullo schermo.
- Animate degli effetti visivi sui contenuti da uno stato all'altro.
- Scorri in risposta all'input.
- Inoltra l'input in modo efficiente nei posti giusti in modo che gli script degli sviluppatori e altri sottosistemi possano rispondere.
Per eseguire il rendering del DOM modificato per la prima scheda:
- Uno script sviluppatore modifica il DOM nel processo di rendering per foo.com.
- Il motore di rendering Blink comunica al compositore che deve essere eseguito un rendering.
- Il compositore comunica a Viz che deve essere eseguito un rendering.
- Viz segnala l'inizio del rendering al compositore.
- Il compositore inoltra l'indicatore di inizio al renderer Blink.
- Il runner del loop di eventi del thread principale esegue il ciclo di vita del documento.
- Il thread principale invia il risultato al thread del compositore.
- Il runner del loop di eventi del compositore esegue il ciclo di vita del compositing.
- Eventuali attività di raster vengono inviate a Viz per raster (spesso ce ne sono più di una).
- Viz rasterizza i contenuti sulla GPU.
- Viz conferma il completamento dell'attività di rasterizzazione. Nota: spesso Chromium non attende il completamento del raster, ma utilizza un elemento chiamato token di sincronizzazione che deve essere risolto dalle attività di raster prima dell'esecuzione del passaggio 15.
- A Viz viene inviato un frame del compositore.
- Viz aggrega i frame del compositore per il processo di rendering di foo.com, il processo di rendering dell'iframe di bar.com e l'interfaccia utente del browser.
- Viz pianifica un sorteggio.
- Viz disegna il frame del compositore aggregato sullo schermo.
Per animare una transizione di trasformazione CSS nella seconda scheda:
- Il thread del compositore per il processo di rendering di bar.com esegue il ciclo di eventi del compositore di un'animazione modificando gli alberi di proprietà esistenti. Viene quindi eseguito di nuovo il ciclo di vita del compositore. (Potrebbero verificarsi attività di rasterizzazione e decodifica, ma non sono mostrate qui).
- A Viz viene inviato un frame del compositore.
- Viz aggrega i frame del compositore per il processo di rendering di foo.com, il processo di rendering di bar.com e l'interfaccia utente del browser.
- Visualizzazione pianifica una estrazione.
- Viz disegna sullo schermo il frame del compositore aggregato.
Per scorrere la pagina web nella terza scheda:
- Una sequenza di eventi
input
(mouse, tocco o tastiera) arriva al processo del browser. - Ogni evento viene indirizzato al thread del compositore del processo di rendering di baz.com.
- Il compositore determina se il thread principale deve essere a conoscenza dell'evento.
- L'evento viene inviato, se necessario, al thread principale.
- Il thread principale attiva i listener di eventi
input
(pointerdown
,touchstar
,pointermove
,touchmove
owheel
) per verificare se i listener chiamerannopreventDefault
sull'evento. - Il thread principale restituisce se
preventDefault
è stato chiamato al compositore. - In caso contrario, l'evento di input viene inviato nuovamente al processo del browser.
- Il processo del browser lo converte in un gesto di scorrimento combinandolo con altri eventi recenti.
- Il gesto di scorrimento viene inviato di nuovo al thread del compositore del processo di rendering di baz.com,
- Lo scorrimento viene applicato lì e il thread del compositore per il processo di rendering di bar.com attiva un'animazione nel suo loop di eventi del compositore.
In questo modo, l'offset dello scorrimento viene modificato nelle strutture ad albero delle proprietà e il ciclo di vita del compositore viene eseguito di nuovo.
Inoltre, indica al thread principale di attivare un evento
scroll
(non mostrato qui). - A Viz viene inviato un frame del compositore.
- Viz aggrega i frame del compositore per il processo di rendering di foo.com, il processo di rendering di bar.com e l'interfaccia utente del browser.
- Visualizzazione pianifica una estrazione.
- Viz disegna sullo schermo il frame del compositore aggregato.
Per instradare un evento click
su un link ipertestuale nell'iframe 2 della scheda 1:
- Nel processo del browser viene generato un evento
input
(mouse, tocco o tastiera). Esegue un test di hit approssimativo per determinare che il processo di rendering dell'iframe di bar.com deve ricevere il clic e lo invia lì. - Il thread del compositore per bar.com inoltra l'evento
click
al thread principale per bar.com e pianifica un'attività di loop di eventi di rendering per elaborarlo. - L'elaboratore di eventi di input per i test di hit del thread principale di bar.com determina su quale elemento DOM nell'iframe è stato fatto clic e attiva un evento
click
da osservare per gli script. Non sentendopreventDefault
, si apre il link ipertestuale. - Al caricamento della pagina di destinazione del link ipertestuale, viene visualizzato il nuovo stato, con passaggi simili a quelli dell'esempio precedente "Eseguire il rendering del DOM modificato". (Queste modifiche successive non sono mostrate qui).
Concetti chiave
Ricordare e interiorizzare il funzionamento del rendering può richiedere molto tempo.
Il risultato più importante è che la pipeline di rendering, grazie a un'attenta modularizzazione e a un'attenzione ai dettagli, è stata suddivisa in una serie di componenti indipendenti. Questi componenti sono stati quindi suddivisi tra processi e thread paralleli per massimizzare le prestazioni scalabili e le opportunità di estensibilità.
Ogni componente svolge un ruolo fondamentale per garantire il rendimento e le funzionalità delle moderne app web.
Continua a leggere per scoprire le strutture chiave dei dati, importante per il renderingNG quanto i componenti di codice.
Illustrazioni di Una Kravets.