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

WebGL2RenderingContext.endTransformFeedback()

Methode

Beendet eine mit <code>beginTransformFeedback()</code> gestartete Transform-Feedback-Operation und löst die Bindung der Ausgabepuffer für das Aufzeichnen.

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

Signatur

WebGL2RenderingContext.prototype.endTransformFeedback()

Beschreibung

Die Methode endTransformFeedback() schließt eine Transform-Feedback-Operation ab. Beim Transform Feedback schreibt der Vertex-Shader seine out-Varyings direkt in Puffer-Objekte (TRANSFORM_FEEDBACK_BUFFER), statt sie nur an den Rasterizer weiterzugeben. So lassen sich z. B. Partikelsysteme, Physik-Simulationen oder Geometrie-Transformationen vollständig auf der GPU berechnen.

Der typische Ablauf ist: Transform-Feedback-Objekt binden (bindTransformFeedback()), Ausgabepuffer mit bindBufferBase() anhängen, beginTransformFeedback(primitiveMode) aufrufen, zeichnen (drawArrays()), dann endTransformFeedback(). Erst danach dürfen die Puffer wieder als normale Vertex-Puffer gelesen oder mit getBufferSubData() ausgelesen werden.

Nach dem Aufruf ist das Transform Feedback nicht mehr aktiv; das Programm darf wieder gewechselt werden und das gebundene Transform-Feedback-Objekt kann neu gebunden werden. Soll eine Aufzeichnung nur kurz unterbrochen und später fortgesetzt werden, verwendet man stattdessen pauseTransformFeedback() und resumeTransformFeedback().

Beispiele

Vollständiger Transform-Feedback-Durchlauf (Werte verdoppeln)

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

const vsSource = `#version 300 es
in float inValue;
out float outValue;
void main() {
  outValue = inValue * 2.0;
}`;
const fsSource = `#version 300 es
precision mediump float;
out vec4 color;
void main() { color = vec4(1.0); }`;

const compile = (type, src) => {
  const s = gl.createShader(type);
  gl.shaderSource(s, src);
  gl.compileShader(s);
  return s;
};

const program = gl.createProgram();
gl.attachShader(program, compile(gl.VERTEX_SHADER, vsSource));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, fsSource));
// Varyings VOR dem Linken festlegen
gl.transformFeedbackVaryings(program, ['outValue'], gl.SEPARATE_ATTRIBS);
gl.linkProgram(program);
gl.useProgram(program);

const input = new Float32Array([1, 2, 3, 4]);
const inBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, inBuf);
gl.bufferData(gl.ARRAY_BUFFER, input, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(program, 'inValue');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 1, gl.FLOAT, false, 0, 0);

const outBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, outBuf);
gl.bufferData(gl.ARRAY_BUFFER, input.byteLength, gl.STATIC_READ);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, outBuf);

gl.enable(gl.RASTERIZER_DISCARD); // kein Rendering nötig
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, input.length);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);

// Puffer lösen, bevor er gelesen wird
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

const result = new Float32Array(input.length);
gl.bindBuffer(gl.ARRAY_BUFFER, outBuf);
gl.getBufferSubData(gl.ARRAY_BUFFER, 0, result);
console.log(Array.from(result));
[2, 4, 6, 8]

Partikel-Update mit Ping-Pong-Puffern pro Frame

// Annahme: gl, updateProgram (mit transformFeedbackVaryings),
// sowie je zwei VAOs/Puffer/TF-Objekte sind bereits eingerichtet.
let read = 0;
const PARTICLE_COUNT = 10000;

const step = () => {
  const write = 1 - read;
  gl.useProgram(updateProgram);
  gl.bindVertexArray(vaos[read]);
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tfs[write]);

  gl.enable(gl.RASTERIZER_DISCARD);
  gl.beginTransformFeedback(gl.POINTS);
  gl.drawArrays(gl.POINTS, 0, PARTICLE_COUNT);
  gl.endTransformFeedback(); // Aufzeichnung abschließen
  gl.disable(gl.RASTERIZER_DISCARD);

  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
  read = write; // Puffer tauschen
  requestAnimationFrame(step);
};
requestAnimationFrame(step);

Fehlerprüfung: end ohne begin

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

gl.endTransformFeedback(); // kein aktives Transform Feedback
console.log(gl.getError() === gl.INVALID_OPERATION);
true

// Wichtig · Fallstricke

  • INVALID_OPERATION: Wird ausgelöst, wenn beim Aufruf kein Transform Feedback aktiv ist (also kein vorheriges beginTransformFeedback()).
  • Ein pausiertes Transform Feedback darf direkt mit endTransformFeedback() beendet werden; ein erneutes resumeTransformFeedback() ist nicht nötig.
  • Solange Transform Feedback aktiv ist, darf das Programm nicht gewechselt (useProgram) oder neu gelinkt werden, und die Ausgabepuffer dürfen nicht gleichzeitig als Vertex-Eingabe gebunden sein – sonst gibt es Fehler.
  • Vor dem Auslesen mit getBufferSubData() sollte der Puffer vom TRANSFORM_FEEDBACK_BUFFER-Bindungspunkt gelöst werden; WebGL verbietet, dass derselbe Puffer gleichzeitig an Transform Feedback und einen anderen Bindungspunkt gebunden ist.
  • getBufferSubData() ist synchron und kann die GPU-Pipeline blockieren. Für bessere Performance einen fenceSync() setzen und asynchron abwarten.
  • Kompatibilität: Nur im webgl2-Kontext verfügbar (nicht in WebGL 1). Unterstützt in allen aktuellen Browsern, Safari ab Version 15.