Przyjrzyjmy się kluczowym strukturom danych, które są wejściami i wyjściami potoku renderowania.
Te struktury danych to:
- Drzewa ramek składają się z lokalnych i zdalnych węzłów, które wskazują, w którym procesie renderowania i którym mechanizmie renderowania Blink znajdują się dokumenty internetowe.
- Niezmienna struktura fragmentu reprezentuje dane wyjściowe algorytmu ograniczeń układu (i dane wejściowe).
- 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 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>
Oprawianie 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 innym procesie jest reprezentowana jako klatka zdalna. Ramka zdalna zawiera minimalną liczbę 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.
Weź pod uwagę bardziej skomplikowany przykład, w którym główna ramka to 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 drzew lokalnych kadrów, a następnie je agreguje. 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ących informacji, aby umieścić ramkę kompozytora wygenerowaną dla elementu foo.com/other-page
bezpośrednio do ramki kompozytora dla ramki głównej foo.com
.
Na przykład ramka nadrzędna bar.com
poza procesem może wpływać na wyświetlanie elementu iframe foo.com/other-url
przez przekształcenie elementu iframe za pomocą CSS lub wyłączenie jego części z innymi elementami w elemencie 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 korzenia każdego lokalnego fragmentu drzewa ramki jest powiązany obiekt widżetu. Wizualne aktualizacje właściwości są przekazywane do widżetu ramki głównej przed przeniesieniem do pozostałych widżetów (od góry do dołu).
Na przykład, gdy zmieni się rozmiar widocznego obszaru:
Ten proces nie jest natychmiastowy, więc zreplikowane właściwości wizualne również zawierają token synchronizacji. Kompozytor Viz używa tego tokena synchronizacji, aby poczekać, aż wszystkie fragmenty drzewa ramek lokalnych prześlą ramkę kompozytora 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 jest on 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 odtwarzać 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. Najlepiej, gdyby układ działał proporcjonalnie do tego, co rzeczywiście zmieniło się na ekranie. Możemy to osiągnąć, wykorzystując jak najwięcej części poprzedniego drzewa. Oznacza to, że (zwykle) musimy tylko odtworzyć 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 wykonać 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 drzewa z polami i wskaźnikami wyświetlamy treści w tekście na płaskiej liście reprezentującej drzewo. 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 potoku, 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 przełamana między „Cześć” a „tam”.
Gdy kontekst formatowania wbudowanego w tej sytuacji jest przedstawiony w postaci drzewa, wygląda to tak:
{
"Line box": {
"Box <span>": {
"Text": "Hi"
}
},
"Line box": {
"Box <b>": {
"Text": "There"
}
},
{
"Text": "."
}
}
Lista kartotekowa wygląda tak:
- (Pole wiersza, 2)
- (Box <span>, 1)
- (Tekst „Cześć”, 0)
- (Element zamówienia w linii, 3)
- (Pole <b>, 1)
- (Tekst „tam”, 0)
- (Tekst „.", 0)
Taka struktura danych ma wielu użytkowników: interfejsy API ułatwień dostępu i interfejsy API geometrii, np. 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
.
Ta reprezentacja kursora sprawdza się bardzo dobrze w przypadku treści tekstowych z wielu 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, algorytm DFS zwiększa tylko przesunięcie tablicy, zapewniając szybkie iteracje i lokalizację pamięci.
- Umożliwia wyszukiwanie najszersze, co jest potrzebne np. przy malowaniu tła w polach wierszy i wbudowanych pól.
- Dzięki znajomości liczby potomków możesz szybko przejść do następnego elementu siostrzanego (wystarczy zwiększyć przesunięcie tablicy o tę liczbę).
Drzewa nieruchomości
DOM to drzewo elementów (oraz węzły tekstowe), a CSS może stosować różne style do elementów.
Są one wyświetlane na 4 sposoby:
- Układ: 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. Zapewniają sposoby odpowiedzi na pytania, gdzie w zależności od ekranu znajduje się dany element DOM, biorąc pod uwagę jego rozmiar i położenie? I jaka sekwencja operacji na GPU powinna być używana do stosowania efektów wizualnych i przewijania?
Wizualne i przewijane efekty w internecie są w pełni 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łasności jest wydzielona w ramach jednego etapu potoku renderowania.
- Dużo łatwiej i szybciej wysyłać drzewa właściwości do różnych wątków i procesów niż w stanie pełnego DOM, dzięki czemu można ich używać w wielu przypadkach.
- Im więcej jest tych przypadków użycia, tym więcej korzyści uzyskujemy dzięki wbudowanemu buforowaniu geometrii, ponieważ mogą one ponownie wykorzystywać swoje pamięci podręczne.
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 z elementami poza ekranem i kafelkami tekstur GPU.
- Skuteczne i dokładne unieważnianie farb i rasterów.
- 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: przekształcenie, klip, efekt i przewijanie.(*). Drzewo przekształcenia przedstawia przekształcenia i przewijanie CSS. (przekształcenie przewijania jest reprezentowane przez macierz przekształcenia 2D). Drzewo klipów reprezentuje klip przepełniający. 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 przewijania łańcucha razem; jest ono potrzebne do przewijania wątku kompozytora. Każdy węzeł w drzewie właściwości odpowiada przewijaniu lub efektowi wizualnemu zastosowanemu przez element DOM. Jeśli taki element ma wiele efektów, w każdym drzewie może być więcej niż 1 węzeł drzewa właściwości dla tego samego elementu.
Topologia każdego drzewa jest jak rozproszona 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. Występują też połączenia między drzewami. 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ącym 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 przekształcić w raster 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>
Kod HTML i CSS spowoduje wygenerowanie tej listy wyświetlanej, z której każda komórka jest wyświetlanym elementem:
Tło widoku | #blue (tło) |
#green (tło) |
#green tekst wbudowany |
---|---|---|---|
drawRect w rozmiarze 800 x 600 i w kolorze białym. |
drawRect w rozmiarze 100 x 100 w pozycji 0,0 i w 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 displayowe odpowiadają poszczególnym etapom specyfikacji kolejności renderowania 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, takiej 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 w rozmiarze 35 x 20 w pozycji 8,16 i w 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 w rozmiarze 100 x 100 w pozycji 0,0 i w kolorze różowym. |
drawTextBlob o pozycji 0,0 i tekście „Hello world”. |
drawRect w rozmiarze 75 x 200 w pozycji 0,0 i w 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 (dla uproszczenia):
Uporządkowana lista fragmentów renderowania, które są grupami wyświetlanych elementów i stanem drzewa właściwości, to dane wejściowe na etapie nakładania warstw w potoku renderowania. Całą listę fragmentów obrazu można scalić w jedną złożoną warstwę 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. Ten etap tworzenia warstw musi kompromisować między pamięcią GPU i obniżać koszty, gdy sytuacja się zmienia. Dobrym ogólnym podejściem jest domyślne scalanie fragmentów oraz niescalanie fragmentów malowania, 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 × 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 potrzebna rasteryzacja.
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, klip z dokumentu, katalog główny, przewijanie dokumentu.
- Poziomy, pionowy i przewijany narożnik elementu div (3 oddzielne fragmenty malowania):
przewijanie dokumentu, klips do dokumentu, rozmycie
#one
, 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 tekstur GPU
Przeglądarka i procesy renderowania zarządzają rasteryzacją treści, a następnie przesyłają ramki kompozytora do procesu Viz, aby wyświetlić je 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). Gdyby jednak ten kompozytor chciał zaktualizować nawet 1 piksel, musiałby ponownie zrasterować cały widoczny obszar i przesłać nową teksturę do Viz.
Zamiast tego widoczny obszar jest podzielony na kafelki. W przypadku każdego kafelka znajduje się oddzielny kafelek z teksturą GPU, który zawiera zrasteryzowane piksele odpowiadające części widocznego obszaru. Mechanizm renderowania może wtedy aktualizować poszczególne kafelki, a nawet zmieniać pozycję istniejących kafelków 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 kafelków zrastrowanych dostępne są dodatkowe typy czworokątó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 innych niż mozaika, takich jak film lub kanwa.
Może się też zdarzyć, że ramka kompozytora zawiera inną ramkę kompozytora. 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. Osadzanie się w ten sposób odbywa się za pomocą przestrzeni.
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, np. filtry lub zaawansowane tryby mieszania, wymagają narysowania co najmniej dwóch czworokątów na teksturę pośrednią. Następnie pośrednia tekstura jest nanoszona do bufora docelowego na karcie graficznej (lub do innej pośredniej tekstury), jednocześnie stosując efekt wizualny. Aby to umożliwić, kompozytorski frame zawiera listę przejść renderowania. Zawsze istnieje główny przebieg renderowania, który jest rysowany jako ostatni i który miejsce docelowe odpowiada buforowi ramki. Może ich być też więcej.
To, że istnieje wiele kart renderowania – wyjaśnia więc, jak nazywa się „renderowanie”. 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. Jest to możliwe w fazie agregacji, która przekształca je w pojedynczą, zagregowaną ramkę kompozytora. 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 elementu iframe izolowanego z witryny nie wymaga własnej tekstury pośredniej i można ją narysować bezpośrednio w buforze ramki za pomocą odpowiednich czworokątó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- Renderowanie z przebiegiem 0: rysowanie na ekranie wyjściowym.
- 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 pozycji X i Y dla każdego z nich.
- 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.
- Renderowanie z przebiegiem 0: rysowanie na ekranie wyjściowym.
- Interfejs przeglądarki: ID=1
- Przetwarzanie pass 0: rysowanie do wyjścia.
- Rysowanie kwadratów na potrzeby interfejsu przeglądarki (również w układce)
- 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