المقدمة
لقد كتبت مؤخرًا عن تجربتي في تنظيف الذاكرة في تطبيق باستخدام three.js . اسمحوا لي أن أذكرك أن الهدف كان إعادة رسم عدة مشاهد مع تحميل نماذج gltf.
منذ ذلك الحين ، أجريت عددًا من التجارب وأرى أنه من الضروري استكمال ما قلته سابقًا بهذه المقالة الصغيرة. فيما يلي بعض النقاط التي ساعدتني في تحسين أداء التطبيق.
الجزء الرئيسي
بدراسة أمثلة مختلفة لجمع القمامة على موقع three.js ، كنت مهتمًا بالنهج المقترح على threejsfundamentals.org . ومع ذلك ، بعد تنفيذ التكوين المقترح وتغليف جميع المواد والهندسة في هذا المسار () ، اتضح أن الحمل على وحدة معالجة الرسومات يستمر في النمو عند تحميل مشاهد جديدة. علاوة على ذلك ، لا يعمل المثال المقترح بشكل صحيح مع EffectComposer وفئات المعالجة اللاحقة الأخرى ، حيث لا يمكن استخدام track () في هذه الفئات.
الحل مع إضافة ResourceTracker إلى جميع الفئات المستخدمة ليس جذابًا ، لأسباب واضحة ، لذلك قررت أن أكمل طريقة تنظيف الفصل المذكور. فيما يلي بعض الأساليب التي تم استخدامها:
الاستقبال 1. الخام
أضف Renderer.info بعد طريقة التنظيف. نقوم بإزالة الموارد من التطبيق واحدًا تلو الآخر من أجل فهم أي منها يشكل الحمل والاختباء في القوام أو المواد. هذه ليست طريقة لحل المشكلات ، ولكنها مجرد طريقة لتصحيح الأخطاء التي قد لا يعرفها شخص ما.
الاستقبال 2. طويل
بعد فتح كود الفصل المستخدم (على سبيل المثال ، AfterimagePass ، والذي يمكن العثور عليه في three.js github ) ، ننظر إلى مكان إنشاء الموارد التي نحتاج إلى تنظيفها من أجل الحفاظ على عدد الأشكال الهندسية والمواد ضمن الإطار المطلوب.
this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, { ... }
هذا ما تحتاجه. وفقًا للوثائق ، يحتوي WebGLRenderTarget على وظيفة التخلص لتنظيف الذاكرة. نحصل على شيء مثل:
class Scene {
//...
postprocessing_init(){ //
this.afterimagePass = new AfterimagePass(0);
this.composer.addPass(this.afterimagePass);
}
//...
}
//...
class ResourceTracker {
//...
dispose() {
//...
sceneObject.afterimagePass.WebGLRenderTarget.dispose();
//...
}
}
الاستقبال 3
يعمل ، لكن رمز التنظيف يتضخم في هذه الحالة. دعنا نحاول استخدام النهج المألوف لنا من المقالة السابقة. اسمحوا لي أن أذكركم بأننا طبقنا فيها طريقة disposeNode (عقدة) ، حيث تم تكرار المورد للعثور على ما يمكن تنظيفه. قد يبدو disposeNode () شيئًا كالتالي:
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;
}
}
رائع ، الآن لنأخذ جميع الفئات الإضافية التي استخدمناها ونضيفها إلى 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]);
}
}
النتيجة
كنتيجة لكل هذه الإجراءات ، قمت بزيادة FPS بشكل كبير وخفضت تحميل GPU في تطبيقي. ربما أكون قد استخدمت ResourceTracker بشكل غير صحيح ، لكنه لن يساعد في الفئات الإضافية على أي حال. لم أر في أي مكان أن التكرار عبر EffectComposer من خلال DisposeNode (العقدة) يؤثر على عدد الأنسجة في الذاكرة (ومع ذلك ، هو كذلك). يجب النظر في هذه المسألة بشكل منفصل.
للمقارنة ، سيبقى الإصدار السابق في العنوان القديم ، بينما يمكن عرض الإصدار الجديد بشكل منفصل .
المشروع في شكل ما على جيثب .
سأكون سعيدًا لسماع تجربتك مع مشاريع مماثلة ومناقشة التفاصيل!