[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/de/docs/Web/API/WebGPU_API [Back]  [Original]

WebGPU API - Web-APIs | MDN

Dieser Inhalt wurde automatisch aus dem Englischen bersetzt, und kann Fehler enthalten. Erfahre mehr ber dieses Experiment.

View in English Always switch to English

WebGPU API

Eingeschrnkt verfgbar

Diese Funktion ist nicht Baseline, da sie in einigen der am weitesten verbreiteten Browser nicht funktioniert.

Want more browser support for this feature? Tell us why.

Sicherer Kontext: Diese Funktion ist nur in sicheren Kontexten (HTTPS) in einigen oder allen untersttzenden Browsern verfgbar.

Die WebGPU API ermglicht es Webentwicklern, die GPU (Graphics Processing Unit) des zugrunde liegenden Systems zu nutzen, um Hochleistungsberechnungen durchzufhren und komplexe Bilder zu zeichnen, die im Browser gerendert werden knnen.

WebGPU ist der Nachfolger von WebGL und bietet eine bessere Kompatibilitt mit modernen GPUs, Untersttzung fr allgemeine GPU-Berechnungen, schnellere Operationen und Zugriff auf umfassendere GPU-Funktionen.

In diesem Artikel

Konzepte und Verwendung

Es ist fair zu sagen, dass WebGL das Web in Bezug auf grafische Fhigkeiten revolutioniert hat, nachdem es erstmals um 2011 erschienen ist. WebGL ist ein JavaScript-Port der Grafikbibliothek OpenGL ES 2.0, der es Webseiten ermglicht, Renderberechnungen direkt an die GPU des Gerts zu bergeben, um diese mit sehr hoher Geschwindigkeit zu verarbeiten und das Ergebnis in ein <canvas>-Element zu rendern.

WebGL und die zum Schreiben von WebGL-Shader-Code verwendete Sprache GLSL sind komplex, weshalb mehrere WebGL-Bibliotheken erstellt wurden, um das Schreiben von WebGL-Anwendungen zu erleichtern: Beliebte Beispiele sind Three.js, Babylon.js und PlayCanvas. Entwickler haben diese Tools genutzt, um immersive webbasierte 3D-Spiele, Musikvideos, Schulungs- und Modellierungstools, VR- und AR-Erlebnisse und mehr zu erstellen.

WebGL hat jedoch einige grundlegende Probleme, die behoben werden mussten:

  • Seit der Verffentlichung von WebGL ist eine neue Generation von nativen GPU-APIs erschienen die bekanntesten sind Microsofts Direct3D 12, Apples Metal und The Khronos Group's Vulkan die eine Flle neuer Funktionen bieten. Es gibt keine weiteren Updates fr OpenGL (und damit WebGL) mehr, sodass es keine dieser neuen Funktionen erhlt. Fr WebGPU werden hingegen in Zukunft neue Funktionen hinzugefgt.
  • WebGL basiert vollstndig auf dem Anwendungsfall des Zeichnens von Grafiken und deren Rendering in einem Canvas. Es kann allgemeine GPU-Berechnungen (GPGPU) nicht gut verarbeiten. GPGPU-Berechnungen werden fr viele verschiedene Anwendungsflle immer wichtiger, zum Beispiel fr solche, die auf maschinellen Lernmodellen basieren.
  • 3D-Grafikanwendungen werden immer anspruchsvoller, sowohl hinsichtlich der Anzahl der gleichzeitig zu rendernden Objekte als auch der Nutzung neuer Rendering-Funktionen.

WebGPU geht auf diese Probleme ein und bietet eine aktualisierte, allgemeine Architektur, die mit modernen GPU-APIs kompatibel und "weborientierter" ist. Es untersttzt sowohl die grafische Darstellung als auch GPGPU-Berechnungen als erstklassige Funktionalitt. Das Rendering einzelner Objekte ist auf der CPU-Seite erheblich gnstiger, und es werden moderne GPU-Rendering-Funktionen untersttzt, wie partikelbasierte Rechnungen und Nachbearbeitungsfilter wie Farbeffekte, Schrfen und Tiefenschrfesimulation. Zudem kann es aufwndige Berechnungen wie Culling und Transformation von skinierten Modellen direkt auf der GPU durchfhren.

Allgemeines Modell

Es gibt mehrere Abstraktionsebenen zwischen einem Gerte-GPU und einem Webbrowser, der die WebGPU API ausfhrt. Es ist ntzlich, diese zu verstehen, wenn Sie beginnen, WebGPU zu lernen:

Ein grundlegendes Stapeldiagramm zeigt die Position der verschiedenen Elemente einer WebGPU-Architektur auf einem Gert [Ein grundlegendes Stapeldiagramm zeigt die Position der verschiedenen Elemente einer WebGPU-Architektur auf einem Gert]

  • Physische Gerte verfgen ber GPUs. Die meisten Gerte haben nur eine GPU, einige jedoch mehrere. Verschiedene GPU-Typen sind verfgbar:

    • Integrierte GPUs, die sich auf derselben Platine wie die CPU befinden und deren Speicher teilen.
    • Diskrete GPUs, die sich auf ihrer eigenen Platine befinden, getrennt von der CPU.
    • Software-"GPUs", die auf der CPU implementiert sind.

    Hinweis: Das obige Diagramm geht von einem Gert mit nur einer GPU aus.

  • Eine native GPU-API, die Teil des Betriebssystems ist (zum Beispiel Metal auf macOS), ist eine Programmierschnittstelle, die es nativen Anwendungen ermglicht, die Fhigkeiten der GPU zu nutzen. API-Befehle werden ber einen Treiber an die GPU gesendet (und Antworten empfangen). Es ist mglich, dass ein System mehrere native OS-APIs und Treiber zur Kommunikation mit der GPU zur Verfgung hat, obwohl das obige Diagramm von einem Gert mit nur einem nativen API/Treiber ausgeht.

  • Eine WebGPU-Implementierung des Browsers verarbeitet die Kommunikation mit der GPU ber einen nativen GPU-API-Treiber. Ein WebGPU-Adapter reprsentiert in Ihrem Code effektiv eine physische GPU und einen Treiber, die im zugrunde liegenden System verfgbar sind.

  • Ein logisches Gert ist eine Abstraktion, ber die eine einzelne Web-App auf GPU-Funktionen auf compartmentalisierte Weise zugreifen kann. Logische Gerte mssen Multiplexfhigkeiten bereitstellen. Die GPU eines physischen Gerts wird von vielen Anwendungen und Prozessen gleichzeitig genutzt, mglicherweise auch von vielen Web-Apps. Jede Web-App muss aus Sicherheits- und Logikgrnden in der Lage sein, isoliert auf WebGPU zuzugreifen.

Zugriff auf ein Gert

Ein logisches Gert dargestellt durch eine GPUDevice-Objektinstanz ist die Grundlage, von der aus eine Web-App auf alle WebGPU-Funktionen zugreift. Der Zugriff auf ein Gert erfolgt wie folgt:

  1. Die Navigator.gpu-Eigenschaft (oder WorkerNavigator.gpu, wenn Sie WebGPU-Funktionalitt aus einem Worker heraus nutzen) gibt das GPU-Objekt fr den aktuellen Kontext zurck.
  2. Sie greifen ber die Methode GPU.requestAdapter() auf einen Adapter zu. Diese Methode akzeptiert ein optionales Einstellungsobjekt, das es Ihnen ermglicht, beispielsweise einen Kompatibilittsmodus, einen Hochleistungs- oder Energiesparadapter anzufordern. Wenn dies nicht enthalten ist, stellt das Gert Zugriff auf den Standardadapter bereit, der fr die meisten Zwecke ausreichend ist.
  3. Ein Gert kann ber GPUAdapter.requestDevice() angefordert werden. Diese Methode akzeptiert ebenfalls ein options-Objekt (als Descriptor bezeichnet), das verwendet werden kann, um die genauen Funktionen und Einschrnkungen anzugeben, die das logische Gert haben soll. Wenn dies nicht enthalten ist, verfgt das bereitgestellte Gert ber eine vernnftige allgemeine Spezifikation, die fr die meisten Zwecke ausreicht.

Zusammen mit einigen Funktionsberprfungen knnte der obige Prozess wie folgt erreicht werden:

js
async function init() {
  if (!navigator.gpu) {
    throw Error("WebGPU not supported.");
  }

  let adapter;
  try {
    adapter = await navigator.gpu.requestAdapter();
  } catch (error) {
    console.error(error);
  }
  if (!adapter) {
    throw Error("Couldn't request WebGPU adapter.");
  }

  const device = await adapter.requestDevice();

  // 
}

WebGPU-Kompatibilittsmodus

Standardmig untersttzt ein GPUAdapter alle Core-WebGPU-Funktionen und -Grenzen, was es Anwendungen ermglicht, Gerte mit modernen Plattform-Grafik-APIs zu untersttzen. Dies wird als "core" WebGPU bezeichnet.

Es ist mglich, WebGPU in den "Kompatibilittsmodus" zu versetzen, was bedeutet, dass der GPUAdapter eine eingeschrnkte Teilmenge der WebGPU API untersttzt, die in lteren Grafik-APIs wie OpenGL ES 3.1 und Direct3D 11 ausfhrbar ist. Dies wird erreicht, indem ein featureLevel Wert von compatibility in Ihrem Call an GPU.requestAdapter() angegeben wird:

js
const adapter = await navigator.gpu.requestAdapter({
  featureLevel: "compatibility",
});

Die genauen Einschrnkungen des Kompatibilittsmodus sind im WebGPU-Kompatibilittsmodus detailliert beschrieben. Eingeschrnkte Anwendungen sind dennoch gltige Anwendungen des WebGPU-Kerns, da sie eine Teilmenge des Kern-WebGPU untersttzen und daher auf allen Browsern mit WebGPU-Kernuntersttzung laufen, selbst wenn sie den Kompatibilittsmodus nicht explizit untersttzen.

Ein GPUAdapter oder GPUDevice, das den WebGPU-Kern untersttzt, verfgt ber das core-features-and-limits-Feature (siehe GPUSupportedFeatures). Um zu testen, ob eine WebGPU-App im Kern- oder Kompatibilittsmodus ist, berprfen Sie, ob das core-features-and-limits-Feature untersttzt wird. Beispielsweise:

js
const isCore = device.features.has("core-features-and-limits");

Siehe auch Verwendung des Kompatibilittsmodus nur bei Bedarf.

Pipelines und Shader: WebGPU-App-Struktur

Eine Pipeline ist eine logische Struktur, die programmierbare Stufen enthlt, die abgeschlossen werden mssen, um die Arbeit Ihres Programms zu erledigen. WebGPU ist derzeit in der Lage, zwei Arten von Pipelines zu verwalten:

  • Eine Render-Pipeline rendert Grafiken, typischerweise in ein <canvas>-Element, kann aber auch Grafiken offscreen rendern. Sie hat zwei Hauptstufen:

    • Eine Vertex-Stufe, in der ein Vertex-Shader Positionierungsdaten empfngt, die in die GPU eingespeist werden, und diese verwendet, um eine Reihe von Vertices im 3D-Raum zu positionieren, indem spezifizierte Effekte wie Rotation, Translation oder Perspektive angewendet werden. Die Vertices werden dann zu Primitiven wie Dreiecken zusammengefgt (das grundlegende Baustein gerenderter Grafiken) und von der GPU rasterisiert, um herauszufinden, welche Pixel jedes abdecken sollte.

    • Eine Fragment-Stufe, in der ein Fragment-Shader die Farbe fr jedes von den durch den Vertex-Shader produzierten Primitiven abgedeckte Pixel berechnet. Diese Berechnungen verwenden hufig Eingaben wie Bilder (in Form von Texturen), die Oberflchendetails bereitstellen, sowie die Position und Farbe virtueller Lichtquellen.

  • Eine Computepipeline ist fr allgemeine Berechnungen. Eine Computepipeline enthlt eine einzelne Computestufe, in der ein Compute-Shader allgemeine Daten verarbeitet, diese parallel ber eine bestimmte Anzahl von Arbeitsgruppen bearbeitet und dann das Ergebnis in einem oder mehreren Buffers zurckgibt. Die Buffers knnen Daten jeder Art enthalten.

Die oben genannten Shader sind Sammlungen von Anweisungen, die von der GPU verarbeitet werden. WebGPU-Shader werden in einer Low-Level-Rust-hnlichen Sprache namens WebGPU Shading Language (WGSL) geschrieben.

Es gibt mehrere verschiedene Mglichkeiten, wie Sie eine WebGPU-App architektonisch gestalten knnten, aber der Prozess wird wahrscheinlich die folgenden Schritte enthalten:

  1. Shader-Module erstellen: Schreiben Sie Ihren Shadercode in WGSL und packen Sie ihn in ein oder mehrere Shader-Module.
  2. Canvas-Kontext abrufen und konfigurieren: Holen Sie sich den webgpu-Kontext eines <canvas>-Elements und konfigurieren Sie es so, dass es Informationen darber empfngt, welche Grafiken von Ihrem GPU-Logikgert gerendert werden sollen. Dieser Schritt ist nicht erforderlich, wenn Ihre App keine grafische Ausgabe hat, wie z.B. eine, die nur Computepipelines verwendet.
  3. Ressourcen mit Ihren Daten erstellen: Die Daten, die von Ihren Pipelines verarbeitet werden sollen, mssen in GPU-Buffern oder -Texturen gespeichert werden, auf die von Ihrer App zugegriffen werden kann.
  4. Pipelines erstellen: Definieren Sie Pipeline-Deskriptoren, die die gewnschten Pipelines im Detail beschreiben, einschlielich der erforderlichen Datenstruktur, Bindungen, Shader und Ressourcenlayouts, und erstellen Sie dann Pipelines daraus. Unsere einfachen Demos enthalten nur eine einzelne Pipeline, aber nicht-triviale Apps enthalten normalerweise mehrere Pipelines fr verschiedene Zwecke.
  5. Einen Rechen-/Rendering-Pass ausfhren: Dies umfasst eine Reihe von Unterpunkten:
    1. Erstellen Sie einen Befehls-Encoder, der eine Reihe von Befehlen kodieren kann, die an die GPU zur Ausfhrung bergeben werden sollen.
    2. Erstellen Sie ein Pass-Encoder-Objekt, auf dem Rechen-/Render-Befehle ausgefhrt werden.
    3. Fhren Sie Befehle aus, um anzugeben, welche Pipelines verwendet werden sollen, welche Buffer(s) die erforderlichen Daten enthalten sollen, wie viele Zeichenoperationen ausgefhrt werden sollen (im Fall von Render-Pipelines) usw.
    4. Finalisieren Sie die Befehlsliste und kapseln Sie sie in einen Befehls-Buffer ein.
    5. bermitteln Sie den Befehls-Buffer an die GPU ber die Befehlsschlange des logischen Gerts.

In den folgenden Abschnitten werden wir ein einfaches Render-Pipeline-Demo untersuchen, um Ihnen die Mglichkeit zu geben, zu erkunden, was es erfordert. Spter werden wir auch ein einfaches Compute-Pipeline-Demo betrachten, bei dem wir sehen, wie es sich von der Render-Pipeline unterscheidet.

Grundlegende Render-Pipeline

In unserem einfachen Render-Demo geben wir einem <canvas>-Element einen festen blauen Hintergrund und zeichnen ein Dreieck darauf.

Shader-Module erstellen

Wir verwenden den folgenden Shadercode. Die Vertex-Shader-Stufe (@vertex-Block) akzeptiert einen Datenblock, der eine Position und eine Farbe enthlt, positioniert den Vertex gem der angegebenen Position, interpoliert die Farbe und bergibt die Daten an die Fragment-Shader-Stufe. Die Fragment-Shader-Stufe (@fragment-Block) akzeptiert die Daten von der Vertex-Shader-Stufe und frbt den Vertex gem der angegebenen Farbe.

js
const shaders = `
struct VertexOut {
  @builtin(position) position : vec4f,
  @location(0) color : vec4f
}

@vertex
fn vertex_main(@location(0) position: vec4f,
               @location(1) color: vec4f) -> VertexOut
{
  var output : VertexOut;
  output.position = position;
  output.color = color;
  return output;
}

@fragment
fn fragment_main(fragData: VertexOut) -> @location(0) vec4f
{
  return fragData.color;
}
`;

Hinweis: In unseren Demos speichern wir unseren Shadercode in einem Template Literal, aber Sie knnen ihn berall speichern, von wo aus er leicht als Text abgerufen und in Ihr WebGPU-Programm eingespeist werden kann. Zum Beispiel ist es eine weitere hufige Praxis, Shader innerhalb eines <script>-Elements zu speichern und den Inhalt mit Node.textContent abzurufen. Der korrekte Mimetyp fr WGSL ist text/wgsl.

Um Ihren Shadercode fr WebGPU verfgbar zu machen, mssen Sie ihn in ein GPUShaderModule einfgen, indem Sie einen Aufruf an GPUDevice.createShaderModule() durchfhren und Ihren Shadercode als Eigenschaft innerhalb eines Deskriptorobjekts bergeben. Zum Beispiel:

js
const shaderModule = device.createShaderModule({
  code: shaders,
});

Canvas-Kontext abrufen und konfigurieren

In einer Render-Pipeline mssen wir einen Ort angeben, an den die Grafiken gerendert werden sollen. In diesem Fall erhalten wir einen Verweis auf ein sichtbares <canvas>-Element und rufen HTMLCanvasElement.getContext() mit einem Parameter von webgpu auf, um dessen GPU-Kontext (eine GPUCanvasContext-Instanz) zurckzugeben.

Von dort aus konfigurieren wir den Kontext mit einem Aufruf von GPUCanvasContext.configure(), indem wir ein Optionsobjekt bergeben, das das GPUDevice enthlt, von dem die Rendering-Informationen kommen werden, das Format, das die Texturen haben werden, und den Alpha-Modus, der beim Rendern halbtransparenter Texturen verwendet werden soll.

js
const canvas = document.querySelector("#gpuCanvas");
const context = canvas.getContext("webgpu");

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

Hinweis: Die beste Praxis zur Bestimmung des Texturformats besteht darin, die Methode GPU.getPreferredCanvasFormat() zu verwenden; diese whlt das effizienteste Format (entweder bgra8unorm oder rgba8unorm) fr das Gert des Nutzers.

Ein Buffer erstellen und unsere Dreiecksdaten darin schreiben

Als nchstes stellen wir unserem WebGPU-Programm unsere Daten in einer Form zur Verfgung, die es verwenden kann. Unsere Daten werden zunchst in einem Float32Array bereitgestellt, das 8 Datenpunkte fr jeden Dreiecksvertex enthlt X, Y, Z, W fr die Position und R, G, B, A fr die Farbe.

js
const vertices = new Float32Array([
  0.0, 0.6, 0, 1, 1, 0, 0, 1, -0.5, -0.6, 0, 1, 0, 1, 0, 1, 0.5, -0.6, 0, 1, 0,
  0, 1, 1,
]);

Wir haben jedoch ein Problem hier. Wir mssen unsere Daten in einen GPUBuffer bekommen. Hinter den Kulissen wird diese Art von Buffer im Speicher gespeichert, der sehr eng mit den Kernen der GPU integriert ist, um die gewnschte Hochleistungsverarbeitung zu ermglichen. Als Nebeneffekt kann dieser Speicher nicht von Prozessen auf dem Host-System, wie dem Browser, zugegriffen werden.

Der GPUBuffer wird durch einen Aufruf an GPUDevice.createBuffer() erstellt. Wir geben ihm eine Gre, die der Lnge des vertices-Arrays entspricht, damit es alle Daten enthalten kann, und VERTEX- und COPY_DST-Nutzungsflags an, um anzugeben, dass der Buffer als Vertexbuffer und das Ziel von Kopiervorgngen verwendet wird.

js
const vertexBuffer = device.createBuffer({
  size: vertices.byteLength, // make it big enough to store vertices in
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});

Wir knnten das Einbringen unserer Daten in den GPUBuffer mit einer Mapping-Operation handhaben, wie wir es im Compute-Pipeline-Demo verwenden, um Daten von der GPU zurck zu JavaScript zu lesen. In diesem Fall verwenden wir jedoch die praktische GPUQueue.writeBuffer()-Convenience-Methode, die als Parameter den Buffer zum Schreiben, die Datenquelle, die geschrieben werden sollen, einen Offset-Wert fr jeden und die Gre der zu schreibenden Daten erhlt (wir haben die gesamte Lnge des Arrays angegeben). Der Browser arbeitet dann den effizientesten Weg aus, um das Schreiben der Daten zu erledigen.

js
device.queue.writeBuffer(vertexBuffer, 0, vertices, 0, vertices.length);

Die Render-Pipeline definieren und erstellen

Nachdem wir unsere Daten in einen Buffer bekommen haben, ist der nchste Teil der Einrichtung, unsere Pipeline tatschlich zu erstellen, damit sie zum Rendern verwendet werden kann.

Zunchst erstellen wir ein Objekt, das das erforderliche Layout unserer Vertexdaten beschreibt. Dies beschreibt perfekt, was wir zuvor in unserem vertices-Array und der Vertex-Shader-Stufe gesehen haben jeder Vertex hat Position-, und Farbdaten. Beide sind im float32x4-Format formatiert (was auf den WGSL vec4<f32>-Typ abbildet), und die Farbdaten beginnen mit einem Offset von 16 Bytes in jedem Vertex. arrayStride gibt die "Schrittweite" an, also die Anzahl der Bytes, die jeden Vertex ausmachen, und stepMode gibt an, dass die Daten pro Vertex abgerufen werden sollen.

js
const vertexBuffers = [
  {
    attributes: [
      {
        shaderLocation: 0, // position
        offset: 0,
        format: "float32x4",
      },
      {
        shaderLocation: 1, // color
        offset: 16,
        format: "float32x4",
      },
    ],
    arrayStride: 32,
    stepMode: "vertex",
  },
];

Als nchstes erstellen wir ein Deskriptorobjekt, das die Konfiguration unserer Render-Pipeline-Stufen festlegt. Fr beide Shader-Stufen spezifizieren wir das GPUShaderModule, in dem der relevante Code gefunden werden kann (shaderModule), und den Namen der Funktion, die als Einstiegspunkt fr jede Stufe dient.

Darber hinaus stellen wir im Fall der Vertex-Shader-Stufe unser vertexBuffers-Objekt bereit, um den erwarteten Zustand unserer Vertexdaten bereitzustellen. Und im Fall unserer Fragment-Shader-Stufe bereitstellen wir ein Array von Farbenzielzustnden, die das spezifizierte Rendering-Format angeben (dies entspricht dem in unserer Canvas-Kontext-Konfiguration frher festgelegten Format).

Wir spezifizieren auch ein primitive-Objekt, das in diesem Fall nur den Typ des Primitiven angibt, das wir zeichnen werden, und wir spezifizieren ein layout von auto. Die Eigenschaft layout definiert das Layout (Struktur, Zweck und Typ) aller GPU-Ressourcen (Buffers, Texturen usw.), die whrend der Ausfhrung der Pipeline verwendet werden. In komplexeren Apps wrde dies die Form eines GPUPipelineLayout-Objekts annehmen, das mit GPUDevice.createPipelineLayout() erstellt wurde (Sie knnen ein Beispiel in unserer Grundlegenden Compute-Pipeline sehen), das es der GPU ermglicht, die Pipeline vorab am effizientesten auszufhren. Wir geben jedoch den Wert auto an, der die Pipeline ein implizites Bindungslayout basierend auf jedem in den Shadercode definiertem Bindung zu erstellen.

js
const pipelineDescriptor = {
  vertex: {
    module: shaderModule,
    entryPoint: "vertex_main",
    buffers: vertexBuffers,
  },
  fragment: {
    module: shaderModule,
    entryPoint: "fragment_main",
    targets: [
      {
        format: navigator.gpu.getPreferredCanvasFormat(),
      },
    ],
  },
  primitive: {
    topology: "triangle-list",
  },
  layout: "auto",
};

Schlielich knnen wir eine GPURenderPipeline basierend auf unserem pipelineDescriptor-Objekt erstellen, indem wir es als Parameter an einen anruf an GPUDevice.createRenderPipeline() bergeben.

js
const renderPipeline = device.createRenderPipeline(pipelineDescriptor);

Einen Rendering-Pass ausfhren

Nun, da die gesamte Einrichtung abgeschlossen ist, knnen wir tatschlich einen Rendering-Pass ausfhren und etwas auf unser <canvas> zeichnen. Um irgendwelche Befehle zu kodieren, die spter an die GPU ausgegeben werden sollen, mssen Sie eine GPUCommandEncoder-Instanz erstellen, die durch einen Aufruf GPUDevice.createCommandEncoder() erfolgt.

js
const commandEncoder = device.createCommandEncoder();

Als nchstes starten wir den Rendering-Pass, indem wir eine GPURenderPassEncoder-Instanz mit einem Aufruf zu GPUCommandEncoder.beginRenderPass() erstellen. Diese Methode nimmt ein Deskriptionsobjekt als Parameter, dessen einzige obligatorische Eigenschaft ein colorAttachments-Array ist. In diesem Fall geben wir an:

  1. Eine Texturansicht, in die gerendert werden soll; wir erstellen eine neue Ansicht ber context.getCurrentTexture().createView() aus dem <canvas>.
  2. Dass die Ansicht auf eine bestimmte Farbe "geklrt" werden soll, sobald sie geladen wird und bevor irgendwelche Zeichnungen durchgefhrt werden. Das ist es, was den blauen Hintergrund hinter dem Dreieck verursacht.
  3. Dass der Wert des aktuellen Rendering-Pass fr diesen Farbanhang gespeichert werden soll.
js
const clearColor = { r: 0.0, g: 0.5, b: 1.0, a: 1.0 };

const renderPassDescriptor = {
  colorAttachments: [
    {
      clearValue: clearColor,
      loadOp: "clear",
      storeOp: "store",
      view: context.getCurrentTexture().createView(),
    },
  ],
};

const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);

Wir knnen jetzt Methoden des Rendering-Pass-Encoders aufrufen, um unser Dreieck zu zeichnen:

  1. GPURenderPassEncoder.setPipeline() wird aufgerufen, indem unser renderPipeline-Objekt als Parameter bergeben wird, um die Pipeline fr den Rendering-Pass festzulegen.
  2. GPURenderPassEncoder.setVertexBuffer() wird aufgerufen, indem unser vertexBuffer-Objekt als Parameter bergeben wird, um als Datenquelle zu dienen, die an die Pipeline bergeben werden soll, um zu rendern. Der erste Parameter ist der Slot, um den Vertexbuffer festzulegen, und ist ein Verweis auf den Index des Elements im vertexBuffers-Array, das dieses Bufferlayout beschreibt.
  3. GPURenderPassEncoder.draw() setzt das Zeichnen in Bewegung. Es gibt Daten fr drei Vertices in unserem vertexBuffer, sodass wir eine Vertexanzahl von 3 festlegen, um sie alle zu zeichnen.
js
passEncoder.setPipeline(renderPipeline);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.draw(3);

Um die Codierung der Befehlssequenz abzuschlieen und sie an die GPU zu bergeben, sind drei weitere Schritte erforderlich.

  1. Wir rufen die Methode GPURenderPassEncoder.end() auf, um das Ende der Renderpass-Befehlsliste zu signalisieren.
  2. Wir rufen die Methode GPUCommandEncoder.finish() auf, um die Aufzeichnung der ausgegebenen Befehlssequenz abzuschlieen und sie in einem GPUCommandBuffer-Objektinstanz zu kapseln.
  3. Wir bermitteln den GPUCommandBuffer an die Befehlsschlange des Gerts (dargestellt durch eine GPUQueue-Instanz), die dann an die GPU gesendet wird. Die Gerteschlange ist ber die GPUDevice.queue-Eigenschaft verfgbar, und ein Array von GPUCommandBuffer-Instanzen kann ber einen Aufruf von GPUQueue.submit() der Schlange hinzugefgt werden.

Diese drei Schritte knnen mit den folgenden zwei Zeilen erreicht werden:

js
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

Grundlegende Compute-Pipeline

In unserem einfachen Compute-Demo lassen wir die GPU einige Werte berechnen, diese in einem Ausgabepuffer speichern, die Daten in einen Zwischenpuffer kopieren, diesen Zwischenpuffer mappen, sodass die Daten in JavaScript gelesen und in der Konsole protokolliert werden knnen.

Die App folgt einer hnlichen Struktur wie das grundlegende Rendering-Demo. Wir erstellen einen GPUDevice-Verweis auf dieselbe Weise wie zuvor und umschlieen unseren Shadercode in einem GPUShaderModule durch einen Aufruf von GPUDevice.createShaderModule(). Der Unterschied hier ist, dass unser Shadercode nur eine Shaderstufe hat, eine @compute-Stufe:

js
// Define global buffer size
const NUM_ELEMENTS = 1000;
const BUFFER_SIZE = NUM_ELEMENTS * 4; // Buffer size, in bytes

const shader = `
@group(0) @binding(0)
var<storage, read_write> output: array<f32>;

@compute @workgroup_size(64)
fn main(
  @builtin(global_invocation_id)
  global_id : vec3u,

  @builtin(local_invocation_id)
  local_id : vec3u,
) {
  // Avoid accessing the buffer out of bounds
  if (global_id.x >= ${NUM_ELEMENTS}) {
    return;
  }

  output[global_id.x] =
    f32(global_id.x) * 1000. + f32(local_id.x);
}
`;

Buffer erstellen, um unsere Daten zu verwalten

In diesem Beispiel erstellen wir zwei GPUBuffer-Instanzen, um unsere Daten zu verwalten: einen output-Buffer, um die GPU-Berechnungsergebnisse mit hoher Geschwindigkeit zu schreiben, und einen stagingBuffer, in den wir die Inhalte des output kopieren, der gemappt werden kann, um JavaScript den Zugriff auf die Werte zu ermglichen.

  • output wird als Speicherbuffer spezifiziert, der die Quelle eines Kopiervorgangs ist.
  • stagingBuffer wird als Buffer spezifiziert, der fr das Lesen durch JavaScript gemappt werden kann, und als Ziel eines Kopiervorgangs verwendet.
js
const output = device.createBuffer({
  size: BUFFER_SIZE,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});

const stagingBuffer = device.createBuffer({
  size: BUFFER_SIZE,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});

Ein Bindungsgruppen-Layout erstellen

Wenn die Pipeline erstellt wird, geben wir eine Bindungsgruppe an, die fr die Pipeline verwendet wird. Dies beinhaltet zunchst die Erstellung eines GPUBindGroupLayout (ber einen Aufruf von GPUDevice.createBindGroupLayout()), der die Struktur und den Zweck von GPU-Ressourcen wie Buffern definiert, die in dieser Pipeline verwendet werden. Dieses Layout wird als Vorlage fr Bindungsgruppen verwendet, an die man sich halten muss. In diesem Fall geben wir der Pipeline den Zugriff auf einen einzelnen Speicherbuffer, der an den Bindungsslot 0 gebunden ist (dies entspricht der relevanten Bindungsnummer in unserem Shadercode @binding(0)), nutzbar in der Computestufe der Pipeline und mit dem Zweck des Buffers als storage.

js
const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    {
      binding: 0,
      visibility: GPUShaderStage.COMPUTE,
      buffer: {
        type: "storage",
      },
    },
  ],
});

Als nchstes erstellen wir eine GPUBindGroup, indem wir GPUDevice.createBindGroup() aufrufen. Wir bergeben diesem Methodenaufruf ein Deskriptorobjekt, das das Bindungsgruppen-Layout spezifiziert, auf dem diese Bindungsgruppe basieren soll, und die Details der Variable, die an den im Layout definierten Slot gebunden werden soll. In diesem Fall deklarieren wir Bindung 0 und spezifizieren, dass der zuvor definierte output-Buffer daran gebunden werden soll.

js
const bindGroup = device.createBindGroup({
  layout: bindGroupLayout,
  entries: [
    {
      binding: 0,
      resource: {
        buffer: output,
      },
    },
  ],
});

Hinweis: Sie knnten ein implizites Layout abrufen, um es beim Erstellen einer Bindungsgruppe zu verwenden, indem Sie die Methode GPUComputePipeline.getBindGroupLayout() aufrufen. Es gibt auch eine Version fr Render-Pipelines: siehe GPURenderPipeline.getBindGroupLayout().

Eine Compute-Pipeline erstellen

Mit dem oben Gesagten knnen wir nun eine Compute-Pipeline erstellen, indem wir GPUDevice.createComputePipeline() aufrufen und ein Pipeline-Deskriptorobjekt bergeben. Dies funktioniert hnlich wie die Erstellung einer Render-Pipeline. Wir beschreiben den Compute-Shader, indem wir angeben, in welchem Modul der Code gefunden werden kann und was der Einstiegspunkt ist. Wir spezifizieren auch ein layout fr die Pipeline, in diesem Fall erstellen wir ein Layout basierend auf dem bindGroupLayout, das wir zuvor durch einen Aufruf von GPUDevice.createPipelineLayout() definiert haben.

js
const computePipeline = device.createComputePipeline({
  layout: device.createPipelineLayout({
    bindGroupLayouts: [bindGroupLayout],
  }),
  compute: {
    module: shaderModule,
    entryPoint: "main",
  },
});

Ein Unterschied hier zum Render-Pipeline-Layout besteht darin, dass wir keinen primitiven Typ angeben, da wir nichts zeichnen.

Einen Compute-Pass ausfhren

Das Ausfhren eines Compute-Passes hnelt im Aufbau dem Ausfhren eines Rendering-Passes, mit einigen unterschiedlichen Befehlen. Zunchst wird der Pass-Encoder mithilfe von GPUCommandEncoder.beginComputePass() erstellt.

Bei der Ausgabe der Befehle spezifizieren wir die Pipeline, die in gleicher Weise wie zuvor verwendet werden soll, mit GPUComputePassEncoder.setPipeline(). Wir verwenden dann jedoch GPUComputePassEncoder.setBindGroup(), um anzugeben, dass wir unsere bindGroup verwenden mchten, um die Daten zu spezifizieren, die in der Berechnung verwendet werden sollen, und GPUComputePassEncoder.dispatchWorkgroups(), um die Anzahl der GPU-Arbeitsgruppen anzugeben, die zur Durchfhrung der Berechnungen verwendet werden sollen.

Wir signalisieren dann das Ende der Renderpass-Befehlsliste mit GPURenderPassEncoder.end().

js
passEncoder.setPipeline(computePipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatchWorkgroups(Math.ceil(NUM_ELEMENTS / 64));

passEncoder.end();

Die Ergebnisse zurck zu JavaScript lesen

Bevor wir die codierten Befehle zur Ausfhrung mit GPUQueue.submit() an die GPU bermitteln, kopieren wir den Inhalt des output-Buffers mit GPUCommandEncoder.copyBufferToBuffer() in den stagingBuffer.

js
// Copy output buffer to staging buffer
commandEncoder.copyBufferToBuffer(
  output,
  0, // Source offset
  stagingBuffer,
  0, // Destination offset
  BUFFER_SIZE, // Length, in bytes
);

// End frame by passing array of command buffers to command queue for execution
device.queue.submit([commandEncoder.finish()]);

Sobald die Ausgabedaten im stagingBuffer verfgbar sind, verwenden wir die Methode GPUBuffer.mapAsync(), um die Daten in den Zwischenpeicher zu mappen, holen uns dann einen Verweis auf den gemappten Bereich mittels GPUBuffer.getMappedRange(), kopieren die Daten in JavaScript und protokollieren sie dann in der Konsole. Auerdem demappen wir den stagingBuffer, sobald wir fertig sind.

js
// map staging buffer to read results back to JS
await stagingBuffer.mapAsync(
  GPUMapMode.READ,
  0, // Offset
  BUFFER_SIZE, // Length, in bytes
);

const copyArrayBuffer = stagingBuffer.getMappedRange(0, BUFFER_SIZE);
const data = copyArrayBuffer.slice();
stagingBuffer.unmap();
console.log(new Float32Array(data));

GPU-Fehlerbehandlung

WebGPU-Aufrufe werden im GPU-Prozess asynchron validiert. Wenn Fehler gefunden werden, wird der problematische Aufruf auf der GPU-Seite als ungltig markiert. Wenn ein anderer Aufruf gemacht wird, der von dem Rckgabewert eines ungltig markierten Aufrufs abhngt, wird auch dieses Objekt als ungltig markiert und so weiter. Aus diesem Grund werden Fehler in WebGPU als "ansteckend" bezeichnet.

Jede GPUDevice-Instanz pflegt ihren eigenen Fehlerbereichs-Stack. Dieser Stack ist zunchst leer, aber Sie knnen beginnen, einen Fehlerbereich an den Stack zu pushen, indem Sie GPUDevice.pushErrorScope() aufrufen, um Fehler eines bestimmten Typs zu erfassen.

Wenn Sie mit der Fehlererfassung fertig sind, knnen Sie die Erfassung beenden, indem Sie GPUDevice.popErrorScope() aufrufen. Dies entfernt den Bereich aus dem Stack und gibt ein Promise zurck, das sich zu einem Objekt (GPUInternalError, GPUOutOfMemoryError oder GPUValidationError) auflst, das den ersten im Bereich erfassten Fehler beschreibt, oder null, wenn keine Fehler erfasst wurden.

Wir haben versucht, ntzliche Informationen bereitzustellen, um Ihnen zu helfen, zu verstehen, warum in Ihrem WebGPU-Code Fehler auftreten, in "Validierungs"-Abschnitten, wo es angebracht ist, die Kriterien aufzulisten, die zu erfllen sind, um Fehler zu vermeiden. Siehe zum Beispiel den Validierungsabschnitt zu GPUDevice.createBindGroup(). Einige dieser Informationen sind komplex; anstatt die Spezifikation zu wiederholen, haben wir beschlossen, nur Fehlerkriterien aufzulisten, die:

  • Nicht offensichtlich sind, zum Beispiel Kombinationen von Deskriptor-Eigenschaften, die Validierungsfehler produzieren. Es bringt nichts, Ihnen mitzuteilen, dass Sie sicherstellen sollen, dass Sie die richtige Deskriptor-Objektstruktur verwenden. Das ist sowohl offensichtlich als auch vage.
  • Vom Entwickler kontrolliert sind. Einige der Fehlerkriterien basieren rein auf Interna und sind fr Webentwickler nicht wirklich relevant.

Sie knnen mehr Informationen ber die Fehlerbehandlung in WebGPU im Erklrer finden siehe Objektgltigkeit und Zerstrung und Fehler. Beste Praktiken zur WebGPU-Fehlerbehandlung bietet ntzliche reale Beispiele und Ratschlge.

Hinweis: Die historische Art und Weise, Fehler in WebGL zu behandeln, besteht darin, eine Methode getError() bereitzustellen, um Fehlerinformationen zurckzugeben. Das ist problematisch, da es Fehler synchron zurckgibt, was schlecht fr die Leistung ist jeder Aufruf erfordert eine Hin- und Rckfahrt zur GPU und verlangt, dass alle zuvor ausgegebenen Operationen abgeschlossen werden. Sein Zustandsmodell ist auch flach, was bedeutet, dass Fehler zwischen nicht verwandtem Code durchlecken knnen. Die Entwickler von WebGPU waren entschlossen, diese Situation zu verbessern.

Schnittstellen

Einstiegspunkt fr die API

Der Einstiegspunkt fr die API gibt das GPU-Objekt fr den aktuellen Kontext zurck.

GPU

Der Ausgangspunkt zur Nutzung von WebGPU. Kann verwendet werden, um einen GPUAdapter zurckzugeben.

GPUAdapter

Reprsentiert einen GPU-Adapter. Von hier aus knnen Sie ein GPUDevice, Adapterinformationen, Funktionen und Limits anfordern.

GPUAdapterInfo

Enthlt identifizierende Informationen ber einen Adapter.

Konfiguration von GPUDevices

GPUDevice

Reprsentiert ein logisches GPU-Gert. Dies ist die Hauptschnittstelle, ber die auf den Groteil der WebGPU-Funktionalitt zugegriffen wird.

GPUSupportedFeatures

Ein setlike-Objekt, das zustzliche Funktionalitt beschreibt, die von einem GPUAdapter oder GPUDevice untersttzt wird.

GPUSupportedLimits

Beschreibt die von einem GPUAdapter oder GPUDevice untersttzten Limits.

Konfiguration eines Rendering-<canvas>

HTMLCanvasElement.getContext() der "webgpu" contextType

Das Aufrufen von getContext() mit dem "webgpu" contextType gibt eine GPUCanvasContext-Objektinstanz zurck, die dann mit GPUCanvasContext.configure() konfiguriert werden kann.

GPUCanvasContext

Reprsentiert den WebGPU-Renderingkontext eines <canvas>-Elements.

Reprsentation von Pipeline-Ressourcen

GPUBuffer

Reprsentiert einen Speicherblock, der verwendet werden kann, um Rohdaten zu speichern, die in GPU-Operationen verwendet werden sollen.

GPUExternalTexture

Ein Wrapper-Objekt, das ein HTMLVideoElement-Schnappschuss enthlt, der als Textur in GPU-Rendering-Operationen verwendet werden kann.

GPUSampler

Kontrolliert, wie Shader Textur-Ressourcendaten transformieren und filtern.

GPUShaderModule

Eine Referenz auf ein internes Shader-Modul-Objekt, einen Container fr WGSL-Shadercode, der zur Ausfhrung durch eine Pipeline an die GPU bergeben werden kann.

GPUTexture

Ein Container, der zum Speichern von 1D-, 2D- oder 3D-Datenarrays, wie Bilder, zum Gebrauch in GPU-Rendering-Operationen verwendet wird.

GPUTextureView

Eine Ansicht auf eine Teilmenge der durch eine bestimmte GPUTexture definierten Textur-Ressourcen.

Reprsentation von Pipelines

GPUBindGroup

Basierend auf einem GPUBindGroupLayout, definiert eine GPUBindGroup eine Gruppe von Ressourcen, die gemeinsam gebunden und genutzt werden und wie diese Ressourcen in Shaderstufen eingesetzt werden.

GPUBindGroupLayout

Definiert die Struktur und den Zweck verwandter GPU-Ressourcen wie Buffers, die in einer Pipeline verwendet werden, und wird als Vorlage beim Erstellen von GPUBindGroups verwendet.

GPUComputePipeline

Kontrolliert die Compute-Shader-Stufe und kann in einem GPUComputePassEncoder verwendet werden.

GPUPipelineLayout

Definiert die GPUBindGroupLayouts, die von einer Pipeline verwendet werden. GPUBindGroups, die mit der Pipeline whrend der Befehlskodierung verwendet werden, mssen kompatible GPUBindGroupLayouts haben.

GPURenderPipeline

Kontrolliert die Vertex- und Fragment-Shader-Stufen und kann in einem GPURenderPassEncoder oder GPURenderBundleEncoder verwendet werden.

Befehle an die GPU kodieren und bermitteln

GPUCommandBuffer

Reprsentiert eine aufgezeichnete Liste von GPU-Befehlen, die zur Ausfhrung an eine GPUQueue bermittelt werden knnen.

GPUCommandEncoder

Reprsentiert einen Befehls-Encoder, der verwendet wird, um Befehle zu kodieren, die an die GPU ausgegeben werden sollen.

GPUComputePassEncoder

Kodiert Befehle, die das Steuern der Compute-Shader-Stufe betreffen, wie sie von einer GPUComputePipeline ausgegeben werden. Teil der gesamten Kodierungsaktivitt eines GPUCommandEncoder.

GPUQueue

Steuert die Ausfhrung kodierter Befehle auf der GPU.

GPURenderBundle

Ein Container fr vorab aufgezeichnete Befehlsbndel (siehe GPURenderBundleEncoder).

GPURenderBundleEncoder

Wird verwendet, um Bndel von Befehlen vorab aufzuzeichnen. Diese knnen so oft wie erforderlich in GPURenderPassEncoders ber die Methode executeBundles() wiederverwendet werden.

GPURenderPassEncoder

Kodiert Befehle, die das Steuern der Vertex- und Fragment-Shader-Stufen betreffen, wie sie von einer GPURenderPipeline ausgegeben werden. Teil der gesamten Kodierungsaktivitt eines GPUCommandEncoder.

Abfragen von Rendering-Passes ausfhren

GPUQuerySet

Wird verwendet, um die Ergebnisse von Abfragen in Passes zu erfassen, wie Occlusion- oder Zeitstempelabfragen.

Fehler debuggen

GPUCompilationInfo

Ein Array von GPUCompilationMessage-Objekten, das vom GPU-Shadermodul-Compiler generiert wurde, um bei der Diagnose von Problemen mit Shadercode zu helfen.

GPUCompilationMessage

Reprsentiert eine einzelne Informations-, Warn- oder Fehlermeldung, die vom GPU-Shadermodul-Compiler generiert wurde.

GPUDeviceLostInfo

Wird zurckgegeben, wenn das GPUDevice.lost-Promise sich auflst und Informationen darber bereitstellt, warum das Gert verloren ging.

GPUError

Die Basis-Schnittstelle fr Fehler, die von GPUDevice.popErrorScope und dem uncapturederror-Ereignis an die Oberflche kommen.

GPUInternalError

Eine der Fehlerarten, die von GPUDevice.popErrorScope und dem GPUDevice uncapturederror-Ereignis an die Oberflche kommen. Gibt an, dass ein Vorgang aus einem system- oder implementierungsspezifischen Grund fehlgeschlagen ist, auch wenn alle Validierungsanforderungen erfllt wurden.

GPUOutOfMemoryError

Eine der Fehlerarten, die von GPUDevice.popErrorScope und dem GPUDevice uncapturederror-Ereignis an die Oberflche kommen. Gibt an, dass nicht gengend freier Speicher verfgbar war, um die angeforderte Operation abzuschlieen.

GPUPipelineError

Beschreibt einen Pipeline-Fehler. Der empfangene Wert, wenn ein von GPUDevice.createComputePipelineAsync() oder GPUDevice.createRenderPipelineAsync() zurckgegebenes Promise abgelehnt wird.

GPUUncapturedErrorEvent

Der Ereignisobjekttyp fr das GPUDevice uncapturederror-Ereignis.

GPUValidationError

Eine der Fehlerarten, die von GPUDevice.popErrorScope und dem GPUDevice uncapturederror-Ereignis an die Oberflche kommen. Beschreibt einen Anwendungsfehler, der anzeigt, dass ein Vorgang die Validierungseinschrnkungen der WebGPU API nicht bestanden hat.

Sicherheitsanforderungen

Die gesamte API ist nur in einem sicheren Kontext verfgbar.

Beispiele

Spezifikationen

Spezifikation
WebGPU
# gpu-interface

Browser-Kompatibilitt

Siehe auch


Web Proxy Viewer  |  New URL  |  Original Page