Udostępnij za pośrednictwem


Dlaczego potrzebne są zasoby kafelkowe?

Zasoby kafelkowe są potrzebne, aby zmniejszyć marnotrawienie pamięci procesora graficznego (GPU) na przechowywanie regionów powierzchni, do których aplikacja wie, że nie będzie miała dostępu, oraz żeby sprzęt potrafił filtrować przez sąsiednie kafelki.

W systemie graficznym (tj. systemie operacyjnym, sterowniku wyświetlania i sprzęcie graficznym) bez obsługi kafelkowanych zasobów system graficzny zarządza wszystkimi alokacjami pamięci Direct3D na poziomie szczegółowości podzasobów. W przypadku Buforu, cały Bufor jest podzasobem. W przypadku tekstury (na przykład Texture2D), każdy poziom mip jest zasobem podrzędnym; dla tablicy tekstur (na przykład Texture2DArray), każdy poziom mip w danym wycinku tablicy jest zasobem podrzędnym. System graficzny udostępnia jedynie możliwość zarządzania mapowaniem alokacji w tej skali zasobów podrzędnych. W kontekście zasobów kafelków "mapowanie" odnosi się do tworzenia danych widocznych dla procesora GPU.

Załóżmy, że aplikacja wie, że określona operacja renderowania musi uzyskiwać dostęp tylko do niewielkiej części łańcucha mipmapy obrazu (być może nawet nie pełnego obszaru danej mipmapy). Najlepiej, aby aplikacja mogła poinformować system graficzny o tej potrzebie. Następnie system graficzny zajmowałby się tylko zapewnieniem, że wymagana pamięć jest mapowana na GPU bez ładowania zbyt dużej ilości pamięci. W rzeczywistości, bez obsługi zasobów kafelkowych, system graficzny może być informowany tylko o pamięci, która musi być mapowana na GPU przy szczegółowości subzasobów (na przykład zakres pełnych poziomów mipmap, które mogą być udostępnione). W systemie graficznym nie ma żadnego błędu popytu, więc potencjalnie wiele nadmiarowej pamięci procesora GPU musi być używane do tworzenia pełnych podsieci zamapowanych przed wykonaniem polecenia renderowania, które odwołuje się do jakiejkolwiek części pamięci. Jest to tylko jeden problem, który sprawia, że korzystanie z dużych alokacji pamięci jest trudne w trybie Direct3D bez obsługi zasobów kafelków.

Direct3D 11 obsługuje powierzchnie Texture2D z maksymalnie 16384 pikselami po danej stronie. Obraz o szerokości 16384 o wysokości 16384 i 4 bajty na piksel zużywałby 1 GB pamięci wideo (a dodanie mipmap podwoiłoby tę ilość). W praktyce rzadko zdarza się, że wszystkie 1 GB należałoby odwoływać się w ramach jednej operacji renderowania.

Niektórzy deweloperzy gier modelują powierzchnie terenu o rozmiarze 128K na 128K. Sposobem na to, aby to działało na istniejących procesorach GPU, jest podzielenie powierzchni na płytki, które są wystarczająco małe, aby sprzęt mógł sobie z nimi poradzić. Aplikacja musi ustalić, które kafelki mogą być potrzebne i załadować je do pamięci podręcznej tekstur na GPU — system stronicowania oprogramowania. Znaczącą wadą tego podejścia jest to, że sprzęt nie wie nic o aktualnie zachodzącym stronicowaniu: Kiedy część obrazu musi być wyświetlana na ekranie, obejmując kilka kafelków, sprzęt nie wie, jak wykonać filtrowanie w stałej funkcji (czyli wydajnie) między kafelkami. Oznacza to, że aplikacja zarządzająca własnym kafelkowaniem oprogramowania musi uciekać się do ręcznego filtrowania tekstur w kodzie shaderów (co staje się bardzo kosztowne, jeśli wymagany jest dobrej jakości filtr anizotropowy) i/lub marnować pamięć na tworzenie marginesów wokół kafelków zawierających dane z sąsiednich, aby filtrowanie sprzętowe funkcji stałej mogło nadal zapewniać pewną pomoc.

Jeśli kaflowana reprezentacja alokacji powierzchni mogłaby być funkcją pierwszej klasy w systemie graficznym, aplikacja mogłaby poinformować sprzęt, które kafelki mają być dostępne. W ten sposób mniej pamięci GPU jest marnowane na przechowywanie regionów powierzchni, do których aplikacja wie, że nie będzie do nich dostępu, a sprzęt może zrozumieć, jak filtrować w obrębie sąsiednich kafelków, co zmniejsza niektóre problemy napotykane przez deweloperów dokonujących samodzielnego układania kafelków programowo.

Ale aby zapewnić kompletne rozwiązanie, należy zrobić coś, aby rozwiązać problem wynikający z faktu, że bez względu na to, czy obsługiwane jest kafelkowanie na powierzchni, maksymalny wymiar powierzchni wynosi obecnie 16384 – co jest dalekie od 128K+, które aplikacje już wymagają. Wymaganie, aby sprzęt obsługiwał większe rozmiary tekstur, to jedno z podejść, jednak wiąże się to ze znacznymi kosztami i/lub kompromisami przy wyborze tej ścieżki. Ścieżki filtru tekstury i renderowania Direct3D 11 są już nasycone pod względem precyzji w obsłudze tekstur 16K wraz z innymi wymaganiami, takimi jak obsługa zakresów wyświetlania wykraczających poza powierzchnię podczas renderowania lub obsługa tekstur owijających się wokół krawędzi powierzchni podczas filtrowania. Istnieje możliwość zdefiniowania kompromisu, że przy wzroście rozmiaru tekstury powyżej 16K, funkcjonalność lub precyzja jest w jakiś sposób ograniczana. Nawet przy tej ustępstwie mogą jednak być wymagane dodatkowe koszty sprzętu związane z możliwościami adresowania w całym systemie sprzętowym, aby dostosować się do większych rozmiarów tekstur.

Jednym z problemów, które pojawiają się, gdy tekstury stają się bardzo duże, jest to, że współrzędne tekstury zmiennoprzecinkowej pojedynczej precyzji (oraz związane z nimi interpolatory wspierające rasteryzację) tracą precyzję, aby dokładnie określić położenia na powierzchni. Filtrowanie tekstur spowoduje drgania. Jedną z kosztownych opcji byłoby wymaganie obsługi interpolatora podwójnej precyzji, choć może to być nadmierne, biorąc pod uwagę rozsądną alternatywę.

Alternatywna nazwa zasobów kafelkowanych to "rozrzedzona tekstura". "Rozrzedzona" przekazuje zarówno kafelkową naturę zasobów, jak i może także główną przyczynę ich kafelkowania - że nie oczekuje się, by wszystkie były jednocześnie zmapowane. W rzeczywistości aplikacja może utworzyć zasób kafelkowy, w którym świadomie nie zapisuje się danych dla wszystkich regionów i mipów zasobu. Dlatego sama zawartość może być rzadka, a mapowanie zawartości w pamięci GPU w danym momencie byłoby podzbiorem tego (jeszcze bardziej rzadka).

Innym scenariuszem, który może być obsługiwany przez zasoby kafelkowe, jest umożliwienie wielu zasobom o różnych wymiarach/formatach współdzielenia tej samej pamięci. Czasami aplikacje mają wyłączne zestawy zasobów, o których wiadomo, że nie są używane jednocześnie, lub zasoby, które są tworzone tylko do bardzo krótkotrwałego użycia, po czym są niszczone, a następnie tworzone są inne zasoby. Forma uogólnienia, która może wynikać z "zasobów kafelkowych", polega na umożliwieniu użytkownikowi odwołania się do wielu różnych zasobów w tej samej (nakładającej się) pamięci. Innymi słowy, tworzenie i niszczenie "zasobów" (które definiują wymiar/format itd.) może zostać oddzielone od zarządzania pamięcią bazową zasobów z punktu widzenia aplikacji.

zasoby płytkowe