Kami terus membersihkan memori dengan three.js

pengantar



Saya baru-baru ini menulis tentang pengalaman saya membersihkan memori dalam aplikasi menggunakan three.js . Izinkan saya mengingatkan Anda bahwa tujuannya adalah menggambar ulang beberapa adegan dengan memuat model gltf.



Sejak itu, saya telah melakukan sejumlah percobaan dan saya menganggap perlu untuk melengkapi apa yang saya katakan sebelumnya dengan artikel kecil ini. Berikut adalah beberapa poin yang membantu saya meningkatkan kinerja aplikasi.



Bagian utama



Mempelajari berbagai contoh pengumpulan sampah di three.js, saya tertarik dengan pendekatan yang diusulkan di threejsfundamentals.org . Namun, setelah mengimplementasikan konfigurasi yang diusulkan dan membungkus semua material dan geometri di this.track (), ternyata beban pada GPU terus bertambah saat memuat adegan baru. Selain itu, contoh yang diusulkan tidak berfungsi dengan benar dengan EffectComposer dan kelas lain untuk pasca-pemrosesan, karena track () tidak dapat digunakan di kelas ini.



Solusi dengan penambahan ResourceTracker ke semua kelas yang digunakan tidak menarik, karena alasan yang jelas, jadi saya memutuskan untuk melengkapi metode pembersihan kelas yang disebutkan. Berikut beberapa teknik yang telah digunakan:



Penerimaan 1. Kasar



Tambahkan renderer.info setelah metode pembersihan. Kami menghapus sumber daya dari aplikasi satu per satu untuk memahami sumber mana yang memuat dan bersembunyi di tekstur atau bahan. Ini bukan cara untuk memecahkan masalah, tetapi hanya cara untuk men-debug yang mungkin tidak diketahui seseorang.



Penerimaan 2. Panjang



Setelah membuka kode kelas yang digunakan (misalnya, AfterimagePass, yang dapat ditemukan di three.js github ), kita melihat di mana sumber daya dibuat yang perlu kita bersihkan untuk mempertahankan jumlah geometri dan material dalam kerangka yang diperlukan.



this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, { ... }


Itulah yang Anda butuhkan. Menurut dokumentasi, WebGLRenderTarget memiliki fungsi buang untuk membersihkan memori. Kami mendapatkan sesuatu seperti:



class Scene {
//...
    postprocessing_init(){ //   
        this.afterimagePass = new AfterimagePass(0);
        this.composer.addPass(this.afterimagePass);
    }
//...
}
//...

class ResourceTracker {
//...
    dispose() {
    //...
    sceneObject.afterimagePass.WebGLRenderTarget.dispose();
    //...
    }
}


Penerimaan 3



Berfungsi, tetapi kode pembersihan membengkak dalam kasus ini. Mari kita coba menggunakan pendekatan yang kita kenal dari artikel sebelumnya. Izinkan saya mengingatkan Anda bahwa di dalamnya kami menerapkan metode disposeNode (node), di mana sumber daya dicari untuk apa yang dapat dibersihkan. disposeNode () mungkin terlihat seperti ini:



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;
        }
    }


Bagus, sekarang mari kita ambil semua kelas tambahan yang kita gunakan dan tambahkan ke 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]);
    }
}


Hasil



Sebagai hasil dari semua tindakan ini, saya meningkatkan FPS secara signifikan dan mengurangi beban GPU di aplikasi saya. Saya mungkin telah menggunakan ResourceTracker secara tidak benar, tetapi itu tidak akan membantu dengan kelas tambahan. Saya belum pernah melihat di mana pun bahwa pengulangan EffectComposer melalui disposeNode (node) kita memengaruhi jumlah tekstur dalam memori (bagaimanapun, memang demikian). Masalah ini harus dipertimbangkan secara terpisah.



Sebagai perbandingan, versi sebelumnya akan tetap menggunakan alamat lama , sedangkan versi baru dapat dilihat secara terpisah .



Proyek ini dalam beberapa bentuk di github .



Saya akan senang mendengar pengalaman Anda dengan proyek serupa dan mendiskusikan detailnya!



All Articles