If we are completely done wanting to have a Three.js scene exist in an application (f.e., suppose we have a single page application where the page was rendering a Three.js scene, and we’ve switched to another view and we no longer need to render any Three.js scene), how do we properly and completely clean the scene up so that no traces of it left behind in memory?
Here’s some things I can think of:
Call dispose() on all geometries
Call dispose() on all materials
What else? What’s the comprehensive list of things to do to completely remove any and all Three.js rendering from a page (and even THREE itself if it is no longer needed)?
I think the best way to do it is to let the javascript garbage collector handle this for you.
All you have to do is make sure there are no references left to your scene object and any objects inside it.
If you have some class in your application that holds references to materials, for example, you’ll have to remove them by simply calling delete this.materials['my_mateiral_name_or_id']. The same thing applies to anything that is in your scene that might live elsewhere in your application.
I think this is essentially what dispose() does for you internally.
Lastly, forcing a context loss on the webgl context and getting rid of the renderer afterward will ensure you’ll be freeing up GPU resources, and allowing you to initialize another renderer afterward without resource usage being stacked up.
@Harold Simply losing rederences I don’t think always works. For example, if you keep a reference to a renderer in order to use it later to render a different scene, not having called dispose() will have leaked geometries/materials/textures/etc into the GPU, right?
As for losing a reference to everything (f.e. Three, canvases, gl contexts, all meshes/geometries/materials/etc) will that successfully clean things up without having to call dispose() on anything? @pailhead@looeee?
Anything that is an object that no longer holds a reference anywhere, will get cleaned up by the GC.
A texture is also an object (THREE.Texture), that holds a reference to either Image, Canvas or ImageData or any other kind of image (or video) source.
Nullifying these references (setting the property that holds the reference to null or undefined), makes the dereferenced object eligible for garbage collection. There is no way to “force” this in any other way.
This is effectively what a dispose() function should do.
As for the renderer, calling dispose() should force a “context loss” that will free up the GL context (and therefore free up GPU resources) once the GC kicks in.
Psuedo dispose() example (not to be taken literally):
this.dispose = function () {
// Iterate through all properties of this object.
Object.keys(this).forEach((key) => {
// Recursively call dispose() if possible.
if (typeof this[key].dispose === 'function') {
this[key].dispose();
}
// Remove any reference.
this[key] = null;
});
}
Of course, you shouldn’t do it like this, but it simply illustrates how a dispose function could work.
If an object holds any reference to another object that is still being used (e.g. a DOM element that is still present), it isn’t eligible for garbage collection and therefore will never be cleaned up.
So, if I lose all references to all Three.js objects, and also the canvas (remove it from DOM), then I suppose I should be okay (I’m guessing losing a reference to canvas plus all Three obects cleans up the GPU too). I’m guessing in that case I don’t need to call any dispose functions, and that dispose is useful only if we’re keeping the canvas and the renderer, and just want to change the scene to show other stuff and to unshow some other stuff (f.e. switching views in the scene).
Testing takes time. I figured someone might know. The docs don’t go into detail on that (yet), and reading source will take some time. I reading little by little…
Dealing with scene cleanup is one of the grey box areas of three.js currently. There isn’t really good documentation and it does take a lot of time to test.
I’m not saying that it should be you that does this testing… But it would be great if somebody wrote a comprehensive guide and added it to the docs.
Alright, the following is what I have so far, and I’m doing it to see if there’s any difference in memory use compared to not doing it (my guess is there won’t be and the Garbage Collector will work great, but just in case). Am I missing anything?
console.log('dispose renderer!')
renderer.dispose()
scene.traverse(object => {
if (!object.isMesh) return
console.log('dispose geometry!')
object.geometry.dispose()
if (object.material.isMaterial) {
cleanMaterial(object.material)
} else {
// an array of materials
for (const material of object.material) cleanMaterial(material)
}
})
const cleanMaterial = material => {
console.log('dispose material!')
material.dispose()
// dispose textures
for (const key of Object.keys(material)) {
const value = material[key]
if (value && typeof value === 'object' && 'minFilter' in value) {
console.log('dispose texture!')
value.dispose()
}
}
}
Hello together. Thanks for all input here. I try to find out what’s the basic proper way today to free ressources of a not used 3d-file in a three.js scene. Let’s say add only one simple model to the scene how it is explained here. Is it possible to say the code from trusktr three post above is common sense today? I mean this dialogue here seems to be the latest discussion of this topic but older posts looks like if they handle this situation in another way with a more extensive code… I first aksed this on stackoverflow.
@naonao_naonvl No idea, because we can’t see your code. Did you try to dispose something, and then use it?
The error stack trace in your screenshot is not from the dispose functions, those errors are happening in Three.js code because something is null. If it has to do with the dispose functions, then my blind guess is that disposing stuff makes something null (in a newer version of Three???) and then you are trying to use it?
That’s random guess. I could be completely wrong. The only way you can get good help is posting live code examples.
i figure it out,i found a way to remove everything from scene is setting scene to null i found that on stackoverflow that is why autoUpdate can’t be read. I remove that and just using your code above,thanks
Here is the code that works for me, adjust your variable accordingly. for scene and renderer.
// dispose off all the 3D scene elements
const cleanMaterial = material => {
material.dispose()
// dispose textures
for (const key of Object.keys(material)) {
const value = material[key]
if (value && typeof value === 'object' && 'minFilter' in value) {
value.dispose()
}
}
}
this.scene.traverse(object => {
if (!object.isMesh) return
object.geometry.dispose()
if (object.material.isMaterial) {
cleanMaterial(object.material)
} else {
// an array of materials
for (const material of object.material) cleanMaterial(material)
}
})
this.renderer.dispose()
this.scene.clear()
this.renderer.forceContextLoss()