Jako programista WebGL możesz być podekscytowany, ale i trochę przerażony, że zaczynasz używać WebGPU, czyli następcy WebGL, który wprowadza do sieci nowoczesne interfejsy API grafiki.
Warto wiedzieć, że WebGL i WebGPU mają wiele wspólnych podstawowych koncepcji. Oba interfejsy API umożliwiają uruchamianie na GPU małych programów zwanych shaderami. WebGL obsługuje shadery wierzchołkowe i fragmentowe, a WebGPU także shadery obliczeniowe. WebGL używa języka cieniowania OpenGL (GLSL), a WebGPU – języka cieniowania WebGPU (WGSL). Chociaż są to dwa różne języki, ich podstawy są w większości takie same.
Z tego względu w tym artykule omówiliśmy różnice między WebGL a WebGPU, aby ułatwić Ci rozpoczęcie pracy.
Stan globalny
WebGL ma wiele stanów globalnych. Niektóre ustawienia mają zastosowanie do wszystkich operacji renderowania, na przykład do określania, które tekstury i bufory są powiązane. Ten stan globalny ustawiasz, wywołując różne funkcje interfejsu API. Stan ten pozostaje w zbiorze do czasu jego zmiany. Stan globalny w WebGL jest głównym źródłem błędów, ponieważ łatwo zapomnieć o zmianie ustawienia globalnego. Dodatkowo stan globalny utrudnia udostępnianie kodu, ponieważ deweloperzy muszą uważać, aby nie zmieniać go przypadkowo w sposób, który wpłynie na inne części kodu.
WebGPU to interfejs API bezstanowy, który nie obsługuje stanu globalnego. Zamiast tego używa ona koncepcji rury, aby ująć wszystkie stany renderowania, które były globalne w WebGL. Proces zawiera informacje o tym, jakiego rodzaju mieszania, topologii i atrybutów użyć. Pliku nie można zmienić. Jeśli chcesz zmienić niektóre ustawienia, musisz utworzyć inny potok. WebGPU używa też koderów poleceń do grupowania poleceń i wykonywaniu ich w kolejności, w jakiej zostały zarejestrowane. Jest to przydatne w przypadku mapowania cieni, gdy na przykład w ramach pojedynczego przejścia nad obiektami aplikacja może zarejestrować wiele strumieni poleceń, po jednym dla mapy cieni każdego światła.
Podsumowując, globalny model stanowy WebGL sprawił, że tworzenie solidnych, kompozycyjnych bibliotek i aplikacji stało się trudniejsze i bardziej delikatne, dlatego WebGPU znacznie zmniejszył ilość stanu, który deweloperzy muszą śledzić przy jednoczesnym wysyłaniu poleceń do GPU.
Nie synchronizuj
Na kartach graficznych wysyłanie poleceń i czekanie na nie w sposób synchroniczny jest zwykle nieefektywne, ponieważ może to spowodować wyczyszczenie potoku i wywołać bąble. Dotyczy to zwłaszcza technologii WebGPU i WebGL, które wykorzystują architekturę wieloprocesową ze sterownikiem GPU działającym w oddzielnym procesie niż JavaScript.
Na przykład w WebGL wywołanie gl.getError()
wymaga synchronicznego IPC z procesu JavaScript do procesu GPU i z powrotem. Może to spowodować powstanie bąbla po stronie procesora, ponieważ 2 procesy będą ze sobą współpracować.
Aby uniknąć tych dymków, procesor WebGPU został zaprojektowany w taki sposób, aby był całkowicie asynchroniczny. Model błędów i wszystkie inne operacje są wykonywane asynchronicznie. Jeśli na przykład utworzysz teksturę, operacja wydaje się natychmiastowa, nawet jeśli tekstura jest w rzeczywistości błędną. Błąd możesz wykryć tylko asynchronicznie. Dzięki temu nie ma potrzeby tworzenia okna komunikacji między procesami, a aplikacje działają niezawodnie.
Programy do cieniowania
Moduły do cieniowania Compute to programy, które działają na procesorze graficznym, i wykonują ogólne obliczenia. Są dostępne tylko w WebGPU, a nie WebGL.
W przeciwieństwie do shaderów wierzchołkowych i fragmentowych nie są one ograniczone do przetwarzania grafiki. Można ich używać do wykonywania różnych zadań, takich jak uczenie maszynowe, symulacja fizyki i obliczenia naukowe. Shadery obliczeniowe są wykonywane równolegle przez setki, a nawet tysiące wątków, co sprawia, że są bardzo wydajne w przypadku przetwarzania dużych zbiorów danych. Więcej informacji o przetwarzaniu na procesorze graficznym znajdziesz w tym obszernym artykule o WebGPU.
Przetwarzanie klatki wideo
Przetwarzanie klatek wideo za pomocą JavaScript i WebAssembly ma pewne wady: koszt kopiowania danych z pamięci GPU do pamięci procesora oraz ograniczone równoległość, której można osiągnąć za pomocą procesów roboczych i wątków procesora. WebGPU nie ma tych ograniczeń, dzięki czemu doskonale nadaje się do przetwarzania klatek wideo dzięki ścisłej integracji z interfejsem WebCodecs API.
Poniższy fragment kodu pokazuje, jak zaimportować element VideoFrame jako zewnętrzną teksturę w WebGPU i go przetworzyć. Możesz wypróbować tę wersję demonstracyjną.
// Init WebGPU device and pipeline...
// Configure canvas context...
// Feed camera stream to video...
(function render() {
const videoFrame = new VideoFrame(video);
applyFilter(videoFrame);
requestAnimationFrame(render);
})();
function applyFilter(videoFrame) {
const texture = device.importExternalTexture({ source: videoFrame });
const bindgroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: texture }],
});
// Finally, submit commands to GPU
}
Domyślne przenoszenie aplikacji
WebGPU wymaga przesłania żądania limits
. Domyślnie funkcja requestDevice()
zwraca obiekt GPUDevice, który może nie odpowiadać możliwościom sprzętowym fizycznego urządzenia, ale raczej rozsądnemu i najniższemu wspólnemu mianownikowi wszystkich GPU. Wymaganie od deweloperów, aby prosili o limity urządzeń, zapewnia, że aplikacje będą działać na jak największej liczbie urządzeń.
Obsługa Canvas
WebGL automatycznie zarządza płótnem po utworzeniu kontekstu WebGL i podaniu atrybutów kontekstu takich jak alpha, antialias, ColorSpace, depth, preserveDrawingBuffer czy stencil.
Z drugiej strony WebGPU wymaga samodzielnego zarządzania płótnem. Na przykład, aby uzyskać wygładzanie krawędzi w WebGPU, utwórz teksturę wielopróbkową i wyrenderuj ją. Następnie przekształcasz teksturę wielopróbkową w zwykłą i rysujesz ją na płótnie. Ręczne zarządzanie pozwala eksportować pliki do dowolnej liczby obszarów roboczych z jednego obiektu GPUDevice. W przypadku WebGL można utworzyć tylko 1 kontekst na 1 płótno.
Obejrzyj prezentację WebGPU dotyczące wielu kanałów.
Pamiętaj, że przeglądarki mają obecnie limit liczby kanw WebGL na stronę. W momencie pisania tego artykułu Chrome i Safari mogą jednocześnie używać tylko 16 płót WebGL; Firefox może utworzyć ich do 200. Z drugiej strony nie ma limitu liczby kanw WebGPU na stronę.
Przydatne komunikaty o błędach
WebGPU udostępnia stos wywołań dla każdej wiadomości zwracanej przez interfejs API. Oznacza to, że możesz szybko sprawdzić, gdzie w kodzie wystąpił błąd, co jest przydatne podczas debugowania i naprawiania błędów.
Oprócz informacji o zbiorze wywołań komunikaty o błędach WebGPU są też łatwe do zrozumienia i działania. Komunikaty o błędach zwykle zawierają opis błędu i sugestie dotyczące jego naprawienia.
WebGPU umożliwia również udostępnienie niestandardowego obiektu label
dla każdego obiektu WebGPU. Ta etykieta jest następnie używana przez przeglądarkę w wiadomościach GPUError, ostrzeżeniach w konsoli i narzędziach dla programistów przeglądarki.
Od nazw do indeksów
W WebGL wiele elementów jest połączonych nazwami. Na przykład możesz zadeklarować w GLSL jedną zmienną o nazwie myUniform
i ustalić jej lokalizację za pomocą atrybutu gl.getUniformLocation(program, 'myUniform')
. Jest to przydatne, ponieważ w przypadku błędnego wpisania nazwy zmiennej jednorodnej pojawi się błąd.
Z drugiej strony w WebGPU wszystko jest połączone za pomocą przesunięcia bajtów lub indeksu (często nazywanego lokalizacją). Twoim obowiązkiem jest dbanie o to, żeby lokalizacje kodu w WGSL i JavaScript były zsynchronizowane.
Generowanie map MIP-ów
W WebGL możesz utworzyć teksturę na poziomie 0 mip, a potem wywołać funkcję gl.generateMipmap()
. WebGL wygeneruje wtedy wszystkie pozostałe poziomy mip.
W WebGPU musisz samodzielnie wygenerować mipmapy. Nie ma wbudowanej funkcji, która umożliwiałaby to zrobienie. Aby dowiedzieć się więcej o tej decyzji, zapoznaj się z dyskusją na temat specyfikacji. Aby wygenerować mapy MIP, możesz użyć przydatnych bibliotek takich jak webgpu-utils lub dowiedzieć się, jak zrobić to samodzielnie.
bufory i tekstury pamięci masowej,
Bufory jednolite są obsługiwane zarówno przez WebGL, jak i WebGPU i umożliwiają przekazywanie do shaderów stałych parametrów o ograniczonym rozmiarze. Bufory pamięci masowej, które wyglądają jak jednolite bufory, są obsługiwane tylko przez WebGPU i są bardziej wydajne i elastyczne niż jednolite bufory.
Dane buforów pamięci przekazywane do shaderów mogą być znacznie większe niż bufory jednolite. Według specyfikacji rozmiar powiązań buforów jednolitych może wynosić do 64 KB (patrz
maxUniformBufferBindingSize
) , ale maksymalny rozmiar powiązania bufora pamięci masowej w WebGPU (patrzmaxStorageBufferBindingSize
) wynosi co najmniej 128 MB.Bufory pamięci masowej są dostępne do zapisu i obsługują niektóre operacje niepodzielne, a jednolite bufory są tylko do odczytu. Umożliwia to wdrażanie nowych klas algorytmów.
Powiązania buforów pamięci obsługują tablice o rozmiarze zdefiniowanym w czasie wykonywania, co umożliwia większą elastyczność algorytmów, podczas gdy rozmiary tablic jednolitych buforów muszą być podawane w shaderze.
Tekstury pamięci masowej są obsługiwane tylko w WebGPU i są dla tekstur tym, czym dla buforów jednolitych są bufory pamięci masowej. Są bardziej elastyczne niż zwykłe tekstury i obsługują zapisy z losowym dostępem (a w przyszłości także odczyty).
Zmiany buforów i tekstury
W WebGL możesz utworzyć bufor lub teksturę, a potem w dowolnym momencie zmienić ich rozmiar za pomocą funkcji gl.bufferData()
i gl.texImage2D()
.
W WebGPU bufory i tekstury są niezmienne. Oznacza to, że po utworzeniu nie można zmienić ich rozmiaru, sposobu użycia ani formatu. Możesz tylko zmieniać ich zawartość.
Różnice w konwencji dotyczącej przestrzeni
W WebGL obszar klipu mieści się w zakresie od -1 do 1. W WebGPU przestrzeń przycięcia Z mieści się w zakresie od 0 do 1. Oznacza to, że obiekty o wartości z równej 0 są najbliżej kamery, a obiekty o wartości z równej 1 są najdalej.
WebGL używa konwencji OpenGL, w której oś Y jest skierowana w górę, a oś Z – w kierunku widza. WebGPU używa konwencji Metal, w której oś Y jest skierowana w dół, a oś Z jest skierowana poza ekran. Zwróć uwagę, że kierunek osi Y jest ustawiony w dół we współrzędnych bufora ramki, współrzędnej widocznego obszaru i współrzędnej fragmentu/piksela. W przestrzeni klipu oś Y jest nadal skierowana w górę, tak jak w WebGL.
Podziękowania
Dziękujemy Corentin Wallez, Gregg Tavares, Stephen White, Ken Russell i Rachel Andrew za sprawdzenie tego artykułu.
Aby dowiedzieć się więcej o różnicach między WebGPU a WebGL, warto też odwiedzić stronę WebGPUFundamentals.org.