Jak działa aktualizacja bufora głębokości w GPU?

10

W tej chwili próbuję zaimplementować jakiś bufor głębokości w oprogramowaniu i mam ogromny problem, kiedy do niego piszę. Posiadanie jednego muteksu to absolutna przesada. Stworzyłem więc wiele muteksów równych liczbie wątków. Blokuję muteks na podstawie bieżącego piksela (pixel_index% mutexes_number) i działa to lepiej, ale nadal bardzo bardzo wolno. I zastanawiam się, jak to się robi w prawdziwym GPU? Czy istnieje sprytny algorytm lub sprzęt go obsługuje?

nikitablack
źródło

Odpowiedzi:

9

Obsługuje go wysoce wyspecjalizowany sprzęt. Typową strategią dla GPU jest rasteryzacja kafelków i przechowywanie informacji o głębokości w skompresowanych formatach (np. Równanie Z, gdy wielokąt całkowicie pokrywa kafelek). Umożliwia to testowanie całego kafelka jednocześnie; inne fajne sztuczki HW obejmują testowanie głębokości przed uruchomieniem modułu cieniującego piksele (przy założeniu, że pozwalają na to warunki - moduł cieniujący nie może zapisać wartości głębokości). Możesz rozważyć coś podobnego w oprogramowaniu, na przykład posiadanie każdego wątku „posiadającego” podzbiór kafelków i samodzielne przejście każdego prymitywu lub naśladowanie strategii wielu GPU, takich jak alternatywne ramki lub alternatywne linie rastrowe.

Daniel M. Gessel
źródło
11

W prawdziwym procesorze graficznym zamiast wielu rdzeni próbujących odczytywać / zapisywać ten sam region bufora głębokości i próbujących synchronizować je, bufor głębokości jest podzielony na kafelki (takie jak 16 × 16 lub 32 × 32), a każdy z nich kafelek jest przypisany do jednego rdzenia. Rdzeń ten jest następnie odpowiedzialny za całą rasteryzację w tym kafelku: wszelkie trójkąty dotykające tego kafelka zostaną zrasteryzowane (w obrębie tego kafelka) przez rdzeń będący właścicielem. Wówczas nie ma interferencji między rdzeniami i nie trzeba ich synchronizować podczas uzyskiwania dostępu do części bufora ramki.

Oznacza to, że trójkąty dotykające wielu płytek będą musiały zostać zrasteryzowane przez wiele rdzeni. Jest więc krok redystrybucji pracy między przetwarzaniem geometrii (operacje na wierzchołkach i trójkątach) a przetwarzaniem pikseli.

Na etapie geometrii każdy rdzeń może przetwarzać fragment operacji pierwotnych; następnie dla każdego elementu pierwotnego może szybko określić, które płytki dotyka element pierwotny (nazywa się to „zgrubną rasteryzacją”), i dodać element podstawowy do kolejki dla każdego rdzenia, który jest właścicielem jednego z dotkniętych płytek.

Następnie, na etapie pikseli, każdy rdzeń może odczytać listę prymitywów w swojej kolejce, obliczyć pokrycie pikseli dla płytek posiadanych przez rdzeń i przejść do testów głębokości, cieniowania pikseli i aktualizacji bufora ramki, bez potrzeby dalszej koordynacji z innymi rdzeniami.

Nathan Reed
źródło