Rendering accelerato in Chrome

Il modello del livello

Tom Wiltzius
Tom Wiltzius

Introduzione

Per la maggior parte degli sviluppatori web, il modello fondamentale di una pagina web è il DOM. Il rendering è il processo spesso oscurato per trasformare questa rappresentazione di una pagina in un'immagine sullo schermo. Negli ultimi anni i browser moderni hanno cambiato il funzionamento del rendering per sfruttare le schede grafiche: questa viene spesso chiamata vagamente "accelerazione hardware". Quando si parla di una normale pagina web (ovvero non di Canvas2D o WebGL), che cosa significa in realtà questo termine? Questo articolo spiega il modello di base alla base del rendering con accelerazione hardware dei contenuti web in Chrome.

Avvertenze grandi e grasse

Stiamo parlando di WebKit, e più nello specifico della porta di WebKit su Chromium. Questo articolo illustra i dettagli di implementazione di Chrome, non delle funzionalità della piattaforma web. La piattaforma web e gli standard non codificano questo livello di dettaglio dell'implementazione, pertanto non vi è alcuna garanzia che nulla in questo articolo si applicherà ad altri browser, ma la conoscenza interna può comunque essere utile per il debug avanzato e l'ottimizzazione delle prestazioni.

Inoltre, tieni presente che nell'intero articolo viene descritto un elemento fondamentale dell'architettura di rendering di Chrome, che sta cambiando molto rapidamente. Questo articolo cerca di trattare solo gli argomenti che difficilmente cambieranno, ma non garantisce che saranno comunque validi entro sei mesi.

È importante capire che da un po' di tempo Chrome ha due diversi percorsi di rendering: il percorso con accelerazione hardware e il percorso software meno recente. Al momento della stesura di questo documento, tutte le pagine seguono il percorso con accelerazione hardware su Windows, ChromeOS e Chrome per Android. Su Mac e Linux, solo le pagine che richiedono la composizione per alcuni dei loro contenuti seguono un percorso accelerato (vedi di seguito per maggiori informazioni su ciò che richiede la composizione), ma presto anche tutte le pagine seguiranno un percorso accelerato anche in questo caso.

Infine, stiamo sbirciando sotto il cofano del motore di rendering e stiamo esaminando le sue caratteristiche che hanno un grande impatto sulle prestazioni. Quando si cerca di migliorare le prestazioni del proprio sito, può essere utile comprendere il modello di livelli, ma è anche facile cadere a terra: i livelli sono costrutti utili, ma crearne molti può introdurre un sovraccarico in tutto lo stack grafico. Ti prevenirai!

Dal DOM allo schermo

Introduzione alla funzionalità Livelli

Una volta caricata e analizzata, la pagina viene rappresentata nel browser come una struttura con cui molti sviluppatori web hanno familiarità: DOM. Quando esegui il rendering di una pagina, tuttavia, il browser presenta una serie di rappresentazioni intermedie che non sono esposte direttamente agli sviluppatori. La più importante di queste strutture è un livello.

In Chrome esistono diversi tipi di livelli: Renderlayer, che sono responsabili dei sottoalberi del DOM, e GraphicsLivelli, che sono responsabili dei sottoalberi di Renderlayer. Quest'ultimo aspetto è per noi più interessante in questo caso, perché i GraphicsLivelli sono quelli che vengono caricati sulla GPU sotto forma di texture. D'ora in poi dirò semplicemente "layer" per indicare Graphics Layer.

Breve parte della terminologia GPU: cos'è una texture? Immaginala come un'immagine bitmap che viene spostata dalla memoria principale (ovvero la RAM) alla memoria video (ad es. VRAM, sulla tua GPU). Una volta che si trova nella GPU, puoi mapparla su una geometria mesh. Nei videogiochi o nei programmi CAD, questa tecnica viene utilizzata per dare "skin" ai modelli 3D scheletrici. Chrome utilizza le texture per inserire blocchi di contenuti di pagine web sulla GPU. Le texture possono essere mappate in modo economico a posizioni e trasformazioni diverse applicandole a una maglia rettangolare molto semplice. Questo è il funzionamento del CSS 3D ed è ideale anche per lo scorrimento rapido. In seguito, parleremo di entrambi.

Diamo un'occhiata a un paio di esempi per spiegare il concetto del livello.

Uno strumento molto utile quando si studiano i livelli in Chrome è il flag "Mostra bordi livelli compositi" nelle impostazioni (ovvero una piccola icona a forma di ingranaggio) in Dev Tools, sotto l'intestazione "rendering". evidenzia in modo molto semplice i punti in cui sono presenti i livelli sullo schermo. Attiviamola. Questi screenshot ed esempi sono tutti tratti dall'ultima versione di Chrome Canary, Chrome 27, al momento della stesura di questo articolo.

Figura 1: una pagina a un livello

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Screenshot dei bordi del rendering del livello composito intorno al livello di base della pagina
Screenshot dei bordi del rendering del livello composito attorno al livello di base della pagina

Questa pagina ha un solo livello. La griglia blu rappresenta i riquadri, che possono essere considerati come unità secondarie di un livello che Chrome utilizza per caricare nella GPU le parti di un livello di grandi dimensioni alla volta. Non sono molto importanti qui.

Figura 2: un elemento nel suo livello

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
Screenshot dei bordi di rendering del livello ruotato
Screenshot dei bordi di rendering del livello ruotato

Inserendo una proprietà CSS 3D in <div> in modo da farla ruotare, possiamo vedere come appare un elemento quando riceve il proprio livello. Osserva il bordo arancione che delinea un livello in questa vista.

Criteri di creazione del livello

Cos'altro riceve il proprio livello? Le euristiche di Chrome si sono evolute nel corso del tempo, ma attualmente sono presenti i seguenti livelli di trigger:

  • Proprietà CSS di trasformazione 3D o prospettiva
  • <video> elemento con decodifica video accelerata
  • <canvas> elemento con contesto 3D (WebGL) o 2D accelerato.
  • Plug-in composti (ad es. Flash)
  • Elementi con animazione CSS per la loro opacità o utilizzando una trasformazione animata.
  • Elementi con filtri CSS accelerati
  • L'elemento ha un discendente con un livello di composizione (in altre parole se l'elemento ha un elemento figlio nel suo livello)
  • L'elemento ha un elemento di pari livello con uno z-index più basso e uno strato di compositing (in altre parole viene visualizzato sopra uno strato composito).

Implicazioni pratiche: animazione

Possiamo anche spostare i livelli, il che li rende molto utili per le animazioni.

Figura 3: livelli animati

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div>I am a strange root.</div>
</body>
</html>

Come accennato in precedenza, i livelli sono davvero utili per spostarsi nei contenuti web statici. Nel caso di base, Chrome colora i contenuti di un livello in una bitmap software prima di caricarli nella GPU sotto forma di texture. Se i contenuti non cambiano in futuro, non è necessario ricolorarli. Questa è una buona cosa: ridipingere richiede tempo che può essere dedicato ad altre attività, come l'esecuzione di JavaScript, e se il disegno è lungo provoca problemi o ritardi nelle animazioni.

Vedi, ad esempio, questa visualizzazione della sequenza temporale di Strumenti per sviluppatori: non vengono eseguite operazioni di colorazione mentre questo livello ruota avanti e indietro.

Screenshot della sequenza temporale di Strumenti per sviluppatori durante l&#39;animazione
Screenshot della sequenza temporale di Strumenti per sviluppatori durante l'animazione

Non valido. Riverniciatura

Tuttavia, se i contenuti del livello cambiano, questo deve essere ricolorato.

Figura 4: nuova pittura dei livelli

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div id="foo">I am a strange root.</div>
  <input id="paint" type="button" value="repaint">
  <script>
    var w = 200;
    document.getElementById('paint').onclick = function() {
      document.getElementById('foo').style.width = (w++) + 'px';
    }
  </script>
</body>
</html>

Ogni volta che viene fatto clic sull'elemento di input, l'elemento rotante diventa più largo di 1 px. Ciò causa il relayout e la modifica dell'intero elemento, che in questo caso è un intero livello.

Un buon modo per vedere cosa viene dipinto è usare lo strumento "Mostra visualizzazioni di vernice" in Strumenti per sviluppatori, anche sotto l'intestazione "Rendering" delle impostazioni degli Strumenti per sviluppatori. Dopo averlo acceso, nota che l'elemento animato e il pulsante lampeggiano entrambi in rosso quando viene fatto clic sul pulsante.

Screenshot della casella di controllo Mostra visualizzazioni di colore
Screenshot della casella di controllo Mostra visualizzazioni di colore

Gli eventi di colorazione vengono visualizzati anche nella sequenza temporale degli strumenti di sviluppo. I lettori con gli occhi acuti potrebbero notare che ci sono due eventi di colorazione: uno per il livello e uno per il pulsante stesso, che viene ridipinto quando cambia lo stato di pressione.

Screenshot della cronologia degli strumenti di sviluppo che mostra la visualizzazione di un livello
Screenshot della sequenza temporale degli strumenti di sviluppo che mostra la visualizzazione di un livello

Tieni presente che Chrome non deve ridipingere sempre l'intero livello, cerca di ridipingere solo la parte del DOM che è stata invalidata. In questo caso, l'elemento DOM che abbiamo modificato è la dimensione dell'intero livello. Ma in molti altri casi, ci saranno molti elementi DOM in un livello.

La prossima domanda ovvia è cosa causi un'annullamento della convalida e costringa a ricolorare. È difficile rispondere in modo esaustivo perché ci sono molti casi limite che possono forzare le invalidazioni. La causa più comune è lo sporco del DOM manipolando gli stili CSS o determinando il relayout. Tony Gentilcore ha un ottimo post del blog sulle cause del relayout e Stoyan Stefanov ha un articolo che tratta la pittura in modo più dettagliato (ma termina solo con la pittura, non con questa fantasia di composizione).

Il modo migliore per capire se influisce su qualcosa su cui stai lavorando è utilizzare gli strumenti Sequenza temporale degli strumenti di sviluppo e Mostra rettangoli di colorazione per vedere se stai ridipingendo quando non lo fosse, quindi prova a identificare dove hai sporcato il DOM appena prima del relayout/ricolorazione. Se la pittura è inevitabile, ma sembra richiedere troppo tempo, dai un'occhiata all'articolo di Eberhard Gräther sulla modalità di pittura continua in Strumenti per sviluppatori.

Elaborazione: dal DOM allo schermo

In che modo Chrome trasforma il DOM in un'immagine dello schermo? A livello concettuale:

  1. Prende il DOM e lo suddivide in livelli
  2. Disegna ciascuno di questi livelli in modo indipendente nelle bitmap del software
  3. Le carica nella GPU sotto forma di texture
  4. Combina i vari livelli nell'immagine finale dello schermo.

Tutto deve accadere la prima volta che Chrome genera un frame di una pagina web. ma poi potrebbero essere necessarie alcune scorciatoie per i frame futuri:

  1. Se alcune proprietà CSS cambiano, non è necessario ricolorare nulla. Chrome può semplicemente ricomporre i livelli esistenti già presenti sulla GPU sotto forma di texture, ma con proprietà di composizione diverse (ad esempio, in posizioni diverse, con opacità diverse e così via).
  2. Se una parte di un livello viene invalidata, viene riverniciata e ricaricata. Se i contenuti rimangono gli stessi, ma gli attributi compositi cambiano (ad es. viene tradotto o l'opacità cambia), Chrome può lasciarli nella GPU e nel repository per creare un nuovo frame.

Come dovrebbe ora essere chiaro, il modello di compositing basato su livelli ha implicazioni profonde per le prestazioni di rendering. La composizione è relativamente economica quando non è necessario colorare nulla, quindi evitare di ridipingere i livelli è un buon obiettivo generale quando si cerca di eseguire il debug delle prestazioni del rendering. Gli sviluppatori più esperti daranno un'occhiata all'elenco di trigger di compositing riportato sopra e capiranno che è possibile forzare facilmente la creazione di livelli. Ma fai attenzione a crearli alla cieca, poiché non sono liberi: occupano memoria nella RAM di sistema e sulla GPU (in particolare limitata sui dispositivi mobili) e averne molti può introdurre un altro overhead nella logica che tiene traccia di quali sono visibili. Molti livelli, inoltre, possono aumentare il tempo impiegato per la rasterizzazione se sono di grandi dimensioni e si sovrappongono molto dove prima non avvenivano, il che porta a ciò che a volte viene definito "superamento". Quindi usa le tue conoscenze con oculatezza!

Per il momento è tutto. Continua a seguirci per altri articoli sulle implicazioni pratiche del modello di livelli.

Altre risorse