Start · Sprachen · JavaScript · Referenz · WebGLRenderingContext.bufferSubData()

WebGLRenderingContext.bufferSubData()

Methode

Überschreibt einen Teilbereich des Datenspeichers des aktuell gebundenen WebGL-Puffers, ohne den Puffer neu anzulegen.

seit JavaScript WebGL 1.0 – Baseline: alle Browser Kategorie: webapi

Signatur

WebGLRenderingContext.prototype.bufferSubData(target, offset, srcData)

Beschreibung

bufferSubData() schreibt neue Daten in einen bereits angelegten Pufferspeicher. Anders als bufferData() wird dabei kein neuer Speicher auf der GPU reserviert – die Größe des Puffers bleibt unverändert, es werden nur die Bytes ab offset ersetzt.

Typische Einsatzfälle sind dynamische Geometrie: Partikelsysteme, animierte Vertex-Positionen, Text- oder UI-Batches, bei denen sich pro Frame nur ein Teil der Daten ändert. Man legt den Puffer einmalig mit bufferData(target, size, gl.DYNAMIC_DRAW) in ausreichender Größe an und aktualisiert ihn danach mit bufferSubData(). Das vermeidet teure Neuallokationen.

Die Methode wirkt immer auf den Puffer, der aktuell an target gebunden ist – vorher muss also gl.bindBuffer() aufgerufen werden. In WebGL 2 (WebGL2RenderingContext) gibt es eine erweiterte Überladung bufferSubData(target, dstByteOffset, srcData, srcOffset, length), mit der nur ein Ausschnitt eines Typed Arrays übertragen werden kann, ohne eine subarray()-Kopie zu erzeugen.

Parameter

Name Typ Default Beschreibung
$target Pflicht GLenum Bindungspunkt des Puffers: gl.ARRAY_BUFFER (Vertex-Attribute) oder gl.ELEMENT_ARRAY_BUFFER (Indizes). In WebGL 2 zusätzlich u. a. gl.UNIFORM_BUFFER, gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, gl.TRANSFORM_FEEDBACK_BUFFER, gl.PIXEL_PACK_BUFFER, gl.PIXEL_UNPACK_BUFFER.
$offset Pflicht GLintptr (number) Startposition im Puffer in Bytes, ab der geschrieben wird. Nicht die Element-Anzahl!
$srcData Pflicht ArrayBuffer | SharedArrayBuffer | ArrayBufferView Die zu kopierenden Daten, meist ein Typed Array wie Float32Array oder Uint16Array.
$srcOffset GLuint (number) 0 Nur WebGL 2, nur bei ArrayBufferView: Startindex in Elementen innerhalb von srcData.
$length GLuint (number) 0 Nur WebGL 2: Anzahl der zu kopierenden Elemente. 0 bedeutet: bis zum Ende von srcData.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined). Fehler werden über gl.getError() gemeldet.

Beispiele

Puffer einmal anlegen, dann Teil aktualisieren

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

// Platz für 3 Vertices × 2 Floats reservieren (24 Bytes)
gl.bufferData(gl.ARRAY_BUFFER, 6 * Float32Array.BYTES_PER_ELEMENT, gl.DYNAMIC_DRAW);

// Nur den zweiten Vertex (Floats 2 und 3) überschreiben
const secondVertex = new Float32Array([0.5, -0.5]);
gl.bufferSubData(gl.ARRAY_BUFFER, 2 * Float32Array.BYTES_PER_ELEMENT, secondVertex);

console.log(gl.getError() === gl.NO_ERROR);
true

Partikelpositionen pro Frame aktualisieren

const gl = document.querySelector('canvas').getContext('webgl');
const MAX_PARTICLES = 1000;
const positions = new Float32Array(MAX_PARTICLES * 2);

const posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions.byteLength, gl.DYNAMIC_DRAW);

const frame = (time) => {
  for (let i = 0; i < MAX_PARTICLES; i++) {
    positions[i * 2] = Math.sin(time * 0.001 + i) * 0.9;
    positions[i * 2 + 1] = Math.cos(time * 0.0013 + i) * 0.9;
  }
  gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
  // Gesamten Inhalt ersetzen, ohne neu zu allozieren
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, positions);
  gl.drawArrays(gl.POINTS, 0, MAX_PARTICLES);
  requestAnimationFrame(frame);
};
requestAnimationFrame(frame);

WebGL 2: Ausschnitt eines Typed Arrays ohne Kopie hochladen

const gl = document.createElement('canvas').getContext('webgl2');
const data = new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]);

const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, data.byteLength, gl.DYNAMIC_DRAW);

// Elemente 4..7 aus data an Byte-Offset 0 im Puffer schreiben
gl.bufferSubData(gl.ARRAY_BUFFER, 0, data, 4, 4);

// Zurücklesen (nur WebGL 2)
const result = new Float32Array(8);
gl.getBufferSubData(gl.ARRAY_BUFFER, 0, result);
console.log(Array.from(result));
[4, 5, 6, 7, 0, 0, 0, 0]

// Wichtig · Fallstricke

  • Offset in Bytes: offset zählt Bytes, nicht Elemente. Bei Float32Array also mit 4 (BYTES_PER_ELEMENT) multiplizieren. In WebGL 2 zählen srcOffset/length dagegen in Elementen – eine häufige Verwechslung.
  • Kein Vergrößern: Wenn offset + srcData.byteLength die mit bufferData() festgelegte Größe überschreitet, entsteht gl.INVALID_VALUE und nichts wird geschrieben. Ein negativer Offset erzeugt ebenfalls INVALID_VALUE.
  • Ist an target kein Puffer gebunden (null), wird gl.INVALID_OPERATION gemeldet; ein ungültiges target ergibt gl.INVALID_ENUM.
  • Performance: Häufige kleine Aufrufe sind teuer – Änderungen besser in einem Typed Array sammeln und in einem Aufruf hochladen. Der Puffer sollte mit DYNAMIC_DRAW bzw. STREAM_DRAW angelegt werden. Bei Pro-Frame-Updates kann „Buffer Orphaning“ (erneutes bufferData() mit gleicher Größe) oder Doppelpufferung Stalls vermeiden, wenn die GPU den Puffer noch liest.
  • ELEMENT_ARRAY_BUFFER: In WebGL gilt die Bindung pro Vertex Array Object; ein Index-Puffer darf zudem nicht gleichzeitig als ARRAY_BUFFER verwendet werden.
  • Kompatibilität: WebGL 1 wird von allen modernen Browsern unterstützt; die Überladung mit srcOffset/length nur in WebGL-2-Kontexten (ebenfalls breit verfügbar, Safari ab 15).