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!