Principales structures de données et leur rôle 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

Les posts précédents de cette série ont donné un aperçu des objectifs, des propriétés clés et des éléments de haut niveau des composants de l'architecture RenderingNG. Explorons à présent les principales structures de données qui sont des entrées et des sorties du pipeline de rendu.

Ces structures de données sont:

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

Avant de passer en revue ces structures de données, je veux montrer l'exemple simple suivant qui s'appuie sur l'une de la publication précédente. J'utiliserai cet exemple plusieurs fois dans cet article, pour montrer comment les structures de données s'appliquent.

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

Encadrement des arbres

Chrome peut parfois choisir d'afficher un frame multi-origine dans un processus de rendu différent de son frame parent.

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

Un cadre parent foo.com contenant deux iFrames.

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

Deux arborescences de cadres représentant les deux processus de rendu.

Un frame rendu dans un processus différent est représenté comme un frame distant. Un frame distant contient le minimum d'informations nécessaires pour servir d'espace réservé dans le rendu, telles que ses dimensions, par exemple. Dans le cas contraire, l'image distante ne contient aucune information nécessaire pour afficher son contenu réel.

En revanche, une image locale représente une image qui transite par le pipeline de rendu standard décrit dans les articles précédents. 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 un contenu pouvant être affiché.

Le pipeline de rendu opère sur la précision d'un fragment d'arborescence de frame local. Prenons un exemple plus compliqué avec foo.com comme frame principal:

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

Et le sous-frame bar.com suivant:

<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 local, 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 des trois fragments d&#39;arborescence de frames.

Pour générer un frame compositeur pour la page Web, Visualisation demande simultanément un frame compositeur à partir du frame racine de chacun des trois arborescences de frames locaux, puis les agrège. (Consultez également la section sur les cadres du compositeur plus loin dans cet article.)

Le frame principal foo.com et le sous-frame foo.com/other-page font partie de la même arborescence de frames et sont rendus selon le même processus. Toutefois, les deux frames ont toujours des cycles de vie de documents indépendants, car ils font partie de différents fragments d'arborescence de frames locaux. Pour cette raison, il n'est pas possible de générer une image compositeur pour les deux dans une même mise à jour. Le processus de rendu ne dispose pas de suffisamment d'informations pour composer le frame du compositeur généré pour foo.com/other-page directement dans le frame du compositeur pour le frame principal foo.com. Par exemple, le cadre parent bar.com hors processus peut affecter l'affichage de l'iFrame foo.com/other-url en modifiant l'iFrame avec du code CSS ou en masquant certaines parties de l'iFrame avec d'autres éléments dans son DOM.

Cascade de mise à jour des propriétés visuelles

Les propriétés visuelles telles que le facteur d'é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 de l'arborescence des frames locaux. Un objet widget est associé à la racine de chaque fragment d'arborescence de frames local. Les mises à jour des propriétés visuelles sont transmises au widget du frame principal avant de se propager vers les widgets restants 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 de visualisation utilise ce jeton de synchronisation pour attendre que tous les fragments de l'arborescence de frames locaux envoient un frame compositeur avec le jeton de synchronisation actuel. Ce processus évite de mélanger des images du compositeur avec différentes propriétés visuelles.

Arborescence des fragments immuables

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

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

Chaque fragment représente une partie d'un élément DOM. Il n'y a généralement qu'un seul fragment par élément, mais il peut y en avoir plus s'il est divisé en plusieurs pages lors de l'impression ou sur plusieurs colonnes dans un contexte à plusieurs colonnes.

Après la mise en page, chaque fragment devient immuable et n'est plus jamais modifié. Plus important encore, nous appliquons également quelques restrictions supplémentaires. Ce que nous ne faisons pas:

  • Autorisez toutes les références "vers le haut" dans l'arborescence. Un enfant ne peut pas avoir de pointeur vers son parent.
  • des données « bulles » 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, il nous faudrait souvent régénérer l'arbre entier, ce qui coûte cher.

La plupart des mises en page sont généralement des mises à jour incrémentielles, par exemple une application Web qui met à jour une petite partie de l'interface utilisateur en réponse à un clic de l'utilisateur sur un élément. Idéalement, la mise en page ne devrait fonctionner que proportionnellement à ce qui a été réellement modifié à l'écran. Pour ce faire, nous pouvons réutiliser autant de parties de l'arborescence précédente que possible. Cela signifie (généralement) que nous n'avons besoin de reconstruire que la colonne vertébrale de l'arbre.

À l'avenir, cette conception immuable nous permettra d'effectuer des choses intéressantes, comme transmettre l'arborescence de fragments immuable au-delà des limites du thread si nécessaire (pour effectuer les phases suivantes sur un thread différent), générer plusieurs arborescences pour une mise en page fluide ou effectuer des mises en page spéculatives parallèles. Cela nous donne également le potentiel de la mise en page multithread elle-même.

Éléments de fragment intégrés

Le contenu intégré (principalement avec des styles) utilise une représentation légèrement différente. Plutôt qu'une arborescence avec des cases et des pointeurs, nous représentons le contenu intégré dans une liste plate représentant l'arborescence. Le principal avantage est qu'une représentation sous forme de liste plate pour les lignes intégrées est rapide, utile pour inspecter ou interroger les structures de données intégrées, et permet d'optimiser la mémoire. Ce point 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, à moins qu'il ne soit hautement optimisé.

Il est intéressant de noter que cette approche est très semblable à la façon dont Internet Explorer représentait son DOM tel qu'il avait été initialement créé de la même manière qu'un éditeur de texte.

La liste plate est créée pour chaque contexte de mise en forme intégrée par ordre de recherche en profondeur dans la sous-arborescence de mise en page intégrée. Chaque entrée de la liste est un tuple de (objet, nombre de descendants). Prenons l'exemple de ce DOM:

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

(Notez que la propriété width est définie sur 0 afin que la ligne renvoie à la ligne "Hi" et "there".) Lorsque le contexte de mise en forme intégrée pour 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:

  • (Zone de texte, 2)
  • (Encadré <span>, 1)
  • (Texte "Bonjour", 0)
  • (Zone de texte, 3)
  • (Encadré <b>, 1)
  • (Texte "là", 0)
  • (Texte ".", 0)

De nombreux consommateurs utilisent cette structure de données: les API d'accessibilité et les API de géométrie telles que getClientRects et contenteditable. Chacun a ses propres exigences. Ces composants accèdent à la structure de données plate via un curseur de commodité.

Le curseur contient 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 de la recherche en profondeur est très rapide. Cette technique est très souvent utilisée, car elle est similaire aux mouvements du curseur de saisie. Comme il s'agit d'une liste plate, la recherche axée sur la profondeur ne fait qu'incrémenter le décalage du tableau, fournissant ainsi des itérations et une localité de la mémoire rapides.
  • Elle permet une recherche globale, qui est nécessaire lorsque, par exemple, vous pouvez peindre l'arrière-plan d'une ligne et de zones intégrées.
  • Connaître le nombre de descendants permet de passer rapidement au frère suivant (en incrémentant simplement le décalage du tableau de ce nombre).

Arbres de propriété

Comme vous le savez, le DOM est une arborescence d'éléments (ainsi que des nœuds de texte) et le CSS peut appliquer différents styles aux éléments.

Ils se déclinent principalement sous quatre types d'effets:

  • Layout (Mise en page) : entrées de l'algorithme de contrainte de mise en page.
  • Paint:comment peindre et matricier l'élément (mais pas ses descendants).
  • Visuel:effets matriciels/de dessin appliqués à la sous-arborescence DOM, tels que les transformations, les filtres et le rognage.
  • Défilement:rognage et défilement de la sous-arborescence contenue selon les axes et les angles arrondis.

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 faut-il utiliser 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 splendeur. Par conséquent, les arborescences de propriétés traduisent cette complexité en une structure de données unique 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 :

  • Les géométries potentiellement sources d'erreurs et d'autres calculs peuvent être centralisées au même endroit.
  • Créer et mettre à jour des arborescences de propriétés est complexe en une seule étape du pipeline de rendu.
  • Il est beaucoup plus facile et plus rapide d'envoyer des arborescences de propriétés à différents threads et processus qu'à l'état DOM complet, ce qui permet de les utiliser dans de nombreux cas d'utilisation.
  • Plus il y a de cas d'utilisation, plus nous pouvons tirer parti de la mise en cache géométrique sur des éléments géographiques supérieurs, car ils peuvent réutiliser les caches des autres.

RenderingNG utilise des arborescences de propriétés à de nombreuses fins, y compris:

Chaque document Web possède quatre arborescences de propriétés distinctes: transformation, rognage, effet et défilement.(*) L'arborescence de transformation représente les transformations CSS et le défilement. (Une transformation de défilement est représentée par une matrice de transformation bidimensionnelle.) L'arborescence d'extraits représente les extraits de dépassement. L'arborescence d'effets représente tous les autres effets visuels: opacité, filtres, masques, modes de combinaison et autres types d'extraits tels que le chemin de rognage. L'arborescence de défilement représente des informations sur le défilement, telles que la chaîne de défilements. Elle est nécessaire pour effectuer le défilement sur le thread 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 y 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 ressemble à une représentation creuse du DOM. Par exemple, s'il y a trois éléments DOM avec des extraits de dépassement, il y aura trois nœuds d'arborescence de extraits, et la structure de l'arborescence d'extraits suivra la relation de bloc conteneur entre les extraits de dépassement. Il existe également des liens 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 en dessous d'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 d'arborescence de propriétés, qui est un 4-tuple (transformation, rognage, effet, défilement) qui indique les nœuds d'arborescence de rognage, de transformation et d'arborescence d'effets les plus proches qui prennent effet sur cet élément. C'est très pratique, car ces informations nous permettent de connaître exactement la liste des extraits, des transformations et des effets qui s'appliquent à cet élément, ainsi que leur ordre. Elle nous indique son emplacement à 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 (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 de propriétés.

Afficher des listes et appliquer des fragments

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. Le parcours de l'arborescence de peinture parcourt l'arborescence de mise en page et les fragments associés en suivant l'ordre de rendu CSS pour générer une liste d'éléments d'affichage.

Exemple :

Un rectangle vert contenant les mots &quot;Hello world&quot;

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

Ces codes HTML et CSS génèrent la liste d'affichage suivante, dans laquelle chaque cellule correspond à un élément d'affichage:

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

La liste des éléments d'affichage est classée dos à l'avant. Dans l'exemple ci-dessus, le div vert est placé avant le div bleu dans l'ordre DOM, mais l'ordre de rendu CSS nécessite que le z-index bleu négatif soit peint avant (étape 3) le div vert (étape 4.1). Les articles affichés correspondent à peu près aux étapes atomiques de la spécification de l'ordre de rendu CSS. Un seul élément DOM peut entraîner plusieurs éléments d'affichage, tels que #green a un élément d'affichage pour l'arrière-plan et un autre pour le texte intégré. Cette précision est importante pour représenter toute la complexité de la spécification de l'ordre de rendu CSS, comme l'entrelacement créé par une marge négative:

Rectangle vert, avec une zone grise partiellement superposée et les mots &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>

La liste d'affichage suivante s'affiche, dans laquelle chaque cellule correspond à un élément d'affichage:

Arrière-plan de la vue Arrière-plan #green Arrière-plan #gray #green texte intégré
drawRect de taille 800 x 600 et de couleur blanche. drawRect avec une taille de 80 x 18 à la position 8,8 et une couleur verte. drawRect avec une taille de 35 x 20 aux positions 8 et 16,et une couleur grise drawTextBlob avec la position 8 et 8 et le texte "Hello world".

La liste des éléments d'affichage est stockée et réutilisée par des mises à jour ultérieures. Si un objet de mise en page n'a pas été modifié pendant le parcours, 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 d'ordre de rendu CSS : les contextes d'empilement sont affichés de manière atomique. Si aucun objet de mise en page n'a été modifié dans un contexte d'empilement, le parcours de l'arbre de peinture ignore le contexte d'empilement et copie la séquence complète d'éléments d'affichage de la liste précédente.

L'état actuel de l'arborescence des propriétés est conservé pendant le parcours. La liste des éléments d'affichage est regroupée en "segments" d'éléments d'affichage qui partagent le même état d'arborescence de propriétés. Cela est illustré dans l'exemple suivant:

Cadre rose avec un rectangle orange incliné.

<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 s'affiche, dans laquelle chaque cellule correspond à un élément d'affichage:

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

L'arborescence de propriétés de transformation et les fragments "paint" se présenteraient alors (simplifiés par souci de concision):

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

La liste numérotée de fragments de peinture, qui sont des groupes d'éléments d'affichage et un état d'arborescence de propriétés, constituent les entrées de l'étape de superposition du pipeline de rendu. La liste complète des fragments de peinture peut être fusionnée en une seule couche composée et rastérisée ensemble, mais cela nécessiterait une rastérisation coûteuse chaque fois que l'utilisateur faisait défiler la page. Une couche composée pour chaque fragment de peinture peut être rastérisée individuellement pour éviter toute nouvelle rastérisation, mais cela épuiserait rapidement la mémoire GPU. L'étape de couche doit faire des compromis entre la mémoire du GPU et réduire les coûts lorsque les choses changent. Une bonne approche générale consiste à fusionner des fragments par défaut, et non à fusionner des fragments paint ayant des états d'arborescence de propriétés susceptibles de changer sur le thread compositeur, par exemple avec le défilement du thread compositeur ou les animations de transformation du thread compositeur.

Dans l'idéal, l'exemple précédent doit produire deux couches composées:

  • Une couche composée de 800 x 600 contenant les commandes de dessin :
    1. drawRect de taille 800 x 600 et de couleur blanche
    2. drawRect avec une taille de 100 x 100 à la position 0,0 et une couleur rose
  • Calque composite de 144 x 224 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 avec une taille de 75 x 200 en position 0,0 et une 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 ici, de la section précédente sur les arbres de propriété, il existe six blocs de peinture. Voici ce que sont les états suivants de l'arborescence de propriétés (transformation, rognage, effet, défilement) :

  • Arrière-plan du document: défilement du document, extrait, racine, défilement du document.
  • Angle horizontal, vertical et de défilement pour l'élément div (trois blocs de peinture distincts) : défilement du document, extrait de document, flou #one, défilement du document
  • iFrame #one: rotation de #one, extrait de défilement en dépassement, #one floutage, défilement div.
  • iFrame #two: échelle de #two, extrait de document, racine, défilement du document.

Cadres compositeurs: surfaces, surfaces de rendu et tuiles de texture GPU

Comme indiqué dans l'article précédent (cliquez ici pour voir un exemple concret), les processus de navigateur et de rendu gèrent la rastérisation du contenu, puis envoient les frames du compositeur au processus de visualisation pour les présenter à l'écran. Les trames compositeurs permettent à RenderingNG d'assembler le contenu rastérisé et de le dessiner efficacement à l'aide du GPU.

Cartes

En théorie, un compositeur de processus de rendu ou de navigateur peut rastériser les pixels en une seule texture sur toute la taille de la fenêtre d'affichage du moteur de rendu et envoyer cette texture à Visualisation. Pour l'afficher, le compositeur d'affichage doit simplement copier les pixels de cette texture unique dans la position appropriée dans le tampon d'images (par exemple, l'écran). Toutefois, si ce compositeur souhaite mettre à jour ne serait-ce qu'un seul pixel, il doit rastériser à nouveau toute la fenêtre d'affichage et envoyer une nouvelle texture à Visualisation.

Au lieu de cela, la fenêtre d'affichage est divisée en tuiles. Une tuile de texture GPU distincte associe chaque tuile avec les pixels rastérisés d'une partie de la fenêtre d'affichage. Le moteur de rendu peut ensuite mettre à jour des tuiles individuelles ou même simplement modifier la position à l'écran des tuiles existantes. Par exemple, lors du défilement d'un site Web, la position des tuiles existantes décalerait vers le haut et, ce n'est qu'occasionnellement qu'une nouvelle carte devra être rastérisée pour le contenu plus bas sur la page.

Quatre vignettes.

L'image ci-dessus représente une journée ensoleillée, avec quatre tuiles. Lors du défilement, une cinquième vignette commence à apparaître. Il se trouve que l'une des tuiles n'a qu'une seule couleur (bleu ciel), et il y a une vidéo et un iFrame en haut. Ce qui mène au sujet suivant.

Tableaux et surfaces

Les tuiles de texture GPU sont un type particulier de quad, qui désigne 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 vignettes de contenu standard ont une transformation indiquant leur position x, y dans la grille de tuiles.

Tuiles de texture GPU.

Ces tuiles rastérisées sont encapsulées dans une passe de rendu, qui est une liste de quads. La passe de rendu ne contient aucune information sur les pixels. Elle indique où et comment dessiner chaque quad afin de produire la sortie de pixel souhaitée. Il existe un quad quad de dessin pour chaque tuile de texture GPU. Le compositeur d'affichage doit simplement itérer la liste des quadruples, en dessinant chacun avec les effets visuels spécifiés, pour produire la sortie de pixel souhaitée pour la passe de rendu. La composition des quads de dessin pour une passe de rendu peut être effectuée efficacement sur le GPU, car les effets visuels autorisés sont soigneusement choisis pour correspondre directement aux fonctionnalités du GPU.

Il existe d'autres types de quads de dessin en plus des tuiles rastérisées. Par exemple, il existe des quadrants de dessin de couleur unie qui ne sont pas du tout soutenus par une texture, ou des quadrants de dessin de texture pour les textures autres que des tuiles, comme la vidéo ou le canevas.

Il est également possible qu'une trame du compositeur puisse en intégrer une autre. Par exemple, le compositeur du navigateur génère un frame compositeur avec l'interface utilisateur du navigateur et un rectangle vide dans lequel le contenu du compositeur de rendu sera intégré. Autre exemple : les iFrames isolés sur un site. Cette intégration s'effectue à l'aide de surfaces.

Lorsqu'un compositeur envoie une trame de compositeur, celle-ci est accompagnée d'un identifiant, appelé ID de surface, permettant à d'autres frames du compositeur de l'intégrer par référence. La trame compositeur la plus récente envoyée avec un ID de surface particulier est stockée par Viz. Un autre cadre compositeur peut ensuite s'y référer ultérieurement via un quad de dessin de surface, et Viz sait donc ce qu'il doit dessiner. (Notez que les quads de dessin de surface ne contiennent que les identifiants de surface, et non des textures.)

Passes de rendu intermédiaires

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

La possibilité qu'il y ait plusieurs cartes de rendu explique le nom "passe de rendu". Chaque carte doit être exécutée de manière séquentielle sur le GPU, en plusieurs "passes", tandis qu'une seule passe peut être effectuée en un seul calcul GPU massivement parallèle.

Agrégation

Plusieurs cadres compositeurs sont envoyés à Viz, et ils doivent être dessinés ensemble à l'écran. Pour ce faire, une phase d'agrégation les convertit en une seule trame de composition agrégée. L'agrégation remplace les quads de dessin de surface par les cadres du compositeur qu'ils spécifient. C'est également l'occasion d'optimiser les textures ou le contenu intermédiaires inutiles qui se trouvent en dehors de l'écran. Par exemple, dans de nombreux cas, le frame compositeur d'un iFrame isolé sur un site n'a pas besoin de sa propre texture intermédiaire et peut être dessiné directement dans le tampon du frame via des quads de dessin appropriés. La phase d'agrégation détermine ces optimisations et les applique en fonction des connaissances globales auxquelles les compositeurs de rendu individuels ne peuvent pas accéder.

Exemple

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

  • Surface foo.com/index.html: id=0
    • Render pass 0 (Passe de rendu 0) : dessinez en sortie.
      • Quadruple dessin de la passe de rendu: dessinez avec un flou de 3 pixels et rognez dans la passe de rendu 0.
        • Passe de rendu 1:
          • Dessinez les quads pour le contenu des cartes de l'iFrame #one, avec les positions x et y pour chaque élément.
      • Quadrillage de dessin de surface: avec l'ID 2, dessiné avec une mise à l'échelle et une transformation de translation.
  • Surface de l'UI du navigateur: ID=1
    • Render pass 0 (Passe de rendu 0) : dessinez en sortie.
      • Dessiner des quads pour l'interface utilisateur du navigateur (également en mosaïque)
  • Surface bar.com/index.html: ID=2
    • Render pass 0 (Passe de rendu 0) : dessinez en sortie.
      • Dessinez des quads pour le contenu de l'iFrame #two, avec les positions x et y pour chacun.

Conclusion

Merci de votre attention, Avec les deux posts précédents, cela conclut la présentation de RenderingNG. Nous verrons ensuite en détail les défis et la technologie associés à de nombreux sous-composants du pipeline de rendu, du début à la fin. Elles le seront bientôt !

Illustrations d'Una Kravets