Przyjrzyjmy się kluczowym strukturom danych, które są wejściami i wyjściami potoku renderowania.
Te struktury danych to:
- Drzewa ram składają się z węzłów lokalnych i zdalnych, które wskazują, które dokumenty sieciowe są w którym procesie renderowania i którym procesorem Blink.
- Niezmienny drzewo fragmentów to dane wyjściowe (i dane wejściowe) algorytmu ograniczeń układu.
- Drzewa właściwości odzwierciedlają hierarchie transformacji, klipów, efektów i przewijania dokumentu internetowego. Są one używane w całym procesie.
- Listy wyświetlania i fragmenty malowania są danymi wejściowymi algorytmów rasteryzacji i warstw.
- Ramki kompozytora zawierają powierzchnie, renderowane powierzchnie i płytki tekstury GPU, które są używane do rysowania za pomocą GPU.
Zanim omówimy te struktury danych, podamy przykład, który jest rozwinięciem przykładu z oceny architektury. Przykład ten jest używany w całym dokumencie do demonstrowania, jak struktury danych są do niego stosowane.
<!-- Example code -->
<html>
<div style="overflow: hidden; width: 100px; height: 100px;">
<iframe style="filter: blur(3px);
transform: rotateZ(1deg);
width: 100px; height: 300px"
id="one" src="foo.com/etc"></iframe>
</div>
<iframe style="top:200px;
transform: scale(1.1) translateX(200px)"
id="two" src="bar.com"></iframe>
</html>
Ramka drzew
Czasami Chrome może renderować ramkę z innej witryny w innym procesie renderowania niż jej ramka nadrzędna.
W przykładowym kodzie występują 3 ramki:
W przypadku izolacji witryn Chromium używa do renderowania tej strony internetowej 2 procesów renderowania. Każdy proces renderowania ma własną reprezentację drzewa ramki dla danej strony internetowej:
Klatka renderowana w ramach innego procesu jest reprezentowana jako klatka zdalna. Ramka zdalna zawiera minimalną ilość informacji potrzebnych do działania jako placeholder podczas renderowania, na przykład wymiary. W przeciwnym razie ramka zdalna nie zawiera żadnych informacji potrzebnych do renderowania jej rzeczywistej zawartości.
Z kolei lokalna klatka reprezentuje klatkę, która przechodzi przez standardowy potok renderowania. Lokalny frame zawiera wszystkie informacje potrzebne do przekształcenia danych tego frame (np. drzewa DOM i danych stylów) w coś, co można wyrenderować i wyświetlić.
Proces renderowania działa na poziomie szczegółowości fragmentu lokalnego drzewa klatek.
Oto bardziej skomplikowany przykład z ramką główną foo.com
:
<iframe src="bar.com"></iframe>
I ramka podrzędna bar.com
:
<iframe src="foo.com/etc"></iframe>
Chociaż nadal są tylko 2 renderowanie, teraz są 3 fragmenty drzewa lokalnego, 2 w procesie renderowania dla foo.com
i 1 w procesie renderowania dla bar.com
:
Aby wygenerować jeden kompozytowany kadr dla strony internetowej, Viz przesyła jednocześnie żądanie kompozytowanego kadru z kadru głównego każdego z 3 lokalnych drzew kadrów, a następnie zbiera te kadry. Zapoznaj się też z sekcją dotyczącą ramek kompozytora.
Główna rama foo.com
i ramka podrzędna foo.com/other-page
są częścią tego samego drzewa ramek i renderowane w tym samym procesie.
Oba ramki mają jednak niezależne cykle życia dokumentu, ponieważ są częścią różnych lokalnych fragmentów drzewa ramki.
Z tego powodu nie można wygenerować jednej ramki kompozytowej dla obu w ramach jednej aktualizacji.
Proces renderowania nie ma wystarczającej ilości informacji, aby złożyć kompozytową ramkę wygenerowaną dla foo.com/other-page
bezpośrednio w kompozytowej ramie ramki głównej foo.com
.
Na przykład ramka nadrzędna bar.com
może wpływać na wyświetlanie ramki iframe foo.com/other-url
poprzez przekształcanie jej za pomocą CSS lub zasłanianie jej części innymi elementami w DOM.
Schemat aktualizacji właściwości wizualnych
Właściwości wizualne, takie jak współczynnik skalowania urządzenia i rozmiar widocznego obszaru, wpływają na renderowany wynik i mu być zsynchronizowane między lokalnymi fragmentami drzewa ramki. Z każdym lokalnym fragmentem drzewa ramki jest powiązany obiekt widżetu. Aktualizacje właściwości wizualnej są przekazywane do widżetu ramki głównej, a potem od góry do dołu do pozostałych widżetów.
Na przykład, gdy zmieni się rozmiar widocznego obszaru:
Ten proces nie jest natychmiastowy, dlatego replikowane właściwości wizualne zawierają również token synchronizacji. Kompozytor wizualny używa tego tokena synchronizacji, aby poczekać, aż wszystkie lokalne fragmenty drzewa klatek prześlą kompozytorski element klatki z bieżącym tokenem synchronizacji. Dzięki temu nie dochodzi do mieszania klatek z różnymi właściwościami wizualnymi.
Drzewo niezmiennych fragmentów
Niezmienny drzewo fragmentów jest wynikiem etapu układu w potoku renderowania. Reprezentuje położenie i rozmiar wszystkich elementów na stronie (bez zastosowanych transformacji).
Każdy fragment reprezentuje część elementu DOM. Zazwyczaj dla każdego elementu jest tylko jeden fragment, ale może ich być więcej, jeśli element jest podzielony na różne strony podczas drukowania lub na kolumny w kontekście wielu kolumn.
Po ułożeniu każdy fragment staje się niezmienny i nie można go już zmienić. Ważne: nakładamy też kilka dodatkowych ograniczeń. Nie:
- Zezwalaj na wszystkie odwołania „w górę” w drzewie. (element podrzędny nie może wskazywać elementu nadrzędnego).
- „przepływają” one w dół drzewa (element podrzędny odczytuje tylko informacje od swoich elementów podrzędnych, a nie od elementu nadrzędnego).
Te ograniczenia umożliwiają ponowne użycie fragmentu w kolejnych układach. Bez tych ograniczeń musielibyśmy często ponownie generować całe drzewo, co jest kosztowne.
Większość układów to zwykle stopniowe aktualizacje, np. aplikacja internetowa aktualizująca niewielką część interfejsu w odpowiedzi na kliknięcie elementu przez użytkownika. W idealnej sytuacji układ powinien działać tylko w stosunku do tego, co faktycznie zmieniło się na ekranie. Możemy to osiągnąć, wykorzystując jak najwięcej części poprzedniego drzewa. Oznacza to, że (zwykle) musimy odtworzyć tylko rdzeń drzewa.
W przyszłości ta niezmienna konstrukcja może pozwolić nam na wykonywanie ciekawych czynności, takich jak przekazywanie niezmiennego drzewa fragmentów przez granice wątków (aby wykonywać kolejne fazy na innym wątku), generowanie wielu drzew w celu płynnej animacji układu lub wykonywanie równoległych układów spekulatywnych. Daje nam to też możliwość korzystania z wieloprzewodowego układu.
Elementy fragmentu w tekście
Treść wstawiona (głównie stylizowany tekst) jest wyświetlana w nieco inny sposób. Zamiast struktury drzewa z polami i wskaźnikami, treści wstawiane w tekście reprezentujemy na liście płaskiej. Główną zaletą jest to, że płaska reprezentacja listy dla elementów wstawianych jest szybka i przydatna do sprawdzania lub zapytań dotyczących struktur danych wstawianych oraz do efektywnego wykorzystania pamięci. Jest to bardzo ważne dla wydajności renderowania stron internetowych, ponieważ renderowanie tekstu jest bardzo złożone i może łatwo stać się najwolniejszym elementem łańcucha, chyba że zostanie bardzo zoptymalizowane.
Płaska lista jest tworzona dla każdego kontekstu formatowania wstawianego w kolejności od najgłębszego do najpłytszego w drzewie podrzędnym układu wstawianego. Każdy wpis na liście jest tuplą (obiekt, liczba potomków). Weź pod uwagę na przykład taki kod DOM:
<div style="width: 0;">
<span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>
Właściwość width
ma wartość 0, dzięki czemu linia jest przewijana między „Cześć” a „tam”.
Gdy kontekst formatowania wstawianego jest przedstawiony w postaci drzewa, wygląda on tak:
{
"Line box": {
"Box <span>": {
"Text": "Hi"
}
},
"Line box": {
"Box <b>": {
"Text": "There"
}
},
{
"Text": "."
}
}
Lista płaska wygląda tak:
- (Pole tekstowe, 2)
- (Box <span>, 1)
- (Text "Cześć", 0)
- (Element liniowy, 3)
- (pole <b>, 1)
- (Tekst „there”, 0)
- (Tekst „.", 0)
Z tej struktury danych korzysta wiele usług: interfejsy API dotyczące dostępności i interfejsy API dotyczące geometrii, takie jak getClientRects
i contenteditable
.
Każdy z nich ma inne wymagania.
Te komponenty uzyskują dostęp do płaskiej struktury danych za pomocą wygodnej kursora.
Kursor ma interfejsy API, takie jak MoveToNext
, MoveToNextLine
i CursorForChildren
.
Takie reprezentacje kursora są bardzo przydatne w przypadku treści tekstowych z kilku powodów:
- Iterowanie w kolejności od najgłębszego do najpłytszego jest bardzo szybkie. Jest to bardzo często używane, ponieważ przypomina ruch kursora. Ponieważ jest to lista płaska, wyszukiwanie od najgłębszego poziomu tylko zwiększa przesunięcie tablicy, zapewniając szybkie iteracje i lokalizację pamięci.
- Zapewnia ono wyszukiwanie od najszerszego do najwęższego, co jest konieczne na przykład do wypełniania tła linii i ramek w wierszu.
- Znajomość liczby potomków pozwala szybko przejść do następnego elementu siostrzanego (wystarczy zwiększyć przesunięcie tablicy o tę liczbę).
Struktury usług
DOM to drzewo elementów (oraz węzły tekstowe), a CSS może stosować różne style do elementów.
Wyświetla się on w 4 sposobach:
- Layout: dane wejściowe algorytmu ograniczeń układu.
- Paint:sposób wypełniania i rasteryzacji elementu (ale nie jego potomków).
- Wizualny: efekty rastrowania lub rysowania zastosowane do poddrzewa DOM, takie jak transformacje, filtry i przycinanie.
- Przewijanie: zaokrąglone narożniki dopasowane do osi przycinanie i przewijanie zawartego poddrzewa.
Drzewa właściwości to struktury danych, które wyjaśniają, jak efekty wizualne i efekty przewijania są stosowane do elementów DOM. Dzięki nim można uzyskać odpowiedzi na pytania w rodzaju: „Gdzie w stosunku do ekranu znajduje się dany element DOM, biorąc pod uwagę jego rozmiar i położenie w układzie?”. I jaka sekwencja operacji na GPU powinna być używana do stosowania efektów wizualnych i przewijania?
Efekty wizualne i efekty przewijania w internecie są bardzo skomplikowane. Najważniejsze zadanie drzew właściwości polega na przekształceniu tej złożoności w jedną strukturę danych, która dokładnie odzwierciedla ich strukturę i znaczenie, a zarazem usuwa pozostałą złożoność DOM i CSS. Dzięki temu możemy stosować algorytmy do kompozytowania i przewijania z większą pewnością. W szczególności:
- Geometria i inne obliczenia, które mogą być podatne na błędy, mogą być scentralizowane w jednym miejscu.
- Złożoność tworzenia i aktualizowania drzew właściwości jest ograniczona do jednego etapu tworzenia.
- Wysyłanie drzew właściwości do różnych wątków i procesów jest znacznie łatwiejsze i szybsze niż wysyłanie pełnego stanu DOM. Dzięki temu można używać ich w wielu przypadkach użycia.
- Im więcej zastosowań, tym więcej korzyści możemy uzyskać dzięki pamięci podręcznej geometrii, ponieważ może ona korzystać z pamięci podręcznej innych aplikacji.
RenderingNG używa drzew właściwości do wielu celów, m.in.:
- oddzielenie kompozytowania od malowania i kompozytowania od głównego wątku.
- Określanie optymalnej strategii łączenia lub rysowania.
- Pomiar geometrii IntersectionObserver.
- unikanie pracy nad elementami poza ekranem i płytkami tekstur GPU;
- Skuteczne i dokładne unieważnianie malowania i rastera.
- pomiar przesunięcia układu i największego wyrenderowania treści w podstawowych wskaźnikach internetowych.
Każdy dokument internetowy ma 4 osobne drzewa właściwości: transform, clip, effect i scroll.(*) Drzewo transform reprezentuje przekształcenia i przewijanie CSS. (przekształcenie przewijania jest reprezentowane przez macierz przekształcenia 2D). Drzewo klipów reprezentuje klip z przepełnieniem. Drzewo efektów reprezentuje wszystkie inne efekty wizualne: przezroczystość, filtry, maski, tryby mieszania i inne rodzaje klipów, takie jak clip-path. Drzewo przewijania zawiera informacje o przewijaniu, takie jak sposób łańcuchowania przewijania. Jest ono potrzebne do przewijania w wątku kompozytora. Każdy węzeł w drzewie właściwości reprezentuje efekt wizualny lub przewijania zastosowany przez element DOM. Jeśli ma to miejsce, w przypadku tego samego elementu w każdym drzewie może być więcej niż 1 węzeł drzewa właściwości.
Topologia każdego drzewa jest jak rzadka reprezentacja DOM. Jeśli na przykład są 3 elementy DOM z klipami przepełnionymi, będą 3 węzły drzewa klipów, a struktura drzewa klipów będzie zgodna z relacją bloku zawierającego między klipami przepełnionymi. Pomiędzy drzewami są też połączenia. Te linki wskazują względną hierarchię DOM, a zatem kolejność stosowania węzłów. Jeśli na przykład transformacja elementu DOM znajduje się pod innym elementem DOM z filtrem, to oczywiście transformacja zostanie zastosowana przed filtrem.
Każdy element DOM ma stan drzewa właściwości, który jest 4-tuplem (transformacja, klip, efekt, przewijanie), wskazujący najbliższe węzły klipu, transformacji i efektu w drzewie, które mają wpływ na ten element. Jest to bardzo wygodne, ponieważ dzięki tym informacjom wiemy dokładnie, jakie klipy, przekształcenia i efekty zostały zastosowane do tego elementu oraz w jakiej kolejności. To wskazuje nam, gdzie jest na ekranie i jak go narysować.
Przykład
(źródło).
<html>
<div style="overflow: scroll; width: 100px; height: 100px;">
<iframe style="filter: blur(3px);
transform: rotateZ(1deg);
width: 100px; height: 300px"
id="one" srcdoc="iframe one"></iframe>
</div>
<iframe style="top:200px;
transform: scale(1.1) translateX(200px)" id=two
srcdoc="iframe two"></iframe>
</html>
W przypadku poprzedniego przykładu (który różni się nieco od przykładu w wprowadzeniu) kluczowe elementy wygenerowanych drzew usług to:
Wyświetlanie list i fragmentów malowania
Element wyświetlania zawiera proste polecenia rysowania (patrz tutaj), które można rastrować za pomocą Skia. Elementy wyświetlania są zwykle proste i składają się z kilku poleceń rysowania, takich jak rysowanie obramowania lub tła. Przejście po drzewie renderowania iteruje po drzewie układu i powiązanych fragmentach zgodnie z kolejnością renderowania w CSS, aby utworzyć listę elementów wyświetlania.
Na przykład:
<div id="green" style="background:green; width:80px;">
Hello world
</div>
<div id="blue" style="width:100px;
height:100px; background:blue;
position:absolute;
top:0; left:0; z-index:-1;">
</div>
Ten kod HTML i CSS wygeneruje listę wyświetlania, w której każda komórka jest elementem wyświetlania:
Tło widoku | #blue (tło) |
#green (tło) |
#green tekst wbudowany |
---|---|---|---|
drawRect o rozmiarze 800 x 600 i kolorze białym. |
drawRect o wymiarach 100 x 100 w pozycji 0,0 i kolorze niebieskim. |
drawRect o wymiarach 80 x 18 w pozycji 8,8 i kolorze zielonym. |
drawTextBlob z pozycją 8,8 i tekstem „Hello world”. |
Lista elementów wyświetlania jest uporządkowana od końca do początku. W przykładzie powyżej element div o kolorze zielonym jest w kolejności DOM przed elementem div o kolorze niebieskim, ale kolejność renderowania CSS wymaga, aby element div o kolorze niebieskim z ujemnym indeksem z-index został wyrenderowany przed elementem div o kolorze zielonym (krok 3) (krok 4.1). Elementy wyświetlania odpowiadają w przybliżeniu atomowym krokom specyfikacji kolejności malowania w CSS. Pojedynczy element DOM może mieć kilka elementów wyświetlania, na przykład #green ma element wyświetlania dla tła i inny element wyświetlania dla tekstu wstawianego. Ta szczegółowość jest ważna, aby odzwierciedlić pełną złożoność specyfikacji kolejności malowania w CSS, takich jak przeplatanie utworzone przez ujemny margines:
<div id="green" style="background:green; width:80px;">
Hello world
</div>
<div id="gray" style="width:35px; height:20px;
background:gray;margin-top:-10px;"></div>
Wygeneruje to listę wyświetlania, w której każda komórka jest elementem wyświetlania:
Tło widoku | #green (tło) |
#gray (tło) |
#green tekst wbudowany |
---|---|---|---|
drawRect o rozmiarze 800 x 600 i kolorze białym. |
drawRect o wymiarach 80 x 18 w pozycji 8,8 i kolorze zielonym. |
drawRect o wymiarach 35 x 20 w pozycji 8,16 i kolorze szarym. |
drawTextBlob z pozycją 8,8 i tekstem „Hello world”. |
Lista elementów wyświetlania jest przechowywana i używana ponownie w przypadku późniejszych aktualizacji. Jeśli obiekt układu nie zmienił się podczas przejścia po drzewie projektu, jego elementy wyświetlania są kopiowane z poprzedniej listy. Dodatkowa optymalizacja polega na wykorzystaniu właściwości specyfikacji kolejności renderowania CSS: konteksty nakładania się renderują się atomowo. Jeśli w ramach kontekstu nakładania się nie zmienił się żaden obiekt układu, przejście po drzewie obiektu pomija kontekst nakładania się i kopiuje całą sekwencję elementów wyświetlania z poprzedniej listy.
Obecny stan drzewa usługi jest zachowany podczas przechodzenia po drzewie wyświetlania, a lista elementów wyświetlania jest grupowana w „fragmenty” elementów wyświetlania, które mają ten sam stan drzewa usługi. Pokazuje to poniższy przykład:
<div id="scroll" style="background:pink; width:100px;
height:100px; overflow:scroll;
position:absolute; top:0; left:0;">
Hello world
<div id="orange" style="width:75px; height:200px;
background:orange; transform:rotateZ(25deg);">
I'm falling
</div>
</div>
Wygeneruje to listę wyświetlania, w której każda komórka jest elementem wyświetlania:
Tło widoku | #scroll (tło) |
#scroll tekst wbudowany |
#orange (tło) |
#orange tekst wbudowany |
---|---|---|---|---|
drawRect o rozmiarze 800 x 600 i kolorze białym. |
drawRect o wymiarach 100 x 100 w pozycji 0,0 i kolorze różowym. |
drawTextBlob o pozycji 0,0 i tekście „Hello world”. |
drawRect o wymiarach 75 x 200 w pozycji 0,0 i kolorze pomarańczowym. |
drawTextBlob z pozycją 0,0 i tekstem „I'm falling” (tłum. „Padam”). |
Drzewo właściwości transformacji i fragmenty malowania będą wtedy wyglądać tak (w uproszczeniu):
Uporządkowana lista fragmentów danych do malowania, czyli grup elementów wyświetlania i stanu drzewa właściwości, stanowi dane wejściowe dla etapu warstwizacji w pipeline renderowania. Całą listę fragmentów obrazu można scalić w jedną warstwę złożoną i zrasteryzować, ale wymagałoby to czasochłonnego rasteryzowania za każdym razem, gdy użytkownik przewinie stronę. Można utworzyć złożoną warstwę dla każdego fragmentu obrazu i rasteryzować ją osobno, aby uniknąć ponownej rasteryzacji, ale spowoduje to szybkie wyczerpanie pamięci GPU. Etap warstwowania musi zapewniać kompromis między pamięcią GPU a obniżeniem kosztów w przypadku zmian. Dobrym ogólnym podejściem jest domyślne scalanie fragmentów oraz niescalanie fragmentów painta, które mają stany drzewa właściwości, które mają się zmienić w wątku kompozytora, na przykład w przypadku animacji przewijania lub transformacji wątku kompozytora.
W tym przykładzie powinny powstać 2 warstwy złożone:
- Warstwę złożoną 800 x 600 zawierającą polecenia rysowania:
drawRect
o wymiarach 800 x 600 i kolorze białymdrawRect
o wymiarach 100 x 100 w pozycji 0,0 i kolorze różowym
- Warstwę złożoną 144 x 224 zawierającą polecenia rysowania:
drawTextBlob
o pozycji 0,0 i tekście „Hello world”.- przetłumacz 0,18
rotateZ(25deg)
drawRect
o wymiarach 75 x 200 w pozycji 0,0 i kolorze pomarańczowymdrawTextBlob
z pozycją 0,0 i tekstem „Jestem w upadku”
Jeśli użytkownik przewinie #scroll
,
druga złożona warstwa zostanie przesunięta, ale nie będzie wymagać rasteryzacji.
Na przykład w sekcji o drzewach usług z poprzedniego rozdziału jest 6 elementów paint. Wraz ze stanami drzewa właściwości (transformacja, klip, efekt, przewijanie) są to:
- Tło dokumentu: przewijanie dokumentu, przycięcie dokumentu, korzeń, przewijanie dokumentu.
- Narożnik poziomy, pionowy i przewijania dla div (3 oddzielne elementy paint): przewijanie dokumentu, przycięcie dokumentu,
#one
rozmycie, przewijanie dokumentu. - Iframe
#one
:#one
obrócenie, przewijanie klipu z przepełnieniem,#one
rozmycie, przewijanie div. - Iframe
#two
:#two
skala, klip dokumentu, root, przewijanie dokumentu.
Ramki kompozytora: powierzchnie, renderowane powierzchnie i płytki tekstury GPU
Procesy przeglądarki i renderowania zarządzają rasteryzacją treści, a potem przesyłają ramki kompozytora do procesu wizualizacji na potrzeby wyświetlania na ekranie. Ramki kompozytora wskazują, jak łączyć zeskanowane treści i skutecznie je wyświetlać za pomocą procesora graficznego.
Karty
Teoretycznie kompozytor procesu renderowania lub przeglądarki mógłby rastrować piksele do pojedynczej tekstury o pełnej wielkości okna renderowania i przekazać tę teksturę do Viz. Aby ją wyświetlić, kompozytor wyświetlacza musiałby skopiować piksele z pojedynczej tekstury do odpowiedniej pozycji w buforze ramki (np. na ekranie). Jeśli jednak kompozytor chciałby zaktualizować choćby jeden piksel, musiałby ponownie przerasteryzować cały widok i przesłać nową teksturę do Viz.
Zamiast tego widok jest podzielony na kafelki. Każda płytka tekstury GPU jest obsługiwana przez osobną płytkę z rastrowanymi pikselami dla części widoku. Renderer może zaktualizować poszczególne elementy lub zmienić ich pozycję na ekranie. Podczas przewijania witryny pozycja dotychczasowych kafelków przesuwa się w górę, a tylko od czasu do czasu nowy kafelek musi zostać przetworzony rastrowo, aby wyświetlić zawartość niżej na stronie.
Kwadraty i powierzchnie
Płytki tekstur GPU to specjalny rodzaj kwadratu, który jest tylko wymyślną nazwą dla jednej z kategorii tekstur. Kwadraty identyfikują teksturę wejściową i wskazują, jak ją przekształcać oraz stosować do niej efekty wizualne. Na przykład zwykłe karty treści mają transformację wskazującą ich pozycję x, y w siatce kart.
Te przetworzone pikselowo płytki są zapakowane w przejście renderowania, czyli listę kwadrów. Przekaz renderowania nie zawiera żadnych informacji o pikselach. Zamiast tego zawiera instrukcje dotyczące tego, gdzie i jak rysować każdy kwadrat, aby uzyskać pożądany wynik w postaci pikseli. Każdy kafelek tekstury GPU ma kwadrat rysowania. Kompozytor wyświetlania musi tylko przejść przez listę kwadratu, rysując każdy z określonymi efektami wizualnymi, aby uzyskać pożądany wynik w pikselach dla passu renderowania. Kompilowanie kwadratów rysowania na potrzeby renderowania można skutecznie wykonywać na karcie graficznej, ponieważ dozwolone efekty wizualne są starannie wybierane tak, aby były mapowane bezpośrednio na funkcje karty graficznej.
Oprócz macierzowych płytek istnieją inne typy kwadratów rysowania. Na przykład są kwadraty z jednolitym kolorem, które nie są w ogóle oparte na teksturze, lub kwadraty z teksturą dla tekstur niepłytkowych, takich jak film lub kanwa.
Może się też zdarzyć, że kompozytor zawiera inny kompozytor. Na przykład kompozytor przeglądarki tworzy ramkę kompozytora z interfejsem przeglądarki oraz puste okienko, w którym zostanie umieszczony renderowany kompozyt. Innym przykładem są tagi iframe z izolacją witryny. Wstawianie odbywa się za pomocą interfejsów.
Gdy kompozytor przesyła ramkę kompozytora, jest ona opatrzona identyfikatorem, zwanym identyfikatorem powierzchni, który pozwala innym ramkom kompozytora osadzić ją w ramach odwołania. Najnowsza klatka kompozytorna przesłana z określonym identyfikatorem powierzchni jest przechowywana przez Viz. Inna klatka kompozytorna może się do niej później odwoływać za pomocą kwadratu rysowania powierzchni, dzięki czemu Viz wie, co ma rysować. (pamiętaj, że kwadraty powierzchni zawierają tylko identyfikatory powierzchni, a nie tekstury).
Pośrednie etapy renderowania
Niektóre efekty wizualne, takie jak wiele filtrów lub zaawansowane tryby mieszania, wymagają, aby co najmniej 2 kwadraty zostały narysowane na pośredniej teksturze. Następnie pośrednia tekstura jest nanoszona do bufora docelowego na procesorze graficznym (lub do innej pośredniej tekstury), jednocześnie stosując efekt wizualny. Aby to umożliwić, kompozytorski frame zawiera listę przejść renderowania. Zawsze istnieje podstawowy przetwarzany przepust, który jest rysowany jako ostatni i którego miejsce docelowe odpowiada buforowi ramki. Może ich być więcej.
Możliwość użycia wielu przejść renderowania wyjaśnia nazwę „przejście renderowania”. Każdy przejazd musi być wykonywany sekwencyjnie na GPU w wielu „przejazdach”, podczas gdy pojedynczy przejazd może być wykonywany w ramach jednego równoległego obliczenia na GPU.
Agregacja
Do Viz przesyłane są liczne ramki kompozytora, które muszą być wyświetlane na ekranie razem. Osiąga się to za pomocą fazy agregacji, która przekształca je w pojedynczą, złożoną ramkę. Agregacja zastępuje kwadraty rysowania powierzchni przez określone przez nie ramki kompozytora. Jest to też okazja do zoptymalizowania niepotrzebnych tekstur pośrednich lub treści, które są poza ekranem. Na przykład w wielu przypadkach ramka kompozytora dla izolowanego iframe w witrynie nie wymaga własnej tekstury pośredniej i może być rysowana bezpośrednio w buforze ramki za pomocą odpowiednich kwadratów rysowania. Faza agregacji określa takie optymalizacje i zachowuje je na podstawie globalnej wiedzy, która nie jest dostępna dla poszczególnych kompozytorów renderowania.
Przykład
Oto ramki kompozytora, które przedstawiają przykład z początku tego posta.
foo.com/index.html
surface: id=0- Przetwarzanie pass 0: rysowanie do wyjścia.
- Render pass draw quad: rysowanie z rozmyciem 3 pikseli i przycięcie do renderowania pass 0.
- Przetwarzanie pass 1:
- Rysuj kwadraty dla zawartości kafelka w ramce
#one
, z podaniem współrzędnych x i y.
- Rysuj kwadraty dla zawartości kafelka w ramce
- Przetwarzanie pass 1:
- Kwadrat rysowania powierzchni: z identyfikatorem 2, narysowany z transformacją skalowania i przesunięcia.
- Render pass draw quad: rysowanie z rozmyciem 3 pikseli i przycięcie do renderowania pass 0.
- Przetwarzanie pass 0: rysowanie do wyjścia.
- Interfejs przeglądarki: ID=1
- Przetwarzanie pass 0: rysowanie do wyjścia.
- Rysowanie kwadratu dla interfejsu przeglądarki (również w postaci kafelków)
- Przetwarzanie pass 0: rysowanie do wyjścia.
bar.com/index.html
surface: ID=2- Przetwarzanie pass 0: rysowanie do wyjścia.
- Rysowanie kwadratów dla zawartości ramki
#two
iframe z położeniami x i y dla każdego z nich.
- Rysowanie kwadratów dla zawartości ramki
- Przetwarzanie pass 0: rysowanie do wyjścia.
Ilustracje: Una Kravets