Fix WebGL shader and texture memory leak on sketch removal by Sanchit2662 · Pull Request #8418 · processing/p5.js

Summary

This PR fixes a critical WebGL lifecycle issue where shader programs and textures were never explicitly released from GPU memory when a sketch was destroyed. As a result, applications that repeatedly create and remove WebGL sketches (e.g. hot-reload editors, instance-mode apps, long-running installations) experienced unbounded GPU memory growth.

The fix introduces explicit dispose() methods for WebGL-backed resources and registers a renderer-level cleanup hook that is automatically invoked when sketch.remove() is called.


Impact

Before

  • WebGL shaders, programs, and textures remain allocated after remove()
  • GPU memory grows unboundedly in multi-sketch or hot-reload scenarios
  • No user-level workaround is possible

After

  • All WebGL shader and texture resources are explicitly released
  • GPU memory remains stable across repeated sketch creation/destruction
  • Instance-mode and long-running WebGL sketches behave reliably

Changes

1. p5.Shader.dispose()

Adds an explicit teardown path for shader GPU resources.

dispose() {
  if (!this._gl || !this._glProgram) return;

  this._glProgram = null;
}
  • Properly detaches shaders before deletion
  • Deletes both shader objects and the linked program
  • Guards against double deletion or uninitialized shaders

2. p5.Texture.dispose()

Ensures GPU textures are freed when no longer needed.

dispose() {
  if (this.glTex && !this.isFramebufferTexture) {
  }
}
  • Releases GPU texture memory
  • Skips framebuffer-managed textures to avoid double-freeing

3. Renderer-level cleanup hook (p5.RendererGL)

Introduces a centralized cleanup method that disposes all WebGL resources owned by the renderer.

Key resources cleaned:

  • Default and user shaders
  • Cached textures (this.textures)
  • Filter shaders and filter layers
  • Framebuffers and retained-mode buffers
  • Empty texture singleton

The cleanup is automatically invoked on sketch teardown:

this._pInst.registerMethod(
  'remove',
  this._cleanupWebGLResources.bind(this)
);

PR Checklist

  • npm run lint passes