Skip to content

Memory leak in TilemapLayerWebGLRenderer #7296

@FranciscoCaetano88

Description

@FranciscoCaetano88

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions