Dlaczego Three.js powoduje wycieki pamięci GPU
Garbage collector JavaScriptu automatycznie obsługuje pamięć CPU.
Ale obiekty Three.js posiadają też zasoby GPU —
obiekty buforowe, tekstury, framebuffery — zarządzane przez API
WebGL. Garbage collector nic o nich nie wie. Gdy porzucasz
Mesh, usuwając referencję JavaScript, pamięć GPU
pozostaje zaalokowana, dopóki nie wywołasz jawnie
dispose().
W długo działającej symulacji, która regularnie tworzy i porzuca obiekty, sumuje się to do setek megabajtów wyciekniętego VRAM, co ostatecznie powoduje awarie lub poważny spadek wydajności.
Sprawdzenie objawów: Otwórz Chrome DevTools → Performance → Memory. Jeśli pamięć GPU stale rośnie podczas działania symulacji (nawet gdy nic wizualnie się nie zmienia), masz wyciek.
Kompletna lista kontrolna
Geometria
geometry.dispose(); // Zwalnia bufory wierzchołków, bufory indeksów
- Wywołuj na każdej utworzonej
BufferGeometry - Geometrie mesh'y instancjonowanych też wymagają usunięcia
-
Scalone geometrie (z
mergeGeometries) to nowe obiekty — usuń je również
Materiały
material.dispose(); // Zwalnia skompilowany program shadera
- Wywołuj na każdej podklasie
Material - Tablice materiałów (mesh z wieloma materiałami): przejdź przez pętlę i usuń każdy element
- NIE usuwa automatycznie tekstur — patrz niżej
Tekstury
// Ręczne usuwanie tekstur
material.map?.dispose();
material.normalMap?.dispose();
material.roughnessMap?.dispose();
material.envMap?.dispose();
// ... wszystkie właściwości map
// Albo użyj przejścia po drzewie
scene.traverse((obj) => {
if (obj.material) {
Object.values(obj.material).forEach(val => {
if (val?.isTexture) val.dispose();
});
}
});
Cele renderowania
renderTarget.dispose(); // Zwalnia FBO + dołączone tekstury
Pełny wzorzec demontażu
function disposeMesh(mesh) {
mesh.geometry?.dispose();
if (Array.isArray(mesh.material)) {
mesh.material.forEach(disposeMaterial);
} else {
disposeMaterial(mesh.material);
}
}
function disposeMaterial(mat) {
if (!mat) return;
mat.dispose();
// Usuń wszystkie tekstury map
['map','normalMap','roughnessMap','metalnessMap',
'aoMap','emissiveMap','envMap','lightMap',
'bumpMap','displacementMap','alphaMap']
.forEach(key => mat[key]?.dispose());
}
// Demontaż sceny
scene.traverse((obj) => {
if (obj.isMesh) disposeMesh(obj);
});
renderer.dispose();
renderer.forceContextLoss();
InstancedMesh
InstancedMesh to po prostu Mesh —
usuwaj jego geometrię i materiał w ten sam sposób. Atrybuty
buforów instanceMatrix i instanceColor
są zwalniane wraz z usunięciem geometrii.
Wskazówka od profesjonalisty: Wywołaj
renderer.info.reset() między scenami i zaloguj
renderer.info.memory, aby zweryfikować, że liczba
geometrii i tekstur spada do zera po demontażu. Jeśli nie —
gdzieś pominąłeś dispose.