Używasz instancji do wszystkiego?

16

Instancja poprawia wydajność (znacznie) podczas renderowania wielu (setki tysięcy?) Kopii tej samej siatki jednocześnie. Ale ile to narzutu podczas renderowania dokładnie jednej kopii za pomocą jednego wywołania losowania? Czy dobrym pomysłem byłoby użycie instancji dla całej geometrii renderowanej przez silnik?

Edycja: Załóżmy, że tworzymy grę FPS. Większość obiektów ma tylko jedną instancję: nóż, pistolet, karabin maszynowy, budynek i wieżę radiową. Ale są też obiekty z wieloma instancjami: drzewa (np. 3 rodzaje drzew z setkami instancji), trawa i tak dalej ... Mam na myśli: zamiast renderować obiekty z jedną instancją w „tradycyjny” sposób i drzewa i trawy za pomocą instancji, renderujemy je wszystkie za pomocą instancji. Tak więc nasza wieża radiowa ma tylko jedną instancję (której informacje przechowujemy w buforze danych instancji) i renderujemy tę wieżę za pomocą pewnego rodzaju DrawInstanced()wywołania z liczbą instancji równą 1. To samo dotyczy wszystkich innych obiektów (oczywiście drzewa i trawa mają wiele wystąpień).

Moje pytanie brzmi: czy złym pomysłem jest narysowanie pojedynczej instancji obiektu za pomocą instancji? Czy wystąpienie ma zbyt dużo narzutu (pod względem pamięci i wydajności), czy też jest w jakikolwiek sposób niepożądane w przypadku renderowania obiektów z jedną instancją?

NPS
źródło

Odpowiedzi:

19

Pod D3D9 z XPDM prawie na pewno chcesz wystąpić, gdziekolwiek to możliwe. Narysuj koszt połączenia jest tak wysoki, że ma sens. W tym scenariuszu punkt podziału może wynosić zaledwie 2 lub 3 wystąpienia.

Jeśli masz tylko jedną instancję danej siatki, może ona wydawać się kusząca, aby narysować ją bez instancji. Spójrz jednak na to, co się wiąże:

  • Musisz przełączyć się z ładowania danych do bufora dla poszczególnych instancji na przesyłanie stałych modułu cieniującego.
  • Musisz zachować dwie kopie modułu cieniującego wierzchołki: jedną dla instancji i jedną dla instancji bez instancji.
  • Musisz zachować dwie kopie kodu renderowania: podobnie.
  • Musisz przełączyć shadery.
  • Musisz przełączyć formaty wierzchołków.
  • Może być konieczne przełączenie buforów wierzchołków.
  • A potem musisz to zrobić od nowa, jeśli wracasz do instancji rysunku dla następnej grupy siatek.

Nawet jeśli masz tylko jedną siatkę (jak model pistoletu w FPS), są przypadki, w których instancja jest przydatna. Załóżmy, że wykonujesz akumulację światła wieloprzebiegowego w rendererze do przodu i przy pomocy prepassa Z. Zamiast dodatkowego przejścia dla każdego światła, tworzysz dane dla poszczególnych wystąpień i rysujesz je instancjami.

Opierając się na pierwszym scenariuszu, morał tej historii jest taki, że jeśli jakaś klasa obiektów może w ogóle korzystać z instancji, sensowne jest, aby zawsze używać instancji dla wszystkich obiektów tej klasy.

Opierając się na drugim scenariuszu, morał tej historii jest taki, że instancje mogą mieć nieoczywiste zastosowania, które wykraczają poza samo powiedzenie „muszę narysować 20 drzew”.

Maximus Minimus
źródło
Podane przez ciebie powody są tymi samymi, które skłoniły mnie do zadania tego pytania. Dziękuję Ci.
NPS
12

(W moim systemie nie testowałem tego nigdzie indziej) W GL, wystąpienie pojedynczej siatki (rysunek z liczbą = 1) ma trochę nieprzyjemnego narzutu, ale nie wiem skąd pochodzi. Zdecydowanie odradzam to.

Testowałem to w praktycznym zastosowaniu kilka miesięcy temu. Zakodowałem niektóre algorytmy globalnego oświetlenia w scenie Crytek Sponza, która składa się z około 350 oczek (nie pamiętam dokładnie), z których kilka dzieli kilka przykładów. Na początku zrobiłem to tak, jak sugerujesz, po prostu wstaw wszystko i narysuj resztę z liczbą wystąpień 1, ponieważ nieco uprościło to renderowanie kodu.

Później podczas optymalizacji renderera, samo przejście z instancji count = 1 obiektów do przesłania ich w zwykły sposób pozwoliło mi zaoszczędzić około 3,5 milisekundy na klatkę na i7 3770k (i GTX 770). Przełączanie siatek z wieloma instancjami na zwykłe wykonywanie ich w tradycyjny sposób pozwoliło mi zaoszczędzić kolejne 0,5 ms. Ogólnie aplikacja wzrosła z ~ 120 FPS do około ~ 230 FPS.

Liczby te oczywiście zawsze zależą od tego, gdzie są wąskie gardła w twojej aplikacji, a ostatnie 0,5 ms może faktycznie stać się spowolnieniem w aplikacji, w której jesteś bardzo związany z drawem. Ale z drugiej strony, z mojego doświadczenia wynika, że ​​instynkt ma paskudne koszty ogólne, jeśli nie rysujesz wielu rzeczy naraz.

TravisG
źródło
Ciekawe, ale byłoby miło zobaczyć dane dla sterowników AMD i Intel, w przeciwnym razie naprawdę powinieneś powiedzieć „W moim systemie” zamiast „W GL”. Z drugiej strony, nawet jeśli nie jest to problem z innymi implementacjami, fakt, że może tak być, jest wystarczającym powodem, aby uniknąć wystąpienia, jeśli go nie używasz.
bcrist
2

Możesz być pewien, że rysowanie instancji pojedynczego obiektu jest droższe niż normalne rysowanie pojedynczego obiektu. Na przykład GPU przygotowuje się na dużą liczbę obiektów, a przygotowanie to będzie inne niż dla pojedynczego obiektu. Jednak, jak duża jest ta luka w wydajności, można znaleźć tylko poprzez eksperymenty i zależy ona w dużej mierze od rzeczywistej konfiguracji renderowania. Jedynym sposobem, aby się upewnić, jest przetestowanie go samemu. Benchmarking pojedynczego losowania jest trudny. Oto kilka pomysłów na dalsze kroki.

Roy T.
źródło
2

Minęły 4 lata ... i myślę, że można spokojnie powiedzieć, że wysyłanie „instancji” wywołań losowania za pomocą 1. Jak można zauważyć, nowe API DX12 i Vk mają liczbę instancji, która może wynosić od 0 do NUM_INSTANCES . Zauważ też, że nie ma DrawIndexed (...) .

EDYTOWAĆ

Uwaga: powyższe jest prawdopodobnie w porządku z tymi nowoczesnymi interfejsami API, być może użycie czegoś starego jak Gl <3.3 lub DX11 będzie wymagało pewnego profilowania, jak wspomniali inni użytkownicy.

Nacho
źródło