Strutture dei dati chiave e relativi ruoli in RenderingNG

Chris Harrelson
Chris Harrelson
Daniel Cheng
Daniel Cheng
Philip Rogers
Philip Rogers
Koji Ishi
Koji Ishi
Ian Kilpatrick
Ian Kilpatrick
Kyle Charbonneau
Kyle Charbonneau

I post precedenti di questa serie fornivano una panoramica degli obiettivi, delle proprietà chiave e dei componenti di alto livello dell'architettura RenderingNG. Esaminiamo ora le strutture di dati chiave che sono input e output per la pipeline di rendering.

Queste strutture di dati sono:

  • Alberi di frame, composti da nodi locali e remoti, che rappresentano i documenti web in cui si trova il processo di rendering e il renderer Blink.
  • L'albero dei frammenti immutabili rappresenta l'output e l'input dell'algoritmo dei vincoli di layout.
  • Le alberi delle proprietà, che rappresentano le gerarchie di trasformazione, clip, effetto e scorrimento di un documento web, e vengono utilizzate in tutta la pipeline.
  • Elenchi di visualizzazione e blocchi di colori sono gli input per gli algoritmi di raster e di stratificazione.
  • I frame compositi incapsulano superfici, superfici di rendering e riquadri di texture GPU utilizzati per disegnare utilizzando la GPU.

Prima di esplorare queste strutture di dati, voglio mostrarti questo semplice esempio di cosa si basa su una del post precedente. Userò questo esempio alcune volte in questo post, per vedere come le strutture dei dati si applicano.

<html>
  <div style="overflow: hidden; width: 100px; height: 100px;">
    <iframe style="filter: blur(3px);
      transform: rotateZ(1deg);
      width: 100px; height: 300px"
      id="one" src="foo.com/etc"></iframe>
  </div>
  <iframe style="top:200px;
    transform: scale(1.1) translateX(200px)"
    id="two" src="bar.com"></iframe>
</html>

Alberi incorniciati

A volte Chrome può scegliere di eseguire il rendering di un frame multiorigine in un processo di rendering diverso dal frame principale.

Nell'esempio dell'introduzione, ci sono tre frame totali:

Un frame principale foo.com, contenente due iframe.

Con l'isolamento dei siti, Chromium utilizza due processi di rendering per eseguire il rendering della pagina web. Ogni processo di rendering ha la propria rappresentazione della struttura ad albero dei frame per quella pagina web:

Due strutture di frame che rappresentano i due processi di rendering.

Un frame visualizzato in un processo diverso viene rappresentato come frame remoto. Un frame remoto contiene le informazioni minime necessarie per agire da segnaposto nel rendering, ad esempio le sue dimensioni. Il frame remoto altrimenti non contiene alcuna informazione necessaria per il rendering dei suoi contenuti effettivi.

Al contrario, un frame locale rappresenta un frame che attraversi la pipeline di rendering standard descritta nei post precedenti. Il frame locale contiene tutte le informazioni necessarie per trasformare i dati del frame (ad esempio l'albero DOM e i dati di stile) in un elemento di cui è possibile eseguire il rendering e la visualizzazione.

La pipeline di rendering opera in base alla granularità di un frammento di albero di frame locale. Considera un esempio più complicato con foo.com come frame principale:

<iframe src="bar.com"></iframe>

E il seguente frame secondario bar.com:

<iframe src="foo.com/etc"></iframe>

Anche se esistono ancora due renderer, ora esistono tre frammenti di albero di frame locali, di cui due nel processo di rendering per foo.com e uno nel processo di rendering per bar.com:

Una rappresentazione dei due rendering e di tre frammenti di albero di frame.

Per produrre un frame compositore per la pagina web, Viz richiede contemporaneamente un frame del compositor dal frame principale di ciascuna delle tre strutture di frame locali e poi li aggrega. (vedi anche la sezione sui frame compositori più avanti in questo post).

Il frame principale foo.com e il frame secondario foo.com/other-page fanno parte della stessa struttura ad albero e visualizzati nello stesso processo. Tuttavia, i due frame hanno ancora cicli di vita dei documenti indipendenti, in quanto fanno parte di diversi frammenti di albero di frame locali. Per questo motivo non è possibile generare un frame compositore per entrambi in un aggiornamento. Il processo di rendering non dispone di informazioni sufficienti per comporre il frame del compositore generato per foo.com/other-page direttamente nel frame del compositore per il frame principale foo.com. Ad esempio, il frame principale bar.com out-of-process potrebbe influire sulla visualizzazione dell'iframe foo.com/other-url, trasformando l'iframe con CSS o occultando parti dell'iframe con altri elementi nel DOM.

La struttura a cascata dell'aggiornamento delle proprietà visive

Le proprietà visive, come il fattore di scala del dispositivo e le dimensioni dell'area visibile, influiscono sull'output visualizzato e devono essere sincronizzate tra i frammenti dell'albero dei frame locali. Alla radice di ogni frammento di albero di frame locali è associato un oggetto widget. Gli aggiornamenti delle proprietà visive passano al widget del frame principale prima di passare ai widget rimanenti dall'alto verso il basso. Ad esempio, quando le dimensioni dell'area visibile cambiano:

Diagramma del processo spiegato nel testo precedente.

Questo processo non è istantaneo, pertanto le proprietà visive replicate includono anche un token di sincronizzazione. Il compositor di Viz utilizza questo token di sincronizzazione per attendere che tutti i frammenti della struttura di frame locali inviino un frame del compositore con il token di sincronizzazione corrente. Questo processo evita di mescolare frame compositori con proprietà visive diverse.

L'albero dei frammenti immutabili

L'albero dei frammenti immutabili è l'output della fase di layout della pipeline di rendering. Rappresenta la posizione e la dimensione di tutti gli elementi sulla pagina (senza trasformazioni applicate).

Rappresentazione dei frammenti in ogni albero, con un frammento contrassegnato come layout necessario.

Ogni frammento rappresenta una parte di un elemento DOM. In genere è presente un solo frammento per elemento, ma può essercene di più se è suddiviso in pagine diverse durante la stampa o in colonne in un contesto a più colonne.

Dopo il layout, ogni frammento diventa immutabile e non viene mai modificato. Inoltre, applichiamo anche alcune limitazioni aggiuntive. Cosa non facciamo:

  • Consenti eventuali riferimenti "in alto" nella struttura ad albero. Un elemento secondario non può avere un puntatore al relativo elemento padre.
  • i dati del "bolle" più in basso nell'albero (un elemento secondario legge solo le informazioni dagli elementi figlio, non da quello padre).

Queste limitazioni ci consentono di riutilizzare un frammento per un layout successivo. Senza queste restrizioni avremmo spesso bisogno di rigenerare l'intero albero, il che è costoso.

La maggior parte dei layout è in genere aggiornamenti incrementali, ad esempio un'app web che aggiorna una piccola parte dell'interfaccia utente in risposta al clic dell'utente su un elemento. Idealmente, il layout dovrebbe funzionare solo in modo proporzionale a ciò che effettivamente è cambiato sullo schermo. Possiamo ottenere questo risultato riutilizzando il maggior numero possibile di parti dell'albero precedente. Questo significa (in genere) dobbiamo solo ricostruire la spina dorsale dell'albero.

In futuro, questo design immutabile ci consentirà di fare cose interessanti come far passare l'albero dei frammenti immutabili oltre i limiti dei thread, se necessario, (per eseguire fasi successive su un thread diverso), generare più alberi per un'animazione del layout fluida o eseguire layout speculativi paralleli. Inoltre, ci offre il potenziale del layout multi-threading stesso.

Elementi con frammenti incorporati

I contenuti in linea (prevalentemente con stile) utilizzano una rappresentazione leggermente diversa. Anziché una struttura ad albero con caselle e puntatori, rapiamo i contenuti in linea in un elenco semplice che rappresenta l'albero. Il vantaggio principale è che una rappresentazione a elenco semplice per i dati in linea è veloce, utile per l'ispezione o l'esecuzione di query sulle strutture di dati in linea e la memoria è efficiente. Questo è estremamente importante per le prestazioni del rendering web, poiché il rendering del testo è molto complesso e può diventare facilmente la parte più lenta della pipeline, a meno che non sia altamente ottimizzato.

Una nota storica interessante è che questo approccio è molto simile al modo in cui Internet Explorer rappresentava in precedenza il suo DOM, in quanto era stato realizzato inizialmente in modo simile a un editor di testo.

L'elenco semplice viene creato per ogni contesto di formattazione in linea nell'ordine di una ricerca approfondita in base al sottoalbero del layout incorporato. Ogni voce nell'elenco è una tupla di (oggetto, numero di discendenti). Ad esempio, considera questo DOM:

<div style="width: 0;">
  <span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>

Tieni presente che la proprietà width è impostata su 0 in modo che la linea va a capo tra "Ciao" e "qui". Quando il contesto di formattazione in linea per questa situazione è rappresentato da una struttura ad albero, l'aspetto è il seguente:

{
  "Line box": {
    "Box <span>": {
      "Text": "Hi"
    }
  },
  "Line box": {
    "Box <b>": {
      "Text": "There"
    }
  },
  {
    "Text": "."
  }
}

L'elenco semplice ha il seguente aspetto:

  • (Riquadro a linee, 2)
  • (Riquadro <span>, 1)
  • (Testo "Ciao", 0)
  • (Riquadro a linee, 3)
  • (Riquadro <b>, 1)
  • (Testo "qui", 0)
  • (Testo ".", 0)

Esistono molti consumer di questa struttura di dati: le API di accessibilità e le API geometriche come getClientRects e contenteditable. Ognuno ha requisiti diversi. Questi componenti accedono alla struttura di dati piatti tramite un cursore di convenienza.

Il cursor ha API come MoveToNext, MoveToNextLine, CursorForChildren. Questa rappresentazione del cursore è molto efficace per i contenuti di testo, per diversi motivi:

  • L'iterazione nell'ordine di ricerca Approfondimento è molto veloce. Questo viene usato molto spesso perché è simile ai movimenti del cursore del testo. Poiché si tratta di un elenco semplice, la ricerca approfondita in primo piano incrementa solo l'offset dell'array, fornendo iterazioni rapide e località della memoria.
  • Offre una ricerca completa, necessaria quando, ad esempio, si dipinge di sfondo di caselle lineari e in linea.
  • Conoscere il numero di discendenti consente di passare rapidamente al gemello successivo (incrementa semplicemente l'offset dell'array di quel numero).

Alberi della proprietà

Come sai, il DOM è una struttura ad albero di elementi (più nodi di testo) e il CSS può applicare vari stili agli elementi.

Esistono principalmente quattro tipi di effetti:

  • Layout: input per l'algoritmo dei vincoli di layout.
  • Vernice: come colorare e rasterare l'elemento (ma non i suoi discendenti).
  • Visibili: effetti raster/disegno applicati al sottoalbero del DOM, ad esempio trasformazioni, filtri e clip.
  • Scorrimento: ritaglio degli angoli allineati e arrotondati all'asse e scorrimento del sottoalbero contenuto.

Gli alberi della proprietà sono strutture di dati che spiegano come gli effetti visivi e di scorrimento vengono applicati agli elementi DOM. Consentono di rispondere a domande quali: in base alle dimensioni e alla posizione del layout, rispetto allo schermo si trova un determinato elemento DOM? Quale sequenza di operazioni della GPU si dovrebbe usare per applicare effetti visivi e di scorrimento?

Gli effetti visivi e di scorrimento sul web sono molto complicati nel loro pieno splendore. Di conseguenza, la cosa più importante che gli alberi delle proprietà è tradurre questa complessità in un'unica struttura di dati che ne rappresenti con precisione la struttura e il significato, rimuovendo allo stesso tempo il resto della complessità del DOM e del CSS. In questo modo, possiamo implementare gli algoritmi per la composizione e lo scorrimento con maggiore sicurezza. In particolare:

  • La geometria potenzialmente soggetta a errori e altri calcoli possono essere centralizzati in un'unica posizione.
  • La complessità di creazione e aggiornamento degli alberi delle proprietà viene isolata in una singola fase della pipeline di rendering.
  • È molto più semplice e veloce inviare gli alberi delle proprietà a thread e processi diversi rispetto allo stato DOM completo, consentendo così di utilizzarli per molti casi d'uso.
  • Maggiore è il numero di casi d'uso, maggiori sono i vantaggi che possiamo ottenere dalla memorizzazione nella cache della geometria basata sulla parte superiore, in quanto possono riutilizzare le reciproche cache.

RenderingNG utilizza gli alberi di proprietà per molti scopi, tra cui:

  • Separazione della composizione dal disegno e dal compositing dal thread principale.
  • Individuazione di una strategia di compositing / disegno ottimale.
  • Misurazione della geometria di IntersectionObserver.
  • Evitare il lavoro per gli elementi fuori schermo e i riquadri di texture GPU.
  • Annullamento accurato e efficiente di paint e raster.
  • Misurazione della variazione del layout e della più grande visualizzazione con contenuti nei Segnali web essenziali.

Ogni documento web ha quattro strutture di proprietà distinte: trasformazione, clip, effetto e scorrimento.(*) L'albero di trasformazione rappresenta le trasformazioni e lo scorrimento CSS. (una trasformazione di scorrimento è rappresentata come una matrice di trasformazione 2D). L'albero dei clip rappresenta i clip extra. La struttura ad albero degli effetti rappresenta tutti gli altri effetti visivi: opacità, filtri, maschere, modalità di fusione e altri tipi di clip, ad esempio il percorso del clip. L'albero di scorrimento rappresenta le informazioni sullo scorrimento, ad esempio il modo in cui scorre insieme la catena; è necessario per eseguire lo scorrimento sul thread del compositor. Ogni nodo in una struttura ad albero delle proprietà rappresenta un effetto visivo o di scorrimento applicato da un elemento DOM. Se ci sono più effetti, in ogni struttura potrebbe esserci più di un nodo della struttura ad albero per lo stesso elemento.

La topologia di ogni albero è come una rappresentazione sparsa del DOM. Ad esempio, se ci sono tre elementi DOM con clip overflow, ci saranno tre nodi dell'albero di clip e la struttura dell'albero dei clip seguirà la relazione del blocco contenitore tra i clip overflow. Gli alberi sono collegati anche tra loro. Questi link indicano la gerarchia DOM relativa e quindi l'ordine di applicazione dei nodi. Ad esempio, se una trasformazione su un elemento DOM si trova al di sotto di un altro elemento DOM con un filtro, la trasformazione viene applicata prima del filtro.

Ogni elemento DOM ha uno stato della struttura ad albero delle proprietà, ovvero una tupla a 4 tuple (trasformazione, clip, effetto, scorrimento) che indica il clip del predecessore più prossimo, la trasformazione e i nodi della struttura ad albero degli effetti che hanno effetto su quell'elemento. Si tratta di una procedura molto pratica, perché con queste informazioni conosciamo esattamente l'elenco di clip, trasformazioni ed effetti applicabili a quell'elemento e in quale ordine. che ci indica dove si trova sullo schermo e come disegnarlo.

Esempio

(fonte)

<html>
  <div style="overflow: scroll; width: 100px; height: 100px;">
    <iframe style="filter: blur(3px);
      transform: rotateZ(1deg);
      width: 100px; height: 300px"
  id="one" srcdoc="iframe one"></iframe>
  </div>
  <iframe style="top:200px;
      transform: scale(1.1) translateX(200px)" id=two
      srcdoc="iframe two"></iframe>
</html>

Per l'esempio precedente (leggermente diverso da quello dell'introduzione), ecco gli elementi chiave degli alberi della proprietà generati:

Un esempio dei vari elementi nell&#39;albero delle proprietà.

Visualizza elenchi e colora blocchi

Un elemento di visualizzazione contiene comandi di disegno di basso livello (vedi qui) che possono essere rasterizzati con Skia. Gli elementi da visualizzare sono in genere semplici, con pochi comandi di disegno, ad esempio per disegnare un bordo o uno sfondo. L'albero di disegno viene iterato sull'albero del layout e sui frammenti associati seguendo l'ordine di pittura del CSS per produrre un elenco di elementi di visualizzazione.

Ad esempio:

Un riquadro blu con la parola &quot;Hello world&quot; all&#39;interno di un rettangolo verde.

<div id="green" style="background:green; width:80px;">
    Hello world
</div>
<div id="blue" style="width:100px;
  height:100px; background:blue;
  position:absolute;
  top:0; left:0; z-index:-1;">
</div>

Questo codice HTML e CSS produrrà il seguente elenco di visualizzazione, in cui ogni cella è un elemento di visualizzazione:

Sfondo della vista Sfondo #blue Sfondo #green Testo in linea di #green
drawRect con dimensione 800 x 600 e colore bianco. drawRect con dimensione 100 x 100 nella posizione 0,0 e colore blu. drawRect con dimensione 80 x 18 nella posizione 8,8 e colore verde. drawTextBlob con posizione 8,8 e testo "Hello world".

L'elenco degli elementi da visualizzare viene ordinato in primo piano. Nell'esempio precedente, il div verde si trova prima del div blu nell'ordine DOM, ma l'ordine di visualizzazione CSS richiede che il div blu z-index negativo venga visualizzato prima (passaggio 3) del div verde (passaggio 4.1). Gli elementi visualizzati corrispondono approssimativamente ai passaggi atomici della specifica dell'ordine di disegno del CSS. Un singolo elemento DOM può generare diversi elementi di visualizzazione, ad esempio perché #green ha un elemento display per lo sfondo e un altro per il testo in linea. Questa granularità è importante per rappresentare l'intera complessità della specifica dell'ordine di disegno del CSS, ad esempio l'interleaving creato da un margine negativo:

Un rettangolo verde con un riquadro grigio parzialmente sovrapposto e le parole &quot;Hello world&quot;.

<div id="green" style="background:green; width:80px;">
    Hello world
</div>
<div id="gray" style="width:35px; height:20px;
  background:gray;margin-top:-10px;"></div>

In questo modo viene generato il seguente elenco di visualizzazione, in cui ogni cella è un elemento di visualizzazione:

Sfondo della vista Sfondo #green Sfondo #gray Testo in linea di #green
drawRect con dimensione 800 x 600 e colore bianco. drawRect con dimensione 80 x 18 nella posizione 8,8 e colore verde. drawRect con dimensione 35 x 20 nella posizione 8,16 e colore grigio. drawTextBlob con posizione 8,8 e testo "Hello world".

L'elenco degli elementi visualizzati viene archiviato e riutilizzato dagli aggiornamenti successivi. Se un oggetto del layout non è cambiato durante il percorso di colorazione dell'albero, gli elementi di visualizzazione vengono copiati dall'elenco precedente. Un'ulteriore ottimizzazione si basa su una proprietà della specifica dell'ordine di disegno del CSS: i contesti di impilamento vengono dipinti a livello atomico. Se nessun oggetto del layout è stato modificato all'interno di un contesto di impilamento, la passeggiata nell'albero di disegno ignora il contesto di impilamento e copia l'intera sequenza di elementi di visualizzazione dall'elenco precedente.

Lo stato attuale della struttura ad albero delle proprietà viene mantenuto durante il percorso dell'albero di disegno e l'elenco degli elementi di visualizzazione viene raggruppato in "blocchi" di elementi di visualizzazione che condividono lo stesso stato della struttura ad albero. come dimostrato nell'esempio seguente:

Un riquadro rosa con un riquadro arancione inclinato.

<div id="scroll" style="background:pink; width:100px;
   height:100px; overflow:scroll;
   position:absolute; top:0; left:0;">
    Hello world
    <div id="orange" style="width:75px; height:200px;
      background:orange; transform:rotateZ(25deg);">
        I'm falling
    </div>
</div>

In questo modo viene generato il seguente elenco di visualizzazione, in cui ogni cella è un elemento di visualizzazione:

Sfondo della vista Sfondo #scroll Testo in linea di #scroll Sfondo #orange Testo in linea di #orange
drawRect con dimensione 800 x 600 e colore bianco. drawRect con dimensione 100 x 100 nella posizione 0,0 e colore rosa. drawTextBlob con posizione 0,0 e testo "Hello world". drawRect con dimensione 75 x 200 nella posizione 0,0 e colore arancione. drawTextBlob con posizione 0,0 e testo "Sto scendendo".

A questo punto, i blocchi dell'albero delle proprietà della trasformazione e del disegno sarebbero (semplificati per brevità):

Un&#39;immagine della tabella precedente, le prime due celle nel blocco 1, la terza nel blocco 2 e le ultime due celle nel blocco 3.

L'elenco ordinato di blocchi di colorazione, ovvero gruppi di elementi display e uno stato della struttura ad albero delle proprietà, è gli input per la fase di stratificazione della pipeline di rendering. L'intero elenco di blocchi di pittura poteva essere unito in un unico livello composito e rasterizzazione insieme, ma ciò richiederebbe una rasterizzazione costosa ogni volta che l'utente scorreva la pagina. Potresti creare uno strato composito per ogni blocco di vernice e rasterizzarlo singolarmente per evitare la ri-rasterizzazione, ma che esaurirebbe rapidamente la memoria della GPU. La fase di stratificazione deve scendere a compromessi tra la memoria GPU e ridurre i costi quando le cose cambiano. Un buon approccio generale è quello di unire i blocchi per impostazione predefinita, e non unire blocchi di Paint con stati della struttura ad albero delle proprietà che si prevede vengano modificati nel thread del compositor, ad esempio con lo scorrimento di tipo compositor-thread o con le animazioni di trasformazione del compositor-thread.

L'esempio precedente dovrebbe idealmente produrre due strati compositi:

  • Un livello composito 800 x 600 contenente i comandi di disegno:
    1. drawRect con dimensione 800 x 600 e colore bianco
    2. drawRect con dimensione 100 x 100 nella posizione 0,0 e colore rosa
  • Un livello composito 144 x 224 contenente i comandi di disegno:
    1. drawTextBlob con posizione 0,0 e testo "Hello world"
    2. traduci 0,18
    3. rotateZ(25deg)
    4. drawRect con dimensione 75 x 200 nella posizione 0,0 e colore arancione
    5. drawTextBlob con posizione 0,0 e testo "Sto scendendo"

Se l'utente scorre #scroll, il secondo livello composto viene spostato, ma non è necessaria alcuna rasterizzazione.

Per questo esempio, qui, della sezione precedente sugli alberi delle proprietà, ci sono sei blocchi di colorazione. Insieme agli stati della struttura ad albero delle proprietà (trasformazione, clip, effetto, scorrimento), sono:

  • Sfondo del documento: scorrimento del documento, graffetta del documento, radice, scorrimento del documento.
  • Angolo orizzontale, verticale e di scorrimento per i div (tre blocchi di testo separati): scorrimento documento, clip documento, sfocatura #one, scorrimento documento.
  • Iframe #one: rotazione di #one, clip di scorrimento extra, sfocatura #one, scorrimento del div.
  • Iframe #two: scala #two, clip del documento, radice, scorrimento del documento.

Fotogrammi compositor: superfici, superfici di rendering e riquadri di texture GPU

Come discusso nel post precedente (un esempio pratico è disponibile qui), i processi del browser e di rendering gestiscono la rasterizzazione dei contenuti, quindi inviano i frame del compositore al processo di visualizzazione per mostrarli sullo schermo. I frame composti sono il modo in cui RenderingNG rappresenta come unire i contenuti rasterizzati e disegnarli in modo efficiente utilizzando la GPU.

Riquadri

In teoria, un compositore di processi di rendering o browser potrebbe rasterizzare i pixel in una singola texture delle dimensioni intere dell'area visibile del renderer e inviare la texture a Viz. Per visualizzarla, il compositore display deve semplicemente copiare i pixel da quella singola texture nella posizione appropriata nel buffer del frame (ad esempio, nella schermata). Tuttavia, se il compositore volesse aggiornare anche un solo pixel, dovrà rasterizzare l'intera area visibile e inviare una nuova texture a Viz.

L'area visibile viene invece suddivisa in riquadri. Un riquadro di texture GPU separato esegue il backup di ogni riquadro con i pixel rasterizzati per una parte dell'area visibile. Il renderer può quindi aggiornare singoli riquadri o semplicemente modificare la posizione dei riquadri esistenti sullo schermo. Ad esempio, quando si scorre un sito web, la posizione dei riquadri esistenti si sposta verso l'alto e solo occasionalmente deve essere rasterizzato un nuovo riquadro per i contenuti più in basso nella pagina.

Quattro riquadri.

L'immagine sopra raffigura una giornata di sole, con quattro riquadri. Quando si scorre, inizia ad apparire un quinto riquadro. Uno dei riquadri ha un solo colore (azzurro cielo) e in alto ci sono un video e un iframe. Il che porta all'argomento successivo.

Quad e piattaforme

I riquadri di texture GPU sono un tipo speciale di quad, che è solo un nome fantasioso per una categoria di texture o un'altra. Il riquadro quadruplo identifica la texture di input e indica come trasformarla e applicarvi effetti visivi. Ad esempio, i riquadri di contenuti normali presentano una trasformazione che indica la posizione x, y nella griglia dei riquadri.

Riquadri texture GPU.

Questi riquadri rasterizzati vengono avvolti in un pass di rendering, che è un elenco di quadricipiti. La pass per il rendering non contiene informazioni sui pixel, ma contiene istruzioni su dove e come disegnare ogni quad per produrre l'output in pixel desiderato. È presente un quadrato di disegno per ogni riquadro di texture GPU. Il compositor di display deve solo eseguire l'iterazione nell'elenco dei quad, disegnando ognuno con gli effetti visivi specificati, per produrre l'output in pixel desiderato per il passaggio di rendering. La composizione dei quadri di disegno per un passaggio di rendering può essere eseguita in modo efficiente sulla GPU, perché gli effetti visivi consentiti vengono scelti con cura per essere quelli che corrispondono direttamente alle funzionalità della GPU.

Oltre ai riquadri rasterizzati, sono disponibili altri tipi di quadricipiti. Ad esempio, esistono quad di disegno a tinta unita privi di alcun supporto da una trama oppure quadrate di disegno a tinta unita per texture non piastrellate come video o tele.

È anche possibile che un frame del compositore incorpora un altro frame del compositore. Ad esempio, il compositor del browser produce un frame del compositore con l'interfaccia utente del browser e un rettangolo vuoto in cui verranno incorporati i contenuti del compositor. Un altro esempio sono gli iframe isolati dal sito. L'incorporamento viene eseguito attraverso le piattaforme.

Quando un compositore invia un frame del compositore, questo è accompagnato da un identificatore, chiamato ID surface, che consente ad altri frame del compositore di incorporarlo per riferimento. Il più recente frame del compositore inviato con un determinato ID di superficie viene memorizzato da Viz. Un altro frame del compositore può quindi fare riferimento in un secondo momento tramite un quad di disegno di superficie, in questo modo Viz sa cosa disegnare. Tieni presente che i quadricipiti delle superfici contengono solo gli ID superficie, non le texture.

Pass per il rendering intermedio

Alcuni effetti visivi, come molti filtri o modalità di fusione avanzate, richiedono l'aggiunta di due o più quadricromia a una texture intermedia. La texture intermedia viene quindi trascinata in un buffer di destinazione sulla GPU (o eventualmente in un'altra texture intermedia), applicando l'effetto visivo contemporaneamente. Per fare ciò, un frame del compositore contiene un elenco di passaggi di rendering. Esiste sempre un passaggio di rendering radice, disegnato per ultimo e la cui destinazione corrisponde al buffer del frame, e ce ne possono essere di più.

La possibilità di più passaggi di rendering spiega il nome "render pass". Ogni passaggio deve essere eseguito in sequenza sulla GPU, in più "pass", mentre un singolo passaggio può essere completato in un singolo calcolo della GPU a parallelismo massivo.

Aggregazione

Più frame del compositore vengono inviati a Viz, che devono essere attirati sullo schermo insieme. Ciò si ottiene mediante una fase di aggregazione che le converte in un singolo frame del compositore aggregato. L'aggregazione sostituisce i quad di disegno di superficie dai frame del compositore specificati. È anche un'opportunità per ottimizzare le texture intermedie non necessarie o i contenuti fuori schermo. Ad esempio, in molti casi il frame del compositore per un iframe con sito isolato non ha bisogno di una propria texture intermedia e può essere disegnato direttamente nel buffer del frame tramite i quadricipiti di disegno appropriati. La fase di aggregazione determina queste ottimizzazioni e le applica in base a conoscenze globali non accessibili ai singoli compositori di rendering.

Esempio

Ecco i frame del compositor effettivi che rappresentano l'esempio dall'inizio di questo post.

  • Piattaforma foo.com/index.html: id=0
    • Rendering del passaggio 0: disegno nell'output.
      • Rendering del riquadro di disegno del passaggio: disegna con una sfocatura da 3 px e ritaglia nel passaggio del rendering 0.
        • Pass di rendering 1:
          • Traccia i quadricipiti per i contenuti dei riquadri dell'iframe #one, con posizioni x e y per ciascuno.
      • Surface Draw Quad: con ID 2, disegnato con la trasformazione scala e traslazione.
  • Piattaforma UI del browser: ID=1
    • Rendering del passaggio 0: disegno nell'output.
      • Traccia quad per l'interfaccia utente del browser (anche a riquadro)
  • Piattaforma bar.com/index.html: ID=2
    • Rendering del passaggio 0: disegno nell'output.
      • Traccia i quadricipiti per i contenuti dell'iframe #two, con posizioni x e y per ciascuno.

Conclusione

Grazie per l'attenzione. Con i due post precedenti, si conclude la panoramica di RenderingNG. Prossimamente approfondiremo le sfide e la tecnologia all'interno di molti dei sottocomponenti della pipeline di rendering, dall'inizio alla fine. Saranno disponibili a breve.

Illustrazioni di Una Kravets.