وسام البهنسي

مدونة مبرمج معماري

نداء الرسم: لحظة الحقيقة

السلام عليكم،

في تدوينة سابقة تحدثنا قليلاً عن الأسباب التي أخرت معشر مطوري الألعاب عن تبني دايركت إكس 12 حتى الآن. بل تجد أيضاً مهندسي دايركت إكس ينصحون بعض الشركات بالبقاء على دايركت إكس 11 كخيار أفضل حتى في الزمن الراهن.

لكني اليوم أود التركيز قليلاً على التعديلات التي يفرضها دايركت إكس 12 وكيف تؤدي هذه التعديلات إلى تحسين أداء اللعبة بشكل عام. ولن أستخدم مثال حزام الكويكبات السخيف الذي تعرضه مايكروسوفت كلما أرادت إظهار محاسن دايركت إكس 12، فأنا أمقته. سأتحدث عن الفروق في سياق لعبة كاملة أو محرك ألعاب ناضج، فنحن هنا لنستفيد ونتعلم وليس لنعمل كمندوبي مبيعات.

تبدأ القصة يا سيدي الكريم عند ما يدعى بنداء الرسم (draw call) وهو -كما يظهر من الإسم- نداء الإجراء الذي يقوم برسم شيء ما على الشاشة. هذا الإجراء قد يكون مثلاً DrawIndexed أو glDrawArrays أو أي شيء مشابه. وقد وجد المبرمجون أن في كثير من الأحيان هناك علاقة شبه خطية بين عدد نداءات الرسم في اللقطة الواحدة وعدد اللقطات في الثانية (FPS). السبب هو أن الوصول لنقطة تنفيذ هذا النداء تعني أن اللعبة قد أتمت كافة العمليات التي تحتاجها لوصف الشيء القادم الذي سيظهر على الشاشة وبشكل تام. فعند هذا النداء يستطيع مشغل الرسوميات (graphics driver) أخذ القيمة القائمة لكافة الحالات (states) التي اختارها المبرمج وتفعيلها على معالج الرسوميات.

قبل هذه اللحظة قد يقوم المبرمج بتقليب نفس الحالة كما يشاء (وإن كان هذا هدراً للأداء) لكن عند الوصول لنداء الرسم الفعلي فإن آخر قيمة منتقاة هي التي ستكون فعالة. الآن دعونا لا نتوقف بتفكيرنا عند هذا الحد.. لنسمح لأنفسنا بالسؤال: ماذا يحدث وراء الكواليس عندما نغير قيم الحالات؟ مالك الإجابة هو برنامج التشغيل (driver)، بينما دايركت إكس ليس سوى طبقة رقيقة تقبع بين البرنامجين: برنامجنا وبرنامج التشغيل.

يقوم برنامج التشغيل باستقبال أوامر تغيير قيم الحالات، لكن الحقيقة المرّة هي أن هذه القيم وطريقة تغييرها لا تطابق 100% طريقة عمل معالج الرسوميات، وإنما يجب إجراء بعض الحسابات عليها (سأضرب مثالاً لذلك.. فقط اصبر قليلاً) قبل إرسالها عبر الناقل إلى سجلات معالج الرسوميات. هذا العمل لا يمكن إنجازه إلا عندما ينادي المستخدم إجراء الرسم، مما يجعل نداء الرسم مكلفاً أكثر مما يبدو عليه.

كمثال على العمليات التي يضطر برنامج التشغيل لتنفيذها عند أوامر الرسم، مطابقة برنامج مظلل البكسلات (pixel shader) مع حالة تقنيع الكتابة على سطوح الرسم (color write mask). فإن وجد برنامج التشغيل أن المبرمج لا يريد الكتابة على أحد سطوح الرسم القائمة فإن برنامج التشغيل يستنسخ كود المظلل ويعدل تعليمات التخريج فيه لإزالة التعليمات المسؤولة عن التخريج على سطح الرسم المستبعد ومن ثم رفع هذا المظلل الجديد إلى معالج الرسوميات بغية تنفيذه.

مثال آخر مشابه له علاقة بهيئة سطح الرسم القائم (surface pixel format). فلو كان عرض البكسل الواحد من سطح الرسم 32 بت أو أقل فإن له تعليمة تخريج في المظلل تختلف عما لو كان سطح الرسم القائم بصيغة 64 بت أو أكثر (بكسلات مؤلفة من أعداد عشرية مثلاً)، وبالمثل، يضطر برنامج التشغيل لتعديل كود المظلل (على الطاير)، وكل هذه الحسابات تبدأ لحظة نداء أمر الرسم.

هذه الأمثلة حقيقية من معمارية GCN الخاصة بمعالجات AMD الرسومية، وقد تختلف قليلاً التفاصيل في معماريات معالجات إنفيديا الرسومية مثل ماكسويل وباسكال.

والآن يطرح السؤال نفسه: هل نستطيع عمل شيء للتخفيف من هذه العمليات في تلك اللحظات الحرجة؟

هذا ما يجيب عنه دايركت إكس 12 باستقدامه فكرة الكائنات المدعوة Pipeline State Objects أو حالة خط المعالجة كما أحب أن أترجمها. هذه الكائنات تعطي نفس الضمان لبرنامج التشغيل بتمام واكتمال تحديد قيم كل حالات الرسم قبل نداء أمر الرسم، لكن في لحظة أبكر بكثير من لحظة نداء أمر الرسم نفسها. كمبرمج، تستطيع إنشاء هذه الكائنات عند إقلاع اللعبة أو أثناء تحميل المرحلة... والغلط كل الغلط أن تؤجل إنشاءها للحظة أمر الرسم... لأن الكلفة على الأداء ستكون باهظة... وللأسف هذا الغلط بالضبط هو ما يقع به الكثير من مبرمجي الرسوميات الذين يحاولون الانتقال بمحركاتهم من دايركت إكس 11 إلى 12. ويمكنك مطالعة العدد الكبير من المنشورات التي تحتج على أداء دايركت إكس 12 بعد الانتقال من 11. عندما أقرأ هذه المنشورات.. أعرف أن هؤلاء الأشخاص لم يستوعبوا بعد مصدر سرعة الأداء في دايركت إكس 12، بل عمدوا إلى عمل أسوأ شيء ممكن أن تفعله بهذه الإصدارة.

للحديث بقية... ولكن في تدوينة أخرى.

أضف تعليقاً

Loading