Zajmuję się więc tworzeniem DirectX, a dokładniej korzystam z SharpDX w .NET (ale rozwiązania API DirectX / C ++ mają zastosowanie). Szukam najszybszego sposobu renderowania linii w rzucie ortogonalnym (np. Symulowanie rysowania linii 2D dla aplikacji naukowych) przy użyciu DirectX.
Zrzut ekranu z rodzajami wykresów, które próbuję wyrenderować:
Często zdarza się, że tego rodzaju wykresy mają linie z milionami segmentów, o zmiennej grubości, z lub bez antyaliasingu na linię (lub włączanie / wyłączanie pełnoekranowego AA). Muszę bardzo często aktualizować wierzchołki dla linii (np. 20 razy / sekundę) i odciążyć jak najwięcej do GPU.
Do tej pory próbowałem:
- Renderowanie oprogramowania, np. GDI +, faktycznie nie jest złą wydajnością, ale oczywiście ma duży wpływ na procesor
- Direct2D API - wolniejszy niż GDI, szczególnie przy włączonym wygładzaniu krawędzi
- Direct3D10 przy użyciu tej metody do emulacji AA przy użyciu kolorów wierzchołków i teselacji po stronie procesora. Również powoli (profilowałem to i 80% czasu spędzam na obliczaniu pozycji wierzchołków)
W przypadku trzeciej metody używam buforów wierzchołków do wysyłania paska trójkąta do GPU i aktualizuję co 200 ms nowymi wierzchołkami. Otrzymuję częstotliwość odświeżania około 5 klatek na sekundę dla 100 000 segmentów linii. Idealnie potrzebuję milionów!
Teraz myślę, że najszybszym sposobem byłoby wykonanie teselacji na GPU, np. W module Geometry Shader. Mógłbym wysłać wierzchołki jako listę linii lub spakować teksturę i rozpakować w module cieniującym geometrię, aby utworzyć quady. Lub po prostu wyślij nieprzetworzone punkty do modułu cieniującego piksele i zaimplementuj rysowanie linii Bresenhama całkowicie w module cieniującym piksele. Mój HLSL jest zardzewiały, moduł cieniujący 2 z 2006 roku, więc nie wiem o szalonych rzeczach, które mogą zrobić współczesne procesory graficzne.
Pytanie brzmi zatem: - czy ktoś zrobił to wcześniej i czy masz jakieś sugestie do wypróbowania? - Czy masz jakieś sugestie dotyczące poprawy wydajności dzięki szybkiej aktualizacji geometrii (np. Nowa lista wierzchołków co 20 ms)?
AKTUALIZACJA 21 stycznia
Od tamtej pory zaimplementowałem metodę (3) powyżej za pomocą shaderów geometrii za pomocą LineStrip i dynamicznych buforów wierzchołków. Teraz dostaję 100 FPS przy 100 000 punktów i 10 FPS przy 1 000 000 punktów. To ogromna poprawa, ale teraz mam ograniczony współczynnik wypełnienia i ograniczoną moc obliczeniową, więc pomyślałem o innych technikach / pomysłach.
- Co z wystąpieniem sprzętowym geometrii segmentu linii?
- Co z Sprite Batch?
- Co z innymi metodami (Pixel Shader)?
- Czy mogę efektywnie zrezygnować z GPU lub procesora?
Twoje uwagi i sugestie są mile widziane!
Odpowiedzi:
Jeśli zamierzasz renderować
Y = f(X)
tylko wykresy, sugeruję wypróbowanie następującej metody.Dane krzywej są przekazywane jako dane tekstury , dzięki czemu są trwałe i umożliwiają na przykład częściowe aktualizacje
glTexSubImage2D
. Jeśli potrzebujesz przewijania, możesz nawet zaimplementować bufor okrągły i zaktualizować tylko kilka wartości na ramkę. Każda krzywa jest renderowana jako quad pełnoekranowy i cała praca jest wykonywana przez moduł cieniujący piksele.Zawartość tekstury jednoskładnikowej może wyglądać następująco:
Działanie modułu cieniującego piksele jest następujące:
41.3
to wybrać40
,41
,42
i43
.X,Y
pary na miejsce na ekranieMożesz zastąpić 4 większymi wartościami w zależności od potencjalnego poziomu powiększenia.
Napisałem bardzo szybki i brudny moduł cieniujący GLSL implementujący tę funkcję . Mogę dodać wersję HLSL później, ale powinieneś być w stanie przekonwertować ją bez większego wysiłku. Wynik można zobaczyć poniżej, przy różnych rozmiarach linii i gęstości danych:
Jedną wyraźną zaletą jest to, że ilość przesyłanych danych jest bardzo mała, a liczba wezwań jest tylko jedna.
źródło
Był rozdział o Klejnotach GPU na temat renderowania linii antyaliasingowych: Szybkie wstępnie filtrowane linie . Podstawową ideą jest renderowanie każdego segmentu linii jako kwadratu i obliczanie, przy każdym pikselu, funkcji Gaussa odległości środka piksela od segmentu linii.
Oznacza to narysowanie każdego segmentu linii na wykresie jako osobnego kwadratu, ale w D3D11 można z pewnością użyć modułu cieniującego geometrię i / lub instancji do wygenerowania kwadratów, zmniejszając ilość danych do przesłania do GPU do samych punktów danych sami. Prawdopodobnie ustawiłbym punkty danych jako StructuredBuffer do odczytu przez moduł cieniujący wierzchołek / geometrię, a następnie wykonałem wywołanie rysujące określające liczbę segmentów do narysowania. Nie byłoby żadnych rzeczywistych buforów wierzchołków; moduł cieniujący wierzchołek użyłby po prostu SV_VertexID lub SV_InstanceID, aby określić, które punkty danych należy obejrzeć.
źródło
@ Dr.ABT - Przepraszamy za to pytanie, a nie odpowiedź. Nie znalazłem sposobu, aby o to zapytać w odpowiedzi Sama Hocevara powyżej.
Czy mógłbyś podzielić się szczegółowymi informacjami na temat tego, jak w końcu zaimplementowałeś linie na swoim wykresie? Mam taką samą potrzebę aplikacji WPF. Z góry dziękuję za wszelkie szczegóły lub kod, który możesz udostępnić na ten temat.
źródło