Skocz do zawartości

Optymalizacja draw


Uzjel

Rekomendowane odpowiedzi

Hej. Wyświetlam na ekranie sporo instancji, każda składa się z maksymalnie sześciu grafik. Przy mapie 25x25 daje to 3750 wywołań draw. Nie mogę pozwolić na dezaktywacja instancji.

 

Jakieś pomysły/dobre rady?

 

Nie wywołuj draw, problem solved xd

 

Fervi

 

Gdyby wywoływać draw co 2 klatkę to by robił przerwę czy zostawiał starego drawa?

Odnośnik do komentarza
Udostępnij na innych stronach

Pytanie z punktu widzenia amatora totalnego(zapewne głupie)- po co w ogóle używać draw do wyświetlania grafik instancji?Nie lepiej przypisać jej sprite i potem podmieniać

 

Nie działa jeśli obiekt rysuje coś po evencie draw. Wtedy sprite nie jest wyświetlany, bo nawet nie wiem czemu xd

 

Fervi

Odnośnik do komentarza
Udostępnij na innych stronach

Pierwszy pomysł: Niewykluczone że Game Maker ma wbudowany sprite batching, więc można mu pomóc rysując wszystko "wszerz", a nie "w głąb". Szkopuł tkwi w tym, że obiekty nie mogą na siebie nachodzić (obiekty, nie sprite).

 

Poniżej pseudokod który pokazuje jak rysujesz obecnie (dla porównania).

for each object {

draw sprite A

draw sprite B

draw sprite C

}

Poniżej pseudokod który może przyśpieszyć.

for each object {

draw sprite A

}

for each object {

draw sprite B

}

for each object {

draw sprite C

}

W pierwszym rozwiązaniu VBO resetuje się za każdym razem przed narysowaniem tekstury (zakładając że nie używasz atlasu tekstur), czyli masz 3750 draw calli. W drugim rozwiązaniu tekstura zmieniana jest jedynie tyle razy, ile masz tekstur (czyli wyjdzie coś koło ~kilkunastu draw calli).

Nie mam benchmarka na potwierdzenie, ale w innych silnikach taka strategia działa, tylko że bardziej komplikuje kod (optymalizacje mają to do siebie że komplikują, kek).

 

Drugi pomysł (nie wyklucza się z pierwszym): Jeśli obrazki nie są dynamiczne, możesz rysować planszę do surface i przerysowywać ją tylko wtedy, kiedy coś się na niej zmienia. Przełączanie bufora (surface_set_target i surface_reset_target) może mieć duży narzut, czyli nie możesz robić tego per obiekt w evencie draw, ale globalnie, raz na klatkę lub rzadziej (bo inaczej będą krótkie "przywieszki" podczas przerysowywania; będziesz miał 25x25 bind calli).

 

Trzeci pomysł (też się nie wyklucza): napisz swój shader wycinając niepotrzebne rzeczy (np. z blending który jest podawany jako atrybut w defaultowym shaderze).

 

Elo.

Odnośnik do komentarza
Udostępnij na innych stronach

A możesz wrzucić jakiś obrazek jak to wygląda. Tak będzie nam łatwiej coś wymyślić.

 

draw_order.png

 

A surface'y?

 

Mam złe doświadczenia z surfacami na Androidzie. Nie wiem jak jest teraz, ale pamiętam grę gnyska z czasów YoyoGames. Tam wszystko było robione na surfacach i widać było artefakty.

 

Hmmm może wyłączyć automatyczny draw, i rysować tylko te instancje które zmieniają swoją animację. I to na surface oczywiście.

 

Jak wyżej.

 

Pytanie z punktu widzenia amatora totalnego(zapewne głupie)- po co w ogóle używać draw do wyświetlania grafik instancji?Nie lepiej przypisać jej sprite i potem podmieniać

 

Jeżeli jedna instancja ma wyświetlać więcej grafik niż swoja własna :)

 

Pierwszy pomysł: Niewykluczone że Game Maker ma wbudowany sprite batching, więc można mu pomóc rysując wszystko "wszerz", a nie "w głąb". Szkopuł tkwi w tym, że obiekty nie mogą na siebie nachodzić (obiekty, nie sprite).

 

Poniżej pseudokod który pokazuje jak rysujesz obecnie (dla porównania).

 

...

 

Dzięki exigo, zrobię testy. Szczególnie z optymalizacją atlasów.

 

Spróbuję też z tilesetami.

 

Największym kłopotem o którym wspomniałem jest Android.

 

 

 

Odnośnik do komentarza
Udostępnij na innych stronach

Ja bym tu zastosował 2 surface. Oba statyczne rysowane ponownie tylko w momencie zmian.

Pierwsza zawierająca tła i wszystko nie animowane co jest rysowane pod grafikami wody. Drugi to wszytko nad.

Możesz zrobić sobie jedna zmienną która sprawdza czy rysować od nowa czy używać wcześniej rysowanego surface. Pseudokod wyobrażam sobie tak:

GML (create)
surBackground=-4

surForeground=-4

doRedraw=true

 

GML (step)
if(jakis event ruchu w grze) {

//jakies akcje

doRedraw=true

}

 

GML (draw)
if(!surface_exists(surBackground)) {

surBackground=surface_create(512, 512) //2^n aby zapobiec glitchom

}

 

if(doRedraw=true) {

surface_set_target(surBackground)

for(var _x=0; _x<25; _x++)

for(var _y=0; _y<25; _y++) {

draw_sprite(tlo)

draw_sprite(plecki)

}

surface_reset_target()

}

draw_surface(surBackground, 0, 0)

 

draw_woda()

 

if(!surface_exists(surForeground)) {

surForeground=surface_create(512, 512) //2^n aby zapobiec glitchom

}

 

if(doRedraw=true) {

surface_set_target(surForeground)

for(var _x=0; _x<25; _x++)

for(var _y=0; _y<25; _y++) {

draw_sprite(blysk)

draw_sprite(kontur)

draw_sprite(kolor)

}

surface_reset_target()

doRedraw=false

}

draw_surface(surForeground, 0, 0)

 

Oczywiście przy tym każdy obiekt ma visible=false i rysowane jest wszystkie w tym jednym evencie.

 

Wadą jest to że wciąż wykorzystuje to taką samą moc obliczeniową ale tylko w jednym momencie. Przez to może być czuć ten peak spadających FPS. Najgorzej jak będzie on w momencie odbioru inputu od gracza. Na przykład w czasie przeciągania.

Odnośnik do komentarza
Udostępnij na innych stronach

Wiec przyjrzyj się jak to działa. Tak jak powinno. Przy każdej próbie rysowania surface sprawdza czy on istnieje i go tworzy od nowa. Nie ma z tym żadnych problemów. Mam 2 gry które tak działają.

 

Ale to jest optymalizacja FPS kosztem pamięci.

Odnośnik do komentarza
Udostępnij na innych stronach

Ale na Androidzie surface działają chujowo.

Zdarzyło mi się mieć tylko jeden przypadek na jednym telefonie. Gdy na surface rozmiaru chyba 128x32 próbowałem rysować przewijający się tekst. Gdy jakiś pixel rysował się na prawe krawędzi surface to nie był odpowiednio czyszczony i zostawiał ślady. draw_clear_alpha(0,0) nie pomagało. Ale pomogło zwiększenie rozmiaru surface do 256x32.

Odnośnik do komentarza
Udostępnij na innych stronach

Dziękuję wszystkim za odpowiedzi :)

Na razie zoptymalizowałem atlasy, żeby zmniejszyć ilość texture swapów.

 

Screenshot_20161112_180656.png

 

  • Na początku na tej największe było prawie 2000 texture swapów swapów
  • Pierwszym krokiem było wrzucenie do jednego atlasu wszystkich elementów rur i mapy
  • Ilość swapów spadła do 300-400
  • Miałem jeszcze trzy przyciski na interfejsie które też dodałem do atlasu z rurami
  • Ilość swapów spadła do 10-15, FPS się unormował ~40 na moim telefonie
  • Pobawiłem się trochę w debugerze profilowaniem i znalazłem kilka słabych miejsc w stepie, co zmniejszyło ilość obliczeń
  • Dopisałem też, żeby rury nie próbowały się rysować poza widzialnym obszarem co powinno przyspieszyć grę na wolnych telefonach, ale tylko w przypadku zooma

 

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

btw. z tego co widzę, gra powinna mieć raptem kilka spritów:

 

- prosta rura

- zakręcona rura

- brzeg rury

- pokrętło (białe)

- kratkę (białą)

- mieszacz (dwie klatki z tego co widzę)

- pełny szary element

 

Reszta powinna być rysowana za pomocą draw_sprite_ext z odpowiednim kolorem i obrotem. Jakby przerobić grę na tablicę, wystarczy jeden obiekt do zarządzania wszystkim (a więc i jeden event draw).

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Przecież takie grafiki co pokazałeś, to nie powinny zająć nawet pół jednej tekstury.

 

Btw. z drugiej strony, jeśli kombinacji nie jest za wiele, szybciej może być jednak rysować gotowe sprite'y niż je nakładać, o ile zmieszczą się na jednej teksturze :)

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...