Informacje o renderowaniu, partiach, karcie graficznej, wydajności itp. + XNA?


12

Wiem, że tytuł jest trochę niejasny, ale trudno opisać, czego naprawdę szukam, ale proszę bardzo.

Jeśli chodzi o renderowanie procesora, wydajność jest w większości łatwa do oszacowania i prosta, ale jeśli chodzi o procesor graficzny ze względu na brak informacji technicznych w tle, nie mam pojęcia. Używam XNA, więc byłoby miło, gdyby teoria mogła być z tym powiązana.

Więc tak naprawdę chcę wiedzieć, co się dzieje, kiedy i gdzie (CPU / GPU) wykonujesz określone akcje losowania? Co to jest partia? Jaki wpływ mają efekty, projekcje itp.? Czy dane są utrwalane na karcie graficznej, czy też są przesyłane na każdym kroku? Kiedy mówi się o przepustowości, czy mówisz o wewnętrznej przepustowości karty graficznej, czy o potoku między procesorem a GPU?
Uwaga: tak naprawdę nie szukam informacji o tym, jak przebiega proces rysowania, to jest sprawa GPU, interesuje mnie cały narzut, który to poprzedza.

Chciałbym zrozumieć, co się dzieje, gdy wykonuję akcję X, aby dostosować do tego moje architektury i praktyki.

Wszelkie artykuły (ewentualnie z przykładami kodu), informacje, linki, samouczki, które dają więcej wglądu w pisanie lepszych gier, są bardzo mile widziane. Dzięki :)


2
Chociaż pierwotnie był to XNA, dodałem tag DirectX, ponieważ jest to podstawowa technologia - może pomóc uzyskać lepsze odpowiedzi. Sprawdź również tę odpowiedź, która może być dobrym punktem wyjścia.
Andrew Russell

@AndrewRussell Wielkie dzięki :). Właściwie przeczytałem już różne artykuły na ten temat, w tym ten. Ale nie obejmuje wszystkiego, co chciałbym wiedzieć.
Aidiakapi

Odpowiedzi:


20

Lubię myśleć o wydajności w kategoriach „ limitów ”. Jest to przydatny sposób na konceptualizację dość skomplikowanego, połączonego systemu. Gdy masz problem z wydajnością, zadajesz pytanie: „Jakie limity uderzam?” (Lub: „Czy jestem związany procesorem / kartą graficzną?”)

Możesz podzielić go na wiele poziomów. Na najwyższym poziomie masz CPU i GPU. Możesz być związany z procesorem (GPU siedzi bezczynnie i czeka na procesor) lub może być związany z GPU (procesor czeka na GPU). Oto dobry post na blogu na ten temat.

Możesz to dalej rozbić. Po stronie procesora możesz wykorzystywać wszystkie swoje cykle na danych znajdujących się już w pamięci podręcznej procesora. Lub możesz mieć ograniczoną pamięć , pozostawiając procesor bezczynny, czekając na dane z pamięci głównej ( zoptymalizuj układ danych ). Nadal możesz to zepsuć.

(Podczas gdy robię szeroki przegląd wydajności w odniesieniu do XNA, zwrócę uwagę, że przydział typu referencyjnego ( classnie struct), chociaż normalnie tani, może spowodować wyrzucanie elementów bezużytecznych, co spowoduje spalenie wielu cykli - szczególnie na Xbox 360 . Zobacz tutaj szczegóły).

Po stronie GPU zacznę od wskazania tego doskonałego posta na blogu, który zawiera wiele szczegółów. Jeśli chcesz uzyskać szalony poziom szczegółowości w przygotowaniu, przeczytaj tę serię postów na blogu . ( Oto prostszy ).

Mówiąc prościej, niektóre z dużych to: „ limit wypełnienia ” (liczba pikseli, które można zapisać w buforze backbuffera - często jak dużo overdraw można mieć), „ limit shadera ” (jak skomplikowane mogą być twoje shadery i ile danych można przez nie przepchnąć), „ pobieranie tekstur / limit przepustowości tekstur ” (ile danych tekstur można uzyskać dostęp).

I teraz dochodzimy do dużego - o to tak naprawdę pytasz - gdzie procesor i GPU muszą oddziaływać (za pośrednictwem różnych interfejsów API i sterowników). Luźno istnieje „ limit partii ” i „ przepustowość ”. (Należy zauważyć, że pierwsza część serii, o której wspomniałem wcześniej, zawiera szczegółowe informacje).

Ale w zasadzie partia ( jak już wiesz ) dzieje się za każdym razem, gdy wywołasz jedną z GraphicsDevice.Draw*funkcji (lub część XNA, na przykład SpriteBatch, robi to za Ciebie). Jak już bez wątpienia czytałeś, dostajesz kilka tysięcy * na ramkę. Jest to limit procesora - więc konkuruje z innym wykorzystaniem procesora. Zasadniczo jest to sterownik pakujący wszystko o tym, co kazałeś mu narysować, i wysyłający go do GPU.

A potem jest przepustowość do GPU. To ile surowych danych możesz tam przenieść. Obejmuje to wszystkie informacje o stanie, które towarzyszą wsadom - wszystko, od ustawiania stanu renderowania i stałych / parametrów modułu cieniującego (który obejmuje takie rzeczy jak świat / widok / matryce projektu), aż do wierzchołków podczas korzystania z DrawUser*funkcji. Obejmuje ona także żadnych połączeń do SetDatai GetDatana fakturach, bufory wierzchołków itp

W tym miejscu powinienem powiedzieć, że wszystko, co można wywołać SetData(tekstury, bufory wierzchołków i indeksów itp.), A także Effects - pozostaje w pamięci GPU. Nie jest stale wysyłane ponownie do GPU. Polecenie rysowania, które odwołuje się do tych danych, jest po prostu wysyłane ze wskaźnikiem do tych danych.

(Również: możesz wysyłać polecenia rysowania tylko z głównego wątku, ale możesz SetDataz dowolnego wątku.)

XNA komplikuje nieco z jego czynią klas państwowych ( BlendState, DepthStencilStateitp). Dane o stanie wysyłane dla każdego losowania (w każdej partii). Nie jestem w 100% pewien, ale mam wrażenie, że jest wysyłany leniwie (wysyła tylko stan, który się zmienia). Tak czy inaczej, zmiany stanu są tanie do tego stopnia, że ​​są wolne od kosztu partii.

Na koniec należy wspomnieć o wewnętrznym potoku GPU . Nie chcesz wymuszać jego opróżnienia przez zapis do danych, które wciąż muszą czytać, lub odczytywanie danych, które wciąż muszą zapisywać. Opróżnianie potoku oznacza, że ​​czeka on na zakończenie operacji, dzięki czemu wszystko jest w spójnym stanie po uzyskaniu dostępu do danych.

Dwa szczególne przypadki, na które należy uważać, to: Wzywanie GetDataczegokolwiek dynamicznego - szczególnie takiego RenderTarget2D, na którym GPU może pisać. Jest to bardzo niekorzystne dla wydajności - nie rób tego.

Innym przypadkiem jest wywoływanie SetDatabuforów wierzchołków / indeksów. Jeśli musisz to robić często, użyj DynamicVertexBuffer(także DynamicIndexBuffer). Pozwalają one GPU wiedzieć, że często się zmieniają, i wykonać wewnętrzną magię buforowania, aby uniknąć przepłukiwania rurociągu.

(Należy również pamiętać, że bufory dynamiczne są szybsze niż DrawUser*metody - ale muszą być wstępnie przydzielone przy maksymalnym wymaganym rozmiarze).

... I to prawie wszystko, co wiem o wydajności XNA :)


Dziękuję bardzo! To jest dokładnie to, czego szukałem i nadzieję :).
Aidiakapi

1
Kilka sto partie na ramie brzmi zbyt pesymistyczny. Zasada, którą zawsze słyszałem, to od 2 do 3 partii na klatkę. Niektóre gry osiągają do 10 000 na PC, ale myślę, że wymaga to dużej staranności.
Nathan Reed,

Całkiem dobrze. Liczba „kilkuset” pochodzi z papieru „wsadowego wsadowego” - który wymieniał „25k partii / s przy 100% procesora 1GHz”. Ale ten papier ma już dekadę, a sterowniki i procesory znacznie się od tego czasu poprawiły. Zaktualizuję to (i moje inne), aby czytać „kilka tysięcy”.
Andrew Russell,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.