Start · Sprachen · JavaScript · Referenz · WebGLTransformFeedback

WebGLTransformFeedback

Schnittstelle

Undurchsichtiges WebGL-2-Objekt, das Transform Feedback steuert, also das Einfangen der vom Vertex-Shader erzeugten Ausgabewerte in Pufferobjekte.

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

Signatur

interface WebGLTransformFeedback extends WebGLObject

Beschreibung

WebGLTransformFeedback gehört zur WebGL-2-API und steht für ein Transform-Feedback-Objekt. Transform Feedback bedeutet: Die out-Variablen eines Vertex-Shaders werden direkt in einen oder mehrere WebGLBuffer geschrieben, statt nur an den Rasterizer weitergereicht zu werden. So lassen sich Berechnungen auf der GPU ausführen und ihre Ergebnisse wiederverwenden, ohne dass Daten über die CPU laufen.

Typische Einsatzfälle sind GPU-Partikelsysteme (Position und Geschwindigkeit werden jedes Bild im Shader fortgeschrieben und per „Ping-Pong“ zwischen zwei Puffern gewechselt), Physik- und Simulationsschritte sowie allgemeine GPGPU-Aufgaben, für die man sonst Compute-Shader bräuchte, die es in WebGL nicht gibt.

Das Objekt hat selbst keine Eigenschaften oder Methoden. Man erzeugt es mit gl.createTransformFeedback(), bindet es mit gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf) und hängt Ausgabepuffer mit gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, index, buffer) an. Welche Shader-Ausgaben erfasst werden, legt gl.transformFeedbackVaryings() fest. Das muss vor gl.linkProgram() aufgerufen werden. Die Aufnahme läuft zwischen gl.beginTransformFeedback() und gl.endTransformFeedback(). Mit gl.pauseTransformFeedback() und gl.resumeTransformFeedback() lässt sie sich unterbrechen und fortsetzen.

Freigegeben wird das Objekt mit gl.deleteTransformFeedback(tf). Mit gl.isTransformFeedback(tf) prüft man, ob es sich um ein gültiges Objekt handelt.

Rückgabewert

Typ
WebGLTransformFeedback
Beschreibung
Wird von WebGL2RenderingContext.createTransformFeedback() zurückgegeben. Das Objekt ist undurchsichtig und hat keine eigenen Eigenschaften oder Methoden.

Beispiele

Transform-Feedback-Objekt erzeugen und binden

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL 2 wird nicht unterstützt');

const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);

console.log(tf instanceof WebGLTransformFeedback);
console.log(gl.isTransformFeedback(tf));
true true

Werte auf der GPU verdoppeln und zurücklesen

const gl = document.createElement('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 highp float;
out vec4 color;
void main() { color = vec4(0.0); }`;

const compile = (type, src) => {
  const s = gl.createShader(type);
  gl.shaderSource(s, src);
  gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
  return s;
};

const program = gl.createProgram();
gl.attachShader(program, compile(gl.VERTEX_SHADER, vsSource));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, fsSource));
// Muss VOR linkProgram aufgerufen werden!
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 Rendern nötig
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, input.length);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);

gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, 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]

Aufnahme pausieren und Objekt freigeben

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

// ... Programm, Puffer und bindBufferBase wie oben einrichten ...
// gl.beginTransformFeedback(gl.POINTS);
// gl.drawArrays(gl.POINTS, 0, 100);
// gl.pauseTransformFeedback();   // z. B. für einen Zwischen-Draw ohne Aufnahme
// gl.resumeTransformFeedback();
// gl.endTransformFeedback();

gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
gl.deleteTransformFeedback(tf);
console.log(gl.isTransformFeedback(tf));
false

// Wichtig · Fallstricke

  • Nur WebGL 2: Transform Feedback gibt es nur mit einem webgl2-Kontext. WebGL 1 bietet dafür keine Erweiterung. Alle modernen Browser unterstützen es. Safari erst ab Version 15.
  • gl.transformFeedbackVaryings() muss vor gl.linkProgram() aufgerufen werden, sonst werden die Ausgaben nicht erfasst.
  • Ein Puffer darf nicht gleichzeitig als Ausgabe (TRANSFORM_FEEDBACK_BUFFER) und als Eingabe (z. B. ARRAY_BUFFER des Vertex-Attributs) gebunden sein. Sonst meldet der Browser INVALID_OPERATION. Für Simulationen deshalb zwei Puffer im Wechsel („Ping-Pong“) verwenden.
  • Der Primitivtyp von drawArrays muss zu dem Modus passen, der bei beginTransformFeedback angegeben wurde (POINTS, LINES oder TRIANGLES).
  • Solange Transform Feedback aktiv ist, darf man weder Programm noch Puffer-Bindungen ändern. Ohne Fragment-Ausgabe spart gl.RASTERIZER_DISCARD Rechenzeit.
  • Ein synchrones getBufferSubData direkt nach dem Zeichnen blockiert die Pipeline. Besser ist es, mit gl.fenceSync() und clientWaitSync zu warten, bis die GPU fertig ist.
  • Geht der Kontext verloren (webglcontextlost), werden alle Transform-Feedback-Objekte ungültig und müssen neu erzeugt werden.