Strutture di dati chiave 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

Diamo un'occhiata alle strutture di dati chiave, che sono gli input e gli output della pipeline di rendering.

Queste strutture di dati sono:

  • Le piante di frame sono composte da nodi locali e remoti che rappresentano i documenti web che si trovano in quale processo di rendering e in quale renderer Blink.
  • L'albero di frammenti immutabili rappresenta l'output e l'input dell'algoritmo di vincoli di layout.
  • Gli alberi di proprietà rappresentano le gerarchie di trasformazione, clip, effetti e scorrimento di un documento web. Vengono utilizzati in tutta la pipeline.
  • Gli elenchi di visualizzazione e i chunk di pittura sono gli input degli algoritmi di rasterizzazione e stratificazione.
  • I frame del compositore incapsulano le superfici, le superfici di rendering e i riquadri delle texture GPU utilizzati per disegnare utilizzando la GPU.

Prima di esaminare queste strutture di dati, il seguente esempio si basa su uno della revisione dell'architettura. Questo esempio viene utilizzato in tutto il documento con dimostrazioni di come vengono applicate le strutture di dati.

<!-- Example code -->
<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>

Inquadrare gli alberi

A volte Chrome potrebbe scegliere di eseguire il rendering di un frame cross-origin in un processo di rendering diverso da quello del frame principale.

Nel codice di esempio sono presenti 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 di questa pagina web. Ogni processo di rendering ha una propria rappresentazione dell'albero di frame per la pagina web in questione:

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

Un frame visualizzato in un processo diverso è rappresentato come frame remoto. Un frame remoto contiene le informazioni minime necessarie per fungere da segnaposto durante il rendering, ad esempio le dimensioni. In caso contrario, il frame remoto non contiene le informazioni necessarie per visualizzare i contenuti effettivi.

Al contrario, un frame locale rappresenta un frame che passa attraverso la pipeline di rendering standard. Il frame locale contiene tutte le informazioni necessarie per trasformare i dati relativi al frame (ad esempio l'albero DOM e i dati di stile) in qualcosa che possa essere visualizzato e visualizzato.

La pipeline di rendering opera sulla granularità di un frammento dell'albero della struttura 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>

Sebbene esistano ancora solo due visualizzatori, ora sono presenti tre frammenti dell'albero di frame locale, due nel processo di rendering per foo.com e uno nel processo di rendering per bar.com:

Una rappresentazione dei due rendering e tre frammenti dell&#39;albero della struttura.

Per produrre un frame compositore per la pagina web, Viz richiede contemporaneamente un frame compositore dal frame principale di ciascuno dei tre alberi di frame locali, quindi li aggrega. Consulta anche la sezione relativa ai frame del compositore.

Il frame principale foo.com e il frame secondario foo.com/other-page fanno parte della stessa struttura ad albero di frame e vengono visualizzati nello stesso processo. Tuttavia, i due frame hanno ancora cicli di vita dei documenti indipendenti perché fanno parte di diversi frammenti dell'albero dei frame locali. Per questo motivo, è impossibile generare un frame del compositore per entrambi in un unico 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 occludendo parti dell'iframe con altri elementi nel suo DOM.

La struttura a cascata dell'aggiornamento delle proprietà visive

Le proprietà visive come il fattore di scalabilità del dispositivo e le dimensioni della visualizzazione influiscono sull'output visualizzato e devono essere sincronizzate tra i frammenti dell'albero della struttura del frame locale. La radice di ogni frammento dell'albero di frame locale ha un oggetto widget associato. Gli aggiornamenti delle proprietà visive vengono applicati al widget del frame principale prima di essere propagati ai widget rimanenti dall'alto verso il basso.

Ad esempio, quando le dimensioni dell'area visibile cambiano:

Diagramma della procedura spiegata nel testo precedente.

Questo processo non è istantaneo, quindi le proprietà visive replicate includono anche un token di sincronizzazione. Il compositore Viz utilizza questo token di sincronizzazione per attendere che tutti i frammenti dell'albero dei frame locali inviino un frame del compositore con il token di sincronizzazione corrente. Questo processo evita di combinare frame del compositore con proprietà visive diverse.

L'albero di frammenti immutabile

L'albero di frammenti immutabile è l'output della fase di layout della pipeline di rendering. Rappresenta la posizione e le dimensioni di tutti gli elementi della pagina (senza trasformazioni applicate).

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

Ogni frammento rappresenta una parte di un elemento DOM. In genere, è presente un solo frammento per elemento, ma possono essercene di più se l'elemento è suddiviso in più pagine durante la stampa o in più colonne in un contesto con più colonne.

Dopo il layout, ogni frammento diventa immutabile e non viene più modificato. È importante sottolineare che applichiamo anche alcune limitazioni aggiuntive. Non:

  • Consenti eventuali riferimenti "up" nell'albero. Un elemento secondario non può avere un puntatore al relativo elemento principale.
  • "gonfiano" i dati lungo l'albero (un nodo secondario legge solo le informazioni dei suoi nodi secondari, non del nodo principale).

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

La maggior parte dei layout è in genere sottoposta ad 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 proporzione a ciò che è effettivamente cambiato sullo schermo. Possiamo farlo riutilizzando il maggior numero possibile di parti dell'albero precedente. Ciò significa che, in genere, dobbiamo solo ricostruire la struttura principale dell'albero.

In futuro, questo design immutabile potrebbe consentirci di fare cose interessanti, come passare l'albero di frammenti immutabile oltre i confini 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 multithreading stesso.

Elementi del frammento in linea

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

L'elenco piatto viene creato per ogni contesto di formattazione in linea nell'ordine di una ricerca in profondità del sottoalbero del layout in linea. Ogni voce dell'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>

La proprietà width è impostata su 0 in modo che la riga venga a capo tra "Un saluto da Google" e "Google".

Quando il contesto di formattazione in linea per questa situazione è rappresentato come un albero, ha il seguente aspetto:

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

L'elenco piatto ha il seguente aspetto:

  • (Riquadro riga, 2)
  • (Box <span>, 1)
  • (Testo "Ciao", 0)
  • (Riquadro riga, 3)
  • (Box <b>, 1)
  • (Testo "there", 0)
  • (Testo ".", 0)

Esistono molti utilizzatori di questa struttura di dati: API di accessibilità e API di geometria come getClientRects e contenteditable. Ognuno ha requisiti diversi. Questi componenti accedono alla struttura di dati a livello tramite un cursore pratico.

Il cursore 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 di primo livello è molto veloce. Viene utilizzato molto spesso perché è simile ai movimenti del cursore. Poiché si tratta di un elenco piatto, la ricerca in profondità incrementa semplicemente l'offset dell'array, fornendo iterazioni rapide e localizzazione in memoria.
  • Fornisce una ricerca in ampiezza, necessaria ad esempio per dipingere lo sfondo delle caselle di riga e in linea.
  • Conoscere il numero di discendenti consente di passare rapidamente al fratello successivo (basta aumentare l'offset dell'array di quel numero).

Alberi di proprietà

Il DOM è una struttura ad albero di elementi (oltre ai nodi di testo) e il CSS può applicare vari stili agli elementi.

Viene visualizzato in quattro modi:

  • Layout:input per l'algoritmo di vincolo del layout.
  • Vernice:come dipingere e rasterizzare l'elemento (ma non i relativi discendenti).
  • Visivo: effetti di rasterizzazione/disegno applicati al sottoalbero DOM, come trasformazioni, filtri e ritaglio.
  • Scorrimento: ritaglio e scorrimento del sottoalbero contenuto con angoli arrotondati e allineati all'asse.

Le strutture ad albero delle proprietà sono strutture di dati che spiegano come vengono applicati gli effetti visivi e di scorrimento agli elementi DOM. Forniscono i mezzi per rispondere a domande quali: dove si trova un determinato elemento DOM rispetto allo schermo, tenendo conto delle dimensioni e della posizione del layout? E ancora: quale sequenza di operazioni della GPU deve essere utilizzata per applicare effetti visivi e di scorrimento?

Gli effetti visivi e di scorrimento sul web sono molto complicati nella loro interezza. Pertanto, la cosa più importante che fanno gli alberi di proprietà è tradurre questa complessità in una singola struttura di dati che ne rappresenta con precisione la struttura e il significato, rimuovendo al contempo il resto della complessità del DOM e del CSS. In questo modo, possiamo implementare algoritmi per la composizione e lo scorrimento con molta più sicurezza. In particolare:

  • La geometria e altri calcoli potenzialmente soggetti a errori possono essere centralizzati in un unico posto.
  • La complessità di creazione e aggiornamento delle strutture ad albero viene isolata in una singola fase della pipeline di rendering.
  • È molto più facile e veloce inviare alberi di proprietà a thread e processi diversi rispetto allo stato DOM completo, in modo da poterli utilizzare per molti casi d'uso.
  • Più casi d'uso sono disponibili, maggiori sono i vantaggi che possiamo ottenere dalla memorizzazione nella cache della geometria creata in base a questi casi, poiché possono riutilizzare le cache di ciascuno.

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

  • Separazione della composizione dalla pittura e della composizione dal thread principale.
  • Determinazione di una strategia di composizione / disegno ottimale.
  • Misurare la geometria di IntersectionObserver.
  • Evitare di lavorare per gli elementi offscreen e i riquadri delle texture della GPU.
  • Annullamento dell'efficacia di pittura e raster in modo efficiente e preciso.
  • Misurazione del spostamento del layout e del Largest Contentful Paint in Core Web Vitals.

Ogni documento web ha quattro alberi di proprietà distinti: transform, clip, effect e scroll.(*) L'albero transform 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 di overflow. L'albero degli effetti rappresenta tutti gli altri effetti visivi: opacità, filtri, maschere, modalità di miscela e altri tipi di clip come clip-path. L'albero di scorrimento rappresenta le informazioni sullo scorrimento, ad esempio la modalità di concatenazione delle varie scorrimenti. È necessario per eseguire lo scorrimento nel thread del compositore. Ogni nodo in una struttura ad albero di proprietà rappresenta uno scorrimento o un effetto visivo applicato da un elemento DOM. Se ha più effetti, in ogni albero potrebbe essere presente più di un nodo dell'albero delle proprietà per lo stesso elemento.

La topologia di ogni albero è simile a una rappresentazione sparsa del DOM. Ad esempio, se sono presenti tre elementi DOM con clip di overflow, esistono tre nodi dell'albero dei clip, e la struttura dell'albero dei clip seguirà la relazione del blocco contenente tra i clip di overflow. Esistono anche collegamenti tra gli alberi. 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 sotto un altro elemento DOM con un filtro, ovviamente la trasformazione viene applicata prima del filtro.

Ogni elemento DOM ha uno stato della struttura ad albero delle proprietà, che è una tupla di 4 elementi (trasformazione, clip, effetto, scorrimento) che indica i nodi della struttura ad albero di clip, trasformazione e effetto dell'antenato più vicino che si applicano a quell'elemento. È molto comodo, perché con queste informazioni sappiamo esattamente l'elenco di clip, trasformazioni ed effetti applicati all'elemento e in quale ordine. Questo ci dice 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 (che è leggermente diverso da quello nell'introduzione), di seguito sono riportati gli elementi chiave delle strutture ad albero delle proprietà generate:

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

.

Visualizza elenchi e blocchi di pittura

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

Ad esempio:

Una casella blu con la scritta &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, dove ogni cella è un elemento di visualizzazione:

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

L'elenco degli elementi visualizzati è ordinato dal retro in avanti. Nell'esempio riportato sopra, il div verde è prima del div blu nell'ordine DOM, ma l'ordine di applicazione del CSS richiede che il div blu con indice z negativo venga visualizzato prima (passaggio 3) del div verde (passaggio 4.1). Gli elementi di visualizzazione corrispondono approssimativamente ai passaggi atomici della specifica dell'ordine di pittura CSS. Un singolo elemento DOM può generare più elementi di visualizzazione, ad esempio #green ha un elemento di visualizzazione per lo sfondo e un altro per il testo in linea. Questa granularità è importante per rappresentare l'intera complessità della specifica dell'ordine di pittura CSS, come l'interlacciamento creato dal margine negativo:

Un rettangolo verde con una casella grigia parzialmente sovrapposta 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>

Viene generato il seguente elenco di visualizzazione, in cui ogni cella è un elemento di visualizzazione:

Sfondo della visualizzazione #green in background #gray in background #green testo in linea
drawRect con dimensioni 800 x 600 e colore bianco. drawRect con dimensioni 80 x 18 nella posizione 8,8 e di colore verde. drawRect con dimensioni 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 di layout non è stato modificato durante l'esplorazione dell'albero di pittura, gli elementi di visualizzazione vengono copiati dall'elenco precedente. Un'ottimizzazione aggiuntiva si basa su una proprietà della specifica dell'ordine di applicazione del CSS: i contesti di impilamento vengono applicati in modo atomico. Se nessun oggetto di layout è stato modificato in un contesto di impilamento, il percorso dell'albero di pittura salta il contesto di impilamento e copia l'intera sequenza di elementi di visualizzazione dall'elenco precedente.

Lo stato corrente dell'albero delle proprietà viene mantenuto durante l'esplorazione dell'albero di pittura e l'elenco degli elementi visualizzati viene raggruppato in "chunk" di elementi visualizzati che condividono lo stesso stato dell'albero delle proprietà. Questo è dimostrato nell'esempio seguente:

Una scatola rosa con una scatola arancione inclinata.

<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>

Viene generato il seguente elenco di visualizzazione, in cui ogni cella è un elemento di visualizzazione:

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

L'albero delle proprietà di trasformazione e i chunk di pittura saranno quindi (semplificati per brevità):

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

L'elenco ordinato di chunk di pittura, ovvero gruppi di elementi di visualizzazione e uno stato dell'albero delle proprietà, sono gli input per il passaggio di suddivisione in livelli della pipeline di rendering. L'intero elenco di chunk di pittura potrebbe essere unito in un unico livello composito e rasterizzato insieme, ma ciò richiederebbe una rasterizzazione costosa ogni volta che l'utente scorre. È possibile creare un livello composito per ogni frammento di pittura e rasterizzarlo singolarmente per evitare la rasterizzazione, ma questo esaurirebbe rapidamente la memoria della GPU. Il passaggio di suddivisione in livelli deve fare i conti con la memoria GPU e la riduzione dei costi quando le cose cambiano. Un buon approccio generale è unire i chunk per impostazione predefinita e non unire i chunk di pittura che hanno stati dell'albero delle proprietà che dovrebbero cambiare nel thread del compositore, ad esempio con lo scorrimento del thread del compositore o le animazioni di trasformazione del thread del compositore.

Idealmente, l'esempio precedente dovrebbe produrre due livelli composti:

  • Un livello composito 800 x 600 contenente i comandi di disegno:
    1. drawRect con dimensioni 800 x 600 e colore bianco
    2. drawRect con dimensioni 100 x 100 nella posizione 0,0 e di 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 dimensioni 75 x 200 nella posizione 0,0 e di colore arancione
    5. drawTextBlob con posizione 0,0 e testo "Sto cadendo"

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

Per l'esempio, nella sezione precedente sugli alberi di proprietà, sono presenti sei chunk di pittura. Insieme agli stati dell'albero delle proprietà (transform, clip, effect, scroll), sono:

  • Sfondo del documento: scorrimento del documento, clip del documento, root, scorrimento del documento.
  • Angolo orizzontale, verticale e di scorrimento per div (tre chunk di pittura separati): scorrimento del documento, clip del documento, #one sfocatura, scorrimento del documento.
  • Iframe #one: #one rotate, overflow scroll clip, #one blur, div scroll.
  • Iframe #two: #two scale, clip del documento, radice, scorrimento del documento.

Frame del compositore: superfici, superfici di rendering e riquadri di texture GPU

I processi del browser e di rendering gestiscono la rasterizzazione dei contenuti, quindi inviano i frame del compositore al processo di visualizzazione per la presentazione sullo schermo. I frame del compositore rappresentano il modo in cui unire i contenuti rasterizzati e disegnarli in modo efficiente utilizzando la GPU.

Riquadri

In teoria, un compositore di processi di rendering o del browser potrebbe rasterizzare i pixel in una singola texture delle dimensioni complete della visualizzazione del renderer e inviarla a Viz. Per visualizzarla, il compositore di visualizzazione deve solo copiare i pixel da quella singola texture nella posizione appropriata nel frame buffer (ad esempio, lo schermo). Tuttavia, se il compositore volesse aggiornare anche un singolo pixel, dovrebbe rasterizzare di nuovo l'intera visualizzazione e inviare una nuova texture a Viz.

Il viewport è invece suddiviso in riquadri. Un riquadro di texture della GPU separato supporta ogni riquadro con i pixel rasterizzati per una parte dell'area visibile. Il renderer può quindi aggiornare i singoli riquadri o anche solo cambiare la posizione sullo schermo dei riquadri esistenti. Ad esempio, quando scorri un sito web, la posizione dei riquadri esistenti si sposta verso l'alto e solo occasionalmente è necessario rasterizzare un nuovo riquadro per i contenuti più in basso nella pagina.

Quattro riquadri.
Questa immagine mostra una giornata di sole con quattro riquadri. Quando si verifica uno scorrimento, inizia a essere visualizzato un quinto riquadro. Uno dei riquadri ha un solo colore (azzurro), mentre sopra sono presenti un video e un iframe.

Quadri e superfici

I riquadri delle texture della GPU sono un tipo speciale di quad, che è solo un nome complicato per una categoria di texture o un'altra. Un quad identifica la texture di input e indica come trasformarla e applicare effetti visivi. Ad esempio, i riquadri di contenuti normali hanno una trasformazione che indica la posizione x, y nella griglia dei riquadri.

Tile di texture GPU.

Questi riquadri rasterizzati sono racchiusi in un passaggio di rendering, ovvero un elenco di quad. Il pass di rendering non contiene informazioni sui pixel, ma contiene istruzioni su dove e come disegnare ogni quad per produrre l'output di pixel desiderato. Esiste un draw quad per ogni riquadro della texture della GPU. Il compositore di display deve solo eseguire l'iterazione dell'elenco di quad, disegnando ciascuno con gli effetti visivi specificati, per produrre l'output dei pixel desiderato per il passaggio di rendering. La composizione di quad di disegno per un passaggio di rendering può essere eseguita in modo efficiente sulla GPU, poiché gli effetti visivi consentiti sono scelti con cura in modo che corrispondano direttamente alle funzionalità della GPU.

Esistono altri tipi di quad di disegno oltre ai riquadri rasterizzati. Ad esempio, esistono quadri di disegno a colori solidi che non sono supportati da alcuna texture o quadri di disegno con texture per texture non a riquadri come video o canvas.

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

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

Passaggi di rendering intermedi

Alcuni effetti visivi, come molti filtri o modalità di miscela avanzate, richiedono che due o più quad vengano disegnati in una texture intermedia. La texture intermedia viene quindi disegnata in un buffer di destinazione sulla GPU (o eventualmente in un'altra texture intermedia), applicando contemporaneamente l'effetto visivo. Per consentire questo, un frame del compositore contiene in realtà un elenco di pass di rendering. Esiste sempre un passaggio di rendering principale, che viene disegnato per ultimo e la cui destinazione corrisponde al frame buffer, e potrebbero essercene altri.

La possibilità di più pass di rendering spiega il nome "pass di rendering". Ogni passaggio deve essere eseguito in sequenza sulla GPU, in più "passaggi", mentre un singolo passaggio può essere completato in un singolo calcolo GPU su larga scala parallela.

Aggregazione

A Viz vengono inviati più frame del compositore, che devono essere disegnati sullo schermo insieme. Ciò viene ottenuto mediante una fase di aggregazione che li converte in un unico frame compositor aggregato. L'aggregazione sostituisce i quad di disegno della superficie con i frame del compositore specificati. È anche un'opportunità per ottimizzare le texture intermedie non necessarie o i contenuti non visibili sullo schermo. Ad esempio, in molti casi il frame del compositore per un iframe isolato del sito non ha bisogno di una propria texture intermedia, e può essere disegnato direttamente nel frame buffer tramite draw quad appropriati. La fase di aggregazione individua queste ottimizzazioni e le applica in base a conoscenze globali non accessibili ai singoli compositori di rendering.

Esempio

Ecco i frame del compositore che rappresentano l'esempio all'inizio di questo post.

  • foo.com/index.html surface: id=0
    • Passaggio di rendering 0: disegno in output.
      • Draw quad pass di rendering: disegna con sfocatura di 3 pixel e ritaglia nel pass di rendering 0.
        • Passaggio di rendering 1:
          • Disegna i quad per i contenuti dei riquadri dell'iframe #one, con le posizioni x e y per ciascuno.
      • Quadrifoglio di disegno della superficie: con ID 2, disegnato con trasformazione di scala e traslazione.
  • Interfaccia utente del browser: ID=1
    • Passaggio di rendering 0: disegno in output.
      • Disegna quad per l'interfaccia utente del browser (anche a riquadri)
  • bar.com/index.html surface: ID=2
    • Passaggio di rendering 0: disegno in output.
      • Disegna quad per i contenuti dell'iframe #two, con le posizioni x e y per ciascuno.

Illustrazioni di Una Kravets.