#7379·phaser

DynamicTexture constructor leaks a WebGL texture on every create/destroy cycle (4.2.1)

Author: rmartoneCreated Sep 14, 2026Updated Sep 14, 2026

Version

  • Phaser Version: 4.2.1
  • Operating system: MAC OSX (latest)
  • Browser: Chrome (latest)

Description

In WebGL mode, creating and removing a DynamicTexture leaves one WebGL texture wrapper retained in renderer.glTextureWrappers.

The DynamicTexture constructor allocates a texture through the base Texture/TextureSource constructor, then replaces frame.source.glTexture with this.drawingContext.texture without disposing the original wrapper.

Removing the DynamicTexture destroys the replacement, but the original remains registered with the renderer. In an isolated reproduction, the original WebGL texture also remains valid according to gl.isTexture().

Expected: after each create/remove cycle, the wrapper count returns to its baseline.

Actual: the retained wrapper count increases by one per cycle.

Example Test Code

Run against unpatched Phaser 4.2.1:

xml
<!doctype html>
<html>
<body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>
<script>
new Phaser.Game({
    type: Phaser.WEBGL,
    width: 64,
    height: 64,
    audio: { noAudio: true },
    scene: {
        create: function () {
            const renderer = this.game.renderer;
            const baseline = renderer.glTextureWrappers.length;

            for (let i = 0; i < 4; i++) {
                const key = `texture-leak-${i}`;
                this.textures.addDynamicTexture(key, 8, 8);
                this.textures.remove(key);

                console.log(
                    `Cycle ${i + 1}: retained wrappers =`,
                    renderer.glTextureWrappers.length - baseline
                );
            }
        }
    }
});
</script>
</body>
</html>

Expected counts: 0, 0, 0, 0. Actual counts: 1, 2, 3, 4.

Additional Information

Disposing the original texture before replacing it in the DynamicTexture constructor eliminates the growth in the isolated reproduction:

javascript
if (!isCanvas)
{
    var frame = this.get();
    renderer.deleteTexture(frame.source.glTexture);
    frame.source.glTexture = this.drawingContext.texture;
}

Related history: #6669 reports a DynamicTexture create/destroy memory leak in Phaser 3.70. This report concerns the DrawingContext replacement path in 4.2.1; I have not established whether it shares the same underlying cause.