Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Shader.preDestroy leaks its ShaderQuad's vertex buffer and VAO on every create/destroy cycle (4.2.1)

Aperta Adatta ai principianti
#7,394 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 2 giorni

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
86/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
javascript
Ambito
game-dev

Direzione di ricerca

Parti da preDestroy nel sorgente del game object Shader (src/gameobjects/Shader/Shader.js) e traccia renderNode fino allo ShaderQuad, al suo WebGLVertexBufferLayoutWrapper e al ProgramManager. Leggi come WebGLRenderer registra e rilascia i wrapper (glBufferWrappers, glVAOWrappers, deleteBuffer) per rispecchiare quel pattern per i VAO, eventualmente aggiungendo un deleteVAO. L'issue è risolta quando la sonda HTML dell'issue stampa 0, 0, 0, 0 per entrambi i contatori, incluso il caso di distruzione prima del primo disegno, senza liberare il programma condiviso né genericQuadIndexBuffer.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

In WebGL mode, every Shader game object builds its own ShaderQuad render node. The node allocates a vertex buffer

(WebGLVertexBufferLayoutWrapper → renderer.createVertexBuffer, registered in renderer.glBufferWrappers).

On first draw, its ProgramManager creates a VAO per linked program

(renderer.createVAO, registered in renderer.glVAOWrappers).

Shader.preDestroy only nulls this.renderNode. Neither the vertex buffer nor the VAO is released, so both stay registered with the renderer, and their GL objects stay allocated, for the life of the game.

This adds up quickly when shaders are created and destroyed repeatedly. For example, a UI button that draws its background with a Shader and lives in an overlay that is rebuilt on every show leaks one buffer and one VAO per button per show.

Expected: after each create/draw/destroy cycle, the buffer and VAO wrapper counts return to their baseline.

Actual: both counts grow by one per cycle. A shader destroyed before its first draw still leaks its vertex buffer.

Example Test Code

Run against unpatched Phaser 4.2.1:

<!doctype html>
<html>
<body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>
<script>
const config = {
    name: 'LeakProbe',
    fragmentSource: `
        precision mediump float;
        varying vec2 outTexCoord;
        void main() { gl_FragColor = vec4(outTexCoord, 0.0, 1.0); }
    `
};

new Phaser.Game({
    type: Phaser.WEBGL,
    width: 128,
    height: 128,
    audio: { noAudio: true },
    scene: {
        create: function () {
            const renderer = this.game.renderer;
            let baseBuffers = null;
            let baseVAOs = null;
            let shader = null;
            let cycle = 0;

            // Each cycle: create a Shader, let it draw once (which creates its VAO), then destroy it.
            this.game.events.on('postrender', () => {
                if (baseBuffers === null) {
                    baseBuffers = renderer.glBufferWrappers.length;
                    baseVAOs = renderer.glVAOWrappers.length;
                } else if (shader) {
                    shader.destroy();
                    shader = null;
                    cycle++;
                    console.log(
                        `Cycle ${cycle}: retained buffers = ${renderer.glBufferWrappers.length - baseBuffers},`,
                        `retained VAOs = ${renderer.glVAOWrappers.length - baseVAOs}`
                    );
                }

                if (cycle < 4) {
                    shader = this.add.existing(new Phaser.GameObjects.Shader(this, config, 64, 64, 64, 64));
                }
            });
        }
    }
});
</script>
</body>
</html>

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

Additional Information

Releasing the render node's own resources in Shader.preDestroy eliminates the growth:

preDestroy: function ()
{
    var renderNode = this.renderNode;

    if (renderNode)
    {
        //  Release this Shader's own GL resources. The program and the
        //  index buffer are shared, so they stay.
        var renderer = renderNode.renderer;
        var programs = renderNode.programManager.programs;

        for (var key in programs)
        {
            var vao = programs[key].vao;
            var vaoIndex = renderer.glVAOWrappers.indexOf(vao);

            if (vaoIndex !== -1)
            {
                renderer.glVAOWrappers.splice(vaoIndex, 1);
            }

            vao.destroy();
        }

        renderer.deleteBuffer(renderNode.vertexBufferLayout.buffer);
    }

    this.renderNode = null;

    // ... existing preDestroy body unchanged
}

The shader program is shared through ShaderProgramFactory's key cache, and the index buffer is the renderer's shared genericQuadIndexBuffer, so neither should be deleted here. WebGLRenderer has no VAO counterpart to deleteBuffer, which is why the fix removes the VAO from glVAOWrappers directly. A deleteVAO method on the renderer might be the cleaner upstream shape.

The same leak affects any ShaderQuad-based object that is destroyed; Shader is just the most direct way to reproduce it. The master branch's Shader.preDestroy is unchanged from 4.2.1.

Lingua principale
JavaScript
Stelle
40.4k
Fork
7.2k
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di phaserjs/phaser

Tutte le issue di phaserjs/phaser

Issue simili

Altre issue su JavaScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.