Architektura renderowania NG

Chris Harrelson
Chris Harrelson

Tutaj dowiesz się, jak komponent RenderingNG a także sposób, w jaki przepływa przez nie potok renderowania.

Początkowo renderowane są te zadania:

  1. Renderuj zawartość ekranu w pikselach na ekranie.
  2. Uruchamiaj efekty wizualne w różnych stanach treści.
  3. Przewiń w odpowiedzi na dane wejściowe.
  4. Sprawnie kieruj dane wejściowe we właściwe miejsca, aby skrypty programistyczne i inne podsystemy mogły na nie reagować.

Zawartość do wyrenderowania składa się z drzewa ramek każdej karty przeglądarki oraz interfejsu przeglądarki. Strumień nieprzetworzonych zdarzeń wejściowych z ekranów dotykowych myszy, klawiatury i inne urządzenia.

Każda ramka zawiera:

  • Stan DOM
  • CSS
  • Obszary robocze
  • zasoby zewnętrzne, takie jak obrazy, filmy, czcionki i SVG;

Ramka to dokument HTML i jego adres URL. Strona internetowa wczytana na karcie przeglądarki ma ramkę najwyższego poziomu, ramki podrzędne dla każdego elementu iframe uwzględnionego w dokumencie najwyższego poziomu, i ich rekurencyjnych elementów potomnych iframe.

Efekt wizualny to operacja graficzna stosowana do bitmapy, takie jak przewijanie, przekształcanie, przycinanie, filtr, przezroczystość czy mieszanie.

Komponenty architektury

W RenderingNG te zadania są logicznie podzielone na kilka etapów i kodu Komponenty trafiają do różnych procesów i wątków procesora i podzielnych podkomponentów w tych wątkach. Każdy z nich odgrywa ważną rolę w realizacji niezawodności, skalowalna wydajność i elastyczności w przypadku wszystkich treści internetowych.

Struktura potoku renderowania

Schemat potoku renderowania.
Strzałki wskazują dane wejściowe i wyjściowe każdego etapu. Etapy są oznaczone kolorem, aby pokazać, który wątek lub proces jest wykonywany. W w niektórych przypadkach etapy mogą być realizowane w wielu miejscach, w zależności od przez przypadek, dlatego niektóre mają dwa kolory. Główne etapy procesu renderowania w zielonych etapach: żółty to kompozytory procesu renderowania; pomarańczowe etapy to proces wizualizacji.

Proces renderowania w potoku z utworzonymi etapami i artefaktami po drodze. Każdy etap reprezentuje kod, który wykonuje jedno dobrze zdefiniowane zadanie w obrębie jak renderowanie. Artefakty to struktury danych które są danymi wejściowymi lub wyjściowymi etapami.

Etapy to:

  1. Animuj:zmieniaj style obliczone i mutuj drzewa właściwości w czasie na podstawie deklaratywnych osi czasu.
  2. Styl: stosowanie CSS do DOM i tworzenie stylów obliczonych.
  3. Układ: określ rozmiar i położenie elementów DOM na ekranie, i utwórz drzewo fragmentów stałych.
  4. Wstępne renderowanie: oblicz drzewa usług unieważnij istniejących list wyświetlanych i fragmentów tekstur GPU.
  5. Przewijanie: aktualizowanie przesunięcia dokumentów i przewijanych elementów DOM za pomocą mutacji drzew właściwości.
  6. Paint: oblicz listę wyświetlania, która opisuje sposób rastrowania fragmentów tekstur GPU z DOM.
  7. Zatwierdzenie: skopiuj drzewa właściwości i listę wyświetlania do wątku kompozytora.
  8. Warstwa: podział listy wyświetlanych na listę warstw skomponowanych na potrzeby niezależnej rasteryzacji i animacji.
  9. Rastruj, dekoduj i maluj worklety: przekształcaj odpowiednio listy wyświetlania, zakodowane obrazy i maluj kod instancji Kafelki tekstur GPU.
  10. Aktywuj:pozwala utworzyć ramkę kompozytora reprezentującą sposób rysowania i umieszczania kafelków GPU na ekranie oraz wszelkich efektów wizualnych.
  11. Agregacja: połącz ramki kompozytora ze wszystkich widocznych ramek kompozytora w pojedynczą, globalną ramkę kompozytora.
  12. Draw: powoduje wykonanie zagregowanej ramki kompozytora w GPU, aby utworzyć piksele na ekranie.

Etapy potoku renderowania można pominąć, jeśli nie są potrzebne. Na przykład animacje efektów wizualnych i przewijanie mogą pomijać układ, wstępne wyrenderowanie i wyrenderowanie. Dlatego animacje i przewijanie są na diagramie oznaczone żółtymi i zielonymi punktami. Jeśli w celu uzyskania efektów wizualnych można pominąć układ, wstępne wyrenderowanie i malowanie, można uruchomić w całości w wątku kompozytora i pominąć wątek główny.

Renderowanie interfejsu przeglądarki nie jest tutaj pokazane bezpośrednio, ale można je traktować jako uproszczoną wersję tego samego potoku (a właściwie jego implementacja dzieli znaczną część kodu). Film (również nieprzedstawiony bezpośrednio) zwykle renderuje się za pomocą niezależnego kodu, który dekoduje ramki na fragmenty tekstur GPU które są następnie łączone w ramkach kompozytora i etapie rysowania.

Proces i struktura wątków

Procesy procesora

Stosowanie wielu procesów procesora zapewnia izolację wydajności i bezpieczeństwa między witrynami i stanem przeglądarki stabilności i bezpieczeństwa przez sprzęt z GPU.

Schemat różnych części procesów procesora

  • Proces renderowania renderuje, animuje, przewija i trasuje dane wejściowe dla obiektu dla pojedynczej witryny i karty. Istnieje kilka procesów renderowania.
  • Proces przeglądarki renderuje, animuje i trafia dane wejściowe dla interfejsu przeglądarki (w tym pasek adresu, tytuły kart i ikony), a także trasy dane wejściowe do odpowiedniego procesu renderowania. Występuje jeden proces przeglądarki.
  • Proces Viz łączy kompozycje z wielu procesów renderowania oraz proces przeglądania. Obraz przesuwa się w górę i rysuje za pomocą GPU. Jest jeden proces Viz.

Zawsze otwierają się różne witryny w różnych procesach renderowania.

Kilka kart lub okien przeglądarki z tą samą witryną zwykle wyświetla się w różnym renderowaniu chyba że karty są ze sobą powiązane, np. otwierając drugie. W przypadku dużego obciążenia pamięci na komputerze Chromium może umieścić wiele kart z tej samej witryny do tego samego procesu renderowania, nawet jeśli nie są powiązane.

Na jednej karcie przeglądarki ramki z różnych witryn są zawsze w różnych procesach renderowania, ale ramki z tej samej witryny są zawsze poddawane temu samemu procesowi renderowania. Jeśli chodzi o renderowanie, Ważną zaletą wielu procesów renderowania jest to, że elementy iframe z różnych witryn i kart uzyskują izolację wydajności. od siebie nawzajem. Poza tym źródła mogą uzyskać jeszcze większą izolację.

Dla całej platformy Chromium istnieje dokładnie jeden proces Viz, ponieważ zwykle jeden GPU i ekran do rysowania.

Oddzielenie usługi Viz do własnego procesu poprawia stabilność w przypadku błędów Sterowniki lub sprzęt GPU. Pozwala to również izolować od zabezpieczeń, Jest to ważne w przypadku interfejsów API GPU, Vulkan ogólne bezpieczeństwo.

Przeglądarka może mieć wiele kart i okien, wszystkie mają piksele interfejsu przeglądarki, możesz się zastanawiać, dlaczego istnieje dokładnie jeden proces przeglądarki. Dzieje się tak dlatego, że w danym momencie skupiona jest tylko jedna z nich. W rzeczywistości niewidoczne karty przeglądarki są w większości dezaktywowane i wykorzystywana jest cała pamięć GPU. Jednak coraz częściej wdrażane są złożone funkcje renderowania UI przeglądarek. również w procesach renderowania (nazywanych WebUI). nie z powodu izolacji wydajności, ale także po to, by wykorzystać łatwość obsługi silnika renderowania internetowego Chromium.

Na starszych urządzeniach z Androidem proces renderowania i przeglądarki jest wspólny, gdy jest używany w komponencie WebView. (ogólnie nie dotyczy to Chromium na Androidzie, tylko WebView). W komponencie WebView proces przeglądarki jest też udostępniany aplikacji umieszczonej na stronie. a komponent WebView ma tylko 1 proces renderowania.

Niekiedy istnieje też proces narzędziowy do dekodowania chronionych treści wideo. Proces ten nie został zaprezentowany na poprzednich diagramach.

Wątki

Wątki pomagają osiągnąć izolację wydajności i responsywność pomimo powolnych zadań. równoległość potoku i wielokrotne buforowanie.

Schemat procesu renderowania.

  • Wątek główny uruchamia skrypty, pętlę zdarzeń renderowania, cykl życia dokumentu testowania trafień, wysyłania zdarzeń skryptu oraz analizy HTML, CSS i innych formatów danych.
    • Asystenty wątków głównych wykonują zadania takie jak tworzenie obrazowych map bitowych i obiektów blob, które wymagają kodowania lub dekodowania.
    • Skrypty internetowe oraz pętlę zdarzeń renderowania dla OffscreenCanvas.
  • Wątek kompozytora przetwarza zdarzenia wejściowe, wykonuje przewijanie i animowanie treści internetowych, oblicza optymalną warstwę warstwową treści internetowych, i koordynuje dekodowanie obrazów, malowanie instancji roboczych i zadania rastrowania.
    • Pomocnicy wątków kompozytora koordynują zadania rastrowe Viz, i wykonywać zadania dotyczące dekodowania obrazów, malowania workletów i rastrów zastępczych.
  • dekodowanie wątków multimedialnych, demukserów i wyjściowych dźwięku, przetwarzania oraz synchronizowania strumieni wideo i audio. Pamiętaj, że film jest uruchamiany równolegle z głównym potokiem renderowania.

Oddzielenie wątku głównego i kompozytora jest niezwykle ważne izolacja wydajności animacji i przewijania z wątku głównego.

na proces renderowania jest tylko 1 wątek główny, nawet jeśli wiele kart lub ramek z tej samej witryny może zakończyć się tym samym procesem. Wydajność jest jednak odizolowana od pracy wykonywanej przy użyciu różnych interfejsów API przeglądarek. Na przykład generowanie obrazów bitmap i blobów w interfejsie Canvas API jest uruchamiane w wątku pomocniczym wątku głównego.

Podobnie na proces renderowania występuje tylko 1 wątek kompozytora. Zwykle nie chodzi o to, że jest tylko jedna, ponieważ wszystkie bardzo kosztowne operacje na wątku kompozytora są delegowane do wątków instancji roboczych kompozytora lub do procesu Viz, Można to robić równolegle z kierowaniem danych wejściowych, przewijaniem i animacją. Wątki instancji roboczych kompozytora koordynują zadania uruchamiane w procesie Viz, ale wszędzie przyspieszające działanie GPU mogą zakończyć się niepowodzeniem z powodów pozostających poza kontrolą Chromium, na przykład błędy kierowcy. W takich sytuacjach wątek instancji roboczej wykona działanie w trybie zastępczym procesora.

Liczba wątków instancji roboczych kompozytora zależy od możliwości urządzenia. Komputery zwykle używają więcej wątków, ponieważ mają więcej rdzeni procesora i mają mniejszą baterię niż urządzenia mobilne. Jest to przykład skalowaniu w górę i w dół.

Architektura podziału na wątki procesu renderowania to aplikacja 3 różnych wzorców optymalizacji:

  • Wątki pomocnicze: wysyłaj długotrwałe podzadania do dodatkowych wątków, aby Wątek nadrzędny reaguje na inne równoczesne żądania. Wątek główny Dobrymi przykładami tej techniki są wątki pomocnicze i kompozycyjne.
  • Buforowanie wielokrotne: pokazywać wyrenderowane wcześniej treści podczas renderowania nowych treści, aby ukryć czas renderowania. Wątek kompozytora używa tej techniki.
  • Równoległość potoku: uruchamianie potoku renderowania w wielu miejscach. jednocześnie. W ten sposób można przyspieszyć przewijanie i animację. nawet jeśli trwa aktualizacja renderowania głównego wątku, przewijanie i animacja mogą działać równolegle.

Proces przeglądarki

Diagram procesu przeglądarki przedstawiający relację między renderowaniem a wątkiem komponowania oraz pomocnikiem wątku renderowania i komponowania.

  • Wątek renderowania i komponowania odpowiada na dane wejściowe w interfejsie przeglądarki, przekierowuje inne dane wejściowe do właściwego procesu renderowania; nakłada i maluje interfejs przeglądarki.
  • Asystenty wątków renderowania i komponowania wykonywania zadań dekodowania obrazów oraz rastrowania i dekodowania kreacji zastępczych.

Renderowanie i komponowanie wątku w przeglądarce są podobne z kodem i funkcjami procesu renderowania, oprócz tego, że wątek główny i wątek kompozytora są połączone w jeden. W tym przypadku potrzebny jest tylko 1 wątek, ponieważ nie ma potrzeby izolacja od długich zadań w wątku głównym, ponieważ tak nie jest.

Proces Viz

Proces Viz obejmuje wątek główny GPU i wątek kompozytora wyświetlania.

  • Wątek główny GPU przesyła listy i klatki wideo do kafelków tekstur GPU, i rysuje na ekranie ramki kompozytora.
  • Wątek kompozytora displayowego agreguje i optymalizuje komponowanie z każdego procesu renderowania. i proces przeglądarki, w pojedynczą ramkę kompozytora, która będzie prezentowana na ekranie.

Raster i rysowanie zwykle odbywają się w tym samym wątku, bo oba zależą od zasobów GPU, i trudno jest niezawodnie wykorzystać wielowątkowy układ GPU, (łatwiejszy wielowątkowy dostęp do GPU jest jedną z motywacji do opracowania Vulkan). W komponencie WebView Androida jest osobny wątek renderowania na poziomie systemu operacyjnego na potrzeby rysowania. z powodu sposobu, w jaki komponenty WebView są osadzone w aplikacjach natywnych. Możliwe, że w przyszłości taki wątek będzie się powtórzył na innych platformach.

Kompozytor reklam displayowych znajduje się w innym wątku, ponieważ musi zawsze odpowiadać, i nie blokuj żadnych możliwych źródeł spowalniania w głównym wątku GPU. Jedną z przyczyn spowolnienia w głównym wątku GPU są wywołania kodu spoza Chromium, np. sterowniki GPU konkretnego dostawcy, które mogą działać wolniej w trudno do przewidzenia.

Struktura komponentu

W każdym głównym lub kompozycyjnym wątku procesu renderowania istnieją logiczne komponenty oprogramowania, które współdziałają ze sobą w uporządkowany sposób.

Komponenty głównego wątku procesu renderowania

Schemat mechanizmu renderowania Blink.

W mechanizmie renderowania Blink:

  • Fragment lokalnego drzewa ramek reprezentuje drzewo lokalnych ramek i elementu DOM w ramkach.
  • Komponent DOM i Canvas API zawiera implementacje wszystkich tych interfejsów API.
  • Uruchamiający cykl życia dokumentu wykonuje kroki potoku renderowania aż do etapu zatwierdzenia włącznie.
  • Komponent Testowanie i wysyłanie działań związanych ze zdarzeniem wejściowym wykonuje testy działań w celu Sprawdzenie, który element DOM jest celem zdarzenia, i uruchamianie zdarzenia wejściowego algorytmy wysyłające i zachowania domyślne.

Harmonogram pętli zdarzeń renderowania i sposób uruchamiający określa, co ma zostać uruchomione w wydarzeniu i kiedy. Zaplanuje renderowanie z częstotliwością odpowiadającą urządzeniu wyświetlacz.

Diagram drzewa ramek.

Fragmenty lokalnych drzew ramek są nieco skomplikowane. Zwróć uwagę, że drzewo ramek to rekurencyjna strona główna i jej podrzędne elementy iframe. Ramka jest lokalna dla procesu renderowania, jeśli jest w nim renderowany. a w przeciwnym razie – zdalnie.

Można sobie wyobrazić kolorowanie ramek zgodnie z procesem renderowania. Na poprzednim obrazie zielone okręgi to wszystkie klatki w ramach jednego procesu renderowania. pomarańczowe – w sekundzie, a niebieskie – w trzeciej.

Fragment drzewa ramek lokalnego to połączony komponent tego samego koloru w drzewie ramki. Na zdjęciu widać 4 lokalne drzewa z ramkami: 2 z miejsca A, 1 z miejscem B i 1 z miejscem C. Każde lokalne drzewo ramek otrzymuje własny komponent mechanizmu renderowania Blink. Mechanizm renderowania Blink lokalnego drzewa ramek może, ale nie musi, być renderowany w tym samym procesie renderowania jak inne lokalne drzewa z ramkami. Zależy to od sposobu wybierania procesów renderowania, jak opisaliśmy wcześniej.

Struktura wątków kompozytora procesu renderowania

Diagram przedstawiający komponenty kompozytora procesu renderowania.

Komponenty kompozytora procesu renderowania obejmują:

  • Moduł obsługi danych, który obsługuje listę skomponowanych warstw, listy wyświetlania i drzewa właściwości.
  • Tag cyklu życia gry, który uruchamia animacje, przewijanie, kompozycje, rastrowanie, oraz dekodowanie i aktywowanie kroków potoku renderowania. Pamiętaj, że animowanie i przewijanie mogą mieć miejsce zarówno w wątku głównym, jak i w kompozytorze.
  • Moduł obsługi danych wejściowych i testów trafień wykonuje przetwarzanie danych wejściowych i testowanie trafień w rozdzielczości skomponowanych warstw. aby określić, czy w wątku kompozytora można uruchamiać gesty przewijania, oraz na który proces renderowania powinny być kierowane testy trafienia procesu renderowania.

Przykładowa architektura w praktyce

W tym przykładzie znajdują się 3 karty:

Karta 1. foo.com

<html>
  <iframe id=one src="foo.com/other-url"></iframe>
  <iframe  id=two src="bar.com"></iframe>
</html>

Karta 2. bar.com

<html>
 …
</html>

Karta 3. baz.com html <html> … </html>

Proces, wątki i struktura komponentów na tych kartach wyglądają tak:

Schemat procesu korzystania z kart.

Omówmy po jednym przykładzie każdego z 4 głównych zadań renderowania. Przypomnienie:

  1. Renderuj treści na ekranie w piksele.
  2. Animowanie efektów wizualnych z jednego stanu do drugiego.
  3. Przewiń w odpowiedzi na dane wejściowe.
  4. Sprawnie kieruj dane wejściowe w odpowiednie miejsca, aby skrypty programistyczne i inne podsystemy mogły na nie zareagować.

Aby wyrenderować zmieniony DOM na pierwszej karcie:

  1. Skrypt programisty zmienia DOM w procesie renderowania na potrzeby foo.com.
  2. Mechanizm renderowania Blink informuje kompozytora, że wymaga wyrenderowania.
  3. Kompozytor informuje zespół Viz, że wymaga wyrenderowania.
  4. Narzędzie Viz sygnalizuje początek renderowania z powrotem do kompozytora.
  5. Kompozytor przekazuje sygnał startowy do mechanizmu renderowania Blink.
  6. Uruchamiający pętlę zdarzeń głównego wątku uruchamia cykl życia dokumentu.
  7. Wątek główny wysyła wynik do wątku kompozytora.
  8. Uruchamiający pętlę zdarzeń kompozytora uruchamia cykl życia komponowania.
  9. Wszystkie zadania rastrowania są wysyłane do Viz w celu wykonania prac rastrowych (często istnieje więcej niż jedno z nich).
  10. Viz rasteryzuje treści w GPU.
  11. Usługa Viz potwierdza wykonanie zadania rastrowego. Uwaga: Chromium często nie czeka na zakończenie procesu, a zamiast tego używa czegoś zwanego token synchronizacji do rozwiązania, przez zadania rastrowania przed wykonaniem kroku 15.
  12. Ramka kompozytora jest wysyłana do Viz.
  13. Viz agreguje ramki kompozytora na potrzeby procesu renderowania foo.com, procesu renderowania iframe w bar.com i interfejsie przeglądarki.
  14. Viz planuje losowanie.
  15. Viz pobiera na ekran zagregowaną ramkę kompozytora.

Aby animować przejście przekształcenia CSS na drugiej karcie:

  1. Wątek kompozytora procesu renderowania bar.com zaznacza animację w pętli zdarzeń kompozytora przez mutację istniejących drzew właściwości. Spowoduje to ponowne uruchomienie cyklu życia kompozytora. Zadania rastrowania i dekodowania mogą być wykonywane, ale nie zostały one tutaj opisane.
  2. Ramka kompozytora jest wysyłana do Viz.
  3. Viz agreguje ramki kompozytora na potrzeby procesu renderowania foo.com, procesu renderowania bar.com i interfejsu przeglądarki.
  4. Viz planuje losowanie.
  5. Viz pobiera na ekran zagregowaną ramkę kompozytora.

Aby przewinąć stronę internetową na karcie trzeciej:

  1. Przeglądarka uruchamia sekwencję zdarzeń input (mysz, dotyk lub klawiatura).
  2. Każde zdarzenie jest kierowane do wątku kompozytora procesu renderowania baz.com.
  3. Kompozytor określa, czy wątek główny musi mieć dostęp do zdarzenia.
  4. W razie potrzeby zdarzenie jest wysyłane do wątku głównego.
  5. Wątek główny uruchamia detektory zdarzeń input (pointerdown, touchstar, pointermove, touchmove lub wheel) aby sprawdzić, czy detektory wywołają zdarzenie preventDefault w tym zdarzeniu.
  6. Wątek główny zwraca, czy do kompozytora wywołano funkcję preventDefault.
  7. W przeciwnym razie zdarzenie wejściowe jest odsyłane z powrotem do procesu przeglądarki.
  8. Proces przeglądarki przekształca go w gest przewijania, łącząc go z innymi ostatnimi zdarzeniami.
  9. Gest przewijania jest wysyłany ponownie do wątku kompozytora procesu renderowania baz.com.
  10. Przewijanie jest tam stosowane, a wątek kompozytora dla bar.com procesu renderowania zaznacza animację w pętli zdarzeń kompozytora. Następnie zmienia przesunięcie przewijania w drzewach właściwości i ponownie uruchamia cykl życia kompozytora. Informuje on też wątek główny o uruchamianiu zdarzenia scroll (nieprzedstawionego tutaj).
  11. Ramka kompozytora jest wysyłana do Viz.
  12. Viz agreguje ramki kompozytora na potrzeby procesu renderowania foo.com, proces renderowania na bar.com i interfejs przeglądarki.
  13. Viz planuje losowanie.
  14. Viz pobiera na ekran zagregowaną ramkę kompozytora.
.

Aby przekierować zdarzenie click w hiperlinku w elemencie iframe nr 2 na karcie 1:

  1. Zdarzenie input (mysz, dotyk lub klawiatura) przekazuje działanie przeglądarki. Przeprowadza przybliżony test trafień aby określić, czy proces renderowania iframe bar.com powinien otrzymać kliknięcie, i je tam wysłać.
  2. Wątek kompozytora dla bar.com kieruje zdarzenie click do wątku głównego. dla bar.com i planuje przetworzenie tego zadania w pętli zdarzeń renderowania.
  3. Procesor zdarzeń wejściowych dla testów trafień wątków głównych na stronie bar.com określa, które Kliknięto element DOM w elemencie iframe i uruchamia zdarzenie click, które mają być obserwowane przez skrypty. Jeśli nie słyszysz słowa preventDefault, nastąpi przejście do hiperlinku.
  4. Po wczytaniu strony docelowej hiperlinku wyświetlany jest nowy stan, z krokami podobnymi do „renderowania zmienionego DOM” poprzedniego przykładu. (kolejne zmiany nie zostały tu pokazane).

Na wynos

Zapamiętanie i przyswojenie działania renderowania może zająć dużo czasu.

Najważniejszą rzeczą jest to, że potok renderowania, przy zachowaniu staranności modularyzację i dbałość o szczegóły zostały podzielone na samodzielnych komponentów. Te komponenty zostały następnie podzielone na równoległe procesów i wątków, aby zmaksymalizować skalowalną wydajność i elastyczności.

Każdy z nich odgrywa kluczową rolę w umożliwianiu działania i funkcjach nowoczesnych aplikacji internetowych.

Przeczytaj więcej o kluczowych strukturach danych, które są dla RenderingNG równie ważne jak komponenty kodu.


Ilustracje Uny Kravets.