Model warstwy
Wprowadzenie
Dla większości programistów stron internetowych podstawowym modelem strony jest DOM. Renderowanie to często niejasny proces polegający na przekształceniu strony w obraz na ekranie. W ostatnich latach nowoczesne przeglądarki zmieniły sposób działania renderowania, wykorzystując karty graficzne. Tę sytuację często określa się rzadko jako „akceleracja sprzętowa”. Co tak naprawdę oznacza dany termin, mówiąc o zwykłej stronie internetowej (tzn. nie o Canvas2D czy WebGL?) W tym artykule opisujemy podstawowy model, na którym opiera się sprzętowe renderowanie treści z internetu w Chrome.
Uwagi na temat dużych ilości tłuszczu
Mówimy tutaj o WebKit, a dokładniej o porcie Chrome WebKit. W tym artykule znajdują się informacje o implementacji Chrome, a nie o funkcjach platformy internetowej. Platforma internetowa i standardy nie wymagają kodowania na tym poziomie szczegółów, więc nie ma gwarancji, że żaden z artykułów w tym artykule nie będzie miał zastosowania do innych przeglądarek, ale wiedza o mechanizmach wewnętrznych może być przydatna przy zaawansowanym debugowaniu i dostosowywaniu wydajności.
Zwróć też uwagę, że w całym artykule omawiamy podstawowy element architektury renderowania w Chrome, który bardzo szybko się zmienia. W tym artykule opisujemy tylko te elementy, które prawdopodobnie się nie zmienią, ale nie gwarantujemy, że te informacje będą obowiązywać przez 6 miesięcy.
Trzeba pamiętać, że od dłuższego czasu Chrome używał dwóch ścieżek renderowania: z przyspieszeniem sprzętowym i starszej ścieżki oprogramowania. W ten sposób wszystkie strony przechodzą przez przyspieszanie sprzętowe ścieżki w systemach Windows, ChromeOS i Chrome na Androida. Na Macach i w Linuksie tylko strony wymagające komponowania części zawartości przechodzą przez przyspieszoną ścieżkę (poniżej znajdziesz informacje o tym, co wymaga komponowania), ale wkrótce wszystkie strony też przejdą w tym samym kierunku.
Na koniec przyjrzymy się bliżej mechanizmowi renderowania i przyjrzymy się jego funkcjom, które mają duży wpływ na wydajność. Przy próbie zwiększenia wydajności własnej witryny pomocne może być zrozumienie modelu warstwy, ale łatwo jest też poczuć się jak w łóżku: warstwy to przydatne konstrukcje, ale stworzenie wielu z nich może powodować przeciążenie całego stosu grafiki. Pamiętaj, że możesz się z tym zapoznać.
Z DOM na ekran
Przedstawiamy Warstwy
Po wczytaniu i analizie strony jest ona reprezentowana w przeglądarce jako struktura znana wielu programistom stron internetowych: DOM. Przeglądarka podczas renderowania strony przedstawia jednak szereg pośrednich reprezentacji, które nie są bezpośrednio dostępne dla programistów. Najważniejszą z nich jest warstwa.
W Chrome jest kilka różnych typów warstw: Warstwy renderowania, które odpowiadają za poddrzewa modelu DOM, i Warstwy graficzne, które odpowiadają za poddrzewa warstw renderowania. Najciekawsza jest dla nas ta druga opcja, ponieważ warstwy graficzne są tym, co jest przesyłane do GPU jako tekstury. Od tego momentu dodam „layer” i to znaczy GraphicsLayer.
Krótko o terminologii dotyczącej GPU: czym jest tekstura? Potraktuj go jako obraz bitmapowy, który jest przenoszony z pamięci głównej (tj. RAM) do pamięci wideo (czyli pamięci VRAM w GPU). Po umieszczeniu tego obrazu na GPU można go zmapować na geometrię siatki. W grach wideo lub programach CAD ta technika służy do tworzenia „skóry” szkieletowych modeli 3D. Chrome używa tekstur, aby przesyłać kawałki treści stron internetowych do GPU. Tekstury można tanio przypisać do różnych pozycji i przekształceń, stosując je do naprawdę prostej prostokątnej siatki. Właśnie tak działa 3D CSS i świetnie sprawdza się w przypadku szybkiego przewijania, ale obie te funkcje omówimy później.
Spójrzmy na kilka przykładów ilustrujących koncepcję warstwy.
Flaga „pokaż skomponowane granice warstw” w Narzędziach deweloperskich w sekcji „Renderowanie” jest bardzo przydatnym narzędziem w ustawieniach (np. małej ikony koła zębatego). Powoduje po prostu wyróżnienie miejsc, w których znajdują się warstwy na ekranie. Włączmy go. 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>
Ta strona ma tylko jedną warstwę. Niebieska siatka przedstawia kafelki, czyli podrzędne jednostki warstwy, których Chrome używa do jednoczesnego przesyłania do GPU elementów 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>
Dzięki dodaniu właściwości 3D CSS do elementu <div>
, który go obraca, możemy zobaczyć, jak element otrzymuje własną warstwę: zwróć uwagę na pomarańczowe obramowanie, które obrysuje warstwę w tym widoku.
Kryteria tworzenia warstwy
Co jeszcze zyskuje własną warstwę? Stosowane tu schematy heurystyczne Chrome ewoluowały i nadal się zmieniają, ale obecnie istnieje możliwość utworzenia dowolnej z tych warstw aktywujących:
- Właściwości CSS „Transformacja 3D” lub „przekształcanie perspektywy”
- Elementy
<video>
korzystają z przyspieszonego dekodowania wideo - Elementy
<canvas>
z kontekstem 3D (WebGL) lub z przyspieszonym kontekstem 2D - Wtyczki skomponowane (np. Flash)
- elementy zawierające animację CSS ze względu na przezroczystość 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 element równorzędny o niższym wskaźniku z-index, który ma warstwę komponującą (czyli jest renderowany na skomponowanej warstwie).
Praktyczne konsekwencje: animacja
Warstwy możemy też przesuwać, dzięki czemu są one bardzo przydatne przy tworzeniu animacji.
Rysunek 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ą naprawdę przydatne podczas przenoszenia statycznych treści z internetu. W podstawowym przypadku Chrome maluje zawartość warstwy do programowej mapy bitowej, a następnie przesyła ją do GPU jako teksturę. Jeśli treści nie zmienią się w przyszłości, nie trzeba będzie malować ich od nowa. To dobrze, bo ponowne malowanie zajmuje czas, który można przeznaczyć na inne czynności, takie jak JavaScript, a jeśli malowanie jest długie, powoduje problemy z efektami lub opóźnienia w animacjach.
Zobacz na przykład ten widok osi czasu w Narzędziach deweloperskich: podczas obracania się warstwą nie ma żadnych operacji renderowania.
Nieprawidłowe! Odmalowywanie
Jeśli jednak zawartość warstwy ulegnie zmianie, trzeba będzie ją ponownie pomalować.
Rysunek 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 rozszerza się o 1 piksel. Powoduje to przesłanie i ponowne wyrenderowanie całego elementu, czyli w tym przypadku całej warstwy.
Aby zobaczyć, co jest malowane, skorzystaj z narzędzia „Pokaż rekty malowania” w Narzędziach deweloperskich, które znajdziesz też w sekcji „Renderowanie” w ustawieniach Narzędzi deweloperskich. Po włączeniu tej funkcji zwróć uwagę, że po kliknięciu przycisku animowany element i przycisk będą migać na czerwono.
Zdarzenia renderowania również pojawiają się na osi czasu Narzędzi deweloperskich. Bystrowi czytelnicy mogą zauważyć 2 zdarzenia renderowania: jedno dla warstwy i drugie dla samego przycisku, które jest ponownie malowane, gdy zmienia się w stan depresji lub z powrotem.
Pamiętaj, że Chrome nie zawsze musi ponownie malować całą warstwę – stara się to zrobić rozsądnie tylko w tej części DOM, która została unieważniona. W tym przypadku zmodyfikowany przez nas element DOM to rozmiar całej warstwy. Jednak w wielu innych przypadkach w warstwie może znajdować się wiele elementów DOM.
Kolejne pytanie dotyczy tego, co powoduje unieważnienie i wymusza ponowne wyrenderowanie. Trudno jest dokładnie odpowiedzieć na to pytanie, ponieważ istnieje wiele skrajnych przypadków, które mogą wymuszać unieważnienie. Najczęstszą przyczyną jest zaburzenie DOM przez manipulowanie stylami CSS lub wywołanie przekazywania. Tony Gentilcore opublikował świetnego posta na blogu na temat przyczyn przekazywania informacji, a Stoyan Stefanov szczegółowo omawia artykuł o malowaniu (ale kończy się wyłącznie malowaniem, a nie kompozycjami).
Najlepszym sposobem na sprawdzenie, czy ma to wpływ na coś, nad którym pracujesz, jest skorzystanie z osi czasu w Narzędziach deweloperskich i narzędzi Pokaż malowanie prostokątów w celu sprawdzenia, czy malujesz, gdy tego nie chcesz. Następnie spróbuj zidentyfikować miejsce, w którym DOM został skasowany tuż przed tym przekazem/odmalowaniem. Jeśli malowanie jest nieuniknione, ale wydaje się trwać zbyt długo, zajrzyj do artykułu Eberharda Gräthera na temat trybu ciągłego malowania w Narzędziach deweloperskich.
Jak połączyć model DOM z ekranem
Jak Chrome przekształca DOM w obraz ekranu? Zasadniczo oznacza to:
- Dzieli DOM na warstwy
- Maluje każdą z tych warstw niezależnie w postaci mapy bitowej
- Przesyła je do GPU jako tekstury
- Połączenie różnych warstw w ostateczny obraz ekranu.
Wszystko to jest konieczne przy pierwszym utworzeniu ramki strony internetowej przez Chrome. W przyszłości możesz jednak użyć kilku skrótów:
- Jeśli określone właściwości CSS ulegną zmianie, nie musisz niczego odświeżać. Chrome może po prostu ponownie skomponować istniejące warstwy, które już znajdują się w GPU, jako tekstury, ale z różnymi właściwościami komponowania (np.w różnych pozycjach, nieprzezroczystości itp.).
- Jeśli część warstwy zostanie unieważniona, zostanie ona odmalowana i przesłana jeszcze raz. Jeśli zawartość pozostaje taka sama, ale skomponowane atrybuty ulegną zmianie (np. przekształcają lub zmieniają nieprzezroczystość), Chrome może pozostawić ją w układzie GPU i złożyć kompozycję, aby utworzyć nową klatkę.
Jak widać, model komponowania oparty na warstwach ma duży wpływ na wydajność renderowania. Komponowanie jest stosunkowo tanie, gdy nie trzeba malować nic, więc przy debugowaniu wydajności renderowania warto unikać ponownego malowania warstw. Doświadczeni programiści zapoznają się z podaną wyżej listą aktywatorów komponowania i zdają sobie sprawę, że można łatwo wymusić tworzenie warstw. Nie należy ich jednak tworzyć na ślepo – nie są one wolne. Zajmują one pamięć w systemowej pamięci RAM i GPU (w szczególności w przypadku urządzeń mobilnych), a wiele z nich może powodować konieczność powstawania dodatkowych narzutów, które są widoczne w logice. Wiele warstw może też wydłużyć czas rasteryzacji, jeśli są duże i znacznie nakładają się na siebie w miejscach, w których wcześniej się nie znajdowały. Dochodzi to do tzw. „przerysowania”. Dlatego mądrze wykorzystaj swoją wiedzę.
Na razie to wszystko. Wkrótce udostępnimy jeszcze kilka artykułów o praktycznych konsekwencjach modelu warstw.