Memory leak in TilemapLayerWebGLRenderer
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 40.3k
- Forks
- 7.2k
- PR merge metrics
- No merged PRs in 30d
Description
Version
- Phaser Version: 4.0.0
- Operating system: MacOS Monterey v12.7.6
- Browser: all browsers
Description
When Phaser.Game is destroyed and a new instance is created on the same page, retained heap memory for TilemapLayerWebGLRenderer nearly doubles with each cycle.
Example Test Code (in phaser sandbox)
class MainScene extends Phaser.Scene {
constructor() {
super({ key: "MainScene" });
}
preload() {
this.load.setBaseURL('https://cdn.phaserfiles.com/v385');
this.load.image('walls_1x2', 'assets/tilemaps/tiles/walls_1x2.png');
}
create() {
const map = this.make.tilemap({ width: 200, height: 200, tileWidth: 32, tileHeight: 32 });
const tiles = map.addTilesetImage('walls_1x2', null, 32, 64);
const layer = map.createBlankLayer('layer1', tiles);
layer.randomize(0, 0, map.width, map.height, [ 0, 1, 2, 3, 4, 5, 6, 7 ]);
}
}
let game = null;
const getGame = () => game;
function createGame() {
game = new Phaser.Game({
type: Phaser.AUTO,
width: 800,
height: 800,
backgroundColor: '#111111',
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
},
scene: [ MainScene ]
});
}
function destroyGame(gameInstance) {
gameInstance.events.once('destroy', () => {
game = null;
});
gameInstance.destroy(true);
}
setInterval(() => {
if (!getGame()) {
createGame();
} else {
destroyGame(getGame());
}
}, 6000);
Additional Information
Possible issue at src/tilemaps/TilemapLayerWebGLRenderer.js :
Module-level singleton retains glTexture reference across Phaser.Game instances. Declares two module-level objects that persist for the entire lifetime of the JavaScript module (i.e. they are never GC'd).
During every render call, the live glTexture from the active Tileset is written into texturerData:
texturerData.frame.source.glTexture = tileset.glTexture;
When WebGLRenderer.destroy() is called (via game.destroy()), it correctly iterates glTextureWrappers and calls .destroy() on each. However, texturerData.frame.source.glTexture is never nulled out.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/tilemaps/TilemapLayerWebGLRenderer.js and inspect the module-level objects and the texturerData.frame.source.glTexture assignment during rendering. Reproduce the cycle with the provided Phaser.Game creation and destruction example, then verify with heap measurements that retained TilemapLayerWebGLRenderer memory no longer grows after repeated cycles.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- performance, web-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100