Profilowanie gry WebGL za pomocą flagi about:tracing

Lilli Thompson
Lilli Thompson

Jeśli nie możesz czegoś zmierzyć, nie możesz tego poprawić.

Lord Kelvin

Aby przyspieszyć działanie gier w formacie HTML5, musisz najpierw zlokalizować wąskie gardła, co może być trudne. Ocena danych o liczbie klatek na sekundę (FPS) to dobry początek, ale aby uzyskać pełny obraz, musisz zrozumieć niuanse związane z aktywnościami w Chrome.

Narzędzie about:tracing udostępnia statystyki, które pomogą Ci uniknąć pochopnych obejść, które mają na celu poprawę skuteczności, ale są w podstawie dobrze pojętą zgaduj zgadło. Zaoszczędzisz dużo czasu i energii, uzyskasz wyraźniejszy obraz tego, co Chrome robi z każdym obrazem, i użyjesz tych informacji do optymalizacji gry.

Cześć about:tracing

Narzędzie about:tracing w Chrome pozwala zobaczyć wszystkie działania w Chrome z określonego okresu w tak szczegółowy sposób, że na początku może to przytłoczyć. Wiele funkcji w Chrome jest już gotowych do śledzenia, więc bez ręcznego instrumentowania możesz nadal używać about:tracing do śledzenia wydajności. (patrz sekcja o ręcznym instrumentowaniu kodu JS)

Aby wyświetlić widok śledzenia, wpisz „about:tracing” w oknie wszechstronnym (pasku adresu) Chrome.

Omnibox w Chrome
Wpisz „about:tracing” w omniboksie Chrome

Za pomocą narzędzia do śledzenia możesz rozpocząć nagrywanie, uruchomić grę na kilka sekund, a potem wyświetlić dane śledzenia. Oto przykład tego, jak mogą wyglądać dane:

Prosty wynik śledzenia
Prosty wynik śledzenia

Tak, to naprawdę mylące. Porozmawiajmy o tym, jak czytać takie raporty.

Każdy wiersz reprezentuje profilowany proces, oś od lewej do prawej wskazuje czas, a każde kolorowe pole to wywołanie funkcji z instrumentacją. Zawiera ona wiersze dotyczące różnych rodzajów zasobów. Najciekawszymi funkcjami do profilowania gier są CrGpuMain, która pokazuje, co robi procesor graficzny (GPU), oraz CrRendererMain. Każdy ślad zawiera wiersze CrRendererMain dla każdej otwartej karty w okresie śledzenia (w tym dla samej karty about:tracing).

Podczas odczytu danych z wykresu pierwszym zadaniem jest określenie, który wiersz CrRendererMain odpowiada Twojej grze.

Podświetlony wynik prostego śledzenia
Podświetlony wynik prostego śledzenia

W tym przykładzie 2 kandydatki to 2216 i 6516. Obecnie nie ma niestety dokładnego sposobu na wybranie aplikacji, z wyjątkiem szukania linii, która często jest aktualizowana (lub, jeśli kod został ręcznie wyposażony w punkty śledzenia, szukania linii zawierającej dane śledzenia). W tym przykładzie wygląda na to, że 6516 uruchamia główny cykl na podstawie częstotliwości aktualizacji. Jeśli przed rozpoczęciem śledzenia zamkniesz wszystkie inne karty, łatwiej będzie znaleźć odpowiedni proces CrRendererMain. Nadal jednak mogą występować wiersze CrRendererMain dla procesów innych niż gra.

Znajdowanie ramki

Po znalezieniu odpowiedniego wiersza w narzędziu do śledzenia gry należy znaleźć główny cykl. Główny cykl wygląda jak powtarzający się w danych śledzenia wzór. Dane śledzenia możesz przeglądać za pomocą klawiszy W, A, S i D: A i D służą do przewijania w lewo i w prawo (wstecz i do przodu w czasie), a W i S – do powiększania i pomniejszania danych. Jeśli gra działa z częstotliwością 60 Hz, główna pętla powinna być wzorcem powtarzanym co 16 milisekund.

Wygląda na to, że są 3 ramki wykonania.
Wygląda na to, że są 3 ramki wykonania

Po znalezieniu informacji o częstotliwości próbkowania gry możesz sprawdzić, co dokładnie robi Twój kod w każdej klatce. Używaj klawiszy W, A, S, D, aby powiększać obraz, aż będzie można odczytać tekst w polach funkcji.

Szczegóły dotyczące ramki wykonania
Szczegółowy opis etapu realizacji

Ta kolekcja pudełek pokazuje serię wywołań funkcji, a każde wywołanie jest reprezentowane przez kolorowe pudełko. Każda funkcja została wywołana przez pole nad nią, więc w tym przypadku widać, że MessageLoop::RunTask wywołało RenderWidget::OnSwapBuffersComplete, które z kolei wywołało RenderWidget::DoDeferredUpdate i tak dalej. Dzięki tym danym możesz uzyskać pełny obraz tego, co wywołało co i jak długo trwało każde wykonanie.

Ale tutaj jest trochę trudniej. Informacje udostępniane przez about:tracing to nieprzetworzone wywołania funkcji z kodu źródłowego Chrome. Na podstawie nazwy można się domyślić, do czego służy dana funkcja, ale informacje te nie są zbyt przyjazne użytkownikom. Przydatne jest, aby zobaczyć ogólny przepływ danych w ramce, ale aby zrozumieć, co się dzieje, potrzebujesz czegoś bardziej czytelnego dla człowieka.

Dodawanie tagów śledzenia

Na szczęście istnieje wygodny sposób ręcznego dodawania instrumentacji do kodu, aby tworzyć dane śladu: console.timeconsole.timeEnd.

console.time("update");
update
();
console
.timeEnd("update");
console
.time("render");
update
();
console
.timeEnd("render");

Powyższy kod tworzy nowe pola w nazwie widoku śledzenia z określonymi tagami, więc jeśli ponownie uruchomisz aplikację, zobaczysz pola „update” (aktualizacja) i „render” (renderowanie), które pokazują czas upływający między wywołaniami początku i końca dla każdego tagu.

Tagi dodane ręcznie
Tagi dodane ręcznie

Dzięki temu możesz tworzyć czytelne dla człowieka dane śledzenia, aby śledzić gorące punkty w kodzie.

GPU czy CPU?

W przypadku grafiki z akceleracją sprzętową jednym z najważniejszych pytań, które możesz zadać podczas profilowania, jest: czy ten kod jest związany z procesorem GPU czy z procesorem CPU? W przypadku każdej klatki część renderowania będzie wykonywana na GPU, a część logiki na procesorze. Aby zrozumieć, co spowalnia grę, musisz sprawdzić, jak jest rozłożona praca na tych 2 zasobach.

Najpierw znajdź w widoku śledzenia wiersz o nazwie CrGPUMain, który wskazuje, czy procesor GPU jest zajęty w określonym momencie.

Ścieżki GPU i CPU

Widać, że każdy klatkę gry powoduje pracę procesora w funkcji CrRendererMain, a także na karcie graficznej. Powyższy ślad pokazuje bardzo prosty przypadek użycia, w którym procesor i procesor graficzny są nieaktywne przez większość 16-msowego interwału.

Widok śledzenia jest bardzo przydatny, gdy gra działa wolno, a nie wiesz, który zasób jest maksymalnie wykorzystywany. Kluczem do debugowania jest sprawdzenie, jak linie GPU i CPU się ze sobą łączą. Weźmy ten sam przykład co wcześniej, ale dodamy trochę dodatkowej pracy w pętli aktualizacji.

console.time("update");
doExtraWork
();
update
(Math.min(50, now - time));
console
.timeEnd("update");

console
.time("render");
render
();
console
.timeEnd("render");

Teraz zobaczysz ślad wyglądający tak:

Ścieżki GPU i CPU

Co wynika z tego śledzenia? Widać, że czas wyświetlania klatki zmienia się z 2270 ms na 2320 ms, co oznacza, że każda klatka zajmuje około 50 ms (częstotliwość wyświetlania klatek wynosi 20 Hz). Obok pola aktualizacji widać fragmenty kolorowych pól, które reprezentują funkcję renderowania, ale ramka jest całkowicie wypełniona przez aktualizację.

W przeciwieństwie do tego, co dzieje się z procesorem, procesor graficzny pozostaje nieaktywny przez większość czasu trwania każdego klatki. Aby zoptymalizować ten kod, możesz poszukać operacji, które można wykonać w kodzie shadera, i przekazać je do procesora graficznego, aby optymalnie wykorzystać zasoby.

A co, jeśli kod shadera jest powolny, a GPU jest przeciążone? Co jeśli usuniemy zbędne zadania z procesora, a zamiast tego dodamy trochę pracy do kodu fragmentu shadera? Oto niepotrzebnie drogi shader fragmentu:

#ifdef GL_ES
precision highp
float;
#endif
void main(void) {
 
for(int i=0; i<9999; i++) {
    gl_FragColor
= vec4(1.0, 0, 0, 1.0);
 
}
}

Jak wygląda ślad kodu korzystającego z tego shadera?

ślady GPU i CPU podczas korzystania z wolnego kodu GPU;
Ścieżki GPU i CPU podczas korzystania z wolnego kodu GPU

Ponownie zwróć uwagę na czas trwania ramki. Tutaj powtarzający się wzór trwa od około 2750 ms do 2950 ms, czyli przez 200 ms (częstotliwość klatek około 5 Hz). Linia CrRendererMain jest prawie pusta, co oznacza, że procesor jest przez większość czasu bezczynny, a procesor graficzny jest przeciążony. To pewny znak, że Twoje shadery są zbyt ciężkie.

Jeśli nie wiesz, co dokładnie powoduje niską liczbę klatek na sekundę, możesz zauważyć aktualizację 5 Hz i być skłonny przejść do kodu gry, aby spróbować zoptymalizować lub usunąć logikę gry. W tym przypadku nie przyniosłoby to żadnych korzyści, ponieważ nie to pochłania czas. Z tego śledzenia wynika, że zwiększenie obciążenia procesora w każdej klatce nie będzie miało większego wpływu na czas generowania klatki, ponieważ procesor jest w większości czasu niewykorzystany.

Przykłady z życia

Zobaczmy teraz, jak wyglądają dane śledzenia z rzeczywistej gry. Jedną z ciekawych rzeczy w przypadku gier stworzonych z użyciem otwartych technologii internetowych jest to, że możesz zobaczyć, co dzieje się w Twoich ulubionych usługach. Jeśli chcesz przetestować narzędzia do profilowania, możesz wybrać ulubiony tytuł WebGL ze sklepu Chrome Web Store i zprofilować go za pomocą about:tracing. To jest przykładowy ślad pochodzący z doskonałej gry WebGL Skid Racer.

Śledzenie prawdziwej gry
Śledzenie prawdziwej gry

Wygląda na to, że każda klatka zajmuje około 20 ms, co oznacza, że liczba klatek wynosi około 50 FPS. Widać, że praca jest równomiernie rozłożona między procesor i GPU, ale GPU jest zasobem, który jest najbardziej obciążony. Jeśli chcesz zobaczyć, jak wygląda profilowanie na przykładach prawdziwych gier WebGL, wypróbuj niektóre tytuły ze sklepu Chrome Web Store, które korzystają z WebGL, takie jak:

Podsumowanie

Jeśli chcesz, aby gra działała z częstotliwością 60 Hz, wszystkie operacje muszą się zmieścić w 16 ms czasu procesora i 16 ms czasu GPU na każdą klatkę. Masz 2 zasoby, które mogą być wykorzystywane równolegle, i możesz przenosić między nimi zadania, aby zmaksymalizować wydajność. Widok about:tracing w Chrome to nieocenione narzędzie, które pozwala poznać działanie kodu i pomaga optymalizować czas poświęcany na programowanie, ponieważ pozwala skupić się na odpowiednich problemach.

Co dalej?

Oprócz karty graficznej możesz śledzić też inne części środowiska wykonawczego Chrome. Chrome Canary, czyli wczesna wersja Chrome, jest wyposażona w instrumenty do śledzenia operacji wejścia/wyjścia, IndexedDB i kilku innych działań. Aby dowiedzieć się więcej o bieżącym stanie śledzenia zdarzeń, przeczytaj ten artykuł na stronie Chromium.

Jeśli jesteś deweloperem gier internetowych, obejrzyj film poniżej. Prezentacja zespołu Google ds. promowania wśród deweloperów gier, która została zaprezentowana podczas GDC 2012 i dotyczy optymalizacji wydajności gier w Chrome: