Start · Sprachen · JavaScript · Referenz · WebGL2RenderingContext.copyBufferSubData()

WebGL2RenderingContext.copyBufferSubData()

Methode

Kopiert einen Bytebereich aus dem an <code>readTarget</code> gebundenen Puffer direkt auf der GPU in den an <code>writeTarget</code> gebundenen Puffer.

seit JavaScript WebGL 2 – Baseline: alle modernen Browser (seit 20 Kategorie: webapi

Signatur

WebGL2RenderingContext.prototype.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size)

Beschreibung

Mit copyBufferSubData() kopierst du Daten zwischen zwei WebGL-Puffern, ohne dass sie über den Hauptspeicher (JavaScript) laufen. Die Kopie findet vollständig auf der GPU statt. Das ist deutlich schneller als der Umweg über getBufferSubData() und bufferSubData().

Typische Einsatzfälle: einen Puffer vergrößern (neuen, größeren Puffer anlegen und den alten Inhalt hineinkopieren), Ergebnisse aus Transform Feedback in einen Vertex-Puffer übernehmen, Sicherungskopien von Partikel-Zuständen anlegen oder Teilbereiche verschiedener Puffer zu einem Gesamtpuffer zusammenführen.

Die Quell- und Zielpuffer werden nicht direkt übergeben, sondern über ihre Bindungspunkte angesprochen. Damit du dabei keine anderen Bindungen (z. B. ARRAY_BUFFER) durcheinanderbringst, gibt es in WebGL 2 die speziellen Ziele gl.COPY_READ_BUFFER und gl.COPY_WRITE_BUFFER. Sie sind genau für solche Kopiervorgänge gedacht und haben sonst keine Wirkung auf das Rendering.

Alle Offsets und die Größe sind in Bytes angegeben, nicht in Elementen. Bei einem Float32Array musst du also mit Float32Array.BYTES_PER_ELEMENT (4) multiplizieren.

Parameter

Name Typ Default Beschreibung
$readTarget Pflicht GLenum — Bindungspunkt des Quellpuffers, z. B. gl.COPY_READ_BUFFER, gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER, gl.UNIFORM_BUFFER, gl.TRANSFORM_FEEDBACK_BUFFER, gl.PIXEL_PACK_BUFFER oder gl.PIXEL_UNPACK_BUFFER.
$writeTarget Pflicht GLenum — Bindungspunkt des Zielpuffers, meist gl.COPY_WRITE_BUFFER. Gleiche erlaubte Werte wie bei readTarget.
$readOffset Pflicht GLintptr (number) — Byte-Offset im Quellpuffer, ab dem gelesen wird. Darf nicht negativ sein.
$writeOffset Pflicht GLintptr (number) — Byte-Offset im Zielpuffer, ab dem geschrieben wird. Darf nicht negativ sein.
$size Pflicht GLsizeiptr (number) — Anzahl der zu kopierenden Bytes. Darf nicht negativ sein.

Rückgabewert

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

Beispiele

Einfache Kopie zwischen zwei Puffern

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

const data = new Float32Array([1, 2, 3, 4, 5, 6]);

// Quellpuffer mit Daten füllen
const src = gl.createBuffer();
gl.bindBuffer(gl.COPY_READ_BUFFER, src);
gl.bufferData(gl.COPY_READ_BUFFER, data, gl.STATIC_DRAW);

// Leeren Zielpuffer gleicher Größe anlegen
const dst = gl.createBuffer();
gl.bindBuffer(gl.COPY_WRITE_BUFFER, dst);
gl.bufferData(gl.COPY_WRITE_BUFFER, data.byteLength, gl.STATIC_DRAW);

// Gesamten Inhalt auf der GPU kopieren
gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, data.byteLength);

// Zur Kontrolle zurücklesen
const result = new Float32Array(6);
gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, result);
console.log(result);
Float32Array(6) [1, 2, 3, 4, 5, 6]

Teilbereich kopieren (Offsets in Bytes)

const gl = document.createElement('canvas').getContext('webgl2');
const BPE = Float32Array.BYTES_PER_ELEMENT; // 4

const src = gl.createBuffer();
gl.bindBuffer(gl.COPY_READ_BUFFER, src);
gl.bufferData(gl.COPY_READ_BUFFER, new Float32Array([10, 20, 30, 40]), gl.STATIC_DRAW);

const dst = gl.createBuffer();
gl.bindBuffer(gl.COPY_WRITE_BUFFER, dst);
gl.bufferData(gl.COPY_WRITE_BUFFER, new Float32Array([0, 0, 0, 0]), gl.STATIC_DRAW);

// Elemente 1 und 2 der Quelle an Position 2 im Ziel kopieren
gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 1 * BPE, 2 * BPE, 2 * BPE);

const out = new Float32Array(4);
gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, out);
console.log(out);
Float32Array(4) [0, 0, 20, 30]

Vertex-Puffer dynamisch vergrößern

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

// Puffer vergrößern und alten Inhalt übernehmen
const growBuffer = (gl, oldBuffer, oldSize, newSize) => {
  const newBuffer = gl.createBuffer();
  gl.bindBuffer(gl.COPY_READ_BUFFER, oldBuffer);
  gl.bindBuffer(gl.COPY_WRITE_BUFFER, newBuffer);
  gl.bufferData(gl.COPY_WRITE_BUFFER, newSize, gl.DYNAMIC_DRAW);
  gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, oldSize);
  gl.bindBuffer(gl.COPY_READ_BUFFER, null);
  gl.bindBuffer(gl.COPY_WRITE_BUFFER, null);
  gl.deleteBuffer(oldBuffer);
  return newBuffer;
};

const initial = new Float32Array(1024);
let vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, initial, gl.DYNAMIC_DRAW);

vbo = growBuffer(gl, vbo, initial.byteLength, initial.byteLength * 2);
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
console.log(gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE));
8192

// Wichtig · Fallstricke

  • Nur WebGL 2: Die Methode gibt es nicht auf WebGLRenderingContext (WebGL 1). Prüfe, ob canvas.getContext('webgl2') nicht null liefert. Alle aktuellen Browser unterstützen WebGL 2, Safari seit Version 15.
  • Bytes statt Elemente: Offsets und size sind Byte-Angaben. Ein häufiger Fehler ist, Elementanzahlen zu übergeben.
  • INVALID_VALUE tritt auf, wenn ein Wert negativ ist, wenn readOffset + size größer ist als der Quellpuffer oder writeOffset + size größer als der Zielpuffer, oder wenn sich bei gleichem Puffer Quell- und Zielbereich überschneiden.
  • INVALID_OPERATION tritt auf, wenn an einem der Ziele kein Puffer gebunden ist. In WebGL gilt zusätzlich: Ein Puffer, der einmal an ELEMENT_ARRAY_BUFFER gebunden war, darf nicht mit Puffern anderer Ziele gemischt werden. Kopien zwischen Index- und Vertex-Puffern schlagen deshalb fehl.
  • Nimm bevorzugt COPY_READ_BUFFER/COPY_WRITE_BUFFER, damit bestehende Bindungen (z. B. am VAO) unverändert bleiben.
  • Die Kopie läuft asynchron auf der GPU und blockiert nicht. Ein direkt folgendes getBufferSubData() erzwingt dagegen eine Synchronisierung und kann das Programm ausbremsen.