Zapowiadamy ComputeSharp 2.0 — z łatwością uruchamiaj C# na GPU dzięki DirectX 12 i D2D1!

Nov 30 2022
Po ponad 2 latach pracy z radością ogłaszam, że ComputeSharp 2.0 jest już dostępny! Jest to kompletne przepisanie biblioteki, obejmujące obsługę obu platform .

Po ponad 2 latach pracy z radością ogłaszam, że ComputeSharp 2.0 jest już dostępny! Jest to kompletne przepisanie biblioteki, obejmujące obsługę zarówno .NET Standard 2.0, jak i .NET 6, nowe generatory źródeł całkowicie zastępujące generowanie kodu w czasie wykonywania, wiele nowych interfejsów API, zupełnie nową obsługę modułów cieniujących D2D1, kontrolki XAML dla UWP i WinUI 3 i wiele więcej!

Wiele wysiłku włożono w to wydanie, a zakres całego projektu znacznie się rozrósł w czasie, o wiele bardziej, niż pierwotnie przewidywałem. W tym poście na blogu chcę przedstawić bibliotekę tym, którzy jej nie znają, zwrócić uwagę na niektóre nowe funkcje i pokazać kilka przykładów tego, co można z nią zrobić.

Dla tych, którzy wolą formę wideo, tutaj jest link do mojego wykładu na temat ComputeSharp na konferencji .NET Conf 2022 , a dla tych, którzy chcą po prostu przejść bezpośrednio do kodu, tutaj jest również link do repozytorium GitHub . Wskoczmy do ComputeSharp 2.0!

Zacznijmy od prezentacji biblioteki: ComputeSharp to biblioteka .NET do równoległego uruchamiania kodu C# na GPU za pośrednictwem DX12, D2D1 i dynamicznie generowanych shaderów obliczeniowych HLSL, w celu uczynienia obliczeń GPU łatwymi w użyciu dla wszystkich. programiści NET! Pozwala pisać moduły cieniujące (tj. kod, który może działać na GPU) przy użyciu C#, a następnie wykonuje całą ciężką pracę, aby przekształcić ten kod i faktycznie wysłać go do GPU używanego na twoim komputerze. Można to wykorzystać do przyspieszenia sprzętowego wszelkiego rodzaju obciążeń — przetwarzania obrazu, obliczeń równoległych, generowania fantazyjnych animacji, co tylko chcesz! A wszystko to bez konieczności zagłębiania się w interfejsy API specyficzne dla platformy, które są niezbędne do wykonania któregokolwiek z tych zadań, ani uczenia się, jak właściwie pisać HLSL, który jest językiem programowania używanym przez DirectX do shaderów.

Praktycznym przykładem jest przykładowa aplikacja ComputeSharp w akcji:

Tutaj możesz zobaczyć, jak jeden z formantów XAML, który ComputeSharp oferuje dla UWP i WinUI 3, jest używany do renderowania kilku animowanych shaderów obliczeniowych, w tym przypadku uruchamianych w aplikacji WinUI 3. Cały kod źródłowy jest dostępny na GitHub , a wszystkie shadery, które widzisz, zostały przeniesione z shaderów GLSL z shadertoy do samego C# i wszystkie są obsługiwane przez ComputeSharp i działają przy użyciu shaderów obliczeniowych DirectX 12.

A oto kolejny przykład działania ComputeSharp:

Paint.NET 5.0 z efektem rozmycia bokeh opartym na technologii ComputeSharp

Spędziłem kilka ostatnich miesięcy, dodając obsługę Direct2D do ComputeSharp, która jest teraz podstawowym składnikiem nadchodzącej wersji Paint.NET 5.0 . ComputeSharp.D2D1 (wersja biblioteki dla shaderów pikseli D2D1) jest używana do implementacji dziesiątek efektów graficznych w aplikacji, które są w całości napisane w języku C#, a GPU jest przyspieszane dzięki mocy D2D. Pozwoliło to Rickowi Brewsterowi , twórcy Paint.NET, znacznie zwiększyć liczbę efektów, które mógł uruchomić na GPU, jednocześnie znacznie zmniejszając złożoność aplikacji w odniesieniu do efektów GPU D2D, ponieważ nie było już konieczność ręcznego pisania efektów w HLSL i kompilowania ich z niestandardowym etapem kompilacji — ComputeSharp wykonuje całą tę pracę za kulisami!

Ciekawostka — rozmycie bokeh pokazane na powyższym zrzucie ekranu Paint.NET to port Ricka mojej próbki rozmycia bokeh ComputeSharp.D2D1 , który sam jest portem mojego przykładu rozmycia bokeh ComputeSharp , który jest portem z wersji C#, którą napisałem dla ImageSharp , który jest portem wersji Pythona, którą profesor Mike Pound napisał do swojego filmu Computerphile na temat efektów rozmycia bokeh . Idź to sprawdzić!

Jest to również dostępne dla autorów wtyczek, co oznacza, że ​​każdy może teraz używać ComputeSharp.D2D1 do pisania w pełni dostosowanych i akcelerowanych przez GPU efektów, które można zintegrować z Paint.NET. To, co kiedyś wymagało znajomości elementów wewnętrznych HLSL i D2D, jest teraz łatwo dostępne dla wszystkich programistów języka C# dzięki zaledwie kilku linijkom kodu i przyjaznym interfejsom API wysokiego poziomu.

Jeśli chcesz zobaczyć, jak wygląda shader ComputeSharp, oto bardzo prosty: shader hello world, który po prostu wyświetla animowany gradient kolorów, który zmienia się w czasie. Oto kod C# dla niego:

Moduł obliczeniowy DirectX 12 napisany w C# z ComputeSharp

Możesz zobaczyć, jak moduł cieniujący jest po prostu strukturą typu C#, która przechwytuje pewne wartości (w tym przypadku liczbę zmiennoprzecinkową wskazującą upływający czas), a następnie wykonuje obliczenia w celu obliczenia koloru do wyświetlenia na wyjściu. To jest cały moduł cieniujący, który ComputeSharp zajmie się przetwarzaniem w czasie kompilacji za pomocą generatora źródłowego, aby przekonwertować go na HLSL, a także wygenerować dodatkowy kod wspierający jego wykonanie.

Oto jak wygląda wynikowy kod HLSL dla tego modułu cieniującego:

Transpilowany moduł cieniujący HLSL dla typu C#

Łatwo zobaczyć niektóre z przekształceń dokonanych przez ComputeSharp za kulisami: moduł cieniujący ma teraz stały bufor, który jest używany do kierowania przechwyconych wartości do GPU, ma adnotacje rozmiaru wątku do kontrolowania wysyłania modułu cieniującego, automatyczne sprawdzanie ograniczeń został dodany, a moduł cieniujący również niejawnie przechwytuje teksturę wyjściową i przypisuje do niej wynikowy piksel. Jako programista C# korzystający z ComputeSharp nigdy nie będziesz musiał się o to martwić: generator źródeł w pakiecie z ComputeSharp zajmie się całym tym ciężkim dźwiganiem, podczas gdy Ty będziesz mógł pisać C# i czerpać korzyści z IntelliSense i wszystkich wspaniałych narzędzi, które oferuje C#.

Chcę również pokazać kilka kompleksowych przykładów tego, jak wygląda uruchamianie kodu na GPU przy użyciu ComputeSharp. Widzieliśmy już, jak napisać moduł cieniujący, ale możesz się zastanawiać, czy wysyłanie go jest rzeczywiście trudne, czy nie. tak nie jest! Jest na to kilka sposobów, więc możesz wybrać ten, który najlepiej odpowiada Twoim potrzebom.

ComputeSharp oferuje tryb szybkiego wykonywania i tryb wykresu obliczeniowego. Ten pierwszy jest szczególnie przydatny, jeśli dopiero zaczynasz pracę z biblioteką, chcesz wypróbować różne rzeczy lub po prostu uruchomić prosty skrypt i nie chcesz martwić się konfiguracją bardziej złożonego potoku wykonawczego. Oto jak możesz uruchomić bardzo prosty testowy moduł cieniujący:

Chętny tryb wykonywania w ComputeSharp

To wszystko, co musisz zrobić, aby uruchomić kod C# na GPU! Możesz zobaczyć, jak mamy bardzo prosty moduł cieniujący, który po prostu mnoży wszystkie wartości w teksturze przez 2, a następnie przesyła je do zapisywalnego bufora, który przydzieliliśmy na GPU z pewnymi losowymi wartościami. Po wysłaniu modułu cieniującego możemy również skopiować ten bufor GPU z powrotem do procesora, abyśmy mogli odczytać jego zawartość. ComputeSharp udostępnia dziesiątki bardzo łatwych w użyciu interfejsów API do wykonywania typowych operacji, takich jak te, które można dostosować do wszystkich scenariuszy, od prostych minimalnych skryptów po bardziej skomplikowane architektury.

Przyjrzyjmy się również przykładowi wykorzystującemu tryb wykresu obliczeniowego:

Tryb wykresu obliczeniowego w ComputeSharp

Ten tryb jest idealny, jeśli chcesz stworzyć w pełni dostosowany potok wykonywania, który może również wycisnąć największą wydajność z GPU. W tym trybie najpierw tworzymy ComputeContext , który jest obiektem umożliwiającym kolejkowanie operacji na GPU. Następnie tworzymy nasz potok (w tym operacje takie jak wysyłanie shaderów, kontrolowanie przejść w celu synchronizacji dostępu do współdzielonych zasobów i nie tylko), a na koniec wysyłamy to na GPU, gdy wychodzimy poza zakres kontekstu. Możesz także zobaczyć, jak tutaj wykorzystujemy składnię await using , aby wykonać całą tę pracę asynchronicznie na GPU, więc nie blokujemy bieżącego wątku, dopóki GPU nie zakończy przetwarzania naszego żądania.

Co z obsługą XAML? ComputeSharp oferuje dwa elementy sterujące ( ComputeShaderPanel i AnimatedComputeShaderPanel ), które bardzo ułatwiają integrację efektów opartych na technologii ComputeSharp w aplikacji UWP lub WinUI 3: wystarczy umieścić element sterujący XAML w widoku, przypisać do niego obiekt modułu cieniującego i dobrze iść!

ComputeShaderPanel używany w widoku XAML

Oto jak można łatwo zadeklarować ComputeShaderPanel w widoku. Następnie potrzebuje tylko instancji IShaderRunner , aby wiedzieć, jak renderować każdą klatkę. W prostych przypadkach, na przykład gdy chcemy wyświetlić tylko animację modułu cieniującego, wbudowany typ ShaderRunner<T> jest więcej niż wystarczający:

ShaderRunner przypisany do ComputeShaderPanel

I to wszystko! Panel użyje modułu cieniującego do narysowania każdej klatki i zajmie się całą konfiguracją potrzebną do faktycznego wyświetlenia każdej wygenerowanej klatki. W przypadku bardziej zaawansowanych scenariuszy możesz także samodzielnie zaimplementować moduł cieniujący, który zapewnia pełną kontrolę nad taktowaniem klatek, wykresem obliczeniowym używanym do tworzenia każdego obrazu do wyświetlenia i nie tylko!

Na koniec chcę również pokazać przykład shadera pikseli D2D1. Są to shadery, których używa Paint.NET, i są one również częścią infrastruktury, nad którą obecnie pracuję, aby umożliwić ich używanie w aplikacjach UWP i WinUI 3 za pośrednictwem biblioteki Win2D (możesz zobaczyć moją propozycję tutaj również ). Różnią się znacznie od shaderów obliczeniowych DirectX 12, ale dzięki infrastrukturze ComputeSharp są równie łatwe do wdrożenia, a także można je uruchomić bez większego wysiłku, wykorzystując wbudowany efekt shaderów pikseli, który zawiera ComputeSharp.D2D1.

Oto przykład efektu pikselizacji z Paint.NET:

Pikselowy moduł cieniujący pikseli D2D1 z Paint.NET

Zauważysz, że istnieją pewne różnice między tym modułem cieniującym a modułem cieniującym DX12 pokazanym powyżej, ale doświadczenie programisty jest w większości takie samo: wystarczy napisać typ C#, użyć dostępnych projekcji HLSL i interfejsów API, które zawiera ComputeSharp, pozwolić IntelliSense poprowadzić Ciebie, a następnie pozwól ComputeSharp wykonać całą resztę pracy za Ciebie w czasie kompilacji.

Przyjrzyjmy się także transpilowanemu kodowi tego modułu cieniującego:

Efekt pikselowy przeniesiony do HLSL

Po raz kolejny typ C# został odpowiednio przekształcony, aby był zgodny z niezbędnymi konwencjami HLSL, w tym przypadku specjalnie dla shaderów pikseli D2D1. Zadeklarowaliśmy pola do odwzorowania na różne przechwycone wartości w języku C#, wszystkie elementy wewnętrzne zostały przekazane do ich odpowiedników HLSL, zaktualizowano składnię modułu cieniującego i wiele więcej. Wszystkie te moduły cieniujące, podobnie jak te DX12, można również prekompilować w czasie kompilacji, co oznacza, że ​​Twoje aplikacje mogą mieć niesamowitą wydajność uruchamiania i nie trzeba w ogóle wywoływać ani dołączać kompilatora modułów cieniujących po wdrożeniu!

W ComputeSharp dostępnych jest znacznie więcej, a to jest dopiero pierwsza stabilna wersja po przepisaniu — jest mnóstwo nowych funkcji i ulepszeń, nad którymi już pracuję i które planuję uwzględnić w przyszłych wydaniach! Zapraszamy do sprawdzenia repozytorium na GitHub , wypróbowania biblioteki ( możesz ją znaleźć na NuGet! ) i pozostawienia opinii i pomysłów!

A jeśli go użyjesz i zbudujesz coś — podziel się tym! Uwielbiam oglądać wszystkie fajne rzeczy, które ludzie mogą zbudować za pomocą ComputeSharp!

Miłego kodowania!