Renderowanie przyspieszone w Chrome

Model warstwy

Tom Wiltzius
Tom Wiltzius

Wstęp

Dla większości programistów internetowych podstawowym modelem strony internetowej jest DOM. Renderowanie to często niejasny proces przekształcania takiej reprezentacji strony w obraz na ekranie. W ostatnich latach nowoczesne przeglądarki zmieniły sposób działania renderowania, aby wykorzystać karty graficzne: często określa się to jako „przyspieszenie sprzętowe”. Co to tak naprawdę oznacza, gdy mówimy o zwykłej stronie internetowej (tzn. nie Canvas2D czy WebGL?) W tym artykule opisujemy podstawowy model, na którym opiera się sprzętowe renderowanie treści internetowych w Chrome.

Duże, grube ostrzeżenia

Mówimy tutaj o WebKit, a konkretnie o porcie w Chromium do WebKit. W tym artykule znajdziesz szczegóły implementacji Chrome, a nie funkcji platformy internetowej. Platforma internetowa i standardy nie określają tego poziomu szczegółowości implementacji, więc nie ma gwarancji, że artykuł będzie odnosił się do innych przeglądarek, ale wiedza o urządzeniu wewnętrznym może się przydać do zaawansowanego debugowania i dostrajania wydajności.

Zwróć też uwagę, że w całym artykule omawiamy podstawowy element architektury renderowania Chrome, który bardzo szybko się zmienia. W tym artykule staramy się poruszyć tylko te rzeczy, które prawdopodobnie się nie zmienią, ale nie gwarantujemy, że wszystkie one zaczną obowiązywać w ciągu 6 miesięcy.

Pamiętaj, że od jakiegoś czasu w Chrome dostępne są 2 różne ścieżki renderowania: ścieżka z akceleracją sprzętową i starsza ścieżka oprogramowania. Od tego momentu wszystkie strony korzystają z akceleracji sprzętowej w systemach Windows, ChromeOS i Chrome na Androida. W systemach macOS i Linux tylko strony wymagające komponowania dla części treści przechodzą w ścieżce przyspieszonej ścieżki (poniżej znajdziesz więcej informacji na temat tego, co wymaga komponowania), ale wkrótce wszystkie strony zostaną przeniesione na tę stronę zgodnie ze ścieżką przyspieszoną.

Na koniec przyjrzymy się bliżej masku mechanizmu renderowania, który ma duży wpływ na wydajność. Przy próbie poprawy wydajności witryny pomocne może być poznanie modelu warstwowego, ale równie dobrze można łatwo rzucić się w oczy: warstwy to przydatne konstrukcje, ale utworzenie wielu z nich może spowodować nadwyżkę całego stosu grafiki. Pamiętaj, że możesz się wcześniej ostrzegać!

Z DOM na ekran

Przedstawiamy Warstwy

Po wczytaniu i analizie strona jest reprezentowana w przeglądarce pod postacią struktury znanej wielu programistów stron internetowych: DOM. Podczas renderowania strony przeglądarka otrzymuje jednak szereg reprezentacji pośrednich, które nie są bezpośrednio widoczne dla programistów. Najważniejsza z tych struktur to warstwa.

W Chrome jest kilka różnych typów warstw: Warstwy renderowania, które odpowiadają za drzewa podrzędne DOM, i Warstwy graficzne (odpowiadają za poddrzewa warstwy renderowania). Ta ostatnia jest w tym przypadku najbardziej interesująca, ponieważ warstwy graficzne są przesyłane do GPU w postaci tekstur. Od teraz będzie to „warstwa”, oznaczająca „GraphicsLayer”.

Wróćmy do terminologii związanej z GPU: czym jest tekstura? Jest to obraz bitmapy przeniesiony z pamięci głównej (RAM) do pamięci wideo (GPU). Gdy już znajdziesz się w GPU, możesz zmapować go na geometrię siatki – w grach wideo i programach CAD ta technika jest stosowana do nadawania szkieletowym modelom 3D wyglądu wyglądu. Chrome wykorzystuje tekstury, aby pobierać fragmenty zawartości stron internetowych do GPU. Tekstury można tanio przyporządkowywać do różnych pozycji i przekształceń, stosując je do naprawdę prostej prostokątnej siatki. W ten sposób działa 3D CSS i świetnie sprawdza się też w przypadku szybkiego przewijania, ale o jednym i drugim aspekcie więcej dowiesz się później.

Przyjrzyjmy się kilku przykładom, które ilustrują koncepcję warstwy.

Bardzo przydatnym narzędziem do badania warstw w Chrome jest flaga „Pokaż obramowanie skomponowanych warstw” w ustawieniach (np. mała ikona koła zębatego) w Narzędziach deweloperskich pod nagłówkiem „rendering”. Wskazuje on miejsca, w których warstwy znajdują się na ekranie. Włączmy to. Wszystkie te zrzuty ekranu i przykłady pochodzą z najnowszej wersji Chrome Canary – Chrome 27 w momencie tworzenia tego tekstu.

Rysunek 1. Strona jednowarstwowa

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Zrzut ekranu przedstawiający ramki renderowania warstwy skomponowanej wokół warstwy podstawowej strony
Zrzut ekranu przedstawiający ramki renderowania warstwy skomponowanej wokół warstwy podstawowej strony

Ta strona ma tylko jedną warstwę. Niebieska siatka reprezentuje kafelki, które można traktować jako jednostki podrzędne warstwy, których Chrome używa do jednoczesnego przesyłania do GPU części dużej warstwy. Nie są one tu naprawdę ważne.

Rysunek 2. Element we własnej warstwie

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
Zrzut ekranu przedstawiający ramki renderowania obróconej warstwy
Zrzut ekranu przedstawiający obramowanie obróconej warstwy

Umieszczenie w elemencie <div> właściwości 3D CSS, która powoduje obrócenie elementu, pozwala zobaczyć, jak wygląda posiadanie własnej warstwy. Zwróć uwagę na pomarańczowe obramowanie, które kreśluje warstwę w tym widoku.

Kryteria tworzenia warstw

Co jeszcze ma własną warstwę? Heurystyka Chrome zmienia się z biegiem czasu, ale obecnie na powstawanie warstwy wpływa jeden z poniższych czynników:

  • Właściwości CSS przekształcania 3D lub perspektywy
  • <video> elementu wykorzystującego przyspieszone dekodowanie wideo
  • Elementy <canvas> z kontekstem 3D (WebGL) lub kontekstem z przyspieszonym wczytywaniem 2D
  • Skomponowane wtyczki (np. Flash)
  • elementy z animacją CSS dotyczącą przezroczystości lub korzystające z animowanego przekształcenia;
  • Elementy z przyspieszonymi filtrami CSS
  • Element ma element podrzędny z warstwą komponującą (czyli jeśli element ma element podrzędny, który znajduje się we własnej warstwie)
  • Element ma odpowiednik o niższej wartości kolejności nakładania elementów i ma warstwę kompozycyjną (czyli jest renderowany na skomponowanej warstwie).

Praktyczne konsekwencje: animacja

Umożliwiamy również przenoszenie warstw, dzięki czemu są one bardzo przydatne w animacji.

Rys. 3: Animowane warstwy

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

Jak już wspomnieliśmy, warstwy są bardzo przydatne do poruszania się po statycznej treści internetowej. W podstawowym przypadku Chrome maluje zawartość warstwy w oprogramową bitmapę, zanim prześlesz ją do GPU jako teksturę. Jeśli te treści nie zmienią się w przyszłości, nie trzeba ich odświeżać. To jest dobre: malowanie wymaga czasu, który można poświęcić na inne rzeczy, np. uruchamianie JavaScriptu. Jeśli malowanie trwa bardzo długo, powoduje to opóźnienia lub opóźnienia w animacji.

Zobacz na przykład ten widok osi czasu w Narzędziach deweloperskich: nie ma operacji malowania, gdy ta warstwa zmienia się w obie strony.

Zrzut ekranu przedstawiający oś czasu w Narzędziach deweloperskich podczas animacji
Zrzut ekranu przedstawiający oś czasu Narzędzi deweloperskich podczas animacji

Nieprawidłowe! Odmalowywanie

Jeśli jednak jej zawartość ulegnie zmianie, trzeba będzie ją na nowo wymalować.

Rys. 4: Ponowne malowanie warstw

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

Po każdym kliknięciu elementu wejściowego obracający się element staje się szerszy o 1 piksel. Powoduje to ponowne układanie i ponowne malowanie całego elementu, w tym przypadku całej warstwy.

Dobrym sposobem na sprawdzenie, co jest malowane, jest użycie narzędzia „show paint rects” (pokaż retusze farby) w Narzędziach deweloperskich, również w sekcji „Rendering” (Renderowanie) w ustawieniach Narzędzi deweloperskich. Zwróć uwagę, że po włączeniu tej funkcji animowany element i przycisk migają na czerwono po kliknięciu.

Zrzut ekranu z polem wyboru „Pokaż prostokąty”
Zrzut ekranu z polem wyboru do pokazania prostokątów

Zdarzenia wyrenderowania pojawią się też na osi czasu w Narzędziach deweloperskich. Czytelnicy o ostrych oczach mogą zauważyć 2 zdarzenia wyrenderowania: jedno dla warstwy, a drugie dotyczące samego przycisku, które jest malowane po zmianie stanu na niższy.

Zrzut ekranu przedstawiający retuszowanie warstwy na osi czasu w Narzędziach deweloperskich
Zrzut ekranu przedstawiający oś czasu w Narzędziach deweloperskich przemalowywaną warstwę

Pamiętaj, że Chrome nie zawsze musi ponownie malować całą warstwę – stara się rozsądnie malować tylko tę część DOM, która została unieważniona. W tym przypadku zmodyfikowany przez nas element DOM ma rozmiar całej warstwy. W wielu innych przypadkach w warstwie pojawia się wiele elementów DOM.

Kolejne oczywiste pytanie brzmi: co powoduje unieważnienie i wymusza ponowne wyrenderowanie. Trudno odpowiedzieć na to pytanie wyczerpująco, ponieważ istnieje wiele skrajnych przypadków, które mogą wymuszać unieważnienia. Najczęstszą przyczyną jest uszkodzenie DOM przez manipulowanie stylami CSS lub wywoływanie przekazywania. Tony Gentilcore opublikował na blogu świetny post na temat przyczyn przekazywania wiadomości, a Stoyan Stefanov napisał bardziej szczegółowo artykuł poświęcony malowaniu (ale kończy się na malowaniu, a nie na tych wymyślnych komponowaniu).

Najlepszym sposobem na sprawdzenie, czy ma to wpływ na coś, nad którym pracujesz, jest użycie narzędzi dla programistów na osi czasu i narzędzi Show Paint Rects, aby sprawdzić, czy malujesz, kiedy wcześniej nie było to możliwe, a następnie spróbuj określić miejsca, w których został zabrudzony DOM. Jeśli malowanie jest nieuniknione, ale wydaje się trwać zbyt długo, przeczytaj artykuł Eberharda Gräthera na temat trybu malowania ciągłego w Narzędziach deweloperskich.

Łączenie w całość: DOM to Screen

Jak Chrome przekształca DOM w obraz ekranu? Według założenia:

  1. Rozdziela DOM na warstwy
  2. Maluje każdą z tych warstw niezależnie w postaci mapy bitowej oprogramowania
  3. Przesyła je do GPU jako tekstury.
  4. Składa się z różnych warstw w ostateczny obraz ekranu.

Wszystko to musi się wydarzyć, gdy Chrome po raz pierwszy utworzy ramkę strony internetowej. W przyszłości może też użyć skrótów:

  1. Jeśli pewne właściwości CSS ulegną zmianie, nie trzeba niczego malować. Chrome może po prostu ponownie skomponować istniejące warstwy, które są już umieszczone na GPU jako tekstury, ale różnią się właściwościami komponowania (np.w różnych położeniach, z różną nieprzezroczystością itd.).
  2. Jeśli część warstwy zostanie unieważniona, zostanie ponownie pomalowana i przesłana ponownie. Jeśli zawartość pozostaje taka sama, ale skomponowane atrybuty ulegną zmianie (np. zostanie przetłumaczona lub zmieni się jej przezroczystość), Chrome może pozostawić ją w GPU i zrekomponować, by utworzyć nową klatkę.

Jak już łatwo zauważyć, model komponowania oparty na warstwach ma znaczący wpływ na wydajność renderowania. Komponowanie jest stosunkowo tanie, gdy nie trzeba malować nic, więc unikanie ponownego wyrenderowania warstw to dobry cel przy debugowaniu wydajności renderowania. Doświadczeni programiści zapoznają się z powyższą listą reguł komponowania i uświadomią sobie, że można łatwo wymusić tworzenie warstw. Pamiętaj jednak, by tworzyć je śmiało, ponieważ nie są one bezpłatne: zajmują pamięć systemową pamięć RAM i GPU (zwłaszcza w przypadku urządzeń mobilnych), a mając ich dużo, możesz zauważyć inne nakłady pracy. Duże warstwy mogą również wydłużyć czas rasteryzacji, jeśli są one duże i w dużym stopniu nakładają się na siebie, co czasami określa się mianem „przerysowania”. Dlatego mądrze wykorzystuj swoją wiedzę.

Na razie to wszystko. Wkrótce udostępnimy kilka artykułów o praktycznych konsekwencjach modelu warstwowego.

Dodatkowe materiały