Przekazywanie listy wartości do modułu cieniującego fragment

Chcę wysłać listę wartości do shadera fragmentów. Jest to prawdopodobnie duża (licząca kilka tysięcy pozycji) Lista pojedynczych precyzyjnych pływaków. Fragment shader wymaga losowego dostępu do tej listy i chcę odświeżyć wartości z procesora na każdej ramce.

Zastanawiam się, jak można to zrobić:

  1. Jako zmienna jednolita typu array ("uniform float x[10];"). Ale wydaje się, że są tu limity, na moim GPU wysyłanie ponad kilkuset wartości są bardzo wolne, a także musiałbym zakodować górny limit w shaderze, gdy wolałbym to zmienić w trybie runtime.

  2. Jako teksturę o wysokości 1 i szerokości mojej listy, następnie Odśwież dane za pomocą glCopyTexSubImage2D.

  3. Inne metody? Nie nadążam ostatnio za wszystkimi zmianami w specyfikacji GL, może jest jakaś inna metoda, która jest specjalnie zaprojektowana do tego celu?

 60
Author: Peter O., 2011-10-31

4 answers

Istnieją obecnie 4 sposoby, aby to zrobić: standardowe tekstury 1D, tekstury buforów,bufory jednolite i bufory pamięci cieniowania.

Tekstury 1D

Za pomocą tej metody, używasz glTex(Sub)Image1D, aby wypełnić teksturę 1D swoimi danymi. Ponieważ Twoje dane są tylko tablicą pływaków, Twój format obrazu powinien być GL_R32F. Następnie uzyskujesz do niego dostęp w shaderze za pomocą prostego wywołania texelFetch. texelFetch pobiera współrzędne texela (stąd nazwa) i wyłącza filtrowanie. Więc dostajesz dokładnie jeden texel.

Uwaga: texelFetch jest 3.0+. Jeśli chcesz użyć wcześniejszych wersji GL, musisz przekazać rozmiar do shadera i ręcznie znormalizować współrzędną tekstury.

Główne zalety to kompatybilność i zwartość. Będzie to działać na sprzęcie GL 2.1 (używając notacji). A ty nie masz do używania formatów GL_R32F; możesz użyć GL_R16F półfabrykatów. Lub GL_R8 Jeśli dane są uzasadnione dla znormalizowanego bajtu. Rozmiar może wiele znaczyć dla ogólnego wydajność.

Główną wadą jest ograniczenie rozmiaru. Tekstura 1D jest ograniczona do maksymalnego rozmiaru tekstury. Na GL 3.sprzęt klasy X, będzie to około 8192, ale gwarantowane jest nie mniej niż 4096.

Obiekty Bufora Jednolitego

Sposób, w jaki to działa, polega na tym, że deklarujesz jednolity blok w swoim shaderze:

layout(std140) uniform MyBlock
{
  float myDataArray[size];
};

Następnie uzyskujesz dostęp do tych danych w shaderze, tak jak tablica.

W kodzie C / C++ / etc tworzysz obiekt bufora i wypełnić go danymi zmiennoprzecinkowymi. Następnie można powiązać ten obiekt bufora z MyBlock jednolitym blokiem. więcej szczegółów można znaleźć tutaj.

Główną zaletą tej techniki jest szybkość i semantyka. Szybkość wynika z tego, jak implementacje traktują jednolite bufory w porównaniu z teksturami. Pobieranie tekstur to globalny dostęp do pamięci. Dostęp do bufora jednolitego na ogół nie jest; dane bufora jednolitego są zwykle ładowane do modułu cieniującego, gdy moduł cieniujący jest inicjowany po jego wykorzystanie w renderowaniu. Stamtąd jest to dostęp lokalny, który jest znacznie szybszy.

Semantycznie, to jest lepsze, ponieważ nie jest to tylko płaska tablica. Dla Twoich konkretnych potrzeb, jeśli wszystko czego potrzebujesz to float[], to nie ma znaczenia. Ale jeśli masz bardziej złożoną strukturę danych, semantyka może być ważna. Na przykład rozważmy tablicę świateł. Światła mają pozycję i kolor. Jeśli używasz tekstury, kod, aby uzyskać pozycję i kolor dla określonego światła, wygląda następująco to:

vec4 position = texelFetch(myDataArray, 2*index);
vec4 color = texelFetch(myDataArray, 2*index + 1);

Z jednorodnymi buforami, wygląda jak każdy inny jednorodny dostęp. Wymieniłeś członków, których można nazwać position i color. Więc wszystkie informacje semantyczne są tam; łatwiej jest zrozumieć, co się dzieje.

Istnieją również ograniczenia rozmiaru. OpenGL wymaga, aby implementacje dostarczały co najmniej 16 384 bajtów dla maksymalnego rozmiaru jednolitych bloków. Co oznacza, że dla macierzy float dostajesz tylko 4096 elementów. Zauważ jeszcze raz, że jest to minimum wymagane od implementacji; niektóre urządzenia mogą oferować znacznie większe bufory. AMD zapewnia na przykład 65 536 na sprzęcie klasy DX10.

Tekstury Bufora

To coś w rodzaju "super 1D texture". Efektywnie umożliwiają dostęp do obiektu bufora z jednostki tekstury . Choć są jednowymiarowe, nie są to Tekstury 1D.

Można używać tylko z GL 3.0 lub nowszych. I możesz uzyskać do nich dostęp tylko za pośrednictwem texelFetch funkcja.

Główną zaletą jest tutaj Rozmiar. Tekstury bufora mogą być na ogół dość gigantyczne. Podczas gdy specyfikacja jest ogólnie zachowawcza, wymagająca co najmniej 65 536 bajtów dla tekstur buforów, większość implementacji GL pozwala im mierzyć rozmiar mega bajtów. Rzeczywiście, zazwyczaj maksymalny rozmiar jest ograniczony przez dostępną pamięć GPU, a nie limity sprzętowe.

Również tekstury bufora są przechowywane w obiektach bufora, a nie w bardziej nieprzezroczystych obiektach tekstur, takich jak tekstury 1D. Oznacza to, że możesz użyć niektórych technik strumieniowania obiektów bufora, aby je zaktualizować.

Główną wadą jest tutaj wydajność, podobnie jak w przypadku tekstur 1D. Tekstury bufora prawdopodobnie nie będą wolniejsze niż tekstury 1D, ale nie będą tak szybkie jak UBOs. Jeśli wyciągasz z nich tylko jedną platformę, nie powinno to być problemem. Ale jeśli pobierasz z nich wiele danych, rozważ użycie UBO.

Obiekty Bufora Pamięci Shader

OpenGL 4.3 zapewnia inny sposób radzenia sobie z tym: bufory pamięci shader . Są one podobne do jednolitych buforów; można je określić za pomocą składni prawie identycznej do składni jednolitych bloków. Zasadnicza różnica polega na tym, że można do nich pisać. Oczywiście nie jest to przydatne dla Twoich potrzeb, ale są inne różnice.

Bufory pamięci Shader są, mówiąc koncepcyjnie, alternatywną formą tekstury bufora. Tak więc limity wielkości buforów pamięci shader są lot większy niż dla buforów jednolitych. Minimum OpenGL dla maksymalnego rozmiaru UBO wynosi 16KB. Maksymalna wielkość SSBO dla OpenGL wynosi 16MB . Więc jeśli masz sprzęt, są ciekawą alternatywą dla UBOs.

Tylko pamiętaj, aby zadeklarować je jako readonly, ponieważ nie piszesz do nich.

Potencjalną wadą jest tutaj znowu wydajność, w stosunku do UBOs. Ssbo działa jak operacja ładowania/przechowywania obrazupoprzez tekstury bufora. Zasadniczo jest to (bardzo ładne) cukrem składniowym wokół typu obrazu imageBuffer. W związku z tym odczyty z nich będą prawdopodobnie wykonywane z prędkością odczytów z readonly imageBuffer.

W tym momencie nie jest jasne, czy odczyt poprzez ładowanie/przechowywanie obrazów przez obrazy bufora jest szybszy czy wolniejszy niż tekstury bufora.

Innym potencjalnym problemem jest to, że musisz przestrzegać zasad dla Nie-synchronicznego dostępu do pamięci . Są one złożone i mogą bardzo łatwo potknąć się.

 118
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-18 14:46:00

To brzmi jak dobry przypadek użycia obiektów bufora tekstur . Nie mają one wiele wspólnego ze zwykłymi teksturami i zasadniczo pozwalają na dostęp do pamięci obiektu bufora w shaderze jako prostej tablicy liniowej. Są one podobne do tekstur 1D, ale nie są filtrowane i dostępne tylko przez indeks całkowity, co brzmi jak to, co musisz zrobić, gdy nazwiesz ją listą wartości. Obsługują również znacznie większe rozmiary niż tekstury 1D. Do aktualizacji można następnie użyć standardowego metody obiektów bufora (glBufferData, glMapBuffer, ...).

Ale z drugiej strony wymagają sprzętu GL3 / DX10 do użycia i nawet zostały wykonane rdzeń w OpenGL 3.1, myślę. Jeśli twój sprzęt/sterownik go nie obsługuje, Twoje 2. rozwiązanie byłoby metodą wyboru, ale raczej używaj tekstury 1D niż tekstury 2D szerokości x 1). W tym przypadku możesz również użyć nie płaskiej tekstury 2D i magii indeksu, aby obsługiwać listy większe niż maksymalny rozmiar tekstury.

Ale bufory tekstur są idealne pasuje do twojego problemu. Aby uzyskać dokładniejszy wgląd, możesz również zajrzeć do odpowiedniej specyfikacji rozszerzenia .

EDIT: w odpowiedzi na komentarz Nicol o obiektach bufora jednorodnego , Możesz również zajrzeć tutaj dla małego porównania tych dwóch. Nadal mam tendencję do TBO, ale naprawdę nie mogę wyjaśnić dlaczego, tylko dlatego, że widzę to lepiej pasuje koncepcyjnie. Ale może Nicol da anwerowi więcej wglądu w sprawę.

 6
Author: Christian Rau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-31 17:57:11

Jednym ze sposobów byłoby użycie jednolitych tablic, jak wspominasz. Innym sposobem jest użycie "tekstury" 1D. Poszukaj GL_TEXTURE_1D i glTexImage1D. ja osobiście wolę ten sposób, ponieważ nie trzeba twardo kodować rozmiaru tablicy w kodzie shadera, jak powiedziałeś, a opengl ma już wbudowane funkcje do przesyłania / dostępu do danych 1D na GPU.

 5
Author: edvaldig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-31 14:57:42

Powiedziałbym, że prawdopodobnie nie numer 1.. masz ograniczoną liczbę rejestrów dla mundurów shaderów, które różnią się w zależności od karty. Możesz zapytać GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, aby sprawdzić swój limit. Na nowszych kartach działa w tysiącach, np. Quadro FX 5500 ma podobno 2048. (http://www.nvnews.net/vbulletin/showthread.php?t=85925). to zależy na jakim sprzęcie chcesz go uruchomić, i jakie inne mundury możesz chcieć wysłać do shadera też.

Liczba 2 może być wykonana do pracy w zależności od twoich wymagań. Przepraszam za niejasności tutaj, mam nadzieję, że ktoś inny może dać ci bardziej precyzyjną odpowiedź, ale musisz być wyraźny w ile połączeń tekstury wykonujesz w starszych kartach modelu shader. Zależy to również od tego, ile tekstur chcesz odczytać na fragment, prawdopodobnie nie chciałbyś próbować odczytać 1000 elementów na fragment, ponownie, w zależności od modelu shadera i wymagań wydajności. Możesz spakować wartości do rgba tekstury, dając 4 odczyty na wywołanie tekstury, ale z losowym dostępem jako wymogiem, może to nie pomóc.

Nie jestem pewien co do numeru 3, ale sugerowałbym, żeby spojrzeć na UAV (widoki dostępu nieuporządkowanego), chociaż myślę, że to tylko DirectX, bez przyzwoitego odpowiednika openGL. Myślę, że jest rozszerzenie nVidia dla openGL, ale znowu ograniczasz się do dość ścisłego minimum specyfikacji.

Jest mało prawdopodobne, aby przekazanie 1000 elementów danych do modułu cieniowania fragmentów było najlepszym rozwiązaniem Twojego problemu.. być może, jeśli podasz więcej szczegółów na temat tego, co próbujesz osiągnąć, możesz uzyskać alternatywne sugestie?

 1
Author: Hybrid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-31 15:06:57