Start · Sprachen · JavaScript · Referenz · GPUDevice

GPUDevice

Schnittstelle

Ein <code>GPUDevice</code> ist ein logisches GPU-Gerät der WebGPU-API, über das man Puffer, Texturen, Shader und Pipelines anlegt und Befehle an die GPU übergibt.

seit JavaScript WebGPU – Chrome/Edge 113+, Safari 26+, Firefox 141 Kategorie: webapi

Signatur

interface GPUDevice extends EventTarget

Beschreibung

GPUDevice ist die zentrale Schnittstelle der WebGPU-API. Ein Gerät bekommt man asynchron über GPUAdapter.requestDevice(). Der Adapter steht für die physische GPU, das Gerät dagegen für eine abgeschottete, logische Verbindung zu ihr. Jede Anwendung – im Grunde jedes Modul – kann ein eigenes Gerät haben, ohne anderen Code zu stören.

Fast alle GPU-Ressourcen entstehen über Fabrikmethoden des Geräts:

  • createBuffer(), createTexture(), createSampler() für Speicherressourcen
  • createShaderModule() für WGSL-Shader
  • createRenderPipeline(), createComputePipeline() und ihre …Async()-Varianten
  • createBindGroupLayout(), createPipelineLayout(), createBindGroup() für Ressourcenbindungen
  • createCommandEncoder(), createRenderBundleEncoder(), createQuerySet()

Wichtige Eigenschaften sind queue (die GPUQueue, an die man Befehlspuffer übergibt und über die man Daten hochlädt), features und limits (die tatsächlich freigeschalteten Fähigkeiten), adapterInfo sowie lost. lost ist ein Promise, das erfüllt wird, sobald das Gerät verloren geht, etwa durch einen Treiberabsturz, einen GPU-Reset oder einen Aufruf von destroy().

Die Fehlerbehandlung läuft über Fehlerbereiche: Mit pushErrorScope() und popErrorScope() fängt man Validierungs-, Speicher- und interne Fehler gezielt ab. Fehler, die kein Bereich abfängt, lösen auf dem Gerät das Event uncapturederror aus.

Rückgabewert

Typ
GPUDevice
Beschreibung
Wird von GPUAdapter.requestDevice(descriptor?) als Promise<GPUDevice> geliefert. Einen öffentlichen Konstruktor gibt es nicht.

Beispiele

Gerät anfordern und Verlust behandeln

const initGPU = async () => {
  if (!navigator.gpu) throw new Error('WebGPU wird nicht unterstützt');

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

  const device = await adapter.requestDevice({
    requiredLimits: { maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize }
  });

  device.lost.then((info) => {
    console.warn(`GPU-Gerät verloren (${info.reason}): ${info.message}`);
    // Bei unerwartetem Verlust neu initialisieren
    if (info.reason !== 'destroyed') initGPU();
  });

  device.addEventListener('uncapturederror', (e) => {
    console.error('Nicht abgefangener WebGPU-Fehler:', e.error.message);
  });

  console.log('Max. Texturgröße 2D:', device.limits.maxTextureDimension2D);
  return device;
};

const device = await initGPU();
Max. Texturgröße 2D: 8192

Compute-Shader: Zahlen auf der GPU verdoppeln

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

const input = new Float32Array([1, 2, 3, 4]);

const storage = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(storage, 0, input);

const readback = device.createBuffer({
  size: input.byteLength,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});

const module = device.createShaderModule({
  code: `
    @group(0) @binding(0) var<storage, read_write> data: array<f32>;
    @compute @workgroup_size(64)
    fn main(@builtin(global_invocation_id) id: vec3u) {
      if (id.x < arrayLength(&data)) { data[id.x] = data[id.x] * 2.0; }
    }`
});

const pipeline = await device.createComputePipelineAsync({
  layout: 'auto',
  compute: { module, entryPoint: 'main' }
});

const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [{ binding: 0, resource: { buffer: storage } }]
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(input.length / 64));
pass.end();
encoder.copyBufferToBuffer(storage, 0, readback, 0, input.byteLength);
device.queue.submit([encoder.finish()]);

await readback.mapAsync(GPUMapMode.READ);
console.log(Array.from(new Float32Array(readback.getMappedRange())));
readback.unmap();
[2, 4, 6, 8]

Validierungsfehler gezielt mit Fehlerbereichen abfangen

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

device.pushErrorScope('validation');

// Ungültig: Ein Puffer darf nicht gleichzeitig MAP_READ und STORAGE sein
device.createBuffer({
  size: 16,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.STORAGE
});

const error = await device.popErrorScope();
console.log(error ? `Validierungsfehler: ${error.constructor.name}` : 'Alles in Ordnung');
Validierungsfehler: GPUValidationError

// Wichtig · Fallstricke

  • Kompatibilität: WebGPU ist nur in sicheren Kontexten (HTTPS, localhost) verfügbar und noch nicht überall ausgeliefert. Chromium-Browser unterstützen es ab Version 113 (Linux und Android teils später), Safari ab 26, Firefox zunächst nur unter Windows. Prüfe deshalb immer navigator.gpu und ob requestAdapter() den Wert null liefert. In Web Workern ist die API ebenfalls verfügbar.
  • Fehler sind asynchron: Ungültige Aufrufe werfen keine JavaScript-Ausnahme. Sie erzeugen ein „ungültiges“ Objekt und melden den Fehler über Fehlerbereiche oder uncapturederror. Ohne Fehlerbehandlung erscheinen Fehler nur als Warnung in der Konsole.
  • Geräteverlust: requestDevice() kann ein Gerät liefern, das schon verloren ist. Verlassen Sie sich deshalb immer auf das Promise device.lost. Ein verlorenes Gerät lässt sich nicht wiederbeleben: Sie müssen beim Adapter ein neues Gerät anfordern und alle Ressourcen neu erzeugen. Ein Adapter kann nur ein Gerät liefern, danach ist ein neuer Adapter nötig.
  • Limits und Features: Standardmäßig gelten konservative Standardlimits, selbst wenn die Hardware mehr kann. Höhere Werte müssen Sie in requiredLimits bzw. requiredFeatures ausdrücklich anfordern und dabei die Werte aus adapter.limits bzw. adapter.features einhalten, sonst schlägt requestDevice() fehl.
  • Leistung: Nutzen Sie createRenderPipelineAsync() bzw. createComputePipelineAsync(). Dann wird der Shader kompiliert, ohne den Hauptthread zu blockieren.
  • Aufräumen: device.destroy() gibt alle Ressourcen sofort frei. Danach ist lost mit dem Grund 'destroyed' erfüllt.