Mesh.dispose trigger Uncaught TypeError: Cannot read property 'meshes' of null

Hello,
When I dispose meshes with mesh.dispose();
disposing meshes give me error:

Uncaught TypeError: Cannot read property 'meshes' of null
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)
    at Array.e.splice (babylonjs.js:16)

Here is my Code

    mesh.onDispose = function(){
        mesh = null;
    };
    mesh.dispose();

I unminified babylon.js and the issue is from here:

return (
    0 === e.length &&
        t.getScene().meshes.forEach(function (e) {
            e._markSubMeshesAsLightDirty();
         }),
      o);

It seems the scene is already disposed when you dispose the mesh, but without a repro in the Playground it’s going to be hard to say more.

2 Likes

It happen when only there is a mesh remains in scene. If more than one mesh exist, error not triggered.
I don’t have any scene.dispose or engine.dispose in my code, but it was working till a few weeks ago, I don’t know what happened? I did a few steps to optimize me scene from this page
It is a huge project, but your help let me think checking where scene customized. I will try to make it a Playground. Thanks @Evgeni_Popov

1 Like

I found the problem, but it is so weird!!!
One of my optimizations was reduce mapSize of shadowGenerator by FPS after loading each new mesh and this is the code:

        var FPS = engine.getFps().toFixed();
        shadowGenerator.mapSize = Math.min(512, parseInt(512 * (FPS / 60))) - parseInt(isSmallDevice() ? 64 : 0);

When I comment it no more errors found.
Can anyone give me another solution to reduce mapSize on lower power devices?

Are you doing this each frame? You should not, it recreates a number of textures under the hood, and the system may not have time to render a new frame that you already change for a new size. Also, you should not change the size if the difference in fps is not significant enough from the last time you changed the size. There’s no point to change from 503 to 494 for eg (if fps changes from 59 to 58).

New explore: the problem is from:
var FPS = engine.getFps().toFixed();
not the mapSize

@Evgeni_Popov

Are you doing this each frame?

No, After user adds a new mesh.

I played around with mapSize of shadowGenerator, it has huge effect on cpu usage.
My point is to reduce mapSize based on user’s device, doesn’t matter when and where for me, I just want higher quality for better devices, and higher fps for lower devices.
Thanks for considering the issue

I guess you will need to provide a repro in the Playground, I can’t understand why engine.getFps().toFixed() could be a problem.

Ok, I will try to make a Playground
@Evgeni_Popov

1 Like

At last I made it,
It happen when a mesh added and mesh accepts shadow, and after that change mapSize of shadowGenerator then remove mesh.
And I successfully made playground (check console)
Note: If another mesh exist in scene that accepts shadow, error will not shows up.

Thanks for the repro, here’s the PR that will fix the problem:

1 Like

Thanks @Evgeni_Popov , I’m not using .ts and so I did my own changes on babaylonjs.js file like so:
In case someone need:

(e.prototype.recreateShadowMap = function () {
                            var e = this._shadowMap;
                            if (e) {
                                var t = e.renderList;
                                this._disposeRTTandPostProcesses(), this._initializeGenerator(), (this.filter = this.filter), this._applyFilterValues();
                                //(this._shadowMap.renderList = t);
                                 if (t) {
                                    // Note: don't do this._shadowMap!.renderList = renderList;
                                    // The renderList hooked array is accessing the old RenderTargetTexture (see RenderTargetTexture._hookArray), which is disposed at this point (by the call to _disposeRTTandPostProcesses)
                                    if (!this._shadowMap.renderList) {
                                        this._shadowMap.renderList = [];
                                    }
                                    for (const mesh of t) {
                                        this._shadowMap.renderList.push(mesh);
                                    }
                                } else {
                                    this._shadowMap.renderList = null;
                                }
                            }
                        })