Einführung
Ich habe kürzlich über meine Erfahrungen mit der Bereinigung des Speichers in einer Anwendung mit three.js geschrieben . Ich möchte Sie daran erinnern, dass das Ziel darin bestand, mehrere Szenen mit dem Laden von gltf-Modellen neu zu zeichnen.
Seitdem habe ich eine Reihe von Experimenten durchgeführt und halte es für notwendig, das, was ich zuvor gesagt habe, durch diesen kleinen Artikel zu ergänzen. Hier sind einige Punkte, die mir geholfen haben, die Leistung der Anwendung zu verbessern.
Hauptteil
Als ich verschiedene Beispiele für die Speicherbereinigung auf three.js studierte, interessierte mich der auf threejsfundamentals.org vorgeschlagene Ansatz . Nach der Implementierung der vorgeschlagenen Konfiguration und dem Umschließen aller Materialien und Geometrien in this.track () stellte sich jedoch heraus, dass die Belastung der GPU beim Laden neuer Szenen weiter zunimmt. Darüber hinaus funktioniert das vorgeschlagene Beispiel mit EffectComposer und anderen Klassen für die Nachbearbeitung nicht ordnungsgemäß, da track () in diesen Klassen nicht verwendet werden kann.
Die Lösung mit dem Hinzufügen von ResourceTracker zu allen verwendeten Klassen ist aus offensichtlichen Gründen nicht attraktiv. Daher habe ich beschlossen, die Methode zum Reinigen der genannten Klasse zu ergänzen. Hier sind einige der Techniken, die verwendet wurden:
Rezeption 1. Rau
Fügen Sie renderer.info nach der Bereinigungsmethode hinzu. Wir entfernen nacheinander Ressourcen aus der Anwendung, um zu verstehen, welche davon die Last ausmachen, und verstecken sie in Texturen oder Materialien. Dies ist keine Möglichkeit, Probleme zu lösen, sondern nur eine Möglichkeit zum Debuggen, von der jemand möglicherweise nichts weiß.
Rezeption 2. Lang
Nachdem wir den Code der verwendeten Klasse geöffnet haben (z. B. AfterimagePass, der sich auf dem Github von three.js befindet ), sehen wir uns an, wo die Ressourcen erstellt werden, die bereinigt werden müssen, um die Anzahl der Geometrien und Materialien innerhalb des erforderlichen Frameworks zu halten.
this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, { ... }
Das ist was du brauchst. Gemäß der Dokumentation verfügt WebGLRenderTarget über eine Entsorgungsfunktion zum Bereinigen des Speichers. Wir bekommen so etwas wie:
class Scene {
//...
postprocessing_init(){ //
this.afterimagePass = new AfterimagePass(0);
this.composer.addPass(this.afterimagePass);
}
//...
}
//...
class ResourceTracker {
//...
dispose() {
//...
sceneObject.afterimagePass.WebGLRenderTarget.dispose();
//...
}
}
Rezeption 3
Funktioniert, aber der Bereinigungscode wird in diesem Fall aufgebläht. Versuchen wir, den uns aus dem vorherigen Artikel bekannten Ansatz zu verwenden. Ich möchte Sie daran erinnern, dass wir darin die Methode disposeNode (node) implementiert haben, bei der die Ressource nach dem durchsucht wurde, was bereinigt werden kann. disposeNode () könnte ungefähr so aussehen:
disposeNode(node) {
node.parent = undefined;
if (node.geometry) {
node.geometry.dispose();
}
let material = node.material;
if (material) {
if (material.map) {
material.map.dispose();
}
if (material.lightMap) {
material.lightMap.dispose();
}
if (material.bumpMap) {
material.bumpMap.dispose();
}
if (material.normalMap) {
material.normalMap.dispose();
}
if (material.specularMap) {
material.specularMap.dispose();
}
if (material.envMap) {
material.envMap.dispose();
}
material.dispose();
}
} else if (node.constructor.name === "Object3D") {
node.parent.remove(node);
node.parent = undefined;
}
}
Großartig, jetzt nehmen wir alle zusätzlichen Klassen, die wir verwendet haben, und fügen sie unserem ResourceTracker hinzu:
dispose() {
for (let key in sceneObject.afterimagePass) {
this.disposeNode(sceneObject.afterimagePass[key]);
}
for (let key in sceneObject.bloomPass) {
this.disposeNode(sceneObject.bloomPass[key]);
}
for (let key in sceneObject.composer) {
this.disposeNode(sceneObject.composer[key]);
}
}
Ergebnis
Infolge all dieser Aktionen habe ich die FPS erheblich erhöht und die GPU-Last in meiner Anwendung verringert. Ich habe den ResourceTracker möglicherweise falsch verwendet, aber es würde bei den zusätzlichen Klassen sowieso nicht helfen. Ich habe nirgendwo gesehen, dass das Durchlaufen von EffectComposer über unseren disposeNode (Knoten) die Anzahl der Texturen im Speicher beeinflusst (dies ist jedoch der Fall). Dieses Problem sollte separat betrachtet werden.
Zum Vergleich bleibt die vorherige Version an der alten Adresse , während die neue separat angezeigt werden kann .
Das Projekt ist in irgendeiner Form auf dem Github .
Ich würde mich freuen, Ihre Erfahrungen mit ähnlichen Projekten zu hören und die Details zu besprechen!