Introdução
Recentemente, escrevi sobre minha experiência de limpeza de memória em um aplicativo usando three.js . Deixe-me lembrá-lo que o objetivo era redesenhar várias cenas com o carregamento de modelos gltf.
Desde então, fiz várias experiências e considero necessário complementar o que disse anteriormente com este pequeno artigo. Aqui estão alguns pontos que me ajudaram a melhorar o desempenho do aplicativo.
Parte principal
Estudando vários exemplos de coleta de lixo em three.js, fiquei interessado na abordagem proposta em threejsfundamentals.org . No entanto, após implementar a configuração proposta e envolver todos os materiais e geometria em this.track (), descobriu-se que a carga na GPU continua a crescer ao carregar novas cenas. Além disso, o exemplo proposto não funciona corretamente com EffectComposer e outras classes para pós-processamento, uma vez que track () não pode ser usado nessas classes.
A solução com a adição de ResourceTracker a todas as classes utilizadas não é atraente, por motivos óbvios, por isso resolvi complementar o método de limpeza da referida classe. Aqui estão algumas das técnicas que foram usadas:
Recepção 1. Áspero
Adicione renderer.info após o método de limpeza. Retiramos recursos do aplicativo um a um para entender quais deles compõem a carga e estão escondidos em texturas ou materiais. Esta não é uma forma de resolver problemas, mas apenas uma forma de depurar que alguém pode não conhecer.
Recepção 2. Longa
Tendo aberto o código da classe usada (por exemplo, AfterimagePass, que pode ser encontrado no github three.js ), olhamos onde os recursos são criados que precisamos limpar para manter o número de geometrias e materiais dentro da estrutura necessária.
this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, { ... }
É disso que você precisa. De acordo com a documentação, o WebGLRenderTarget possui uma função dispose para limpar a memória. Recebemos algo como:
class Scene {
//...
postprocessing_init(){ //
this.afterimagePass = new AfterimagePass(0);
this.composer.addPass(this.afterimagePass);
}
//...
}
//...
class ResourceTracker {
//...
dispose() {
//...
sceneObject.afterimagePass.WebGLRenderTarget.dispose();
//...
}
}
Recepção 3
Funciona, mas o código de limpeza fica inchado neste caso. Vamos tentar usar a abordagem que conhecemos do artigo anterior. Deixe-me lembrar que nele implementamos o método disposeNode (node), no qual o recurso foi iterado para encontrar o que pode ser limpo. disposeNode () pode ser parecido com isto:
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;
}
}
Ótimo, agora vamos pegar todas as classes adicionais que usamos e adicionar ao nosso ResourceTracker:
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]);
}
}
Resultado
Como resultado de todas essas ações, aumentei significativamente o FPS e reduzi a carga da GPU em meu aplicativo. Posso ter usado o ResourceTracker incorretamente, mas não ajudaria com as classes adicionais de qualquer maneira. Eu não vi em nenhum lugar que iterar sobre EffectComposer por meio de nosso disposeNode (nó) afeta o número de texturas na memória (mas é o caso). Esta questão deve ser considerada separadamente.
Para efeito de comparação, a versão anterior permanecerá no endereço antigo , enquanto a nova pode ser visualizada separadamente .
O projeto está de alguma forma no github .
Eu ficaria feliz em ouvir sua experiência com projetos semelhantes e discutir os detalhes!