Wie der Browser zeichnet. Yandex-Bericht

Bis vor kurzem habe ich im Yandex.Browser-Team gearbeitet und im Anschluss an diese Erfahrung eine Präsentation auf der YaTalks-Konferenz gehalten. In dem Bericht ging es darum, was der Browser unter der Haube hat und wie Ihre Seiten auf dem Bildschirm in Pixel umgewandelt werden. Minimales Frontend, nur die Innenseiten des Browsers, nur Hardcore.



- Hallo allerseits, mein Name ist Kostya. Überraschenderweise arbeite ich jetzt im Team des virtuellen Netzwerks Yandex.Cloud. Zuvor hatte ich mehr als fünf Jahre im Browser-Team gearbeitet, daher werde ich heute über Dinge sprechen, die uns und Ihnen gemeinsam sind.

Wie Sie sich vorstellen können, verstehe ich das Frontend nicht sehr gut. Wenn Sie mit mir über React oder ähnliches sprechen, werde ich Sie wahrscheinlich nicht verstehen. Aber ich habe viele Dinge im Browser getan: Videodecodierung, Geschäftslogik. Einschließlich ich habe viel Zeit damit verbracht, verschiedene Dinge beim Rendern des Browsers zu tun. Heute werden wir beispielsweise ein solches Bildungsprogramm über das interne Gerät des Browsers haben. Ich werde versuchen, die interessantesten Dinge durchzugehen, die wir in Yandex.Browser oder Google in Chromium gemacht haben.



Wenn wir über das Rendern im Browser sprechen, ist dies eine sehr komplizierte Sache, die aus einer großen Anzahl von Komponenten besteht. Zunächst müssen Sie Ressourcen herunterladen, um sie anzuzeigen. Dann müssen Sie sie analysieren, einen DOM-Baum, Stile, Layouts usw. erstellen. Die ersten drei Punkte sind Ihnen höchstwahrscheinlich bekannt. Mein Bericht wird sich mehr den anderen drei Teilen widmen: Malen, Rastern und Zusammensetzen - was passiert unter der Haube, wenn Sie das Layout bereits geschrieben haben. Nur in Worten kann es so aussehen, als ob es sich um dasselbe handelt - tatsächlich handelt es sich um völlig unterschiedliche Komponenten.



Beginnen wir mit Malen und Zusammensetzen. Worum geht es hier? Gehen wir zurück vor vielen, vielen Jahren, als das Web nicht so komplex war wie heute, als es nicht alle Arten von 3D-, CSS-Animationen und anderen Dingen gab. Wie hat dann der Browser gezeichnet? Stellen Sie sich vor, Sie haben Ihre Seite, einige wundervolle Elemente, Bilder usw. Der Browser hat all dies auf eine kräftige Textur in einem großen Speicherblock gemalt. Er wusste, wie man jedes Element zeichnet, und wenn wir Änderungen hatten, werden diese gelb hervorgehoben, so etwas wie das Folgende geschah.

Der Browser hat sie in Bereichen zusammengefasst, die hier blau angezeigt werden. In diesem Bereich hat sich etwas geändert. Zeichnen wir ihn neu. Alles in diesem Bereich wurde einfach neu gezeichnet und in die Textur kopiert.

Es hat für sich selbst funktioniert. Dann kamen kluge Leute auf 3D-CSS-Animationen, andere Dinge. Wir könnten viele Renderings an verschiedenen Orten haben. Wenn wir einen Spinner drehen, ist es nicht sehr effektiv, den gesamten Kreis der Elemente, die sich darunter befinden, neu zu zeichnen.



Dann beschlossen andere kluge Leute, alles ein wenig zu wiederholen. Wir haben eine Art DOM-Baum, wir haben ihn im Speicher erstellt. Dies sind Plus-Objekte. Sie werden mit dem von Ihnen geschriebenen Layout verglichen.



Und dann beginnt die Magie zu geschehen. Der Browser konvertiert den gesamten DOM-Baum in einen Renderobjektbaum. Dieses Ding weiß, wie man jedes spezifische DOM-Element zeichnet. Das heißt, sie weiß, was zu tun ist, damit anstelle Ihres Baums oder P-Elements etwas auf dem Bildschirm angezeigt wird.



Der nächste Baum ist der Baum der Schichten. Was ist das? Jedes unserer Elemente kann einer beliebigen Ebene zugeordnet werden, und eine Renderebene kann mehrere Objekte gleichzeitig enthalten. Warum wird das gemacht? Dies wird hier sehr gut gezeigt. Wir generieren eine Reihe von Ebenen, von denen jede bestimmte Elemente enthält. Angenommen, auf einer der Ebenen ändert sich etwas - eine Animation findet statt, ein Stempelelement fliegt. Dann zeichnen wir nur eine Ebene neu und der Rest, zum Beispiel der Hintergrund, bleibt unverändert. Wir kleben sie dann einfach in Verbundwerkstoffe und erhalten am Ausgang das endgültige Bild - den aktuellen Frame der Animation.



Es gibt eine Reihe fester Gründe für das Erstellen neuer Ebenen. Beispielsweise wird eine Ebene erstellt, um 3D-CSS-Animationen, eine Leinwand und ein Videoelement darauf zu rendern - im Allgemeinen etwas, das sich auf schwere Animationen bezieht und zum separaten Zeichnen von allen anderen Inhalten geeignet ist.



Dieser Ansatz weist jedoch mehrere Probleme auf. Jetzt müssen Sie den Kraftstoff einschalten. Überlegen Sie, was hier abgebildet wird? Es gibt nur zwei Elemente.



Hier. Obwohl es scheint, dass die Elemente einzeln angeordnet sind. Warum fliegt Leinwand hoch? In unserem Land sind sie nacheinander angeordnet, ich habe keine Reihenfolge festgelegt.



Lassen Sie uns komplizieren. Hier haben wir noch einen Div, so.



Im Allgemeinen das erwartete Verhalten. Wir haben ein Div über dem Div, aber Leinwand ist aus irgendeinem Grund oben. Die Magie! Ok, lassen Sie uns dieses Beispiel noch einmal komplizieren.



Genau eine Zeile hat sich geändert, ich habe transform hinzugefügt.



Und jetzt haben wir alles richtig positioniert - zumindest in Bezug auf Leinwand und Div. Aber dieses Div befindet sich immer noch unten, obwohl es das nächste Element in unserem Layout war.

Dies ist der sogenannte grundlegende Compositing-Fehler. Wenn Sie nach dem Chromium-Tracker suchen, werden Sie eine Reihe von Fehlern sehen, die mit einem alten verknüpft sind. Er heißt so.



Also, was ist passiert? Wie gesagt, einige Elemente werden auf der Renderebene gerendert, andere nicht. Einige werden zusammen mit anderen gezeichnet. Hier geschah Folgendes: div-Elemente bleiben in derselben Ebene wie der Hintergrund. Canvas stürzt auf einer separaten Ebene ab. Und die Z-Reihenfolge wird nur zwischen Schichten ausgeführt. Aufgrund der Tatsache, dass wir einen Hintergrund und ein Div in einer Ebene und eine Leinwand in einer anderen haben, erhalten wir einen Fehler: Leinwand überlappt die Div.

Sobald wir dieses div-Element auf eine separate Ebene verschieben und es beginnt, die z-Ordnung normal zu verwenden, beginnt es auch zu verstehen, wer hinter wem steht. Und hier wird alles "normal" gemacht.



Und eine der letzten Initiativen, die sich seit mehreren Jahren entwickelt hat, ist die sogenannte Schlankheitsfarbe, die das Problem beheben sollte. Seine Bedeutung ist, dass wir das Malen vom Zeichnen in Ebenen trennen müssen, dh verstehen müssen, was getan werden muss, um diese Elemente zu zeichnen, und wie wir sie dann miteinander zusammensetzen können. Wenn wir ein so einfaches Layout haben, wird es zu so etwas. Es gibt eine einfache Liste von Befehlen, die Sie ausführen müssen, um Seiteninhalte abzurufen. Und wenn wir zu diesem Beispiel zurückkehren, wird es ungefähr so ​​aussehen.



Das heißt, wir sagten: Hier ist Paint für Sie, hier ist der Inhalt für Sie - bitte geben Sie mir etwas. Es enthält eine Liste zum Rendern, die an Compositor gesendet wird, und Compositor versteht es, den gesamten Inhalt in Ebenen zu unterteilen, sodass sie normalerweise relativ zueinander angeordnet sind.

Und wenn Sie es nicht bemerkt haben, ist dies ein Screenshot von Chrome. Ich habe es vor ungefähr zwei Wochen geschafft, das heißt, der Fehler lebt noch. Das Projekt ist noch nicht abgeschlossen, es befindet sich derzeit in der Entwicklung.



Das heißt, der Compositor aus dieser Liste und einige geheime Kenntnisse, die Plink weitergibt, können verstehen, wie man alles richtig in Schichten legt.



Neben der Tatsache, dass dieser Ansatz diesen Fehler im Grunde behebt, erhalten wir auch ziemlich billige Änderungen beim Rendern. Angenommen, wir hatten eine Liste von Zeichenbefehlen und Änderungen - sagen wir, Element B verlässt, Element E wird hinzugefügt. Dann können wir die beiden Listen einfach zusammenhalten, ohne uns um Bäume usw. kümmern zu müssen. Bei der Ausgabe erhalten wir eine neue Liste von Elementen zum Rendern und möglicherweise eine neue Liste von Ebenen, die in Zukunft kompiliert werden.

Dies war eine kurze Geschichte darüber, was passiert, wenn der Browser Paint implementiert und was passiert, nachdem er versucht hat, Ebenen zu erstellen.

Kommen wir zu einem anderen Thema: Rasterisierung. Nur in Rasterization in Yandex.Browser wurden viele Dinge getan, und das habe ich auch getan. Was bedeutet Rasterung? Am Ende der vorherigen Phase, als wir Paint ausgeführt haben, gibt es eine Liste von Befehlen, die wir implementieren müssen, um ein Bild zu erhalten. Rasterisierung ist die Umwandlung einer Liste von Befehlen in echte Pixel.





Wenn Sie im Browser-Inspektor die Registerkarte Weitere Tools → Rendern öffnen, gibt es ein Häkchen für Ebenenränder. Und Sie sehen genau so ein Gitter. Was ist hier los? Wenn der Browser die Seite zeichnet, macht er jetzt nicht das Ganze. Der Browser nimmt und unterteilt jede Ebene in eine bestimmte Anzahl solcher kleinen Quadrate. Historisch gesehen sind sie 256 mal 256 Pixel groß. Das heißt, jede Ebene ist in so viele separate Texturen unterteilt. Der Inhalt der aktuellen Kachel wird dann auf jede Textur gezeichnet, und dann werden sie alle zu einer großen Textur zusammengeklebt.



Es hilft sehr. Zunächst können wir nur die Kacheln neu zeichnen, die sich geändert haben. Es ermöglicht uns aber auch, das Rendern zu priorisieren. Das heißt, zunächst sollten die Kacheln gezeichnet werden, die der Benutzer sieht, das sogenannte Ansichtsfenster. Dann müssen wir bald Grenze ziehen, das ist, was um das Ansichtsfenster ist. Als nächstes folgt die Richtung der Schriftrolle: Wenn sie nach unten gescrollt wird, zeichnen wir so viel wie möglich nach unten. Wenn hoch - ziehe so viel wie möglich hoch. Wenn wir noch eine Speicherquote haben, werden wir etwas anderes zeichnen, aber nicht die Tatsache, dass es an diesem Punkt bleibt.



Wir bekommen also ein ziemlich billiges Inhaltsupdate auf der Seite. Angenommen, wir haben den aktuellen Frame verwendet und der Benutzer hat etwas deaktiviert - beispielsweise hervorgehobenen Text. Dann zeichnen wir nur die Kacheln, die sich geändert haben.



Das heißt, grüne Kacheln sind diejenigen, die vom vorherigen Rendering übrig geblieben sind, rote, die wir neu gezeichnet haben. Dieser Ansatz hat jedoch andere Vorteile.



Wir können - und Chrome hat es getan - die sogenannte Optimierung kleiner Neuzeichnungen durchführen. Angenommen, Sie haben eine Art Pochen, Cursor oder ähnliches, das in einem kleinen Rechteck ein wenig neu gezeichnet wird. Dann müssen wir nicht das gesamte Quadrat neu zeichnen. Das ist logisch. Wenn beispielsweise unser Cursor blinkt, wird nur er neu gezeichnet. Dies spart viel CPU.



Die nächste Optimierung haben sie gemacht. Wo kann es Ineffizienz geben? Sind die Fliesen verschoben? Gute Idee, aber ich tendiere zu einer anderen. Hier ist nur ein weißes Rechteck. Dies ist eine weiße Kachel, auf die kein einziges Pixel gezeichnet ist. Aber es ist eine Textur. Es nimmt 256 mal 256 mal vier Bytes Speicher ein.



Eine weitere Optimierung, die in Chrome erfunden wurde: Nehmen wir diese Kacheln einfarbig und codieren sie nicht mit einer Reihe von Pixeln, sondern mit den Koordinatoren, der Größe und der Farbe. Das Internet ist jetzt voll von Seiten mit vielen monochromen Bereichen für große Monitore. Und solche Seiten sind entsprechend optimiert, wir sparen viel Speicher.

Wir bei Yandex gingen noch einen Schritt weiter und beschlossen, ein spezifischeres Experiment durchzuführen. Wo können Sie Ihrer Meinung nach mehr sparen?

Wir haben eine Fliese. Der Inhalt befindet sich in einem winzigen Bereich - einem Streifen, dem Wort Yandex. Warum müssen wir als Ganzes zeichnen, wenn der Inhalt sehr klein und alles andere monochrom ist?



Was haben wir getan Das habe ich speziell getan. Wir haben jede Kachel in fünf Kacheln unterteilt. Wenn sich der Inhalt nur in der Mitte befindet, wählen wir die Textur für diesen Inhalt nur für das aus, was in der Mitte ausgeführt wird. Hier ist der rote Bereich. Alles andere codieren wir auf die gleiche Weise - Größe, Koordinaten und Farbe.



Das heißt, speziell auf dieser Seite sind alle diese Bereiche jetzt nicht mehr texturiert. Es werden keine Bytes im Speicher verwendet, sondern lediglich Befehle darüber, was wir hier zeichnen müssen, mit einer Farbe füllen. Dies ergab eine durchschnittliche Einsparung von ca. 40% beim GPU-Speicher pro Benutzer.



Auf komplexeren Seiten sieht es so aus. Da komplexere Seiten mehr Ebenen verwenden und jede Ebene eine separate Kachelung ist, können Sie auf jeder Ebene ein wenig sparen.

Wenn Sie dieses Häkchen jetzt aktivieren, sehen Sie nicht ein solches Rechteckraster, sondern dieses.



Was ist das, warum sind die Fliesen hier so breit und warum gibt es nur wenige davon? Die Bedeutung hier ist wie folgt. In Chrome dachten sie: Warum machen wir nicht nicht nur Hardware-Compositing, sondern auch Hardware-Rendering? Was machen sie? Wir haben eine Liste von Befehlen, was zu tun ist: Zeichnen eines Rechtecks, Füllen mit Farbe usw. All dies geht an die GPU, und die GPU zeichnet eine solche Textur. Das Neuzeichnen ist sehr schnell, sodass Kacheln auch groß gemacht werden können. Hier ist ein kleines Schakalvideo, aber es zeigt sehr gut den Vorteil, der auf den Telefonen aufgrund der Tatsache, dass das Rendern hardwarebeschleunigt wurde, aufgetreten ist. Ich denke, der Unterschied hier ist sehr, sehr auffällig.

Die Kommunikation zwischen Browser-Entwicklern und Front-End-Anbietern scheint mir sehr nützlich zu sein. Es kommt nicht sehr oft vor, bietet aber viele Vorteile. Wenn unsere Kollegen aus anderen Abteilungen zu uns kommen und fragen, wie das Layout schneller und besser gestaltet werden kann, versuchen wir ihnen zu helfen und über Orte zu sprechen, an denen etwas nicht optimal ist und Sie schneller werden können.

Und ich werde nicht müde, meinen Rat zu wiederholen. (Ich werde hier nicht zitieren, es gab einen separaten großen Bericht zu diesem Thema. - Kommentar des Autors.)

Hier habe ich eine Reihe nützlicher Links zusammengestellt, über das Rendern und nicht nur, sondern auch ein wenig über Yandex.Browser. Vielen Dank.

Source: https://habr.com/ru/post/de468165/


All Articles