Rendering accelerato in Chrome

Il modello a livelli

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 poco chiaro, di 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 al meglio le schede grafiche: questo processo è spesso vagamente chiamato "accelerazione hardware". Quando si parla di una pagina web normale (ad esempio non Canvas2D o WebGL), che cosa significa veramente questo termine? Questo articolo illustra il modello di base alla base del rendering con accelerazione hardware dei contenuti web in Chrome.

Avvertenze importanti e grosse

Qui stiamo parlando di WebKit e più specificamente del port di WebKit su Chromium. Questo articolo illustra i dettagli dell'implementazione di Chrome, non le funzionalità della piattaforma web. La piattaforma web e gli standard non codificano questo livello di dettaglio dell'implementazione, quindi non vi sono garanzie che nulla in questo articolo venga applicato ad altri browser, ma la conoscenza dei componenti interni può comunque essere utile per il debug avanzato e l'ottimizzazione delle prestazioni.

Inoltre, tieni presente che l'intero articolo descrive un elemento fondamentale dell'architettura di rendering di Chrome che sta cambiando molto rapidamente. In questo articolo si tratta solo di argomenti che difficilmente cambieranno, ma non garantisce che continueranno a essere applicati nel giro di sei mesi.

È importante capire che Chrome ha già da un po' di tempo due percorsi di rendering diversi: il percorso con accelerazione hardware e il percorso del software precedente. Al momento della stesura del presente documento, tutte le pagine seguono il percorso dell'accelerazione hardware su Windows, ChromeOS e Chrome per Android. Su Mac e Linux, solo le pagine che necessitano della composizione di alcuni contenuti seguono il percorso accelerato (vedi di seguito per maggiori informazioni su ciò che richiederebbe la composizione), ma presto tutte le pagine seguiranno anche il percorso accelerato.

Infine, daremo un'occhiata ai meccanismi sottostanti del motore di rendering e vedremo le sue funzionalità che hanno un grande impatto sulle prestazioni. Quando si cerca di migliorare le prestazioni del proprio sito può essere utile comprendere il modello dei livelli, ma è anche facile concentrarsi: gli strati sono costrutti utili, ma crearne molti può introdurre overhead nello stack grafico. Non ti preoccupare!

Dal DOM allo schermo

Introduzione ai Livelli

Una volta caricata e analizzata, una pagina viene rappresentata nel browser come una struttura nota a molti sviluppatori web: DOM. Tuttavia, durante il rendering di una pagina, il browser ha una serie di rappresentazioni intermedie che non sono direttamente esposte agli sviluppatori. La più importante di queste strutture è il livello.

In Chrome esistono diversi tipi di livelli: Renderlayer, che sono responsabili dei sottoalberi del DOM, e Graphicslayer, che sono responsabili dei sottoalberi di Renderlayer. Quest'ultimo è più interessante per noi qui, perché i livelli grafici sono i livelli che vengono caricati sulla GPU come texture. D'ora in poi, dirò solo "livello" per indicare Graphicslayer.

Una rapida occhiata alla terminologia GPU: che cos'è una texture? È come un'immagine bitmap che viene spostata dalla memoria principale (ad es. RAM) alla memoria video (ad es. VRAM, sulla GPU). Una volta che è sulla GPU, puoi mapparla a una geometria di mesh. Nei videogiochi o nei programmi CAD, questa tecnica viene utilizzata per assegnare ai modelli 3D scheletrici la "pelle". Chrome utilizza le texture per inserire blocchi di contenuti di pagine web sulla GPU. Le texture possono essere mappate in modo economico su diverse posizioni e trasformazioni applicandole a una mesh rettangolare molto semplice. Questo è il funzionamento di CSS 3D ed è ideale per scorrere velocemente, ma parleremo di entrambe le opzioni in seguito.

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

Uno strumento molto utile durante lo studio dei livelli in Chrome è il flag "mostra i bordi dei livelli compositi" nelle impostazioni (ovvero l'icona a forma di ingranaggio) in Dev Tools, sotto l'intestazione "rendering". Evidenzia semplicemente dove si trovano i livelli sullo schermo. Accendilo. Questi screenshot ed esempi sono ricavati dalla versione più recente di Chrome Canary, Chrome 27 al momento della stesura del presente documento.

Figura 1: una pagina a livello singolo

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

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

Figura 2: un elemento nel proprio 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

Se inserisci una proprietà CSS 3D sull'elemento <div> che lo ruota, possiamo vedere come appare quando un elemento riceve il proprio livello: nota il bordo arancione che delinea un livello in questa visualizzazione.

Criteri per la creazione dei livelli

Cos'altro ottiene il proprio livello? Le euristiche di Chrome si sono evolute nel tempo e continuano a farlo, ma al momento è possibile creare uno qualsiasi dei seguenti livelli di trigger:

  • Proprietà CSS 3D o prospettive transform
  • Elementi <video> che utilizzano la decodifica video accelerata
  • Elementi <canvas> con un contesto 3D (WebGL) o un contesto 2D accelerato
  • Plug-in compositi (ad es. Flash)
  • Elementi con animazione CSS per la loro opacità o che utilizzano una trasformazione animata
  • Elementi con filtri CSS accelerati
  • L'elemento ha un discendente con uno strato di composizione (in altre parole se l'elemento ha un elemento secondario che si trova nel proprio livello)
  • L'elemento ha un elemento di pari livello con uno z-index inferiore che ha un livello di composizione (in altre parole, viene visualizzato sopra un livello composto).

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 tra i contenuti web statici. Nel caso di base, Chrome visualizza i contenuti di un livello in una bitmap del software prima di caricarlo nella GPU come texture. Se i contenuti non vengono modificati in futuro, non è necessario rivedere i contenuti. Questa è una buona cosa: la ricolorazione richiede tempo che può essere dedicato ad altre cose, come l'esecuzione di JavaScript, e se il disegno è lungo causa intoppi o ritardi nelle animazioni.

Puoi vedere, ad esempio, questa vista della sequenza temporale degli Strumenti per sviluppatori: non vengono eseguite operazioni di colorazione durante la rotazione di questo livello.

Screenshot della sequenza temporale degli strumenti per sviluppatori durante l&#39;animazione
Screenshot della sequenza temporale degli strumenti per sviluppatori durante l'animazione

Non valido. Riverniciatura

Se, però, i contenuti del livello cambiano, dovrà essere ricolorato.

Figura 4: ricolorazione degli strati

<!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 in rotazione diventa più largo di 1 px. Ciò causa il relayout e la ricolorazione dell'intero elemento, che in questo caso è un livello intero.

Un buon modo per vedere cosa viene dipinto è con lo strumento "Mostra i grafici di rettangoli" negli Strumenti per sviluppatori, anche sotto l'intestazione "Rendering" delle impostazioni degli Strumenti per sviluppatori. Dopo l'attivazione, nota che l'elemento animato e il pulsante lampeggiano in rosso quando l'utente fa clic sul pulsante.

Screenshot della casella di controllo Mostra rettangolo di visualizzazione
Screenshot della casella di controllo Mostra rettangoli

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

Screenshot della sequenza temporale degli strumenti per sviluppatori durante la ricolorazione di un livello
Screenshot di un livello nella sequenza temporale degli strumenti per sviluppatori

Tieni presente che Chrome non ha sempre bisogno di ridipingere l'intero livello, ma cerca di essere intelligente nel ridipingere solo la parte del DOM che è stata invalidata. In questo caso, l'elemento DOM che abbiamo modificato corrisponde alle dimensioni dell'intero livello. Ma in molti altri casi, ci saranno molti elementi DOM in un livello.

Una domanda successiva ovvia è ciò che causa un'annullamento della convalida e forza una nuova colorazione. Non è facile rispondere a questa domanda 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 causando il relayout. Tony Gentilcore ha un ottimo post del blog sulle cause del relayout, mentre Stoyan Stefanov ha un articolo che tratta la pittura in modo più dettagliato (ma si conclude solo con la pittura, non con questa stravagante composizione).

Il modo migliore per capire se ciò influisce su qualcosa su cui stai lavorando è utilizzare gli strumenti Sequenza temporale degli strumenti di sviluppo e Mostra rettangoli per vedere se stai ridipingendo quando preferisci, quindi prova a identificare dove hai sporcato il DOM subito prima del relayout/ricolorazione. Se la pittura è inevitabile, ma sembra richiedere tempi irragionevolmente, leggi l'articolo di Eberhard Gräther sulla modalità di pittura continua in Dev Tools.

Integrazione: DOM to Screen

In che modo Chrome trasforma il DOM in un'immagine dello schermo? Concettualmente, questo:

  1. Prende il DOM e lo suddivide in livelli
  2. Disegna ciascuno di questi livelli in modo indipendente in bitmap software
  3. Le carica nella GPU come texture
  4. Combina i vari livelli nell'immagine finale della schermata.

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

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

Come dovrebbe essere chiaro, il modello di compositing basato su livelli ha profonde implicazioni per le prestazioni di rendering. La composizione è relativamente economica quando non è necessario dipingere nulla, quindi evitare la ricolorazione degli strati è un buon obiettivo generale quando si cerca di eseguire il debug delle prestazioni di rendering. Gli sviluppatori più esperti daranno un'occhiata all'elenco di trigger di compositing illustrato sopra e capiranno che è possibile forzare facilmente la creazione di strati. Ma fai attenzione a crearle alla cieca, perché non sono liberi: occupano memoria nella RAM di sistema e nella GPU (particolarmente limitata sui dispositivi mobili) e averne molti può introdurre altri overhead nella logica che tiene traccia di quali sono visibili. Molti strati possono anche aumentare il tempo dedicato alla rasterizzazione se sono di grandi dimensioni e si sovrappongono molto dove non in precedenza, portando a ciò che a volte viene chiamato "in ritardo". Quindi usa le tue conoscenze con oculatezza!

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

Risorse aggiuntive