Signatur
Beschreibung
Mit invalidateFramebuffer() teilst du dem Treiber mit, dass der Inhalt eines oder mehrerer Anhänge (Farb-, Tiefen- oder Stencil-Puffer) des aktuell an target gebundenen Framebuffers nicht mehr gebraucht wird. Danach ist der Inhalt dieser Anhänge undefiniert. Die GPU muss ihn also weder aufbewahren noch in den Hauptspeicher zurückschreiben.
Die Methode ist vor allem ein Performance-Hinweis, besonders für mobile GPUs mit kachelbasiertem Rendering (tile-based rendering, z. B. Mali, Adreno, PowerVR, Apple). Solche GPUs rendern in einen schnellen Speicher direkt auf dem Chip und schreiben die Ergebnisse danach in den Grafikspeicher zurück. Invalidierst du zum Beispiel den Tiefenpuffer am Ende eines Render-Durchgangs, fällt dieses Zurückschreiben weg. Das spart Speicherbandbreite und damit Energie.
Typische Einsatzfälle:
- Tiefen- und Stencil-Puffer nach dem letzten Zeichenaufruf eines Durchgangs verwerfen.
- Einen Multisample-Renderbuffer nach
blitFramebuffer()verwerfen. - Zu Beginn eines Frames den alten Inhalt verwerfen, wenn sowieso alles neu gezeichnet wird. Damit entfällt das Laden aus dem Speicher.
Soll nur ein rechteckiger Teilbereich verworfen werden, gibt es invalidateSubFramebuffer().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Bindungspunkt des Framebuffers: gl.FRAMEBUFFER, gl.DRAW_FRAMEBUFFER (gleichwertig zu FRAMEBUFFER) oder gl.READ_FRAMEBUFFER. |
|
| $attachments Pflicht | GLenum[] | Array der zu verwerfenden Anhänge. Bei einem eigenen Framebuffer-Objekt: gl.COLOR_ATTACHMENT0 bis gl.COLOR_ATTACHMENTn, gl.DEPTH_ATTACHMENT, gl.STENCIL_ATTACHMENT oder gl.DEPTH_STENCIL_ATTACHMENT. Beim Standard-Framebuffer (null gebunden): gl.COLOR, gl.DEPTH, gl.STENCIL. |
Rückgabewert
undefined). Fehler meldet die Methode über gl.getError().Beispiele
Tiefenpuffer nach dem Render-Durchgang verwerfen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
// Offscreen-Framebuffer mit Farbtextur und Tiefen-Renderbuffer
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
const colorTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, colorTex);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 512, 512);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTex, 0);
const depthRb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT24, 512, 512);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRb);
gl.viewport(0, 0, 512, 512);
gl.clearColor(0.1, 0.1, 0.1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// ... Zeichenaufrufe ...
// Tiefenwerte werden nicht mehr gebraucht, nicht in den Speicher zurückschreiben
gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.DEPTH_ATTACHMENT]);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
console.log(gl.getError() === gl.NO_ERROR);
Multisample-Puffer nach dem Auflösen per blitFramebuffer verwerfen
const gl = document.querySelector('canvas').getContext('webgl2');
const w = gl.drawingBufferWidth;
const h = gl.drawingBufferHeight;
// MSAA-Framebuffer
const msaaFbo = gl.createFramebuffer();
const msaaColor = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, msaaColor);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, w, h);
gl.bindFramebuffer(gl.FRAMEBUFFER, msaaFbo);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msaaColor);
gl.clearColor(0, 0.5, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// ... Szene zeichnen ...
// In den Standard-Framebuffer auflösen
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, msaaFbo);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.blitFramebuffer(0, 0, w, h, 0, 0, w, h, gl.COLOR_BUFFER_BIT, gl.NEAREST);
// Die Multisample-Daten werden nicht mehr benötigt
gl.invalidateFramebuffer(gl.READ_FRAMEBUFFER, [gl.COLOR_ATTACHMENT0]);
Standard-Framebuffer: Tiefe und Stencil verwerfen
const gl = document.querySelector('canvas').getContext('webgl2', { depth: true, stencil: true });
const renderFrame = () => {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
// ... Szene zeichnen ...
// Beim Standard-Framebuffer heißen die Anhänge COLOR / DEPTH / STENCIL
gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.DEPTH, gl.STENCIL]);
requestAnimationFrame(renderFrame);
};
requestAnimationFrame(renderFrame);
// Wichtig · Fallstricke
- Der Inhalt ist danach undefiniert: Lies einen invalidierten Anhang erst wieder, wenn du ihn neu beschrieben hast, zum Beispiel per
clear()oder durch Zeichnen. Sonst bekommst du Datenmüll oder sichtbare Artefakte. - Anhang-Namen hängen vom Framebuffer ab: Beim Standard-Framebuffer (
null) sind nurgl.COLOR,gl.DEPTHundgl.STENCILgültig. Bei eigenen Framebuffer-Objekten musst duCOLOR_ATTACHMENTi,DEPTH_ATTACHMENTusw. verwenden. Falsche Werte lösenINVALID_ENUMaus, ungültige Farb-IndizesINVALID_OPERATION. - Nur ein Hinweis an den Treiber: Die Methode ist keine Garantie. Auf Desktop-GPUs bringt sie oft wenig oder nichts, auf kachelbasierten Mobil-GPUs spart sie dagegen oft deutlich Bandbreite. Falsch eingesetzt verursacht sie trotzdem Darstellungsfehler.
- Rufe die Methode direkt nach dem letzten Zugriff auf, noch bevor du den Framebuffer wechselst. Dann kann der Treiber das Zurückschreiben wirklich einsparen.
- Kompatibilität: Die Methode gibt es nur im WebGL-2-Kontext (
getContext('webgl2')), nicht in WebGL 1. Alle aktuellen Browser unterstützen sie. - Bei
preserveDrawingBuffer: false(Standard) verwirft der Browser den Standard-Framebuffer nach dem Anzeigen ohnehin. Beim Standard-Framebuffer lohnt sich die Methode deshalb vor allem für Tiefe und Stencil.