Schlüsseldatenstrukturen in 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

Sehen wir uns die wichtigsten Datenstrukturen an: Ein- und Ausgaben für die Rendering-Pipeline.

Diese Datenstrukturen sind:

  • Frame-Baumstrukturen bestehen aus lokalen und Remote-Knoten, die darstellen, welche Webdokumente sich in welchem Renderingprozess befinden und welcher Blink-Renderer ist.
  • Die unveränderliche Fragmentstruktur stellt die Ausgabe (und die Eingabe) des Algorithmus für die Layouteinschränkung dar.
  • Property-Bäume stellen die Transformations-, Clip-, Effekt- und Scroll-Hierarchie eines Webdokuments dar. Sie werden in der gesamten Pipeline verwendet.
  • Anzeigelisten und Farbblöcke sind die Eingaben für die Raster- und Ebenenalgorithmen.
  • Kompositor-Frames kapseln Oberflächen, Renderingflächen und GPU-Texturkacheln, die zum Zeichnen mit der GPU verwendet werden.

Das folgende Beispiel baut auf einer Datenstruktur aus Architekturprüfung auf, bevor Sie diese Datenstrukturen durchgehen. Dieses Beispiel wird im gesamten Dokument verwendet und zeigt, wie die Datenstrukturen darauf angewendet werden.

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

Rahmen für Bäume

Chrome rendert einen ursprungsübergreifenden Frame manchmal in einem anderen Renderingprozess als den übergeordneten Frame.

Im Beispielcode gibt es insgesamt drei Frames:

Ein übergeordneter Frame &quot;foo.com&quot; mit zwei iFrames.

Bei der Website-Isolierung verwendet Chromium zum Rendern dieser Webseite zwei Rendering-Prozesse. Jeder Renderingprozess verfügt über eine eigene Darstellung der Frame-Baumstruktur der Webseite:

Zwei Frame-Bäume, die die beiden Renderingprozesse darstellen.

Ein Frame, der in einem anderen Prozess gerendert wird, wird als Remote-Frame dargestellt. Ein Remote-Frame enthält die Mindestinformationen, die für die Funktion als Platzhalter beim Rendering erforderlich sind, z. B. die Abmessungen. Andernfalls enthält der Remote-Frame keine Informationen, die zum Rendern seines eigentlichen Inhalts erforderlich sind.

Im Gegensatz dazu stellt ein lokaler Frame einen Frame dar, der die Standard-Rendering-Pipeline durchläuft. Der lokale Frame enthält alle Informationen, die erforderlich sind, um die Daten für diesen Frame (z. B. die DOM-Baum- und -Stildaten) in etwas umzuwandeln, das gerendert und angezeigt werden kann.

Die Rendering-Pipeline arbeitet mit dem Detaillierungsgrad eines lokalen Framebaumfragments. Sehen Sie sich ein etwas komplizierteres Beispiel mit foo.com als Hauptframe an:

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

Und den folgenden bar.com-Subframe:

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

Obwohl es immer noch nur zwei Renderer gibt, gibt es jetzt drei lokale Frame Tree-Fragmente, wobei zwei im Renderingprozess für foo.com und eines im Renderingprozess für bar.com vorhanden sind:

Darstellung der beiden Renderings und der drei Frame-Baumfragmente

Um einen zusammengesetzten Frame für die Webseite zu erstellen, fordert Viz gleichzeitig einen zusammengesetzten Frame aus dem Stammframe jeder der drei lokalen Frame-Strukturen an und aggregiert diese dann. Weitere Informationen finden Sie im Abschnitt Compositor Frames.

Der Hauptframe foo.com und der Subframe foo.com/other-page sind Teil derselben Framestruktur und werden im selben Prozess gerendert. Die beiden Frames haben jedoch weiterhin unabhängige Dokumentlebenszyklen, da sie Teil verschiedener lokaler Framestrukturfragmente sind. Aus diesem Grund ist es unmöglich, einen zusammengesetzten Frame für beide in einer Aktualisierung zu generieren. Der Renderingprozess hat nicht genügend Informationen, um den für foo.com/other-page generierten zusammengesetzten Frame direkt im zusammengesetzten Frame für den foo.com-Hauptframe zusammenzusetzen. Beispielsweise kann sich der übergeordnete Out-of-Process-Frame bar.com auf die Anzeige des iFrames foo.com/other-url auswirken, indem der iFrame mit CSS umgewandelt oder Teile des iFrames mit anderen Elementen im DOM verdeckt wird.

Wasserfall für die Aktualisierung visueller Eigenschaften

Visuelle Eigenschaften wie der Skalierungsfaktor des Geräts und die Größe des Darstellungsbereichs wirken sich auf die gerenderte Ausgabe aus und müssen zwischen den lokalen Frame Tree-Fragmenten synchronisiert werden. Dem Stamm jedes lokalen Frame-Baumfragments ist ein Widget-Objekt zugeordnet. Aktualisierungen visueller Eigenschaften werden zuerst im Widget des Hauptframes übernommen, bevor sie von oben nach unten auf die verbleibenden Widgets angewendet werden.

Wenn sich die Größe des Darstellungsbereichs ändert, geschieht beispielsweise Folgendes:

Diagramm des im vorherigen Text erläuterten Prozesses.

Da dieser Vorgang nicht sofort erfolgt, enthalten die replizierten visuellen Eigenschaften auch ein Synchronisierungstoken. Der Viz-Compositor verwendet dieses Synchronisierungstoken, um zu warten, bis alle lokalen Frame Tree-Fragmente einen Compositor-Frame mit dem aktuellen Synchronisierungstoken gesendet haben. Dadurch wird vermieden, dass zusammengesetzte Frames mit unterschiedlichen visuellen Eigenschaften vermischt werden.

Der unveränderliche Fragmentbaum

Die unveränderliche Fragmentstruktur ist die Ausgabe der Layoutphase der Rendering-Pipeline. Er stellt die Position und Größe aller Elemente auf der Seite (ohne angewendete Transformationen) dar.

Darstellung der Fragmente in jedem Baum, wobei ein Fragment als erforderliches Layout markiert wird.

Jedes Fragment stellt einen Teil eines DOM-Elements dar. Normalerweise gibt es nur ein Fragment pro Element. Wenn es jedoch beim Drucken auf verschiedene Seiten aufgeteilt ist, können auch mehr vorhanden sein. Bei einem mehrspaltigen Kontext können jedoch auch mehr Spalten vorhanden sein.

Nach dem Layout ist jedes Fragment unveränderlich und nie wieder geändert. Außerdem weisen wir einige zusätzliche Einschränkungen auf. Was wir nicht tun:

  • Lassen Sie alle „nach oben“-Verweise in der Baumstruktur zu. Ein untergeordnetes Element darf keinen Verweis auf das übergeordnete Element haben.
  • Blasen Sie Daten in der Baumstruktur herunter (ein untergeordnetes Element liest nur Informationen von seinen untergeordneten Elementen, nicht von seinem übergeordneten Element).

Diese Einschränkungen ermöglichen es uns, ein Fragment für ein nachfolgendes Layout wiederzuverwenden. Ohne diese Einschränkungen müssten wir häufig den gesamten Baum neu generieren, was teuer ist.

Die meisten Layouts sind in der Regel inkrementelle Updates, z. B. wenn eine Webanwendung einen kleinen Teil der Benutzeroberfläche aktualisiert, nachdem der Nutzer auf ein Element geklickt hat. Idealerweise sollte das Layout nur proportional zu den Änderungen auf dem Bildschirm passen. Das erreichen wir, indem wir so viele Teile des vorherigen Baums wie möglich wiederverwenden. Das bedeutet in der Regel, dass wir nur die Wirbelsäule des Baums neu aufbauen müssen.

In Zukunft könnte dieses unveränderliche Design es uns ermöglichen, interessante Dinge zu tun, wie z. B. den unveränderlichen Fragmentbaum bei Bedarf über Thread-Grenzen zu übergeben (um nachfolgende Phasen in einem anderen Thread auszuführen), mehrere Bäume für eine reibungslose Layoutanimation zu generieren oder parallele spekulative Layouts auszuführen. Es bietet uns auch das Potenzial eines Multithreading-Layouts selbst.

Fragmentierte Inline-Elemente

Bei Inline-Inhalten (hauptsächlich formatierter Text) wird eine etwas andere Darstellung verwendet. Anstelle einer Baumstruktur mit Feldern und Zeigern stellen wir Inline-Inhalte in einer flachen Liste dar, die den Baum darstellt. Der Hauptvorteil besteht darin, dass eine einfache Listendarstellung für Inlines schnell ist, nützlich für die Prüfung oder Abfrage von Inline-Datenstrukturen ist und speichereffizient ist. Dies ist für die Rendering-Leistung im Web äußerst wichtig, da das Rendern von Text sehr komplex ist und, sofern nicht stark optimiert, leicht zum langsamsten Teil der Pipeline werden kann.

Die einfache Liste wird für jeden Inline-Formatierungskontext in der Reihenfolge einer tieferen Suche in der zugehörigen Unterstruktur des Inline-Layouts erstellt. Jeder Eintrag in der Liste ist ein Tupel von (Objekt, Anzahl der Nachfolgerelemente). Betrachten Sie zum Beispiel dieses DOM:

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

Das Attribut width ist auf 0 gesetzt, sodass die Linie zwischen „Hi“ und „there“ verläuft.

Wenn der Inline-Formatierungskontext für diese Situation als Baum dargestellt wird, sieht das so aus:

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

Die einfache Liste sieht so aus:

  • (Linienfeld, 2)
  • (Feld <span>, 1)
  • (SMS an „Hallo“, 0)
  • (Zeilenfeld, 3)
  • (Feld <b>, 1)
  • (Text "an", 0)
  • (Text ".", 0)

Es gibt viele Nutzer dieser Datenstruktur: Bedienungshilfen-APIs und Geometrie-APIs wie getClientRects und contenteditable. Jede App hat unterschiedliche Anforderungen. Diese Komponenten greifen über einen Convenience-Cursor auf die flache Datenstruktur zu.

Der Cursor hat APIs wie MoveToNext, MoveToNextLine und CursorForChildren. Diese Cursordarstellung ist aus mehreren Gründen sehr leistungsfähig für Textinhalte:

  • Die Iteration in der Reihenfolge der Tiefensuche geht sehr schnell. Diese Methode wird sehr oft verwendet, weil sie den Caret-Bewegungen ähnelt. Da es sich um eine flache Liste handelt, erhöht die Tiefensuche nur das Array-Offset, um schnelle Iterationen und Speicherlokalität zu ermöglichen.
  • Sie ermöglicht eine breit angelegte Suche, die beispielsweise beim Malen des Hintergrunds von Linien- und Inline-Boxen erforderlich ist.
  • Wenn Sie die Anzahl der Nachfolger kennen, gelangen Sie schneller zum nächsten gleichgeordneten Element (inkrementieren Sie einfach das Array-Offset um diese Zahl).

Immobilienbäume

Das DOM ist eine Baumstruktur aus Elementen (plus Textknoten). CSS kann verschiedene Stile auf Elemente anwenden.

Dies wird auf vier Arten angezeigt:

  • Layout:Eingaben in den Algorithmus für die Layouteinschränkung.
  • Paint:Hier legen Sie fest, wie das Element (jedoch nicht seine Nachfolgerelemente) gezeichnet und gerastert wird.
  • Visuell:Raster-/Zeicheneffekte, die auf die DOM-Unterstruktur angewendet werden, z. B. Transformationen, Filter und Begrenzungen.
  • Scrollen: achsenseitige und abgerundete Ecken zum Beschneiden und Scrollen der enthaltenen Unterstruktur.

Eigenschaftsbäume sind Datenstrukturen, die erklären, wie visuelle und Scrolleffekte auf DOM-Elemente angewendet werden. Damit lassen sich Fragen wie die folgenden beantworten: Wo befindet sich ein bestimmtes DOM-Element basierend auf seiner Layoutgröße und -position im Verhältnis zum Bildschirm? Und: Welche Reihenfolge von GPU-Vorgängen sollten verwendet werden, um visuelle und Scroll-Effekte anzuwenden?

Visuelle und Scroll-Effekte im Web sind in ihrer vollen Pracht sehr kompliziert. Daher ist das Wichtigste, was Eigenschaftsbäume tun, diese Komplexität in eine einzige Datenstruktur zu übertragen, die ihre Struktur und Bedeutung genau darstellt, während gleichzeitig der Rest der Komplexität von DOM und CSS beseitigt wird. Auf diese Weise können wir Algorithmen für Compositing und Scrollen viel zuverlässiger implementieren. Wichtig ist insbesondere:

  • Potenziell fehleranfällige Geometrie und andere Berechnungen können an einem Ort zentralisiert werden.
  • Die Komplexität der Erstellung und Aktualisierung von Attributstrukturen ist in eine Renderingpipeline-Phase eingeteilt.
  • Es ist viel einfacher und schneller, Attributstrukturen an verschiedene Threads und Prozesse zu senden als mit dem vollständigen DOM-Status. Dadurch wird es möglich, sie für viele Anwendungsfälle zu verwenden.
  • Je mehr Anwendungsfälle es gibt, desto mehr Vorteile können wir durch aufeinander aufbauendes Geometrie-Caching erzielen, da sie die Caches der anderen wiederverwenden können.

RenderingNG verwendet Eigenschaftsbäume für viele Zwecke, unter anderem für:

  • Komposition von Farbe trennen und vom Hauptthread trennen
  • Ermitteln einer optimalen Compositing-/Draw-Strategie.
  • IntersectionObserver-Geometrie messen
  • Vermeiden Sie die Arbeit mit nicht sichtbaren Elementen und GPU-Texturkacheln.
  • Paint und Raster werden effizient und genau entwertet.
  • Layout Shift und Largest Contentful Paint in Core Web Vitals messen

Jedes Webdokument verfügt über vier separate Eigenschaftsbäume: transform, abschneiden, effekt und scroll.(*) Der Transformationsbaum repräsentiert CSS-Transformationen und das Scrollen. (Eine Scroll-Transformation wird als 2D-Transformationsmatrix dargestellt.) Der Clipbaum stellt Überlaufclips dar. Die Effektstruktur stellt alle anderen visuellen Effekte dar: Deckkraft, Filter, Masken, Mischmodi und andere Arten von Clips wie Clippfad. Der Scrollbaum repräsentiert Informationen zum Scrollen, z. B. wie das Scrollen verkettet wird. Er ist erforderlich, um im zusammengesetzten Thread zu scrollen. Jeder Knoten in einer Eigenschaftsstruktur stellt einen Scroll- oder visuellen Effekt dar, der von einem DOM-Element angewendet wird. Wenn das mehrere Auswirkungen hat, kann es in jedem Baum mehr als einen Attributbaumknoten für dasselbe Element geben.

Die Topologie der einzelnen Bäume ist wie eine dünnbesetzte Darstellung des DOM. Wenn beispielsweise drei DOM-Elemente mit Überlauf-Clips vorhanden sind, gibt es drei Clipbaumknoten und die Struktur des Clipbaums folgt der beinhaltenden Blockbeziehung zwischen den Überlaufclips. Es gibt auch Verbindungen zwischen den Bäumen. Diese Links geben die relative DOM-Hierarchie und somit die Reihenfolge der Anwendung der Knoten an. Wenn beispielsweise eine Transformation für ein DOM-Element unter einem anderen DOM-Element mit einem Filter liegt, wird sie natürlich vor dem Filter angewendet.

Jedes DOM-Element hat einen Eigenschaftsstrukturstatus. Dabei handelt es sich um ein 4-Tupel (Transformieren, Abschneiden, Effekt, Scrollen), das den nächstgelegenen Ancestor-Clip, Transformations- und Effektbaumknoten angibt, der auf dieses Element angewendet wird. Das ist sehr praktisch, da wir anhand dieser Informationen genau wissen, welche Clips, Transformationen und Effekte auf dieses Element angewendet werden und in welcher Reihenfolge. So wissen wir, wo es sich auf dem Bildschirm befindet und wie es gezeichnet werden soll.

Beispiel

(Quelle)

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

Im vorherigen Beispiel, das sich geringfügig von dem in der Einführung unterscheidet, sind dies die wichtigsten Elemente der generierten Property-Strukturen:

Ein Beispiel für die verschiedenen Elemente in der Eigenschaftsstruktur.

Listen und Farbblöcke anzeigen

Ein Anzeigeelement enthält Zeichenbefehle auf niedriger Ebene (siehe hier), die mit Skia gerastert werden können. Anzeigeelemente sind in der Regel einfach und erfordern nur wenige Zeichenbefehle, z. B. das Zeichnen eines Rahmens oder eines Hintergrunds. Der Paint Tree Walk durchläuft die Layoutstruktur und die zugehörigen Fragmente gemäß der CSS-Painting-Reihenfolge, um eine Liste der Anzeigeelemente zu erstellen.

Beispiel:

Ein blaues Feld mit den Worten „Hallo Welt“ in einem grünen Rechteck.

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

Dieser HTML- und CSS-Code erzeugt die folgende Anzeigeliste, wobei jede Zelle ein Anzeigeelement ist:

Hintergrund der Ansicht #blue (Hintergrund) #green (Hintergrund) #green Inline-Text
drawRect mit der Größe 800 × 600 und der Farbe Weiß. drawRect mit der Größe 100 × 100 an Position 0,0 und der Farbe Blau. drawRect mit der Größe 80 × 18 an Position 8,8 und der Farbe Grün. drawTextBlob mit Position 8,8 und dem Text „Hello world“.

Die Liste der Anzeigeelemente ist von hinten nach vorne geordnet. Im obigen Beispiel befindet sich das grüne div-Element in der DOM-Reihenfolge vor dem blauen div-Element. Die CSS-Farbreihenfolge erfordert jedoch, dass das blaue div-Element mit negativem Z-Index vor dem grünen div-Element (Schritt 3) dargestellt wird (Schritt 4.1). Anzeigeelemente entsprechen in etwa den atomaren Schritten der CSS-Spezifikation für die Farbreihenfolge. Ein einzelnes DOM-Element kann zu mehreren Anzeigeelementen führen, z. B. dass #green ein Anzeigeelement für den Hintergrund und ein weiteres Anzeigeelement für den Inline-Text hat. Dieser Detaillierungsgrad ist wichtig, um die gesamte Komplexität der CSS-Farbauftragsspezifikation darzustellen, z. B. durch einen negativen Rand:

Ein grünes Rechteck mit einem teilweise überlagerten grauen Feld und den Worten „Hallo Welt“.

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

Daraufhin wird die folgende Anzeigeliste angezeigt, wobei jede Zelle ein Anzeigeelement ist:

Hintergrund der Ansicht #green (Hintergrund) #gray (Hintergrund) #green Inline-Text
drawRect mit der Größe 800 × 600 und der Farbe Weiß. drawRect mit der Größe 80 × 18 an Position 8,8 und der Farbe Grün. drawRect mit den Maßen 35 × 20 an Position 8,16 und grau. drawTextBlob mit Position 8,8 und dem Text „Hello world“.

Die Liste der Anzeigeelemente wird gespeichert und bei späteren Aktualisierungen wiederverwendet. Wenn ein Layoutobjekt während des Paint Trees nicht geändert wurde, werden seine Anzeigeelemente aus der vorherigen Liste kopiert. Eine weitere Optimierung basiert auf einer Eigenschaft der CSS-Spezifikation für die Farbreihenfolge: Stapelkontexte werden in kleinstmöglichen Schritten dargestellt. Wenn sich innerhalb eines Stapelkontexts kein Layoutobjekt geändert hat, wird der Stapelkontext übersprungen und die gesamte Reihenfolge der Anzeigeelemente aus der vorherigen Liste kopiert.

Der aktuelle Status der Eigenschaftsstruktur wird während des Paint Trees beibehalten und die Liste der Anzeigeelemente wird in „Blöcke“ von Anzeigeelementen gruppiert, die denselben Status der Eigenschaftsstruktur haben. Dies wird im folgenden Beispiel veranschaulicht:

Ein rosa Kasten mit einem orangefarbenen, geneigten Kasten.

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

Daraufhin wird die folgende Anzeigeliste angezeigt, wobei jede Zelle ein Anzeigeelement ist:

Hintergrund der Ansicht #scroll (Hintergrund) #scroll Inline-Text #orange (Hintergrund) #orange Inline-Text
drawRect mit der Größe 800 × 600 und der Farbe Weiß. drawRect mit der Größe 100 × 100 an Position 0,0 und der Farbe Pink. drawTextBlob mit Position 0,0 und dem Text „Hello world“. drawRect mit der Größe 75 × 200 an Position 0,0 und der Farbe Orange. drawTextBlob mit Position 0,0 und dem Text „I'm Falling“.

Die Transformationseigenschaftsstruktur und die Paint-Blöcke wären dann (zur Übersichtlichkeit vereinfacht):

Ein Bild der vorhergehenden Tabelle: die ersten beiden Zellen in Block 1, die dritte in Block 2 und die letzten beiden Zellen in Block 3.

Die geordnete Liste von Paint-Chunks, bei denen es sich um Gruppen von Anzeigeelementen und einen Eigenschaftsbaumstatus handelt, ist die Eingaben für den Layerize-Schritt der Rendering-Pipeline. Die gesamte Liste der Farbblöcke könnte zu einer einzigen zusammengesetzten Ebene zusammengeführt und zusammen gerastert werden. Dafür wäre jedoch jedes Mal eine aufwendige Rasterung erforderlich, wenn der Nutzer scrollt. Für jeden Farbblock könnte eine zusammengesetzte Ebene erstellt und einzeln gerastert werden, um eine erneute Rasterung zu vermeiden. Dies würde jedoch schnell den GPU-Speicher erschöpfen. Bei diesem Schritt müssen Kompromisse zwischen dem GPU-Arbeitsspeicher und der Reduzierung der Kosten eingegangen werden, wenn sich etwas ändert. Ein guter allgemeiner Ansatz besteht darin, Blöcke standardmäßig zusammenzuführen und keine Farbblöcke mit Eigenschaftsstrukturstatus zusammenzuführen, die sich im Compositor-Thread wahrscheinlich ändern, z. B. beim Scrollen durch den Compositor-Thread oder durch Transformationsanimationen zu Compositor-Threads.

Das obige Beispiel sollte idealerweise zwei zusammengesetzte Ebenen erzeugen:

  • Eine zusammengesetzte Ebene im Format 800 x 600, die die Zeichenbefehle enthält:
    1. drawRect mit der Größe 800 x 600 und der Farbe Weiß
    2. drawRect mit der Größe 100 × 100 an Position 0,0 und der Farbe Pink
  • Eine zusammengesetzte Ebene im Format 144 × 224, die die Zeichenbefehle enthält:
    1. drawTextBlob mit Position 0,0 und dem Text „Hello world“
    2. übersetzen 0,18
    3. rotateZ(25deg)
    4. drawRect mit der Größe 75 × 200 an Position 0,0 und der Farbe Orange
    5. drawTextBlob mit Position 0,0 und dem Text „I'm Falling“ (Ich fällt fallen)

Wenn der Nutzer durch #scroll scrollt, wird die zweite zusammengesetzte Ebene verschoben, aber es ist keine Rasterung erforderlich.

Im Beispiel aus dem vorherigen Abschnitt zu Property-Strukturen gibt es sechs Farbblöcke. Zusammen mit den Status der Eigenschaftsstruktur (Transformieren, Abschneiden, Effekt, Scrollen) sind sie:

  • Dokumenthintergrund: Scrollen von Dokumenten, Dokumentclip, Stamm, Scrollen von Dokumenten
  • Horizontale, vertikale und Scroll-Ecke für div (drei separate Farbblöcke): Scrollen von Dokumenten, Dokumentclips, Weichzeichnen #one, Scrollen von Dokumenten.
  • iFrame #one: #one drehen, Scroll-Clip mit Überlauf, #one Weichzeichnen, div-Scrollen
  • iFrame #two: #two Skalierung, Dokumentclip, Stamm, Dokumentscrollen.

Compositor-Frames: Oberflächen, Renderingflächen und GPU-Texturkacheln

Der Browser und die Renderingprozesse verwalten die Rasterung von Inhalten und senden dann zusammengesetzte Frames zur Darstellung auf dem Bildschirm an den Viz-Prozess. Compositor-Frames geben an, wie Rasterinhalte zusammengefügt und effizient mit der GPU gezeichnet werden.

Ansichten

Theoretisch könnte ein Renderingprozess oder ein Browserprozess-Kompositor Pixel in einer einzigen Textur so rastern, dass sie die volle Größe des Darstellungsbereichs des Renderers hat, und diese Textur an Viz senden. Um sie anzuzeigen, müsste der Display-Compositor lediglich die Pixel aus dieser einzelnen Textur an die entsprechende Position im Frame-Zwischenspeicher kopieren (zum Beispiel den Bildschirm). Möchte dieser Compositor jedoch auch nur ein einzelnes Pixel aktualisieren, müsste der gesamte Darstellungsbereich neu gerastert und eine neue Textur an Viz gesendet werden.

Stattdessen wird der Darstellungsbereich in Kacheln unterteilt. Eine separate GPU-Texturkachel unterstützt jede Kachel mit den Rasterpixeln für einen Teil des Darstellungsbereichs. Der Renderer kann dann einzelne Kacheln aktualisieren oder sogar nur die Position der vorhandenen Kacheln auf dem Bildschirm ändern. Wenn auf einer Website beispielsweise gescrollt wird, verschiebt sich die Position vorhandener Kacheln nach oben und nur gelegentlich muss eine neue Kachel gerastert werden, damit Inhalte weiter unten auf der Seite angezeigt werden.

Vier Kacheln.
Dieses Bild zeigt ein Bild eines sonnigen Tages mit vier Kacheln. Beim Scrollen wird eine fünfte Kachel angezeigt. Eine der Kacheln hat zufällig nur eine Farbe (Himmelblau), und darüber befinden sich ein Video und ein iFrame.

Quads und Oberflächen

GPU-Texturkacheln sind eine spezielle Art von Quad, das einfach nur ein ausgefallener Name für eine Texturkategorie ist. Ein Quad identifiziert die Eingabetextur und gibt an, wie sie transformiert und visuelle Effekte angewendet werden soll. Zum Beispiel verfügen reguläre Inhaltskacheln über eine Transformation, die ihre x- und y-Position im Kachelraster angibt.

GPU-Texturkacheln

Diese gerasterten Kacheln werden in einen Rendering-Pass eingebunden, also eine Liste von Quadraten. Der Renderingdurchlauf enthält keine Pixelinformationen. Stattdessen enthält er eine Anleitung dazu, wo und wie jedes Quad gezeichnet wird, um die gewünschte Pixelausgabe zu erzeugen. Für jede GPU-Texturkachel gibt es ein Draw Quad. Der Display-Compositor muss lediglich die Liste der Quads durchlaufen und jedes mit den angegebenen visuellen Effekten zeichnen, um die gewünschte Pixelausgabe für den Renderingdurchlauf zu erzeugen. Das Erstellen von Zeichenquadern für einen Renderingpass kann effizient auf der GPU durchgeführt werden, da die zulässigen visuellen Effekte sorgfältig ausgewählt werden, die direkt GPU-Features zugeordnet sind.

Neben den gerasterten Kacheln gibt es noch weitere Arten von Zeichenquadraten. Es gibt beispielsweise einfarbige Zeichenquadrate, die überhaupt nicht von einer Textur gestützt werden, oder Textur-Zeichenquadrate für nicht gekachelte Texturen wie Video oder Canvas.

Es ist auch möglich, dass ein zusammengesetzter Frame einen weiteren zusammengesetzten Frame einbetten kann. Der Browser-Compositor erstellt beispielsweise einen Compositor-Frame mit der Browser-UI und ein leeres Rechteck, in das der Compositor-Inhalt für das Rendering eingebettet wird. Ein weiteres Beispiel sind Website-isolierte iFrames. Diese Einbettung erfolgt mithilfe von Oberflächen.

Wenn ein zusammengesetzter Frame einen zusammengesetzten Frame sendet, wird ihm eine Kennung zugeordnet, die sogenannte Oberflächen-ID. Diese ermöglicht es anderen zusammengesetzten Frames, ihn durch Verweis einzubetten. Der neueste zusammengesetzte Frame, der mit einer bestimmten Oberflächen-ID eingereicht wurde, wird von Viz gespeichert. Ein anderer Kompositor-Frame kann später über ein Surface Drawing Quad darauf verweisen, sodass Viz weiß, was gezeichnet werden soll. Beachten Sie, dass Flächen-Zeichen-Quadrate nur Oberflächen-IDs und keine Texturen enthalten.

Render-Zwischenfälle

Für einige visuelle Effekte, z. B. viele Filter oder erweiterte Mischmodi, müssen zwei oder mehr Quadrate auf einer Zwischentextur gezeichnet werden. Dann wird die Zwischentextur in einen Zielpuffer auf der GPU (oder möglicherweise eine andere Zwischentextur) gezogen, wobei gleichzeitig der visuelle Effekt angewendet wird. Um dies zu ermöglichen, enthält ein zusammengesetzter Frame eigentlich eine Liste von Rendering-Durchgängen. Es gibt immer einen Root-Rendering-Pass, der zuletzt gezeichnet wird und dessen Ziel dem Frame-Zwischenspeicher entspricht. Es können auch mehr vorhanden sein.

Die Möglichkeit mehrerer Renderingdurchläufe erklärt den Namen „Renderingpass“. Jede Karte / jedes Ticket muss in mehreren „Karten/Tickets“ nacheinander auf der GPU ausgeführt werden, während ein einzelner Durchlauf in einer einzigen massiv parallelen GPU-Berechnung abgeschlossen werden kann.

Aggregation

Mehrere Compositor-Frames werden an Viz gesendet und müssen zusammen auf den Bildschirm gezeichnet werden. Dies wird durch eine Aggregationsphase erreicht, die sie in einen einzelnen, aggregierten Compositor-Frame konvertiert. Die Aggregation ersetzt Oberflächenzeichnungs-Quads durch die von ihnen angegebenen Compositor-Frames. Es bietet auch die Möglichkeit, unnötige Zwischentexturen oder nicht sichtbare Inhalte zu entfernen. Beispielsweise benötigt der Compositor-Frame für einen Website-isolierten iFrame in vielen Fällen keine eigene Zwischentextur. Er kann über entsprechende Zeichen-Quads direkt in den Frame-Zwischenspeicher gezogen werden. In der Aggregationsphase werden solche Optimierungen definiert und auf der Grundlage von globalem Wissen angewendet, das den einzelnen Rendering-Kompositoren nicht zugänglich ist.

Beispiel

Hier sind die zusammengesetzten Frames, die das Beispiel vom Anfang dieses Posts darstellen.

  • foo.com/index.html-Oberfläche: id=0
    • Renderübergabe 0:Bis zur Ausgabe zeichnen.
      • Render-Pass-Zeichnungs-Quad: Mit 3-Pixel-Weichzeichnen zeichnen und in Rendering-Pass 0 fixieren.
        • Render-Pass 1:
          • Zeichnen Sie Vierecke für Kachelinhalte von #one iFrame mit jeweils X- und Y-Positionen.
      • Flächendiagramm mit der ID 2, mit der Transformation „Scale“ und „Translate“.
  • Oberfläche der Browseroberfläche: ID=1
    • Renderübergabe 0:Bis zur Ausgabe zeichnen.
      • Vierecke für Browser-Benutzeroberfläche zeichnen (auch gekachelt)
  • bar.com/index.html-Oberfläche: ID=2
    • Renderübergabe 0:Bis zur Ausgabe zeichnen.
      • Zeichnen Sie Vierecke für den Inhalt von #two iFrame mit jeweils X- und Y-Positionen.

Illustrationen von Una Kravets