Signatur
Beschreibung
writeBuffer() ist der einfachste Weg, Daten von der CPU in einen GPUBuffer zu übertragen. Die Methode kopiert den angegebenen Datenbereich sofort bei ihrem Aufruf. Danach kannst du das Quell-Array direkt wiederverwenden oder verändern. Die eigentliche Übertragung zur GPU reiht der Browser in die Queue ein. Sie findet also vor allen Befehlspuffern statt, die du danach mit queue.submit() abschickst.
Typische Anwendungen sind Uniform-Daten, die sich in jedem Frame ändern (Matrizen, Zeit, Mausposition), Teilaktualisierungen von Vertex- oder Storage-Buffern und das erste Befüllen von Buffern, wenn mappedAtCreation zu umständlich ist. Anders als bei mapAsync() brauchst du kein Promise und musst den Buffer nicht zuordnen (mappen) und wieder freigeben.
Der Ziel-Buffer braucht das Nutzungsflag GPUBufferUsage.COPY_DST. bufferOffset und die tatsächlich geschriebene Byteanzahl müssen Vielfache von 4 sein. Wichtig: Ist data ein TypedArray, gibst du dataOffset und size in Elementen an. Bei ArrayBuffer und DataView gibst du beides in Bytes an.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $buffer Pflicht | GPUBuffer | Der Ziel-Buffer. Er braucht das Nutzungsflag GPUBufferUsage.COPY_DST und darf beim Ausführen weder gemappt noch zerstört sein. |
|
| $bufferOffset Pflicht | number | Byte-Offset im Ziel-Buffer, ab dem geschrieben wird. Muss ein Vielfaches von 4 sein. | |
| $data Pflicht | ArrayBuffer | SharedArrayBuffer | TypedArray | DataView | Die Quelldaten, die in den Buffer kopiert werden. | |
| $dataOffset | number | 0 | Startposition in data. Bei TypedArrays in Elementen, sonst in Bytes. |
| $size | number | Rest von data ab dataOffset | Menge der zu schreibenden Daten. Bei TypedArrays in Elementen, sonst in Bytes. Umgerechnet in Bytes muss der Wert ein Vielfaches von 4 sein. |
Rückgabewert
submit()-Aufrufen ist trotzdem garantiert.Beispiele
Uniform-Buffer in jedem Frame aktualisieren
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// 4 Floats: Zeit, Breite, Höhe, Padding
const uniformBuffer = device.createBuffer({
size: 16,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const uniforms = new Float32Array(4);
const frame = (t) => {
uniforms[0] = t / 1000;
uniforms[1] = innerWidth;
uniforms[2] = innerHeight;
device.queue.writeBuffer(uniformBuffer, 0, uniforms);
// ... Render-Pass kodieren und device.queue.submit([...])
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
Vertex-Daten teilweise ersetzen (dataOffset/size in Elementen)
const vertices = new Float32Array([
0.0, 0.5, // Vertex 0
-0.5, -0.5, // Vertex 1
0.5, -0.5, // Vertex 2
]);
const vertexBuffer = device.createBuffer({
size: vertices.byteLength, // 24 Bytes
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, vertices);
// Nur Vertex 1 (Elemente 2 und 3) verschieben
vertices[2] = -0.8;
vertices[3] = -0.8;
// bufferOffset in Bytes (2 * 4 = 8), dataOffset/size in Elementen
device.queue.writeBuffer(vertexBuffer, 8, vertices, 2, 2);
Gemischte Datentypen mit ArrayBuffer (Angaben in Bytes)
const data = new ArrayBuffer(32);
const f32 = new Float32Array(data, 0, 4);
const u32 = new Uint32Array(data, 16, 4);
f32.set([1, 0, 0, 1]); // Farbe RGBA
u32.set([42, 7, 0, 0]); // IDs/Flags
const storage = device.createBuffer({
size: 32,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
// Nur den u32-Teil schreiben: 16 Bytes ab Byte 16
device.queue.writeBuffer(storage, 16, data, 16, 16);
Validierungsfehler abfangen
device.pushErrorScope('validation');
const noCopyDst = device.createBuffer({
size: 16,
usage: GPUBufferUsage.UNIFORM, // COPY_DST fehlt!
});
device.queue.writeBuffer(noCopyDst, 0, new Float32Array(4));
const error = await device.popErrorScope();
console.log(error?.constructor.name);
// Wichtig · Fallstricke
- Einheiten verwechselt:
bufferOffsetgibst du immer in Bytes an.dataOffsetundsizegibst du bei TypedArrays in Elementen an, beiArrayBuffer/DataViewin Bytes. Das ist die häufigste Fehlerquelle. - Ausrichtung:
bufferOffsetund die geschriebene Byteanzahl müssen Vielfache von 4 sein. EinUint8Arraymit 3 Bytes führt deshalb zu einem Validierungsfehler. - Fehlerverhalten: Liegt der Bereich
dataOffset/sizeaußerhalb vondata, wirft der Aufruf sofort eineOperationError-DOMException. Andere Fehler meldet die GPU asynchron alsGPUValidationError, zum Beispiel ein fehlendesCOPY_DST, ein Schreiben über das Buffer-Ende hinaus oder ein gemappter bzw. zerstörter Buffer. Diese Fehler fängst du überpushErrorScope()oder das Ereignisuncapturederrorab. - Performance: Jeder Aufruf erzeugt eine interne Kopie. Bei vielen kleinen Updates ist es schneller, die Daten in einem Array zu sammeln und einmal zu schreiben. Für sehr große Datenströme kann ein Staging-Buffer mit
mapAsync()undcopyBufferToBuffer()günstiger sein. - Uniform-Layout: Achte bei WGSL-Structs auf die Ausrichtungsregeln. Ein
vec3fbelegt zum Beispiel 16 Bytes. Deine JS-Daten müssen das Padding abbilden. - Kompatibilität: WebGPU steht nur in sicheren Kontexten zur Verfügung und ist noch nicht überall aktiviert. Prüfe vorher, ob
navigator.gpuvorhanden ist.