Beschleunigtes Rendering in Chrome

Das Ebenenmodell

Tom Wiltzius
Tom Wiltzius

Einleitung

Für die meisten Webentwickler ist das DOM das grundlegende Modell einer Webseite. Beim Rendering handelt es sich um den oft unklaren Prozess, bei dem diese Darstellung einer Seite in ein Bild auf dem Bildschirm umgewandelt wird. In den letzten Jahren haben moderne Browser das Rendering durch Grafikkarten verändert: Dies wird oft vage als „Hardwarebeschleunigung“ bezeichnet. Was bedeutet der Begriff „Hardwarebeschleunigung“ im Zusammenhang mit einer normalen Webseite, also nicht von Canvas2D oder WebGL? In diesem Artikel wird das Grundmodell erläutert, das dem hardwarebeschleunigten Rendering von Webinhalten in Chrome zugrunde liegt.

Schwerwiegende Vorbehalte

Wir sprechen hier über WebKit, insbesondere über den Chromium-Port von WebKit. In diesem Artikel werden Details zur Implementierung von Chrome und nicht die Funktionen der Webplattform beschrieben. Diese Art der Implementierung wird von der Webplattform und den Standards nicht codiert. Daher gibt es keine Garantien, dass die Informationen in diesem Artikel auch für andere Browser gelten. Kenntnisse über interne Strukturen können jedoch für erweitertes Debugging und Leistungsoptimierung nützlich sein.

Hinweis: In diesem Artikel geht es um einen Kernbestand der Rendering-Architektur von Chrome, der sich sehr schnell ändert. In diesem Artikel werden nur Themen behandelt, die sich wahrscheinlich nicht ändern werden. Es wird jedoch nicht garantiert, dass alles in sechs Monaten noch gilt.

Bei Chrome gibt es seit einiger Zeit zwei verschiedene Rendering-Pfade: den hardwarebeschleunigten Pfad und den älteren Softwarepfad. Zum Zeitpunkt der Abfassung dieses Artikels führen alle Seiten den hardwarebeschleunigten Pfad unter Windows, ChromeOS und Chrome für Android hin. Auf Mac- und Linux-Seiten, für die ein Compositing für einen Teil ihres Inhalts erforderlich ist, folgen dem beschleunigten Pfad. Weitere Informationen dazu, wo ein Compositing erforderlich ist, finden Sie weiter unten. Bald werden aber auch alle Seiten den beschleunigten Pfad durchlaufen.

Zu guter Letzt werfen wir einen Blick ins Detail der Rendering-Engine und sehen uns Funktionen an, die einen großen Einfluss auf die Leistung haben. Wenn Sie versuchen, die Leistung Ihrer eigenen Website zu verbessern, kann es hilfreich sein, das Ebenenmodell zu verstehen. Es ist aber auch ganz einfach, sich selbst ein Bild zu machen: Ebenen sind zwar nützliche Konstrukte, aber die Erstellung vieler solcher Elemente kann zu Mehraufwand im Grafikstapel führen. Betrachte dich selbst gewarnt!

Vom DOM zum Bildschirm

Jetzt neu: Ebenen

Sobald eine Seite geladen und geparst wurde, wird sie im Browser als eine Struktur dargestellt, mit der viele Webentwickler vertraut sind: DOM. Beim Rendern einer Seite verfügt der Browser jedoch über eine Reihe von Zwischendarstellungen, die Entwicklern nicht direkt zur Verfügung stehen. Die wichtigste dieser Strukturen ist eine Schicht.

In Chrome gibt es tatsächlich mehrere verschiedene Ebenentypen: RenderLayers ist für DOM-Unterstrukturen verantwortlich und GraphicsLayers sind Unterstrukturen von RenderLayers. Letzteres ist für uns hier am interessantesten, da GraphicsLayers als Texturen in die GPU hochgeladen werden. Ab jetzt nenne ich „Layer“, also „GraphicsLayer“.

Ein kleiner Außerirdische zur GPU-Terminologie: Was ist eine Textur? Stellen Sie sich dies als ein Bitmapbild vor, das aus dem Hauptspeicher (RAM) in den Videospeicher (z. B. VRAM auf Ihrer GPU) verschoben wird. Sobald sie sich auf der GPU befinden, können Sie sie einer Mesh-Geometrie zuordnen. In Videospielen oder CAD-Programmen wird diese Technik verwendet, um 3D-Skelett-Modellen ein „Skin“ zu verleihen. Chrome verwendet Texturen, um Blöcke von Webseiteninhalten auf die GPU zu übertragen. Texturen lassen sich kostengünstig verschiedenen Positionen und Transformationen zuordnen, indem sie auf ein wirklich einfaches rechteckiges Netz angewendet werden. So funktioniert 3D-CSS und eignet sich auch hervorragend für schnelles Scrollen. Mehr dazu später mehr.

Sehen wir uns einige Beispiele an, um das Ebenenkonzept zu veranschaulichen.

Ein sehr nützliches Tool bei der Untersuchung von Ebenen in Chrome ist das Flag „Rahmen für zusammengesetzte Ebenen anzeigen“ in den Einstellungen (d.h. das kleine Zahnradsymbol) in den Entwicklertools unter der Überschrift „Rendering“. Es wird ganz einfach hervorgehoben, wo sich Ebenen auf dem Bildschirm befinden. Jetzt aktivieren. Diese Screenshots und Beispiele stammen alle aus Chrome 27, der aktuellen Version von Chrome Canary zum Zeitpunkt dieses Artikels.

Abbildung 1: Seite mit einer Schicht

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Screenshot der Rendering-Rahmen der zusammengesetzten Ebene um die Basisebene der Seite
Screenshot eines Renderingrahmens der zusammengesetzten Ebene um die Basisebene der Seite

Diese Seite hat nur eine Ebene. Das blaue Raster stellt Kacheln dar, die sich als Untereinheiten einer Ebene vorstellen können, mit denen Chrome Teile einer großen Ebene nacheinander in die GPU hochlädt. Die sind hier nicht so wichtig.

Abbildung 2: Ein Element in seiner eigenen Ebene

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
Screenshot der gedrehten Rendering-Rahmen der Ebene
Screenshot der gedrehten Renderingrahmen der Ebene

Durch Hinzufügen einer 3D-CSS-Eigenschaft zu <div>, mit der das Element gedreht wird, können wir sehen, wie es aussieht, wenn ein Element eine eigene Ebene erhält: Beachten Sie den orangefarbenen Rahmen, der eine Ebene in dieser Ansicht umreißt.

Kriterien für die Ebenenerstellung

Welche anderen Elemente erhalten eine eigene Ebene? Die Heuristik von Chrome hier hat sich im Laufe der Zeit weiterentwickelt, aber derzeit wird eine der folgenden Triggerebenen erstellt:

  • CSS-Eigenschaften für 3D- oder perspektivische Transformation
  • <video>-Elemente mit beschleunigter Videodecodierung
  • <canvas>-Elemente mit einem 3D-Kontext (WebGL) oder beschleunigtem 2D-Kontext
  • Zusammengesetzte Plug-ins (z.B. Flash)
  • Elemente mit CSS-Animation auf ihre Deckkraft oder mit einer animierten Transformation
  • Elemente mit beschleunigten CSS-Filtern
  • Element hat ein Nachfolgerelement mit einer Compositing-Ebene (wenn das Element also ein untergeordnetes Element hat, das sich in einer eigenen Ebene befindet)
  • Ein Element hat ein gleichgeordnetes Element mit einem niedrigeren Z-Index, das eine Verbundebene hat, d. h., es wird über einer zusammengesetzten Ebene gerendert.

Praktische Auswirkungen: Animation

Wir können auch Ebenen verschieben, was sie für Animationen sehr nützlich macht.

Abbildung 3: Animierte Ebenen

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

Wie bereits erwähnt, sind Ebenen sehr nützlich, um statische Webinhalte zu verschieben. Im einfachen Fall überträgt Chrome die Inhalte einer Ebene in eine Software-Bitmap, bevor sie als Textur auf die GPU hochgeladen wird. Wenn sich diese Inhalte in Zukunft nicht ändern, müssen sie nicht neu gezeichnet werden. Das ist eine gute Sache: Das Malen muss Zeit in Anspruch nehmen, die für andere Dinge wie etwa das Ausführen von JavaScript aufgewendet werden kann. Wenn die Farbe lang ist, kommt es bei den Animationen zu Einhängen oder Verzögerungen.

Sehen wir uns zum Beispiel diese Ansicht der Zeitachse der Entwicklertools an: Es gibt keine Paint-Vorgänge, während diese Ebene vor- und zurückdreht.

Screenshot der Zeitachse der Entwicklertools während der Animation
Screenshot der Zeitachse der Entwicklertools während der Animation

Ungültig! Neuanstrich

Wenn sich der Inhalt der Ebene jedoch ändert, muss sie neu gezeichnet werden.

Abbildung 4: Ebenen neu streichen

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

Jedes Mal, wenn auf das Eingabeelement geklickt wird, wird das rotierende Element um 1 Pixel breiter. Dies führt zu einer Neuanordnung und Aktualisierung des gesamten Elements, in diesem Fall einer ganzen Ebene.

Um zu sehen, was dargestellt wird, können Sie in den Entwicklertools mit dem Tool „Rektreiche anzeigen“ in den Einstellungen der Entwicklertools unter der Überschrift „Rendering“ sehen, was dargestellt wird. Nach dem Aktivieren werden das animierte Element und die Schaltfläche rot blinken, wenn auf die Schaltfläche geklickt wird.

Screenshot des Kästchens zum Anzeigen von Paint Rects
Screenshot des Kästchens zum Anzeigen von Paint Rects

Die Paint-Ereignisse werden auch in der Zeitachse der Entwicklertools angezeigt. Lesende mit Spitzenaugen bemerken möglicherweise, dass es hier zwei Paint-Ereignisse gibt: eines für die Ebene und eines für die Schaltfläche selbst, die neu dargestellt wird, wenn sie in den bzw. aus dem heruntergedrückten Zustand wechselt.

Screenshot der Zeitachse der Entwicklertools mit der Darstellung einer Ebene
Screenshot der Zeitachse der Entwicklertools mit dem Rendern einer Ebene

Beachten Sie, dass Chrome nicht immer die gesamte Ebene neu malen muss, sondern nur den Teil des DOMs neu streichen muss, der ungültig ist. In diesem Fall ist das geänderte DOM-Element die Größe der gesamten Ebene. Aber in vielen anderen Fällen gibt es viele DOM-Elemente in einer Ebene.

Eine offensichtliche nächste Frage ist, was eine Entwertung verursacht und eine Darstellungsaktualisierung erzwingt. Dies ist schwer, vollständig zu beantworten, da es viele Grenzfälle gibt, die Entwertungen erzwingen können. Die häufigste Ursache ist eine fehlerhafte Darstellung des DOMs durch Manipulation von CSS-Stilen oder eine Neugestaltung des Layouts. Tony Gentilcore hat einen tollen Blogpost zu den Ursachen von Layout-Layouts und Stoyan Stefanov hat einen Artikel zu Malerei veröffentlicht, in dem er sich ausführlich mit Malerei befasst.

Um herauszufinden, ob dies Auswirkungen auf etwas hat, an dem Sie gerade arbeiten, verwenden Sie die Tools „Dev Tools Timeline“ und „Show Paint Rects“, um zu sehen, ob Sie eine Neuanstrichung durchführen, wenn Sie dies nicht möchten. Versuchen Sie dann zu ermitteln, wo Sie das DOM direkt vor dieser Neugestaltung/Darstellung verschmutzt haben. Wenn das Malen unvermeidlich ist, aber unzumutbar lange dauert, lesen Sie den Artikel von Eberhard Gräther zum Modus „Kontinuierliches Malen“ in den Entwicklertools.

Zusammenfassung: DOM zu Screen

Wie wird also das DOM von Chrome in ein Bildschirmbild umgewandelt? Konzeptionelle Funktion:

  1. Teilt das DOM in Ebenen auf
  2. Malt jede dieser Ebenen unabhängig in Software-Bitmaps ab
  3. Sie werden als Texturen in die GPU hochgeladen.
  4. Setzt die verschiedenen Schichten zu einem endgültigen Bildschirmbild zusammen.

Dies muss alles passieren, wenn Chrome zum ersten Mal einen Frame einer Webseite erstellt. Für zukünftige Frames können dann jedoch einige Tastenkombinationen erforderlich sein:

  1. Wenn sich bestimmte CSS-Eigenschaften ändern, ist keine Darstellung erforderlich. Chrome kann einfach die vorhandenen Ebenen, die sich bereits auf der GPU befinden, als Texturen neu zusammensetzen, aber mit unterschiedlichen Eigenschaften (z. B. an verschiedenen Positionen, mit unterschiedlicher Deckkraft usw.).
  2. Wenn ein Teil einer Ebene ungültig wird, wird sie neu gezeichnet und noch einmal hochgeladen. Wenn der Inhalt gleich bleibt, sich aber die zusammengesetzten Attribute ändern (z.B. weil er übersetzt oder die Deckkraft geändert wird), kann Chrome den Inhalt auf der GPU belassen und ihn neu zusammensetzen, um einen neuen Frame zu erstellen.

Wie Sie nun deutlich machen, hat das schichtbasierte Zusammensetzungsmodell tiefgreifende Auswirkungen auf die Rendering-Leistung. Die Erstellung von Zusammenstellungen ist vergleichsweise kostengünstig, wenn nichts gemalt werden muss. Daher ist es generell ein gutes Ziel, das Übermalen von Ebenen zu vermeiden, wenn Sie Fehler bei der Rendering-Leistung beheben möchten. Erfahrene Entwickler sehen sich die Liste der Compositing-Trigger oben an und erkennen, dass sich das Erstellen von Ebenen problemlos erzwingen lässt. Aber Vorsicht: Sie erstellen sie einfach blind, da sie nicht kostenlos sind: Sie belegen Arbeitsspeicher im System-RAM und in der GPU (besonders auf Mobilgeräten begrenzt). Wenn viele davon vorhanden sind, kann das zu einem weiteren Overhead in der Logik führen, der sichtbar ist. Viele Ebenen können auch die Zeit für das Rastern verlängern, wenn sie groß sind und sich dort stark überschneiden, wo dies zuvor nicht der Fall war. Dies wird manchmal als „Überzeichnen“ bezeichnet. Setzen Sie Ihr Wissen also mit Bedacht ein!

Das ist im Moment erstmal alles. Es folgen weitere Artikel zu den praktischen Auswirkungen des Ebenenmodells.

Weitere Ressourcen