Signatur
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 SpeicherressourcencreateShaderModule()für WGSL-ShadercreateRenderPipeline(),createComputePipeline()und ihre…Async()-VariantencreateBindGroupLayout(),createPipelineLayout(),createBindGroup()für RessourcenbindungencreateCommandEncoder(),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
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();
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();
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');
// 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 immernavigator.gpuund obrequestAdapter()den Wertnullliefert. 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 Promisedevice.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
requiredLimitsbzw.requiredFeaturesausdrücklich anfordern und dabei die Werte ausadapter.limitsbzw.adapter.featureseinhalten, sonst schlägtrequestDevice()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 istlostmit dem Grund'destroyed'erfüllt.