Affichage accéléré dans Chrome

Le modèle de couche

Tom Wiltzius
Tom Wiltzius

Introduction

Pour la plupart des développeurs Web, le modèle de base d'une page Web est le DOM. L'affichage est le processus souvent obscur qui consiste à transformer cette représentation d'une page en image à l'écran. Ces dernières années, les navigateurs modernes ont modifié le fonctionnement du rendu afin de tirer parti des cartes graphiques. On parle souvent vaguement d'"accélération matérielle". Dans le contexte d'une page Web normale (et non Canvas2D ou WebGL), que signifie réellement ce terme ? Cet article présente le modèle de base sur lequel repose le rendu Web avec accélération matérielle dans Chrome.

Mises en garde importantes

Nous parlons ici de WebKit, et plus précisément, du port Chromium de WebKit. Cet article décrit en détail l'implémentation de Chrome, et non les fonctionnalités de la plate-forme Web. La plate-forme Web et les normes ne codifient pas ce niveau de détail de l'implémentation. Il n'y a donc aucune garantie que cet article s'appliquera à d'autres navigateurs. Toutefois, la connaissance des composants internes peut néanmoins être utile pour le débogage avancé et l'ajustement des performances.

Notez également que cet article traite d'un élément central de l'architecture de rendu de Chrome qui évolue très rapidement. Cet article a pour but de ne traiter que des sujets peu susceptibles de changer, mais il ne garantit pas que tous ces changements s'appliqueront dans six mois.

Il est important de comprendre que Chrome a maintenant deux chemins de rendu différents: le chemin d'accélération matérielle et le chemin d'accès logiciel plus ancien. À l'heure où nous écrivons ces lignes, toutes les pages empruntent le processus d'accélération matérielle sous Windows, ChromeOS et Chrome pour Android. Sous Mac et Linux, seules les pages qui nécessitent une composition pour une partie de leur contenu suivent la voie accélérée (voir ci-dessous pour en savoir plus sur ce qui nécessiterait une composition), mais bientôt toutes les pages suivront également la procédure accélérée.

Pour finir, nous découvrons en détail le moteur de rendu et les fonctionnalités qui ont un impact important sur les performances. Lorsque vous essayez d'améliorer les performances de votre propre site, il peut être utile de comprendre le modèle des couches, mais il est également facile de prendre une photo dans le pied: les calques sont des constructions utiles, mais la création d'un grand nombre d'éléments peut entraîner une surcharge de l'ensemble de la pile graphique. Soyez prévenu !

Du DOM à l'écran

Présentation des calques

Une fois qu'une page est chargée et analysée, elle est représentée dans le navigateur sous la forme d'une structure que de nombreux développeurs Web connaissent: le DOM. Toutefois, lors de l'affichage d'une page, le navigateur présente une série de représentations intermédiaires qui ne sont pas directement exposées aux développeurs. La plus importante de ces structures est une couche.

Il existe actuellement plusieurs types de calques dans Chrome: RenderLayers, qui est responsable des sous-arborescences du DOM, et GraphicsLayers, qui sont responsables des sous-arborescences de RenderLayers. Ce dernier point nous intéresse le plus dans le cas présent, car les calques graphiques sont ceux qui sont importés dans le GPU en tant que textures. À partir de maintenant, je vais simplement dire « calque » pour signifier GraphicsLayer.

Voyons la terminologie liée aux GPU à part: qu'est-ce qu'une texture ? Considérez-la comme une image bitmap qui est déplacée de la mémoire principale (la RAM) vers la mémoire vidéo (par exemple, VRAM, sur votre GPU). Une fois qu'il est sur le GPU, vous pouvez le mapper à une géométrie de maillage. Dans les jeux vidéo ou les programmes de CAO, cette technique est utilisée pour donner une "apparence" aux modèles 3D squelettiques. Chrome utilise des textures pour transférer des fragments de contenu de la page Web au GPU. Vous pouvez mapper à moindre coût des textures à différentes positions et transformations en les appliquant à un maillage rectangulaire très simple. C'est ainsi que fonctionne le CSS 3D. Il est également idéal pour un défilement rapide, mais nous y reviendrons plus tard.

Examinons quelques exemples pour illustrer le concept de couche.

Lorsque vous étudiez les calques dans Chrome, l'indicateur "Afficher les bordures des calques composés" est très utile dans les paramètres (c'est-à-dire, une petite icône en forme de roue dentée) dans les outils de développement, sous l'en-tête "Rendu". Il indique très simplement où les calques se trouvent à l'écran. Allumons-le. Ces captures d'écran et exemples sont tous issus de la dernière version de Chrome Canary, Chrome 27 au moment de la rédaction de ce document.

Figure 1: Page à une seule couche

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Capture d&#39;écran des bordures de rendu d&#39;une couche composite autour de la couche de base de la page
Capture d'écran des bordures de rendu d'une couche composée autour de la couche de base de la page

Cette page ne comporte qu'une seule couche. La grille bleue représente les tuiles, que vous pouvez considérer comme des sous-unités d'un calque que Chrome utilise pour importer des parties d'un grand calque à la fois dans le GPU. Ils ne sont pas vraiment importants ici.

Figure 2: Un élément dans sa propre couche

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
Capture d&#39;écran des bordures de rendu d&#39;un calque ayant fait l&#39;objet d&#39;une rotation
Capture d'écran des bordures de rendu du calque ayant fait l'objet d'une rotation

En plaçant une propriété CSS 3D sur la <div> qui la fait pivoter, nous pouvons voir à quoi elle ressemble lorsqu'un élément obtient son propre calque. Notez la bordure orange qui délimite un calque dans cette vue.

Critères de création de calques

Quel autre élément possède sa propre couche ? Les méthodes heuristiques de Chrome ont évolué au fil du temps et continuent de le faire, mais les couches de déclencheur suivantes sont actuellement créées:

  • Propriétés CSS de transformation en 3D ou en perspective
  • <video> éléments utilisant le décodage vidéo accéléré
  • Éléments <canvas> avec un contexte 3D (WebGL) ou un contexte 2D accéléré
  • Plug-ins composites (Flash)
  • Éléments comportant une animation CSS en raison de leur opacité ou utilisant une transformation animée
  • Éléments avec filtres CSS accélérés
  • L'élément a un descendant qui possède une couche de composition (en d'autres termes, si l'élément a un élément enfant qui se trouve dans sa propre couche).
  • L'élément a un frère ou une sœur ayant un z-index inférieur et une couche de composition (en d'autres termes, il est rendu au-dessus d'une couche composée).

Implications pratiques: animation

Nous pouvons également déplacer les calques, ce qui les rend très utiles pour l'animation.

Figure 3: Calques animés

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

Comme indiqué précédemment, les calques sont très utiles pour déplacer du contenu Web statique. En règle générale, Chrome peigne le contenu d'un calque dans un bitmap logiciel avant de l'importer dans le GPU en tant que texture. Si ce contenu ne change pas à l'avenir, il n'est pas nécessaire de le repeindre. C'est une bonne chose: le repeinture prend du temps qui peut être consacré à d'autres tâches, comme l'exécution de JavaScript. Si le rendu est long, cela entraîne des problèmes ou des retards dans les animations.

Voici, par exemple, cette vue de la timeline des outils de développement: aucune opération "Paint" n'est effectuée pendant la rotation de ce calque.

Capture d&#39;écran de la timeline des outils de développement pendant l&#39;animation
Capture d'écran de la timeline des outils de développement pendant l'animation

Non valide. Remise en peinture

Toutefois, si le contenu du calque change, il doit être repeint.

Figure 4: Repeindre des calques

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

Chaque fois que l'utilisateur clique sur l'élément d'entrée, il devient plus large de 1 pixel. L'ensemble de l'élément est alors remis en page et repeint. Dans le cas présent, il s'agit d'un calque entier.

L'outil "Show paint rects" (Afficher les rectangles de peinture) des outils de développement, également sous l'en-tête "Rendering" (Rendu) des paramètres des outils de développement, est un bon moyen de voir ce qui se passe. Après l'avoir activé, notez que l'élément animé et le bouton clignotent tous deux en rouge lorsque l'utilisateur clique dessus.

Capture d&#39;écran de la case à cocher &quot;Afficher les rectangles de peinture&quot;
Capture d'écran de la case à cocher "Afficher les rectangles"

Les événements "Paint" s'affichent également dans la timeline des outils de développement. Les lecteurs aigus remarqueront peut-être la présence de deux événements "Paint" : un pour le calque et un pour le bouton lui-même, qui est repeint lorsqu'il passe d'un état abaissé.

Capture d&#39;écran du nouveau calque d&#39;un calque dans la timeline des outils de développement
Capture d'écran de la timeline des outils de développement qui repeigne un calque

Notez que Chrome n'a pas toujours besoin de repeindre l'intégralité du calque. Il essaie de ne repeindre que la partie du DOM qui a été invalidée. Dans le cas présent, l'élément DOM que nous avons modifié correspond à la taille du calque entier. Mais dans de nombreux autres cas, il y aura de nombreux éléments DOM dans un calque.

Une question évidente est celle qui provoque une invalidation et force un repeinture. Il est difficile d'y répondre de manière exhaustive, car de nombreux cas particuliers peuvent forcer les invalidations. La cause la plus courante est la modification du DOM en manipulant des styles CSS ou en provoquant une nouvelle mise en page. Tony Gentilcore a publié un excellent article de blog sur les causes de la remise en page, et Stoyan Stefanov propose un article sur la peinture plus en détail (mais qui se termine simplement par la peinture, et non par ce trucage sophistiqué).

La meilleure façon de déterminer si cela a une incidence sur un élément sur lequel vous travaillez est d'utiliser les outils de la timeline des outils de développement et de l'option "Show Paint Rects" (Afficher le rectangle de peinture) pour voir si vous repeignez alors que ce n'était pas le cas. Essayez ensuite d'identifier l'endroit où vous avez nettoyé le DOM juste avant cette nouvelle mise en page/repeindre. Si peindre est inévitable, mais semble prendre beaucoup de temps, consultez l'article d'Eberhard Gräther sur le mode peinture continue dans les outils de développement.

Synthèse: du DOM à l'écran

Comment Chrome transforme-t-il le DOM en image d'écran ? Sur le plan conceptuel:

  1. Elle utilise le DOM et le divise en couches.
  2. Peint chacune de ces couches indépendamment en bitmaps logiciels
  3. Les importer dans le GPU en tant que textures
  4. Combine les différentes couches dans l'image finale de l'écran.

Tout cela doit se produire la première fois que Chrome génère un frame d'une page Web. Toutefois, des raccourcis peuvent être nécessaires pour les frames suivants:

  1. Si certaines propriétés CSS changent, il n'est pas nécessaire de repeindre quoi que ce soit. Chrome peut simplement recomposer les calques existants qui se trouvent déjà sur le GPU en tant que textures, mais avec différentes propriétés de composition (par exemple, dans des positions différentes, avec des opacités différentes, etc.).
  2. Si une partie d'un calque est invalidée, elle est repeinte et réimportée. Si son contenu reste le même, mais que ses attributs composés changent (par exemple, si le contenu est traduit ou que son opacité change), Chrome peut le laisser sur le GPU et le recomposer pour créer une nouvelle image.

Comme vous pouvez le constater, le modèle de composition basé sur des couches a des conséquences importantes sur les performances d'affichage. La composition est relativement peu coûteuse lorsque rien n'a besoin d'être peint. Par conséquent, éviter de repeindre des couches est un bon objectif global lorsque vous essayez de déboguer les performances de rendu. Les développeurs expérimentés examineront la liste des déclencheurs de composition ci-dessus et réaliseront qu'il est possible de forcer facilement la création de couches. Attention cependant à les créer à l'aveugle, car ils ne sont pas libres: ils occupent de la mémoire dans la RAM du système et sur le GPU (particulièrement limité sur les appareils mobiles), et leur nombre élevé peut entraîner d'autres surcharges dans la logique et qui sont visibles. De nombreux calques peuvent également augmenter le temps consacré à la rastérisation s'ils sont volumineux et se chevauchent beaucoup, ce qui entraîne ce que l'on appelle parfois une "superposition". Alors, utilisez vos connaissances à bon escient !

C'est tout pour aujourd'hui. Nous vous communiquerons prochainement d'autres articles sur les implications pratiques du modèle à couches.

Autres ressources