Co to jest przepustowość i jak działa?


18

Dokumentacja OpenGL stwierdza, że ​​przepustowość returns the sum of the absolute value of derivatives in x and y.

Co to oznacza w kategoriach mniej matematycznych i czy można to sobie wyobrazić?

W oparciu o moje rozumienie funkcji, fwidth(p)ma dostęp do wartości pw sąsiednich pikselach. Jak działa to na GPU, nie wpływając drastycznie na wydajność i czy działa niezawodnie i jednolicie we wszystkich pikselach?

Odpowiedzi:


18

Pochodne pikseli na ekranie mają ogromny wpływ na wydajność, ale wpływają na wydajność, niezależnie od tego, czy ich używasz, czy nie, więc z pewnego punktu widzenia są one bezpłatne!

Każda karta graficzna w najnowszej historii zawiera cztery piksele razem i umieszcza je w tym samym warp / wavefront, co w zasadzie oznacza, że ​​działają one obok siebie na GPU, więc dostęp do nich jest bardzo tani. Ponieważ osnowy / czoła fali są uruchamiane w trybie blokowania, pozostałe piksele również będą dokładnie w tym samym miejscu w module cieniującym, co Ty, więc wartość ptych pikseli będzie po prostu czekała na ciebie w rejestrze. Te trzy pozostałe piksele będą zawsze wykonywane, nawet jeśli ich wyniki zostaną wyrzucone. Tak więc trójkąt obejmujący pojedynczy piksel zawsze będzie zacieniał cztery piksele i odrzucał wyniki trzech z nich, tak aby te funkcje pochodne działały!

Jest to uważane za akceptowalny koszt (dla obecnego sprzętu), ponieważ nie tylko takie funkcje fwidthwykorzystują te pochodne: każda pojedyncza próbka tekstury również, aby wybrać, z której mipmapy tekstury należy odczytać. Zastanów się: jeśli jesteś bardzo blisko powierzchni, współrzędna UV, której używasz do próbkowania tekstury, będzie miała bardzo małą pochodną w przestrzeni ekranu, co oznacza, że ​​musisz użyć większej mipmapy, a jeśli jesteś dalej, współrzędna UV będzie miała większa pochodna w przestrzeni ekranu, co oznacza, że ​​musisz użyć mniejszej mipmapy.

O ile oznacza to mniej matematycznie: fwidthjest równoważne abs(dFdx(p)) + abs(dFdy(p)). dFdx(p)jest po prostu różnicą między wartością pw pikselu x + 1 a wartością pw pikselu x i podobnie dla dFdy(p).


Więc jeśli dFdx(p) = p(x1) - p(x), to x1może być albo, (x+1)albo (x-1), w zależności od pozycji piksela xw kwadracie. Tak czy inaczej, x1musi być w tej samej warp / wavefront co x. Mam rację?
ApoorvaJ

3
@ApoorvaJ Zasadniczo ta sama wartość dla dFdxjest obliczana dla każdego z 2 sąsiednich pikseli w siatce 2x2. Ta wartość jest po prostu obliczana na podstawie różnicy między dwiema wartościami sąsiadów, jeśli tak jest p(x+1)-p(x)lub p(x)-p(x-1)zależy od twojego pojęcia, co xdokładnie jest tutaj. Rezultat jest jednak taki sam. Więc tak, masz rację.
Chris mówi Przywróć Monikę

@ChristianRau To odpowiada na moje pytanie. Dzięki.
ApoorvaJ

11

Z technicznego punktu widzenia fwidth(p)jest zdefiniowany jako

fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))

I dFdx(p)/ dFdy(p)są częściowymi pochodnymi wartości pw odniesieniu do wymiarów xi yekranu. Oznacza to więc, jak pzachowuje się wartość parametru, gdy przesuwa się jeden piksel w prawo ( x) lub jeden piksel w górę ( y).

Jak można je praktycznie obliczyć? Cóż, jeśli znasz wartości sąsiednich pikseli p, możesz po prostu obliczyć te pochodne jako bezpośrednie różnice skończone jako przybliżenie ich rzeczywistych pochodnych matematycznych (które mogą nie mieć w ogóle dokładnego rozwiązania analitycznego):

dFdx(p) := p(x+1) - p(x)

Ale oczywiście teraz możesz zapytać, skąd w ogóle znamy wartości p(które mogą być dowolną dowolnie obliczoną wartością w programie modułu cieniującego) dla sąsiednich pikseli? Jak obliczyć te wartości bez ponoszenia dużego obciążenia, wykonując obliczenia całego modułu cieniującego dwa (lub trzy) razy?

Cóż, wiesz co, te sąsiednie wartości są i tak obliczane, ponieważ dla sąsiedniego piksela uruchamiasz również moduł cieniujący fragmenty. Tak więc wszystko, czego potrzebujesz, to dostęp do wywołania tego modułu cieniującego fragmenty przy uruchomieniu dla sąsiedniego piksela. Ale jest to jeszcze łatwiejsze, ponieważ te sąsiednie wartości są również obliczane dokładnie w tym samym czasie.

Nowoczesne rasteryzatory nazywają shadery fragmentów w większych kafelkach więcej niż jednego sąsiedniego piksela. W najmniejszym przypadku byłaby to siatka pikseli 2x2. I dla każdego takiego bloku pikseli wywoływany jest moduł cieniujący fragmenty dla każdego piksela, a te wywołania przebiegają w idealnie równoległym kroku blokowania, dzięki czemu wszystkie obliczenia są wykonywane w dokładnie tej samej kolejności i w tym samym czasie dla każdego z tych pikseli w bloku (dlatego też rozgałęzienie w module cieniującym fragmenty, chociaż nie jest zabójcze, należy unikać, jeśli to możliwe, ponieważ każde wywołanie bloku musiałoby zbadać każdą gałąź, która jest pobierana przez co najmniej jedno wywołanie, nawet jeśli po prostu wyrzuca) wyniki później, jak podano również w odpowiedziach na to powiązane pytanie). Tak więc w dowolnym momencie moduł cieniujący fragment teoretycznie ma dostęp do wartości modułu cieniującego fragmentu sąsiednich pikseli. A gdy nie mają bezpośredniego dostępu do tych wartości, masz dostęp do wartościami obliczonymi z nich, podobnie jak funkcje pochodnych dFdx, dFdy, fwidth, ...

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.