WebGPU Grundlagen: Erste Schritte jenseits von WebGL
AI generated
JS
() =>
JavaScript · Grafik · GPU-Programmierung
WebGPU Grundlagen
Erste Schritte jenseits von WebGL

WebGPU ist die moderne Antwort des Webs auf direkte GPU-Programmierung und löst schrittweise WebGL als Standard für aufwendige Grafik- und Berechnungsaufgaben im Browser ab. Wer die Grundkonzepte einmal verstanden hat, dem eröffnet sich Zugriff auf Compute-Shader, effizientere Speicherverwaltung und eine deutlich modernere API.

17 Min. Lesezeit GPUDevice WGSL Compute Shader

1. Warum WebGL an seine Grenzen stößt

WebGL basiert auf OpenGL ES, einer API, die für die Grafik-Hardware und Programmiermodelle des frühen 2010er-Jahrzehnts konzipiert wurde. Moderne GPUs arbeiten heute mit deutlich anderen Konzepten wie expliziten Command-Buffern, Pipeline-Objekten und paralleler Befehlserstellung, die WebGL nur umständlich oder gar nicht abbilden kann.

Das äußert sich in der Praxis als CPU-Overhead: WebGL-Aufrufe wie gl.drawArrays() lösen intern viel Validierungs- und Zustandsverwaltungs-Arbeit im Treiber aus, was bei komplexen Szenen mit vielen Draw-Calls zum Flaschenhals wird. WebGPU wurde von Grund auf für moderne Grafik-APIs wie Vulkan, Metal und Direct3D 12 konzipiert und übernimmt deren effizientere Grundprinzipien. Dazu zählt auch eine deutlich striktere Validierung zur Erstellungszeit von Pipelines und Ressourcen, statt Fehler erst mitten im Rendering an versteckter Stelle auszulösen, was Fehlersuche in komplexen Anwendungen spürbar erleichtert.

2. Das GPUDevice als zentraler Einstiegspunkt

Der Einstieg in WebGPU beginnt mit dem Anfordern eines GPUAdapter über navigator.gpu.requestAdapter(), der die verfügbare GPU-Hardware repräsentiert. Aus diesem Adapter lässt sich anschließend ein GPUDevice anfordern, die eigentliche Schnittstelle, über die sämtliche weiteren Operationen laufen. Sowohl Adapter- als auch Device-Anforderung erfolgen asynchron und können mit optionalen Feature- und Limit-Anforderungen parametrisiert werden, sodass eine Anwendung gezielt nur die Fähigkeiten anfragt, die sie tatsächlich benötigt.

Das Device kapselt Ressourcen wie Puffer, Texturen und Pipelines und stellt Queues bereit, über die Befehle an die GPU gesendet werden. Anders als bei WebGL, wo ein globaler Kontext implizit Zustand hält, ist bei WebGPU jede Ressource explizit erzeugt und referenziert, was Fehlerquellen reduziert und Optimierungen durch den Browser erleichtert.


async function initWebGPU() {
  if (!navigator.gpu) {
    throw new Error('WebGPU wird von diesem Browser nicht unterstützt');
  }

  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) {
    throw new Error('Kein geeigneter GPU-Adapter gefunden');
  }

  const device = await adapter.requestDevice();
  const canvas = document.querySelector('#gpu-canvas');
  const context = canvas.getContext('webgpu');

  context.configure({
    device,
    format: navigator.gpu.getPreferredCanvasFormat(),
  });

  return { device, context };
}

3. Command-Buffer und explizite Befehlserstellung

Statt Befehle einzeln und sofort an die GPU zu senden, sammelt WebGPU sie in einem GPUCommandEncoder, der Zeichnen-, Compute- und Kopieroperationen aufzeichnet. Am Ende wird daraus ein GPUCommandBuffer erzeugt, der gebündelt über eine GPUQueue zur Ausführung eingereicht wird.

Dieses Modell erlaubt es der Anwendung, Befehle auf mehreren CPU-Threads parallel vorzubereiten, bevor sie gesammelt eingereicht werden, ein Muster, das aus nativer Grafikprogrammierung stammt und in WebGL schlicht nicht existierte. Für typische Web-Anwendungen bedeutet das vor allem: weniger CPU-Zeit pro Frame und mehr Vorhersagbarkeit bei komplexen Szenen. Selbst wenn eine konkrete Anwendung nicht aktiv mehrere Threads für die Befehlserstellung nutzt, profitiert sie allein durch das Batching-Prinzip von weniger einzelnen Treiber-Aufrufen pro Frame.


function renderFrame(device, context, pipeline) {
  const encoder = device.createCommandEncoder();
  const textureView = context.getCurrentTexture().createView();

  const renderPass = encoder.beginRenderPass({
    colorAttachments: [{
      view: textureView,
      clearValue: { r: 0.05, g: 0.05, b: 0.08, a: 1 },
      loadOp: 'clear',
      storeOp: 'store',
    }],
  });

  renderPass.setPipeline(pipeline);
  renderPass.draw(3);
  renderPass.end();

  device.queue.submit([encoder.finish()]);
}

4. Shader in WGSL schreiben

WebGPU nutzt mit WGSL, der WebGPU Shading Language, eine eigene, textbasierte Sprache für Vertex-, Fragment- und Compute-Shader, anstelle des von WebGL bekannten GLSL. WGSL wurde speziell für Sicherheit und einfache Übersetzung in die Shader-Sprachen der zugrunde liegenden nativen APIs entworfen, was Browsern hilft, konsistentes Verhalten über Plattformen hinweg zu garantieren.

Syntaktisch erinnert WGSL an eine Mischung aus Rust und C, mit expliziten Typannotationen und klar definierten Ein- und Ausgabestrukturen für jede Shader-Stufe. Wer bereits GLSL kennt, findet sich schnell zurecht, muss sich aber an die strengeren Typregeln und die explizite Struktur-Deklaration gewöhnen. Viele Tooling-Anbieter, darunter Editor-Erweiterungen für Syntax-Highlighting und Linting, unterstützen WGSL inzwischen ebenfalls, was den Einstieg gegenüber den Anfangstagen der Sprache spürbar erleichtert hat.


const shaderCode = `
@vertex
fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {
  var positions = array<vec2f, 3>(
    vec2f( 0.0,  0.5),
    vec2f(-0.5, -0.5),
    vec2f( 0.5, -0.5)
  );
  return vec4f(positions[index], 0.0, 1.0);
}

@fragment
fn fs_main() -> @location(0) vec4f {
  return vec4f(0.2, 0.7, 1.0, 1.0);
}
`;

const shaderModule = device.createShaderModule({ code: shaderCode });

5. Compute-Shader für Berechnungen jenseits von Grafik

Neben klassischem Grafik-Rendering bietet WebGPU vollwertige Compute-Shader, mit denen sich beliebige parallele Berechnungen direkt auf der GPU ausführen lassen, etwa Physik-Simulationen, Bildverarbeitung oder sogar kleinere Machine-Learning-Inferenzen. WebGL kannte diese Fähigkeit praktisch nicht, Umwege über Texturen als Datenspeicher waren nötig.

Ein Compute-Shader wird ähnlich wie ein Grafik-Shader in WGSL geschrieben, arbeitet aber auf Work-Groups aus parallelen Threads, die über @compute @workgroup_size(...) deklariert werden. Ergebnisse landen in Storage-Buffern, die anschließend zurück zur CPU gelesen oder direkt für weiteres Rendering wiederverwendet werden können.

6. Puffer, Bindgroups und explizite Speicherverwaltung

Anders als bei WebGL, wo Uniforms und Attribute über implizite Locations angesprochen werden, verwaltet WebGPU Speicherressourcen über explizite GPUBuffer-Objekte und GPUBindGroup-Strukturen, die genau festlegen, welche Ressource an welcher Bindungsstelle im Shader verfügbar ist. Ein Buffer wird mit einer festen Größe und einem Nutzungsflag wie GPUBufferUsage.VERTEX oder GPUBufferUsage.STORAGE erzeugt, wodurch der Browser und letztlich der Treiber von Anfang an wissen, wie die Ressource optimal im Speicher platziert werden sollte.

Diese Explizitheit wirkt zunächst nach mehr Boilerplate-Code als das freizügigere WebGL-Modell, zahlt sich aber bei größeren Anwendungen aus: Bindgroups lassen sich vorab erstellen und zwischen Frames wiederverwenden, statt bei jedem Zeichenaufruf erneut Zustand zu setzen. Für Teams, die von WebGL kommen, ist dieser Schritt meist die größte gedankliche Umstellung, weil er verlangt, Ressourcen-Layout und Bindungs-Struktur schon beim Pipeline-Entwurf festzulegen, statt sie beiläufig zur Laufzeit zusammenzustecken.

7. Wann sich der Umstieg von WebGL lohnt

Für einfache 2D-Visualisierungen, kleine 3D-Szenen oder Projekte mit begrenztem Zeitbudget bleibt WebGL oft die pragmatischere Wahl, da die Toolchain reifer ist und deutlich mehr Bibliotheken, Tutorials und fertige Beispiele existieren. WebGPU lohnt sich vor allem dort, wo CPU-Overhead durch viele Draw-Calls zum echten Problem wird oder wo Compute-Shader gebraucht werden.

Auch Projekte, die stark auf GPU-basierte Physik, Partikel-Simulationen mit hunderttausenden Elementen oder eigene Rendering-Pipelines mit modernen Techniken wie Deferred Shading setzen, profitieren deutlich stärker von WebGPU als von WebGL. Die Entscheidung sollte an der tatsächlichen Flaschenhals-Analyse hängen, nicht an reiner Neugier auf die neue API. Ein pragmatischer Zwischenweg besteht darin, bestehende WebGL-Anwendungen zunächst unverändert zu lassen und WebGPU gezielt für einzelne, neue Feature-Bereiche einzusetzen, etwa eine neu hinzukommende Partikel-Simulation, während der Rest der Anwendung unangetastet bleibt.

8. Aktueller Stand des Browser-Supports

Chrome und Edge unterstützen WebGPU inzwischen standardmäßig auf Desktop und zunehmend auch auf Android, Firefox bietet Unterstützung hinter Flags beziehungsweise in aktuellen Versionen bereits stabil, Safari zieht mit eigenem Implementierungsfortschritt nach. Der Support wächst spürbar schneller als seinerzeit bei WebGL in dessen Anfangsjahren.

Für produktive Anwendungen empfiehlt sich weiterhin eine Feature-Detection über if ('gpu' in navigator) kombiniert mit einem WebGL-Fallback-Pfad, insbesondere wenn die Zielgruppe ältere Geräte oder Browser umfasst. Bibliotheken wie Three.js bieten inzwischen experimentelle WebGPU-Renderer neben ihrem etablierten WebGL-Renderer an. Auch auf mobilen Geräten ist Vorsicht angebracht: Selbst wenn ein GPUAdapter erfolgreich angefordert werden kann, unterscheiden sich Limits wie maximale Buffergröße oder Anzahl gleichzeitiger Bindgroups teils deutlich zwischen Desktop- und Mobil-GPUs, weshalb sich ein Blick in adapter.limits vor dem produktiven Einsatz lohnt.

9. Ein minimales, vollständiges Dreieck

Um die Konzepte greifbar zu machen, folgt hier ein vollständiges, wenn auch minimales Beispiel, das die zuvor beschriebenen Bausteine, Device, Shader-Modul, Render-Pipeline und Command-Buffer, zu einem lauffähigen Dreieck zusammensetzt. Dieses Muster ist der klassische Einstiegspunkt in praktisch jedem WebGPU-Tutorial und bildet die Grundlage für komplexere Szenen.

Die folgende Tabelle fasst die wichtigsten konzeptionellen Unterschiede zwischen WebGL und WebGPU zusammen und hilft bei der Einschätzung, welche API für ein konkretes Projekt die passendere Grundlage ist.


async function createTrianglePipeline(device, format) {
  const module = device.createShaderModule({ code: shaderCode });

  return device.createRenderPipeline({
    layout: 'auto',
    vertex: { module, entryPoint: 'vs_main' },
    fragment: {
      module,
      entryPoint: 'fs_main',
      targets: [{ format }],
    },
    primitive: { topology: 'triangle-list' },
  });
}

const { device, context } = await initWebGPU();
const format = navigator.gpu.getPreferredCanvasFormat();
const pipeline = await createTrianglePipeline(device, format);
renderFrame(device, context, pipeline);
Aspekt WebGL WebGPU Auswirkung
Shader-Sprache GLSL WGSL Andere Syntax, striktere Typisierung
Befehlsmodell Sofortiger State-basierter Aufruf Command-Buffer, gebündelte Einreichung Weniger CPU-Overhead bei vielen Draw-Calls
Compute-Fähigkeit Nicht vorgesehen, Workarounds über Texturen Native Compute-Shader Direkte GPU-Berechnungen ohne Umwege
Browser-Reife Sehr breit, seit vielen Jahren stabil Wachsend, Chrome/Edge/Firefox voran WebGL bleibt sicherer Fallback

Mironsoft

Moderne Browser-APIs, Performance und wartbares JavaScript

JavaScript, das im echten Browser robust bleibt, nicht nur im Tutorial?

Wir prüfen bestehenden Frontend-Code auf veraltete Patterns, unnötige Bibliotheken und Performance-Fallen und ersetzen sie durch moderne, native Browser-APIs, die weniger Bundle-Gewicht und weniger Wartungslast bedeuten.

Code-Review

Veraltete Patterns, unnötige Dependencies und Memory Leaks systematisch aufspüren.

Performance-Optimierung

Bundle-Größe, Ladezeit und Runtime-Performance mit modernen APIs verbessern.

Modernisierung

Native Browser-APIs statt schwerer Bibliotheken gezielt einführen.

10. Zusammenfassung

WebGPU: Das Wichtigste auf einen Blick

Einstieg

GPUAdapter anfordern, daraus GPUDevice erzeugen, zentraler Ausgangspunkt aller Operationen.

Befehle

Command-Encoder sammelt Operationen, gebündelte Einreichung über die Queue reduziert CPU-Overhead.

Shader

WGSL ersetzt GLSL, unterstützt sowohl Grafik- als auch Compute-Shader.

Entscheidung

Umstieg lohnt sich bei vielen Draw-Calls oder echtem Compute-Bedarf, sonst bleibt WebGL pragmatisch.

11. FAQ: WebGPU: Das Wichtigste auf einen Blick

1Was ist der grundlegende Unterschied zwischen WebGL und WebGPU?
WebGPU basiert auf modernen nativen Grafik-APIs wie Vulkan und Metal und nutzt ein explizites Command-Buffer-Modell, während WebGL auf dem älteren OpenGL ES aufbaut.
2Was ist ein GPUDevice?
Das GPUDevice ist die zentrale Schnittstelle in WebGPU, über die Puffer, Texturen, Pipelines und Queues erzeugt und verwaltet werden.
3Was ist WGSL?
WGSL ist die WebGPU Shading Language, eine eigene, textbasierte Sprache für Vertex-, Fragment- und Compute-Shader, die GLSL bei WebGPU ersetzt.
4Kann WebGPU auch für reine Berechnungen ohne Grafik genutzt werden?
Ja, über Compute-Shader lassen sich parallele Berechnungen wie Physik-Simulationen oder Bildverarbeitung direkt auf der GPU ausführen.
5Warum ist der CPU-Overhead bei WebGPU geringer?
Weil Befehle gebündelt in Command-Buffern gesammelt und erst dann eingereicht werden, statt wie bei WebGL einzeln sofortige State-Validierung im Treiber auszulösen.
6Lohnt sich WebGPU für ein einfaches Projekt?
Meist nicht, für einfache 2D- oder kleine 3D-Visualisierungen bleibt WebGL wegen der reiferen Toolchain oft die pragmatischere Wahl.
7Wie ist der aktuelle Browser-Support für WebGPU?
Chrome und Edge unterstützen WebGPU standardmäßig, Firefox zunehmend stabil, Safari zieht mit eigenem Implementierungsfortschritt nach.
8Sollte man WebGL komplett ersetzen?
Nein, ein WebGL-Fallback bleibt sinnvoll, solange nicht alle Zielbrowser WebGPU zuverlässig unterstützen.
9Unterstützen bestehende 3D-Bibliotheken WebGPU?
Ja, Bibliotheken wie Three.js bieten inzwischen experimentelle WebGPU-Renderer parallel zu ihrem etablierten WebGL-Renderer an.
10Wie prüft man, ob ein Browser WebGPU unterstützt?
Mit einer einfachen Prüfung auf 'gpu' in navigator, bevor navigator.gpu.requestAdapter() aufgerufen wird.