John napisał już świetną odpowiedź, więc zastanów się nad jej przedłużeniem.
Obecnie dużo pracuję z modułami obliczeniowymi dla różnych algorytmów. Ogólnie rzecz biorąc, odkryłem, że shadery obliczeniowe mogą być znacznie szybsze niż ich równoważne shadery pikseli lub alternatywy oparte na sprzężeniu zwrotnym.
Gdy obejrzysz się, jak działają shadery obliczeniowe, w wielu przypadkach mają one również większy sens. Użycie modułu cieniującego piksele do filtrowania obrazu wymaga skonfigurowania bufora ramki, wysyłania wierzchołków, korzystania z wielu stopni modułu cieniującego itp. Dlaczego powinno to być wymagane do filtrowania obrazu? Przyzwyczajenie się do renderowania pełnoekranowych quadów do przetwarzania obrazów jest z pewnością jedynym „ważnym” powodem, aby nadal je używać. Jestem przekonany, że nowicjusz w dziedzinie grafiki obliczeniowej uznałby, że shadery obliczeniowe są bardziej naturalne do przetwarzania obrazu niż renderowania tekstur.
Twoje pytanie dotyczy w szczególności filtrowania obrazów, więc nie będę zbyt szczegółowo omawiał innych tematów. W niektórych naszych testach po prostu skonfigurowanie sprzężenia zwrotnego transformacji lub przełączenie obiektów bufora ramki w celu renderowania na teksturę może spowodować koszty wydajności około 0,2 ms. Pamiętaj, że wyklucza to wszelkie renderowanie! W jednym przypadku zachowaliśmy dokładnie ten sam algorytm przeniesiony do obliczeń shaderów i zauważyliśmy zauważalny wzrost wydajności.
Podczas korzystania z shaderów obliczeniowych można wykorzystać więcej krzemu na GPU do wykonania rzeczywistej pracy. Wszystkie te dodatkowe kroki są wymagane podczas korzystania z trasy modułu cieniującego piksele:
- Zespół wierzchołków (czytanie atrybutów wierzchołków, dzielniki wierzchołków, konwersja typów, rozwijanie ich do vec4 itp.)
- Moduł cieniujący wierzchołek musi być zaplanowany bez względu na to, jak minimalny jest
- Rasterizer musi obliczyć listę pikseli w celu przyciemnienia i interpolacji wyników wierzchołków (prawdopodobnie tylko współrzędne tekstury do przetwarzania obrazu)
- Wszystkie różne stany (test głębokości, test alfa, nożycowy, mieszanie) muszą być ustawione i zarządzane
Można argumentować, że inteligentne sterowniki mogą negować wszystkie wspomniane wcześniej zalety wydajności. Miałbyś rację. Taki sterownik może stwierdzić, że renderujesz quad pełnoekranowy bez testowania głębokości itp., I skonfigurować „szybką ścieżkę”, która pomija wszelkie bezużyteczne prace obsługiwane w celu obsługi modułów cieniujących piksele. Nie zdziwiłbym się, gdyby niektórzy kierowcy zrobili to w celu przyspieszenia przetwarzania końcowego w niektórych grach AAA dla ich konkretnych układów GPU. Możesz oczywiście zapomnieć o takim leczeniu, jeśli nie pracujesz nad grą AAA.
Sterownik nie może jednak znaleźć lepszych możliwości równoległości oferowanych przez potok shadera obliczeniowego. Weź klasyczny przykład filtra gaussowskiego. Korzystając z shaderów obliczeniowych, możesz zrobić coś takiego (oddzielając filtr lub nie):
- Dla każdej grupy roboczej podziel próbkowanie obrazu źródłowego na wielkość grupy roboczej i zapisz wyniki w pamięci wspólnej grupy.
- Oblicz dane wyjściowe filtra, korzystając z przykładowych wyników zapisanych we wspólnej pamięci.
- Napisz do tekstury wyjściowej
Krok 1 jest tutaj kluczem. W wersji cieniowania pikseli obraz źródłowy jest próbkowany wiele razy na piksel. W wersji modułu obliczeniowego każdy tekst źródłowy jest odczytywany tylko raz w grupie roboczej. Odczyty tekstur zwykle używają pamięci podręcznej opartej na kafelkach, ale ta pamięć podręczna jest nadal znacznie wolniejsza niż pamięć współdzielona.
Filtr gaussowski jest jednym z prostszych przykładów. Inne algorytmy filtrowania oferują inne możliwości udostępniania wyników pośrednich wewnątrz grup roboczych przy użyciu pamięci współdzielonej.
Jest jednak pewien haczyk. Shadery obliczeniowe wymagają wyraźnych barier pamięci, aby zsynchronizować swoje dane wyjściowe. Istnieje również mniej zabezpieczeń chroniących przed błędnym dostępem do pamięci. Dla programistów z dobrą znajomością programowania równoległego shadery obliczeniowe oferują znacznie większą elastyczność. Ta elastyczność oznacza jednak, że łatwiej jest również traktować shadery obliczeniowe jak zwykły kod C ++ i pisać wolny lub niepoprawny kod.
Referencje
- Strona wiki OpenGL Compute Shaders
- DirectCompute: Optymalizacje i najlepsze praktyki, Eric Young, NVIDIA Corporation, 2010 [pdf]
- Efficient Compute Shader Proramming, Bill Bilodeau, AMD, 2011? [pps]
- DirectCompute do gier - doładuj swój silnik za pomocą Shaderów obliczeniowych, Layla Mah i Stephan Hodes, AMD, 2013, [pps]
- Compute Shader Optimizations for AMD GPU: Parallel Reduction, Wolfgang Engel, 2014