Jak poprawić wydajność grupowania

9

Tworzę opartą na sprite grę 2D na platformy mobilne i używam OpenGL (właściwie Irrlicht) do renderowania grafiki. Najpierw zaimplementowałem renderowanie ikonek w prosty sposób: każdy obiekt gry jest renderowany jako quad z własnym wywołaniem losowania GPU, co oznacza, że ​​gdybym miał 200 obiektów gry, wykonałem 200 wywołań losowania na ramkę. Oczywiście był to zły wybór, a moja gra była całkowicie związana z procesorem, ponieważ w każdym wywołaniu GPU występuje niewielki narzut procesora. GPU przez większość czasu pozostawała bezczynna.

Teraz pomyślałem, że mogę poprawić wydajność, gromadząc obiekty w duże partie i renderując te partie za pomocą zaledwie kilku wywołań losowania. Zaimplementowałem przetwarzanie wsadowe (aby każdy obiekt gry o tej samej teksturze był renderowany w tej samej partii) i pomyślałem, że moje problemy zniknęły ... tylko po to, aby dowiedzieć się, że moja częstotliwość klatek była jeszcze niższa niż wcześniej.

Dlaczego? Mam 200 (lub więcej) obiektów gry i są one aktualizowane 60 razy na sekundę. W każdej ramce muszę ponownie obliczyć nową pozycję (translację i obrót) dla wierzchołków w CPU (GPU na platformach mobilnych nie obsługuje instancji, więc nie mogę tego zrobić) i wykonując te obliczenia 48000 na sekundę (200 * 60 * 4 od czasu każdy duszek ma 4 wierzchołki) po prostu wydaje się być zbyt wolny.

Co mogę zrobić, aby poprawić wydajność? Wszystkie obiekty gry poruszają się / obracają (prawie) każdą klatkę, więc naprawdę muszę przeliczyć pozycje wierzchołków. Jedyną optymalizacją, o której mogłem pomyśleć, jest tabela przeglądowa dla obrotów, aby nie musiałem ich obliczać. Czy punktowe duszki by pomogły? Jakieś paskudne hacki? Coś jeszcze?

Dzięki.

użytkownik4241
źródło

Odpowiedzi:

5

Czy korzystałeś z mojego portu Irlicht dla Androida? W przypadku duszków 2d na Androida i iPhone'a używam tych samych sztuczek, co Ty: grupowanie. Próbuję wielu rozwiązań w OpenGL ES 1.xi 2.x:

  • posortuj według z (paralaksa) i tekstury, wykonaj transformacje na CPU i wywołaj glDrawArrays lub glDrawElements (najszybszy sposób). Jeśli możesz, użyj jednej dużej tekstury.
  • taka sama sztuczka z VBO, nie szybciej, ponieważ dla każdej ramki odświeżasz wszystkie informacje. Może być przydatny dla duszków statyki.
  • użyj OpenGL ES 2.xi użyj Vertex shadera do obliczenia pozycji (wolniej)
  • użyj PointSprites (brak rozwiązania, jeśli nie jest kwadratem, a zbyt wiele przezroczystych pikseli zabija wypełnianie)
  • użyj rozszerzenia gldrawtexoes ...
  • użyj drawcall dla każdego duszka (najwolniejsza metoda)

Tak jak Ty, wszystkie transformacje są wykonywane przez CPU dla OGLES 1.x lub OGLES 2.x. Jeśli masz instrukcje neonowe, możesz ich użyć do przyspieszenia obliczeń.

Ps: na urządzeniach iPhone i Android nie mam ograniczonego procesora, ale szybkość napełniania ograniczona. Dlatego bardzo ważne jest ograniczenie overdraw.

Ellis
źródło
Doskonale, tego szukałem. Nie wiedziałem o twoim porcie Irrlicht, ale mam już wersję Irrlicht na iOS. Mówisz, że nie jesteś ograniczony procesorem - ile rysujesz rysunków? A powiedzmy, jakie są Twoje liczby klatek na sekundę dla 100 duszków na iPhonie? Jeśli mam 200 obiektów, wykonuję 48000 obliczeń na sekundę. Twój punkt widzenia na temat wypełniania jest dobry.
user4241
Statyczne duszki (tło) są w VBO. Używam jednego VBO na paralaksę. W przeciwnym razie mam 100 do 200 duszków na Moblox. Na wszystkich telefonach, w tym 3G, mam więcej niż 30 klatek na sekundę (jak pamiętam). Ale duże duszki są bardzo kosztowne (problem z wypełnianiem) ....
Ellis
Pracuję nad silnikiem cząstek, który mogę wykorzystać do 20 000 cząstek przy obliczaniu wszystkich pozycji na procesorze i mam 10 klatek na sekundę z ekstremalnymi ustawieniami (na 3GS i iPhone4). Tak więc 1000 duszek musi być możliwych na 3GS lub iPhone4 z dobrą szybkością klatek.
Ellis
Dziękuję, bardzo pomocny! Jak wdrażasz swój silnik cząstek? Podejrzewam, że bawisz się shaderem?
user4241
Używam shaderów, ponieważ potrzebuję gl_PointSize, aby ustawić każdy rozmiar cząstek. Nie współpracuję już z OGLES 1.x, ponieważ stare telefony nie są moim celem. Najpierw cały mój kod to OGLES 1.x, następnie OGLES 1.x i OGLES 2.x (bez poprawy wydajności), a teraz OGLES 2.x (poprawa renderowania).
Ellis,
1

Poleciłbym mieć VBO, z każdym wierzchołkiem zawierającym pozycję / obrót każdego renderowanego obiektu i grupowanie na podstawie tekstury, tak jak robisz. Nie jestem zbyt obeznany z ogl ES, więc nie jestem pewien, która wersja glsl obsługuje, ale możesz nawet być w stanie wsadować na podstawie zestawu tekstur i przechowywać, którą z 4 lub więcej tekstur przekazujesz w których będziesz używać wewnątrz wierzchołka. Punktowe duszki zdecydowanie poprawiłyby twoją wydajność, ponieważ drastycznie zmniejszyłyby ilość danych, które wysyłasz, a grupowanie nigdy nie powinno zmniejszać wydajności, jeśli robisz to poprawnie. Można również nieco poprawić wydajność, obliczając obrót na module cieniującym i przekazując tylko wartość int / float do parametrów lub do samego wierzchołka. (parametry byłyby szybsze,

sringer
źródło
Dziękuję za Twoją odpowiedź. Twoja sugestia dotycząca wykonywania obliczeń obrotu w module cieniującym jest doskonała, ale niestety używam OpenGL ES 1, który nie obsługuje modułów cieniujących, więc utknąłem z ustalonym potokiem. Wypróbuję sprite'y punktowe, ale nie mogę ich używać we wszystkich przypadkach, ponieważ istnieje górny limit ich rozmiaru. Nadal jestem trochę pesymistyczny w stosunku do VBO, jeśli ponownie obliczam pozycję każdego wierzchołka w każdej ramce, w jaki sposób VBO pomaga?
user4241
pozwala, aby twoje dane wierzchołków pozostały na GPU, co zmniejsza ilość danych, które musisz wysłać do GPU w każdej ramce. nie potrzebujesz shaderów, aby z tego skorzystać, nie musisz w ogóle zmieniać danych wierzchołków, jeśli masz pozycję bazową (taką jak początek) dla każdego duszka, możesz po prostu zmienić macierz świata poprzez przekształca się przed wywołaniem losowania. może to jednak być trudne przy tworzeniu partii. Korzystając ze stałej funkcji, korzystniej byłoby po prostu przełączyć się na VBO i przynajmniej na razie zrezygnować z grupowania, co na pewno da ci impuls.
sringer
Rozumiem co masz na myśli. W końcu nie mówisz o grupowaniu, ale po prostu za pomocą jednego wywołania losowania, aby narysować jeden obiekt gry. Zdecydowanie przetestuję, w jaki sposób VBO bez grupowania wpływa na liczbę klatek na sekundę w mojej grze, ale nadal 200 połączeń losujących na klatkę wydaje się zbyt duże ... ale chyba muszę z tym żyć. Przyjmę twoją odpowiedź, jeśli nie pojawią się żadne inne odpowiedzi.
user4241
1

Wspominasz platformy mobilne, które nie mają instancji. Ale nadal masz shadery wierzchołków, prawda?

W takim przypadku możesz nadal wykonywać pseudo instancje, co jest również bardzo szybkie. Zrób VBO (GL_STATIC_DRAW) z punktami narożnymi (względem punktu środkowego duszka, np. -1 / -1, 1 / -1, 1/1, -1/1) i dowolnymi potrzebnymi współrzędnymi tekstury, w nim .
Następnie ustaw jeden z ogólnych atrybutów wierzchołków dla każdego wywołania rysowania do punktu centralnego duszka i narysuj dwa trójkąty z ograniczeniem bufora. W module cieniującym wierzchołków przeczytaj ogólny atrybut wierzchołka i dodaj współrzędne wierzchołka.

Pozwoli to zaoszczędzić na blokowaniu transferu danych dla każdego duszka i powinno być znacznie szybsze. Rzeczywista liczba losowań nie jest tak bardzo ważna, blokowanie / przeciąganie pomiędzy nimi jest.

dm.skt
źródło
To brzmi dobrze rozwiązanie dla OpenGL ES 2.0. Niestety używam ES 1, który w ogóle nie ma shaderów.
user4241
0

Problem tkwi w ilości danych wysyłanych do GPU w każdej ramce. Wystarczy utworzyć VBO dla każdej partii i wypełnić ją jeden raz, a następnie zastosować odpowiednie macierze transformacji (poprzez glMultMatrix lub moduł cieniujący, jeśli używasz ES 2.0) podczas rysowania partii.

r2d2rigo
źródło
Nie rozumiem, jak to pomaga, gdy mam 200 osobnych obiektów gry z unikalnymi transformacjami? Użycie glMultMatrix zastosowałoby taką samą transformację do wszystkich obiektów, co nie jest tym, czego chcę. Ponadto przesyłanie danych do GPU nie stanowi wąskiego gardła; jeśli usunę transformacje po stronie procesora, wydajność jest bardzo dobra.
user4241
Tak, ale VBO może nadal poprawić wydajność, jeśli zostanie zastosowany poprawnie. Jak obecnie renderujesz swoje 200 obiektów? Czy używasz glBegin / glEnd?
TheBuzzSaw
1
Używam silnika Irrlicht 3D z niestandardowym węzłem sceny, więc nie używam bezpośrednio OpenGL (ale przypuszczam, że w tym przypadku używa on prostego glBegin / glEnd). Czy VBO naprawdę by pomógł, ponieważ musiałbym modyfikować cały bufor co ramkę? Nie rozwiązuje to również podstawowego problemu związanego z ograniczeniem procesora z powodu obliczeń transformacji wierzchołków. Ale i tak dziękuję za odpowiedzi!
użytkownik4241