Pule pamięci w DirectX

2009-11-21 12:24

Jedną z rzeczy, która na początku programowania w DirectX może wydawać się dziwna, jest tajemniczy parametr Pool. Pojawia się on w każdej funkcji, która tworzy jakiś zasób graficzny: teksturę, bufor wierzchołków, siatkę modelu (ID3DXMesh), bufor głębokości, itp.
Rolą tego parametru jest określenie, w której puli pamięci znajdzie się tworzony zasób. DX wyróżnia bowiem ich kilka, co jest związane przede wszystkim z (przynajmniej) dwoma rodzajami pamięci, z jakimi możemy mieć do czynienia programując grafikę: zwykłym systemowym RAM-em oraz pamięcią karty graficznej.

W jakiej puli powinniśmy więc umieszczać swoje obiekty? To zależy od kilku czynników. Na początek na pewno warto przyjrzeć możliwościom:

  • D3DPOOL_DEFAULT, jak wskazuje na to nazwa, oznacza pulę domyślną. Gdy użyjemy tej flagi, DirectX umieści nasz zasób w najlepszym – pod względem wydajności – miejscu, bazując przy tym na innym parametrze, Usage (określa on, mówiąc w skrócie, sposób wykorzystania danego zasobu). Tym najlepszym miejscem jest prawie zawsze pamięć karty graficznej
    To sprawia jednak, że przy utracie urządzenia (o ile nie programujemy co najmniej w DX9Ex) taki zasób należy zwolnić, a potem utworzyć ponownie – musimy więc pamiętać sposób, w jaki go utworzyliśmy. Ponadto istnieją też pewne ograniczenia (bezwzględne lub wydajnościowe) w dostępie do obiektów z puli domyślnej: o ile nie zadeklarujemy ich jako dynamiczne (D3DUSAGE_DYNAMIC), ich blokowanie wiąże się ze stratą szybkości albo jest wręcz niemożliwe (w przypadku tekstur).
  • D3DPOOL_MANAGED to pula zarządzana. Oznacza to, że pieczę nad nią sprawują sam DirectX i to on decyduje, w którym rodzaju pamięci zasoby z tej puli zostaną umieszczone. Zazwyczaj oznacza to, że w pamięci operacyjnej trzymana jest kopia obiektu, znajdującego się też w pamięci graficznej. Dzięki temu nie trzeba go tworzyć ponownie w przypadku straty urządzenia, a także można go blokować i modyfikować niezależnie od typu i flag Usage; w tym przypadku DX zadba o odpowiednią synchronizację.
  • D3DPOOL_SYSTEMMEM oznacza pulę pamięci systemowej. Zasoby tu stworzone będą znajdowały się w zwykłym RAM-ie i nie będą mogły być bezpośrednio renderowane. Dane z nich mogą jednak być kopiowane do zasobów znajdujących się w puli domyślnej, jak chociażby poprzez funkcję UpdateTexture.
  • D3DPOOL_SCRATCH jest również usytuowana w pamięci systemowej (RAM). W odróżnieniu od poprzedniej, zasoby z tej puli nie są jednak w żaden sposób (także pośredni) dostępne dla urządzenia i nie mogą być używane podczas renderowania. Oznacza to też, że nie podlegają one ograniczeniom związanym z kartą graficzną. Można więc, przykładowo, tworzyć w tej puli tekstury o rozmiarach niebędących potęgami dwójki także wtedy, gdy karta nie wspiera takich tekstur.

Spotkałem się z dwiema ogólnymi wytycznymi dotyczącymi tego, z których pul pamięci należy korzystać:

  1. Pierwsza z nich mówi, że należy wszystko, co się da, umieszczać w D3DPOOL_DEFAULT, bo to zapewnia największą wydajność, jako że wtedy zasoby generalnie umieszczane są w pamięci karty graficznej.
  2. Druga szkoła sugeruje z kolei, żeby wszystko, co się da, umieszczać w D3DPOOL_MANAGED, gdyż wtedy pozwalamy DirectX-owi zdecydować, w jakim rodzaju pamięci trzymać nasz zasób – a już on powinien o tym wiedzieć lepiej.

W sumie więc wygląda na to, że nic nie wiadomo :) Oczywiście są przypadki, gdy wyboru nie mamy, jak to się dzieje choćby dla tekstur typu render target, które muszą być stworzone w D3DPOOL_DEFAULT. Zawsze jednak będziemy mieli takie zasoby, które “równie dobrze” dałoby się umieścić w puli domyślnej, jak i zarządzanej. Co wtedy?
Otóż wydaje mi się, że to zależy od wielkości naszej aplikacji (w sensie rozmiaru używanych zasobów) oraz… stopnia zaawansowania w programowaniu w DirectX. Na początek bowiem D3DPOOL_MANAGED jest pulą bezpieczniejszą: nie musimy się w niej martwić o stratę urządzenia i możemy każdy zasób blokować i zmieniać. Ceną za to jest wzrost zużycia pamięci przez aplikację, spowodowany trzymaniem przez DX kopii obiektów w pamięci systemowej.
Jeśli jednak nasza gra (nie bójmy się użyć tego słowa ;)) używa dużej ilości zasobów, to takie marnotrawstwo jest zwykle nie do przyjęcia. Wtedy przynajmniej część z nich należy przenieść do D3DPOOL_DEFAULT. Istnieje szansa, że na tym etapie będziemy już wiedzieć lepiej, którą część ;-)

Na koniec pozwolę sobie też wspomnieć o – często pomijanej – puli D3DPOOL_SCRATCH. Jej przeznaczeniem są wszelkiego rodzaju zasoby pomocnicze, których renderujemy, ale mimo to wykorzystujemy do jakiegoś celu – na przykład tworzenia innych zasobów. Typowym przykładem są wszelkiego rodzaju pomocnicze, narzędziowe tekstury – jak choćby mapy wysokości (heightmap), na podstawie których generujemy ukształtowanie terenu.
Najlepszą pulą dla takich obiektów jest właśnie D3DPOOL_SCRATCH. Użycie jakiejkolwiek innej spowodowałoby uszczerbek na wydajności lub wręcz błędy, jak np. niezamierzone przeskalowanie tekstury do rozmiaru 2n.



Możesz śledzić komentarze do tej notki poprzez kanał RSS 2.0.
Możesz przejść do końca i zostawić komentarz. Śledzenie notek (trackback) jest aktualnie wyłączone.


Dodaj komentarz

Znaki nowej linii dodawane są automatycznie.
Do wstawiania kodu użyj [code][/code], a do wzorów (w LaTeX-u) [tex][/tex].
Dozwolne tagi HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 



© 2009 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.