Left over MeshDepthMaterial after attempting to dispose entire scene

Hey three.js friends!

I’m attempting to perform an entire clean up of my three.js scene to help prevent memory leaks. As a broad overview - Our three.js application will live within a larger, non three.js app. The three.js portion can be created/close/re-created again on demand with different parameters. There’s no guarantee that the textures used during one creation will be needed/used in a future creation. Because of this, I’m looking to completely dispose of each geometry, material and texture when closed and recover the JS heap.

I’m using a cleanup method that looks like this:

cleanUp() {
	let { rafRequestId, actors, modelTemplate, stadium, scene, hemiLight, dirLight, ground, ball, camera, renderer,} =  context;
	const  disposeGeoMatsAndTextures  = (c: Object3D) => {
		const  mesh  =  c  as  Mesh;
		if (mesh.isMesh) {
			mesh.geometry?.dispose(); 
			mesh.material?.dispose();
			if (mesh.material) {
				for (const  key  of  Object.keys(mesh.material)) {
					const  value  =  mesh.material[key];
					if (
					value  &&
					typeof  value  ===  "object"  &&
					value.dispose  &&
					typeof value.dispose  ===  "function"
					) {
						value.dispose();
					}
				}
			}
		}
	};
	cancelAnimationFrame(rafRequestId);
	for (const  actorArray  of  actors.values()) {
		for (const  actor  of  actorArray) {
			actor.dispose();
		}
	}
	for (const  item  of  scene.children) {
		item.traverse(disposeGeoMatsAndTextures);
	}
	if (modelTemplate) {
	modelTemplate.traverse(disposeGeoMatsAndTextures);
	}
	if (stadium) {
		stadium.traverse(disposeGeoMatsAndTextures); 
		const existing = scene.getObjectByName(stadium.name);
		if (existing) {
			existing.traverse(disposeGeoMatsAndTextures);
			scene.remove(existing);
		}
	}
	if (hemiLight) {
		hemiLight.traverse(disposeGeoMatsAndTextures);
	}
	if (dirLight) {
		dirLight.traverse(disposeGeoMatsAndTextures);
	}
	if (ground) {
		ground.traverse(disposeGeoMatsAndTextures);
	}
	if (ball) {
		ball.group.traverse(disposeGeoMatsAndTextures);
		ball.trail.trail.traverse(disposeGeoMatsAndTextures);
	}
	if (camera) {
		camera.traverse(disposeGeoMatsAndTextures);
	}
	renderer.dispose();
	renderer.renderLists.dispose();
	console.log("clean up after", renderer.info);
}

Here you’ll see the before and after of the renderer.info object:

You’ll see we’ve removed all memory.geometries, but we’re left over with quite a few memory.textures.

programs also has only MeshDepthMaterial left over. I’m wondering:

  • Will these left over MeshDepthMaterials be a problem? We’re not explicitly creating or defining them that I can see. (One thing I did notice, when I destroy our scene, then re-create it from the parent app, then destroy it again there are even more MeshDepthMaterials and our memory.texture count increases. This signals to me that we would have a memory leak if I leave the cleanUp method as is.)
  • Is using renderer.info in this way an accurate test for cleaning up? Using the devtools Performance recorder isn’t showing any JSHeap recovery, but I understand that JS garbage collection is a bit mercurial and is not guaranteed to have run while I’m recording performance.

Please let me know if there’s any more info or examples I can drop in here.
Thanks!

1 Like

Hello,

As far as I can tell, the MeshDepthMaterial that remains even after disposing of all materials is the material used by the renderer to create shadows. I’ve been trying to dispose of this material for two days without any success. Were you able to resolve this problem?