Stack overflow with plugin material
See original GitHub issueI’m getting a RangeError: Maximum call stack size exceeded
with material plugins. This happens when this.markAllAsDirty()
is called in set isEnabled()
, using pretty much the same code from https://playground.babylonjs.com/#P8B91Z#32. This seems to happen only in larger scenes and it’s heavily influenced by loading multiple models and the order they are loaded, so it is difficult to reproduce.
However the problem seems pretty clear: calling the dirty callbacks on set isEnabled()
seems to generate a big stack trace, apparently from an infinite loop. From what I’m seeing it calls the plugin constructor again and again, perhaps because of repeated observers? This is a compiled code dump, but one can easily see the loop with the dirty calls.
CausticPluginMaterial | @ | Underwaterb.vue:95
-- | -- | --
| (anonymous) | @ | Underwaterb.vue:696
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| get | @ | babylon.js:16
| e.getMaterial | @ | babylon.js:16
| e._markAllSubMeshesAsDirty | @ | babylon.js:16
| e._markAllSubMeshesAsAllDirty | @ | babylon.js:16
| set | @ | Underwaterb.vue:111
| (anonymous) | @ | Underwaterb.vue:697
| (anonymous) | @ | babylon.js:16
| e.notifyObservers | @ | babylon.js:16
| e | @ | babylon.js:16
| t | @ | babylon.js:16
| t | @ | babylon.js:16
| a.a.DefaultMaterialFactory | @ | babylon.js:16
| Promise.then (async) | |
| e._loadAsync | @ | babylonjs.loaders.min.js:1
| (anonymous) | @ | babylonjs.loaders.min.js:1
| Promise.then (async) | |
| e.importMeshAsync | @ | babylonjs.loaders.min.js:1
| (anonymous) | @ | babylonjs.loaders.min.js:1
| Promise.then (async) | |
| e.importMeshAsync | @ | babylonjs.loaders.min.js:1
| (anonymous) | @ | babylon.js:16
| p | @ | babylon.js:16
| (anonymous) | @ | babylonjs.loaders.min.js:1
| Promise.then (async) | |
| (anonymous) | @ | babylonjs.loaders.min.js:1
| (anonymous) | @ | babylon.js:16
| m | @ | babylon.js:16
| XMLHttpRequest.send (async) | |
| e.send | @ | babylon.js:16
| p | @ | babylon.js:16
| p | @ | babylon.js:16
| P | @ | babylon.js:16
| R | @ | babylon.js:16
| t._loadFile | @ | babylon.js:16
| e._loadFile | @ | babylonjs.loaders.min.js:1
| e.loadFile | @ | babylonjs.loaders.min.js:1
| g | @ | babylon.js:16
| e | @ | babylon.js:16
| xe.a.OfflineProviderFactory | @ | babylon.js:16
| e._LoadData | @ | babylon.js:16
| e.ImportMesh | @ | babylon.js:16
| t.runTask | @ | babylon.js:16
| e.run | @ | babylon.js:16
| e._runTask | @ | babylon.js:16
| e.load | @ | babylon.js:16
| Underwater | @ | Underwaterb.vue:224
| mounted | @ | Underwaterb.vue:1866
| invokeWithErrorHandling | @ | vue.runtime.esm.js:1863
| callHook | @ | vue.runtime.esm.js:4235
| insert | @ | vue.runtime.esm.js:3158
| invokeInsertHook | @ | vue.runtime.esm.js:6390
| patch | @ | vue.runtime.esm.js:6609
| Vue._update | @ | vue.runtime.esm.js:3963
| updateComponent | @ | vue.runtime.esm.js:4075
| get | @ | vue.runtime.esm.js:4495
| run | @ | vue.runtime.esm.js:4570
| flushSchedulerQueue | @ | vue.runtime.esm.js:4326
| (anonymous) | @ | vue.runtime.esm.js:1989
| flushCallbacks | @ | vue.runtime.esm.js:1915
| Promise.then (async) | |
| timerFunc | @ | vue.runtime.esm.js:1942
| nextTick | @ | vue.runtime.esm.js:1999
| queueWatcher | @ | vue.runtime.esm.js:4418
| update | @ | vue.runtime.esm.js:4560
| Vue.$forceUpdate | @ | vue.runtime.esm.js:3984
| (anonymous) | @ | index.js:244
| (anonymous) | @ | index.js:242
| (anonymous) | @ | index.js:119
| ./components/Underwaterb.vue | @ | Underwaterb.vue?869a:29
| __webpack_require__ | @ | bootstrap:853
| hotApplyInternal | @ | bootstrap:749
| hotApply | @ | bootstrap:411
| cb | @ | process-update.js:76
| (anonymous) | @ | process-update.js:91
| Promise.then (async) | |
| check | @ | process-update.js:90
| push../node_modules/webpack-hot-middleware/process-update.js.module.exports | @ | process-update.js:52
| processMessage | @ | client.js:279
| handleMessage | @ | client.js:139
| handleMessage | @ | client.js:102
My apologies, but I couldn’t get a faithful way to repro this on a PG. Even in my application it doesn’t happen 100% of the time, but it’s often enough (over half of the time). If I would guess (and perhaps be completely wrong), this seems to be caused when the plugin is instantiated as other models are still being loaded in other parallel promises with ImportMeshAsync()
. But calling isEnabled
in the factory makes the problem happen more often:
BABYLON.RegisterMaterialPlugin('Caustic', (material) => {
material.caustic = new CausticPluginMaterial(material);
material.caustic.isEnabled = true;
return material.caustic;
});
Temporary fix: not calling this.markAllAsDirty()
on set isEnabled
fixes the issue with no apparent side effects (I’m guessing something else calls dirty() in my case). This is perfectly fine to me, but since this is the reference implementation on the documentation I’m reporting the issue to see what to do. Do we just change the doc examples to not call markAllAsDirty()
? The examples seem to work just the same without that call.
I’m happy to debug or get more information on request.
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (6 by maintainers)
Top GitHub Comments
No worries! you’re part of the family so you have a special treatment 😉
My apologies for not posting on the forum first, since this was a straight bug I thought it’d just pollute things there. It won’t happen again.