Principales structures de données dans 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

Examinons les principales structures de données, qui sont les entrées et les sorties du pipeline de rendu.

Voici les structures de données:

  • Les arborescences de cadres sont composées de nœuds locaux et distants qui représentent les documents Web dans quel processus de rendu et quel moteur de rendu Blink.
  • L'arborescence de fragments immuable représente la sortie (et l'entrée) de l'algorithme de contrainte de mise en page.
  • Les arborescences de propriétés représentent les hiérarchies de transformation, de découpage, d'effet et de défilement d'un document Web. Ils sont utilisés tout au long du pipeline.
  • Les listes d'affichage et les blocs de peinture sont les entrées des algorithmes de rastérisation et de stratification.
  • Les cadres du compositeur encapsulent les surfaces, les surfaces de rendu et les tuiles de texture GPU utilisées pour dessiner à l'aide du GPU.

Avant d'examiner ces structures de données, l'exemple suivant s'appuie sur un exemple de l'examen de l'architecture. Cet exemple est utilisé tout au long de ce document avec des démonstrations de la façon dont les structures de données s'y appliquent.

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

Arbres en forme de cadre

Chrome peut parfois choisir d'afficher un cadre inter-origine dans un processus d'affichage différent de celui de son cadre parent.

Dans l'exemple de code, il y a trois frames au total:

Cadre parent foo.com, contenant deux iframes.

Avec l'isolation de sites, Chromium utilise deux processus d'affichage pour afficher cette page Web. Chaque processus de rendu possède sa propre représentation de l'arborescence des cadres pour cette page Web:

Deux arbres de frames représentant les deux processus de rendu.

Un frame affiché dans un autre processus est représenté sous la forme d'un frame distant. Un frame distant contient les informations minimales requises pour servir d'espace réservé lors du rendu, telles que ses dimensions. Sinon, le frame distant ne contient aucune information nécessaire pour afficher son contenu réel.

À l'inverse, un frame local représente un frame qui passe par le pipeline de rendu standard. Le frame local contient toutes les informations nécessaires pour transformer les données de ce frame (telles que l'arborescence DOM et les données de style) en éléments pouvant être affichés.

Le pipeline de rendu fonctionne avec la granularité d'un fragment d'arborescence de frame local. Prenons un exemple plus complexe avec foo.com comme frame principal:

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

Et la sous-fframe bar.com suivante:

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

Bien qu'il n'y ait toujours que deux moteurs de rendu, il existe désormais trois fragments d'arborescence de frames locaux, dont deux dans le processus de rendu pour foo.com et un dans le processus de rendu pour bar.com:

Représentation des deux rendus et de trois fragments d&#39;arborescence de frame.

Pour produire un frame de composition pour la page Web, Viz demande simultanément un frame de composition à partir du frame racine de chacun des trois arbres de frame locaux, puis les agrée. Consultez également la section sur les cadres du moteur de composition.

Le frame principal foo.com et le sous-cadre foo.com/other-page font partie du même arbre de frame et sont affichés dans le même processus. Toutefois, les deux frames ont toujours des cycles de vie de document indépendants, car ils font partie de différents fragments d'arborescence de frame locaux. Par conséquent, il est impossible de générer un seul frame de composition pour les deux en une seule mise à jour. Le processus de rendu ne dispose pas d'informations suffisantes pour composer le frame du moteur de composition généré pour foo.com/other-page directement dans le frame du moteur de composition pour le frame principal foo.com. Par exemple, le frame parent bar.com hors processus peut affecter l'affichage de l'iFrame foo.com/other-url en transformant l'iFrame avec CSS ou en masquant des parties de l'iFrame avec d'autres éléments de son DOM.

Cascade de mises à jour visuelles des établissements

Les propriétés visuelles telles que le facteur de mise à l'échelle de l'appareil et la taille de la fenêtre d'affichage affectent la sortie affichée et doivent être synchronisées entre les fragments d'arborescence de frame locaux. La racine de chaque fragment d'arborescence de frame local est associée à un objet widget. Les mises à jour de la propriété visuelle sont transmises au widget du frame principal avant de se propager aux autres widgets de haut en bas.

Par exemple, lorsque la taille de la fenêtre d'affichage change:

Schéma du processus expliqué dans le texte précédent.

Ce processus n'est pas instantané. Par conséquent, les propriétés visuelles répliquées incluent également un jeton de synchronisation. Le compositeur Viz utilise ce jeton de synchronisation pour attendre que tous les fragments d'arborescence de frame locaux envoient un frame de compositeur avec le jeton de synchronisation actuel. Ce processus évite de mélanger les cadres du compositeur avec des propriétés visuelles différentes.

Arbre de fragments immuables

L'arborescence de fragments immuable est la sortie de l'étape de mise en page du pipeline de rendu. Il représente la position et la taille de tous les éléments de la page (sans transformation appliquée).

Représentation des fragments dans chaque arborescence, avec un fragment marqué comme nécessitant une mise en page.

Chaque fragment représente une partie d'un élément DOM. En règle générale, il n'y a qu'un seul fragment par élément, mais il peut y en avoir d'autres s'il est réparti sur différentes pages lors de l'impression ou sur différentes colonnes dans un contexte multicolonne.

Après la mise en page, chaque fragment devient immuable et ne change plus jamais. Nous appliquons également quelques restrictions supplémentaires. Ce que nous ne faisons pas:

  • Autorisez toutes les références "haut" dans l'arborescence. (Un enfant ne peut pas avoir de pointeur vers son parent.)
  • "bulle" les données dans l'arborescence (un enfant ne lit que les informations de ses enfants, pas de son parent).

Ces restrictions nous permettent de réutiliser un fragment pour une mise en page ultérieure. Sans ces restrictions, nous devrions souvent régénérer l'ensemble de l'arborescence, ce qui est coûteux.

La plupart des mises en page sont généralement des mises à jour incrémentielles. Par exemple, une application Web met à jour une petite partie de l'UI en réponse au clic de l'utilisateur sur un élément. Idéalement, la mise en page ne doit effectuer que des tâches proportionnelles à ce qui a réellement changé à l'écran. Pour ce faire, nous devons réutiliser autant de parties de l'arbre précédent que possible. Cela signifie que nous n'avons généralement besoin que de reconstruire la colonne vertébrale de l'arborescence.

À l'avenir, cette conception immuable pourrait nous permettre de faire des choses intéressantes, comme transmettre l'arborescence de fragments immuable au-delà des limites de thread si nécessaire (pour effectuer les phases ultérieures sur un autre thread), générer plusieurs arborescences pour une animation de mise en page fluide ou effectuer des mises en page spéculatives parallèles. Il nous permet également de mettre en place une mise en page multithread.

Éléments de fragment intégrés

Le contenu intégré (texte stylisé principalement) utilise une représentation légèrement différente. Au lieu d'une structure arborescente avec des cases et des pointeurs, nous représentons le contenu intégré dans une liste plate représentant l'arborescence. L'avantage principal est qu'une représentation de liste plate pour les éléments intégrés est rapide, utile pour inspecter ou interroger des structures de données intégrées, et efficace en termes de mémoire. Cela est extrêmement important pour les performances de rendu Web, car le rendu du texte est très complexe et peut facilement devenir la partie la plus lente du pipeline, sauf s'il est très optimisé.

La liste plate est créée pour chaque contexte de mise en forme en ligne dans l'ordre d'une recherche en profondeur de son sous-arbre de mise en page en ligne. Chaque entrée de la liste est un tuple (objet, nombre de descendants). Prenons l'exemple suivant:

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

La propriété width est définie sur 0 afin que la ligne se plie entre "Bonjour" et "."

Lorsque le contexte de mise en forme en ligne de cette situation est représenté sous forme d'arborescence, il se présente comme suit:

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

La liste plate se présente comme suit:

  • (Line box, 2)
  • (Box <span>, 1)
  • (Texte "Bonjour", 0)
  • (Cadre de ligne, 3)
  • (Box <b>, 1)
  • (Texte "là", 0)
  • (Texte ".", 0)

Cette structure de données est utilisée par de nombreux consommateurs: API d'accessibilité et API de géométrie telles que getClientRects et contenteditable. Chacun a des exigences différentes. Ces composants accèdent à la structure de données plate via un curseur pratique.

Le curseur possède des API, telles que MoveToNext, MoveToNextLine et CursorForChildren. Cette représentation du curseur est très efficace pour le contenu textuel, pour plusieurs raisons:

  • L'itération dans l'ordre de recherche en profondeur est très rapide. Cette méthode est très utilisée, car elle est semblable aux mouvements du curseur. Comme il s'agit d'une liste plate, la recherche en profondeur n'augmente que le décalage du tableau, ce qui permet d'obtenir des itérations rapides et une localité de mémoire.
  • Il fournit une recherche en largeur, ce qui est nécessaire, par exemple, pour peindre l'arrière-plan des lignes et des cases en ligne.
  • Connaître le nombre de descendants permet de passer rapidement au frère suivant (il suffit d'incrémenter le décalage du tableau de ce nombre).

Arbres de propriétés

Le DOM est un arbre d'éléments (plus des nœuds de texte), et le CSS peut appliquer différents styles aux éléments.

Cela se manifeste de quatre manières:

  • Mise en page:entrées de l'algorithme de contrainte de mise en page.
  • Paint (Peinture) : indique comment peindre et rasteriser l'élément (mais pas ses descendants).
  • Visuel:effets de rastérisation/dessin appliqués au sous-arbre DOM, tels que les transformations, les filtres et le recadrage.
  • Défilement:découpage et défilement des coins arrondis et alignés sur l'axe du sous-arbre contenu.

Les arborescences de propriétés sont des structures de données qui expliquent comment les effets visuels et de défilement s'appliquent aux éléments DOM. Ils permettent de répondre à des questions telles que: où se trouve un élément DOM donné par rapport à l'écran, compte tenu de sa taille et de sa position de mise en page ? Et quelle séquence d'opérations GPU doit être utilisée pour appliquer des effets visuels et de défilement ?

Les effets visuels et de défilement sur le Web sont très complexes dans leur ensemble. L'essentiel de la fonction des arbres de propriétés est donc de traduire cette complexité en une seule structure de données qui représente précisément leur structure et leur signification, tout en éliminant le reste de la complexité du DOM et du CSS. Cela nous permet d'implémenter des algorithmes de composition et de défilement avec beaucoup plus de confiance. En particulier :

  • La géométrie et d'autres calculs potentiellement sujets à des erreurs peuvent être centralisés en un seul et même endroit.
  • La complexité de la création et de la mise à jour des arborescences de propriétés est isolée dans une seule étape du pipeline de rendu.
  • Il est beaucoup plus facile et rapide d'envoyer des arbres de propriétés à différents threads et processus que l'état DOM complet, ce qui permet de les utiliser pour de nombreux cas d'utilisation.
  • Plus il y a de cas d'utilisation, plus nous pouvons tirer parti du cache de géométrie construit par-dessus, car ils peuvent réutiliser les caches des uns et des autres.

RenderingNG utilise des arbres d'établissements à plusieurs fins, y compris les suivantes:

  • Séparation de la composition de la peinture et de la composition du thread principal.
  • Déterminer une stratégie de composition / dessin optimale
  • Mesure de la géométrie IntersectionObserver.
  • Éviter le travail pour les éléments hors écran et les tuiles de texture du GPU.
  • Invalidation efficace et précise de la peinture et du raster.
  • Mesurer le déplacement de la mise en page et le Largest Contentful Paint dans Core Web Vitals.

Chaque document Web comporte quatre arbres de propriétés distincts : "transform", "clip", "effect" et "scroll"(*). L'arbre "transform" représente les transformations CSS et le défilement. (Une transformation de défilement est représentée par une matrice de transformation 2D.) L'arborescence des clips représente les clips de débordement. L'arborescence des effets représente tous les autres effets visuels: opacité, filtres, masques, modes de fusion et autres types de clips tels que clip-path. L'arborescence de défilement représente des informations sur le défilement, telles que la façon dont les défilements sont enchaînés. Elle est nécessaire pour effectuer le défilement sur le thread du compositeur. Chaque nœud d'une arborescence de propriétés représente un défilement ou un effet visuel appliqué par un élément DOM. S'il a plusieurs effets, il peut y avoir plusieurs nœuds d'arborescence de propriétés dans chaque arborescence pour le même élément.

La topologie de chaque arbre est comme une représentation sporadique du DOM. Par exemple, s'il existe trois éléments DOM avec des clips de débordement, il y aura trois nœuds d'arborescence de clip, et la structure de l'arborescence de clip suivra la relation de bloc contenant entre les clips de débordement. Des liens existent également entre les arbres. Ces liens indiquent la hiérarchie DOM relative, et donc l'ordre d'application, des nœuds. Par exemple, si une transformation sur un élément DOM se trouve sous un autre élément DOM avec un filtre, la transformation s'applique bien sûr avant le filtre.

Chaque élément DOM possède un état de l'arborescence des propriétés, qui est un tuple à quatre éléments (transformation, clip, effet, défilement) qui indique les nœuds d'arborescence de clip, de transformation et d'effet les plus proches qui s'appliquent à cet élément. C'est très pratique, car ces informations nous permettent de connaître exactement la liste des clips, des transformations et des effets qui s'appliquent à cet élément, et dans quel ordre. Cela nous indique où il se trouve à l'écran et comment le dessiner.

Exemple

(source)

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

Pour l'exemple précédent (qui est légèrement différent de celui de l'introduction), voici les principaux éléments des arborescences de propriétés générées:

Exemple des différents éléments de l&#39;arborescence des propriétés.

Afficher des listes et peindre des blocs

Un élément d'affichage contient des commandes de dessin de bas niveau (voir ici) qui peuvent être rastérisées avec Skia. Les éléments d'affichage sont généralement simples, avec seulement quelques commandes de dessin, telles que le dessin d'une bordure ou d'un arrière-plan. L'itération de l'arborescence de peinture parcourt l'arborescence de mise en page et les fragments associés en suivant l'ordre de peinture CSS pour générer une liste d'éléments d'affichage.

Exemple :

Boîte bleue avec les mots &quot;Hello world&quot; dans un rectangle vert.

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

Ce code HTML et CSS produirait la liste d'affichage suivante, où chaque cellule est un élément d'affichage:

Arrière-plan de la vue #blue en arrière-plan #green en arrière-plan #green Texte intégré
drawRect de taille 800 x 600 et de couleur blanche. drawRect de taille 100 x 100 à la position 0,0 et de couleur bleue. drawRect de taille 80x18 à la position 8,8 et de couleur verte. drawTextBlob avec la position 8,8 et le texte "Hello world".

La liste des éléments à afficher est triée de l'arrière vers l'avant. Dans l'exemple ci-dessus, la div verte est placée avant la div bleue dans l'ordre DOM, mais l'ordre de peinture CSS exige que la div bleue à l'indice z négatif soit peinte avant (étape 3) la div verte (étape 4.1). Les éléments d'affichage correspondent à peu près aux étapes atomiques de la spécification de l'ordre de peinture CSS. Un seul élément DOM peut générer plusieurs éléments d'affichage, par exemple #green a un élément d'affichage pour l'arrière-plan et un autre pour le texte intégré. Cette granularité est importante pour représenter toute la complexité de la spécification de l'ordre de peinture CSS, comme l'entrelacement créé par la marge négative:

Rectangle vert, avec un cadre gris partiellement superposé et les mots &quot;Hello world&quot; (Bonjour le monde).

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

La liste d'affichage suivante est générée, où chaque cellule est un élément d'affichage:

Arrière-plan de la vue #green en arrière-plan #gray en arrière-plan #green Texte intégré
drawRect de taille 800 x 600 et de couleur blanche. drawRect de taille 80x18 à la position 8,8 et de couleur verte. drawRect de taille 35x20 à la position 8,16 et de couleur grise. drawTextBlob avec la position 8,8 et le texte "Hello world".

La liste des éléments à afficher est stockée et réutilisée par les mises à jour ultérieures. Si un objet de mise en page n'a pas changé lors de l'exploration de l'arborescence de peinture, ses éléments d'affichage sont copiés à partir de la liste précédente. Une optimisation supplémentaire repose sur une propriété de la spécification de l'ordre de peinture CSS : les contextes d'empilement peignent de manière atomique. Si aucun objet de mise en page n'a changé dans un contexte d'empilement, l'exploration de l'arborescence de peinture ignore le contexte d'empilement et copie l'ensemble de la séquence d'éléments d'affichage de la liste précédente.

L'état actuel de l'arborescence de propriétés est conservé lors de l'exploration de l'arborescence de peinture, et la liste des éléments d'affichage est regroupée en "blocs" d'éléments d'affichage qui partagent le même état de l'arborescence de propriétés. Cela est illustré dans l'exemple suivant:

Boîte rose avec une boîte orange inclinée.

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

La liste d'affichage suivante est générée, où chaque cellule est un élément d'affichage:

Arrière-plan de la vue #scroll en arrière-plan #scroll Texte intégré #orange en arrière-plan #orange Texte intégré
drawRect de taille 800 x 600 et de couleur blanche. drawRect de taille 100 x 100 à la position 0,0 et de couleur rose. drawTextBlob avec la position 0,0 et le texte "Hello world". drawRect de taille 75 x 200 à la position 0,0 et de couleur orange. drawTextBlob avec la position 0,0 et le texte "Je tombe".

L'arborescence des propriétés de transformation et les blocs de peinture seraient alors les suivants (simplifiés pour plus de concision):

Image du tableau précédent, avec les deux premières cellules du bloc 1, la troisième du bloc 2 et les deux dernières du bloc 3.

La liste ordonnée des segments de peinture, qui sont des groupes d'éléments d'affichage et un état d'arborescence de propriétés, sont les entrées de l'étape de stratification du pipeline de rendu. La liste complète des segments de peinture pourrait être fusionnée dans une seule couche composite et échantillonnée ensemble, mais cela nécessiterait une échantillonnage coûteuse à chaque fois que l'utilisateur ferait défiler la page. Une couche composée peut être créée pour chaque bloc de peinture et échantillonnée individuellement pour éviter toute re-échantillonnage, mais cela épuiserait rapidement la mémoire du GPU. L'étape de stratification doit faire des compromis entre la mémoire du GPU et la réduction des coûts en cas de modification. Une bonne approche générale consiste à fusionner les blocs par défaut et à ne pas fusionner les blocs de peinture dont les états d'arborescence de propriétés sont censés changer sur le thread du compositeur, comme avec le défilement du thread du compositeur ou les animations de transformation du thread du compositeur.

L'exemple précédent devrait idéalement produire deux calques composites:

  • Calque composite 800x600 contenant les commandes de dessin :
    1. drawRect avec une taille de 800 x 600 et une couleur blanche
    2. drawRect de taille 100 x 100 à la position 0,0 et de couleur rose
  • Calque composite 144x224 contenant les commandes de dessin :
    1. drawTextBlob avec la position 0,0 et le texte "Hello world"
    2. traduire 0,18
    3. rotateZ(25deg)
    4. drawRect de taille 75 x 200 à la position 0,0 et de couleur orange
    5. drawTextBlob avec la position 0,0 et le texte "Je tombe"

Si l'utilisateur fait défiler #scroll, la deuxième couche composée est déplacée, mais aucune rastérisation n'est nécessaire.

Pour l'exemple de la section précédente sur les arbres de propriétés, il y a six blocs de peinture. Avec les états de l'arborescence des propriétés (transformation, découpe, effet, défilement), ils sont les suivants:

  • Arrière-plan du document: défilement du document, extrait du document, racine, défilement du document.
  • Coin horizontal, vertical et de défilement pour le div (trois segments de peinture distincts) : défilement du document, extrait du document, floutage #one, défilement du document.
  • Iframe #one: rotation #one, extrait de défilement en cas de dépassement, floutage #one, défilement div.
  • Iframe #two: échelle #two, extrait de document, racine, défilement du document.

Cadres du moteur de composition: surfaces, surfaces de rendu et cartes de texture du GPU

Les processus de navigateur et de rendu gèrent la rastérisation du contenu, puis envoient des frames de composition au processus Viz pour présentation à l'écran. Les frames du compositeur indiquent comment assembler le contenu rasterisé et le dessiner efficacement à l'aide du GPU.

Cartes

En théorie, un processeur de rendu ou un compositeur de processus de navigateur peut rastériser des pixels dans une seule texture de la taille complète de la vue du moteur de rendu et envoyer cette texture à Viz. Pour l'afficher, le compositeur d'affichage n'a qu'à copier les pixels de cette seule texture à la position appropriée dans le tampon de trame (par exemple, l'écran). Toutefois, si ce compositeur souhaite mettre à jour ne serait-ce qu'un seul pixel, il doit re-rasteriser l'intégralité du viewport et envoyer une nouvelle texture à Viz.

Au lieu de cela, le viewport est divisé en tuiles. Une carte de texture GPU distincte est utilisée pour chaque carte avec les pixels échantillonnés pour une partie du viewport. Le moteur de rendu peut ensuite mettre à jour des cartes individuelles ou simplement modifier la position des cartes existantes à l'écran. Par exemple, lorsque vous faites défiler un site Web, la position des tuiles existantes est décalée vers le haut et une nouvelle tuile ne doit être échantillonnée que de temps en temps pour le contenu situé plus bas sur la page.

Quatre cartes.
Cette image représente une journée ensoleillée, avec quatre cartes. Lorsqu'un défilement se produit, une cinquième carte commence à apparaître. L'une des cartes n'a qu'une seule couleur (bleu ciel), et une vidéo et une iFrame sont placées au-dessus.

Quadrilatères et surfaces

Les tuiles de texture du GPU sont un type spécial de quad, qui n'est qu'un nom fantaisiste pour une catégorie de texture ou une autre. Un quad identifie la texture d'entrée et indique comment la transformer et lui appliquer des effets visuels. Par exemple, les cartes de contenu standards comportent une transformation indiquant leur position X, Y dans la grille de cartes.

Tuiles de texture du GPU.

Ces tuiles rastérisées sont encapsulées dans un pass de rendu, qui est une liste de quads. La passe de rendu ne contient aucune information sur les pixels. À la place, elle contient des instructions sur l'emplacement et la méthode à utiliser pour dessiner chaque quad afin de produire la sortie de pixel souhaitée. Il existe un quadrilateral de dessin pour chaque carte de texture de GPU. Le compositeur d'affichage n'a qu'à itérer dans la liste des quads, en dessinant chacun d'eux avec les effets visuels spécifiés, pour produire la sortie de pixel souhaitée pour le passage de rendu. La composition de quads de dessin pour une étape de rendu peut être effectuée efficacement sur le GPU, car les effets visuels autorisés sont soigneusement choisis pour être ceux qui se mappent directement sur les fonctionnalités du GPU.

Il existe d'autres types de quads de dessin que les tuiles rasterisées. Par exemple, il existe des quadrilaterals de dessin de couleur unie qui ne sont pas du tout basés sur une texture, ou des quadrilaterals de dessin de texture pour les textures non carrelées telles que la vidéo ou le canevas.

Il est également possible qu'un frame de composition intègre un autre frame de composition. Par exemple, le compositeur du navigateur produit un frame de compositeur avec l'UI du navigateur et un rectangle vide dans lequel le contenu du compositeur de rendu sera intégré. Autre exemple : les iFrames isolées par site. Cette intégration est effectuée via des surfaces.

Lorsqu'un compositeur envoie un frame de compositeur, il est accompagné d'un identifiant, appelé ID de surface, qui permet aux autres frames de compositeur de l'intégrer par référence. Le dernier frame de composition envoyé avec un ID de surface particulier est stocké par Viz. Un autre frame de composition peut ensuite s'y référer plus tard via un quadrillage de dessin de surface. Viz sait donc quoi dessiner. (Notez que les quads de dessin de surface ne contiennent que des ID de surface, et non des textures.)

Passes de rendu intermédiaires

Certains effets visuels, tels que de nombreux filtres ou modes de mélange avancés, nécessitent que deux quads ou plus soient dessinés sur une texture intermédiaire. La texture intermédiaire est ensuite dessinée dans un tampon de destination sur le GPU (ou éventuellement dans une autre texture intermédiaire), en appliquant l'effet visuel en même temps. Pour ce faire, un frame de composition contient en réalité une liste de passes de rendu. Il existe toujours une passe de rendu racine, qui est dessinée en dernier et dont la destination correspond au tampon d'images, et il peut y en avoir d'autres.

La possibilité de plusieurs passes de rendu explique le nom "pass de rendu". Chaque étape doit être exécutée de manière séquentielle sur le GPU, en plusieurs "passes", alors qu'une seule étape peut être effectuée en un seul calcul GPU massivement parallèle.

Agrégation

Plusieurs cadres de composition sont envoyés à Viz et doivent être dessinés à l'écran ensemble. Cela se fait via une phase d'agrégation qui les convertit en un seul frame de composition agrégé. L'agrégation remplace les quads de dessin de surface par les cadres de compositeur qu'ils spécifient. C'est également l'occasion d'optimiser les textures intermédiaires inutiles ou les contenus hors écran. Par exemple, dans de nombreux cas, le frame du compositeur pour une iframe isolée du site n'a pas besoin de sa propre texture intermédiaire et peut être dessiné directement dans le tampon d'images via des quads de dessin appropriés. La phase d'agrégation détermine ces optimisations et les applique en fonction de connaissances globales auxquelles les différents compilateurs de rendu n'ont pas accès.

Exemple

Voici les cadres du compositeur qui représentent l'exemple du début de cet article.

  • foo.com/index.html surface: id=0
    • Passe de rendu 0:dessiner en sortie.
      • Quad de dessin de la passe de rendu: dessinez avec un flou de 3 px et découpez dans la passe de rendu 0.
        • Passe de rendu 1:
          • Dessinez des quads pour le contenu des cartes de l'iframe #one, avec des positions X et Y pour chacune.
      • Quadrilatère de dessin de surface: avec l'ID 2, dessiné avec une transformation de mise à l'échelle et de translation.
  • Surface de l'UI du navigateur: ID=1
    • Passe de rendu 0:dessiner en sortie.
      • Dessiner des quads pour l'interface utilisateur du navigateur (également en mode carte)
  • bar.com/index.html surface: ID=2
    • Passe de rendu 0:dessiner en sortie.
      • Dessinez des quads pour le contenu de l'iframe #two, avec des positions x et y pour chacun.

Illustrations par Una Kravets.