إنشاء ملف تعريفي للعبة WebGL باستخدام علامة about:تتبّع

ليلي طومسون
ليلي طومسون

إذا لم تتمكن من قياسها، فلا يمكنك تحسينها.

اللورد كيلفن

لتشغيل ألعاب HTML5 بشكل أسرع، يجب أولاً تحديد المؤثِّرات السلبية في الأداء، ولكن قد يكون ذلك صعبًا. يعتبر تقييم بيانات عدد اللقطات في الثانية بمثابة بداية، ولكن لرؤية الصورة الكاملة، عليك استيعاب الفروقات الدقيقة في أنشطة Chrome.

توفر أداة about:tracing الرؤية التي تساعدك في تجنب الحلول السريعة التي تهدف إلى تحسين الأداء، ولكنها في الأساس تخمين جيد النية. يمكنك توفير الكثير من الوقت والجهد، والحصول على صورة أوضح حول الإجراءات التي ينفّذها Chrome في كل إطار، واستخدام هذه المعلومات لتحسين لعبتك.

مرحبًا بـ:التتبّع

تمنحك أداة "about:التتبّع" في Chrome نافذة للاطّلاع على جميع أنشطة Chrome على مدار فترة زمنية معيّنة بدقة كبيرة لدرجة أنّها قد تكون مربكة في البداية. يتم إعداد العديد من الوظائف في Chrome للتتبّع خارج العلبة، لذلك بدون استخدام أي أدوات يدوية، يمكنك الاستمرار في استخدام about:tracing لتتبُّع أدائك. (راجِع قسم لاحق حول قياس بيانات JavaScript يدويًا)

للاطّلاع على عرض التتبُّع، ما عليك سوى كتابة "about:tracing" في المربّع المتعدد الاستخدامات في Chrome (شريط العناوين).

المربّع المتعدد الاستخدامات في Chrome
اكتب "about:tracing" في المربّع المتعدد الاستخدامات في Chrome

من أداة التتبُّع، يمكنك بدء التسجيل وتشغيل اللعبة لبضع ثوانٍ ثم الاطّلاع على بيانات التتبُّع. هذا مثال على الشكل الذي قد تبدو عليه البيانات:

نتيجة تتبُّع بسيطة
نتيجة تتبُّع بسيطة

نعم، هذا محير. لنتحدث عن كيفية قراءتها

يمثل كل صف عملية يتم تكوينها تلقائيًا، ويشير المحور الأيسر الأيمن إلى الوقت، وكل مربع ملون هو استدعاء دالة قابلة للقياس. هناك صفوف لعدد من أنواع الموارد المختلفة. أكثرها إثارة للاهتمام لتحليلها هو CrGpuMain الذي يوضح مهام وحدة معالجة الرسومات (GPU)، وCrRendererMain. يحتوي كل تتبُّع على أسطر CrRendererMain لكل علامة تبويب مفتوحة خلال فترة التتبُّع (بما في ذلك علامة التبويب about:tracing نفسها).

عند قراءة بيانات التتبع، تكون مهمتك الأولى هي تحديد صف CrRendererMain المتوافق مع لعبتك.

تم تمييز نتيجة التتبُّع البسيطة.
تم تمييز نتيجة التتبُّع البسيطة

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

جارٍ العثور على الإطار

بعد تحديد موقع الصف الصحيح في أداة التتبُّع الخاصة باللعبة، تكون الخطوة التالية هي العثور على الحلقة الرئيسية. تبدو الحلقة الرئيسية كنمط متكرر في بيانات التتبع. يمكنك التنقل في بيانات التتبع باستخدام المفاتيح W وA وS وD: A وD للتحرك يمينًا أو يسارًا (ذهابًا وإيابًا) وW وS لتكبير البيانات وتصغيرها. في حال تشغيل اللعبة بسرعة 60 هرتز، قد تتوقّع أن تكون حلقة التكرار الرئيسية بنمط يتكرر كل 16 ملّي ثانية.

يبدو أنّ هناك ثلاثة إطارات تنفيذ
يبدو كثلاثة إطارات تنفيذ

بعد تحديد الموقع الجغرافي للنبضات، يمكنك البحث في آلية عمل الرمز بالضبط في كل إطار. استخدم W وA وS وD للتكبير حتى تتمكن من قراءة النص في مربعات الدوال.

تعمّق في إطار التنفيذ
تعمَّق في إطار التنفيذ

تعرض مجموعة المربعات هذه سلسلة من استدعاءات الدوال، ويتم تمثيل كل استدعاء بمربع ملون. تم استدعاء كل دالة بواسطة المربع الموجود فوقها، لذا في هذه الحالة، يمكنك رؤية أن MessageLoop::RunTask تسمى RenderWidget::OnSwapBuffersComplete، والتي تسمى بدورها RenderWidget::DoDeferredUpdate، وهكذا. عند قراءة هذه البيانات، يمكنك الحصول على عرض كامل لما يسمى بماذا والمدة التي استغرقتها كل عملية تنفيذ.

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

إضافة علامات التتبع

لحسن الحظ، هناك طريقة سهلة لإضافة أدوات يدوية إلى الرمز لإنشاء بيانات التتبع: console.time وconsole.timeEnd.

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

يُنشئ الرمز أعلاه مربّعات جديدة في اسم عرض التتبُّع بالعلامات المحددة، لذا في حال إعادة تشغيل التطبيق، سيظهر لك مربّعا "تحديث" و"عرض" يعرضان الوقت المنقضي بين طلبَي البدء والانتهاء لكل علامة.

العلامات التي تمت إضافتها يدويًا
العلامات التي تمت إضافتها يدويًا

باستخدام ذلك، يمكنك إنشاء بيانات تعقب قابلة للقراءة البشرية لتتبع نقاط الاتصال في التعليمات البرمجية.

وحدة معالجة رسومات أم وحدة معالجة مركزية؟

مع الرسومات المسرّعة للأجهزة، أحد أهم الأسئلة التي يمكنك طرحها أثناء التحليل هو: هل الرمز البرمجي مرتبط بوحدة معالجة الرسومات أم وحدة المعالجة المركزية (CPU)؟ في كل إطار، سيكون عليك تنفيذ بعض إجراءات العرض على وحدة معالجة الرسومات وإجراء بعض العمليات المنطقية على وحدة المعالجة المركزية (CPU). لفهم سبب بطء اللعبة، عليك معرفة التوازن بين العمل والجهد.

أولاً، ابحث عن الخط في عرض التتبع المسمى CrGPUMain، والذي يشير إلى ما إذا كانت وحدة معالجة الرسومات مشغولة في وقت معيَّن.

آثار بيانات وحدة معالجة الرسومات ووحدة المعالجة المركزية

يتضح لك أنّ كل إطار في لعبتك يؤدي إلى عمل وحدة المعالجة المركزية (CPU) في CrRendererMain وكذلك في وحدة معالجة الرسومات. يوضح التتبُّع أعلاه حالة استخدام بسيطة للغاية تكون فيها وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات (GPU) غير نشِطة لمعظم الإطارات التي تبلغ مدتها 16 ملي ثانية.

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

console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");

console.time("render");
render();
console.timeEnd("render");

سيظهر لك الآن آثار الأنشطة على النحو التالي:

آثار بيانات وحدة معالجة الرسومات ووحدة المعالجة المركزية

ما المعلومات التي يخبرنا بها هذا آثار الأنشطة؟ يتضح لنا أنّ الإطار المصوَّر يتغيّر من 2270 ملي ثانية تقريبًا إلى 2320 ملي ثانية، ما يعني أنّ كل إطار يستغرق حوالي 50 ملي ثانية (عدد اللقطات في الثانية يبلغ 20 هرتز). ويمكنك رؤية أجزاء من المربعات الملوّنة التي تمثّل دالة العرض بجانب مربّع التحديث، ولكن التحديث نفسه يهيمن على الإطار بالكامل.

على عكس ما يحدث في وحدة المعالجة المركزية (CPU)، يمكنك ملاحظة أن وحدة معالجة الرسومات لا تزال في وضع عدم النشاط لمعظم اللقطات في الثانية. لتحسين هذا الرمز، يمكنك البحث عن العمليات التي يمكن إجراؤها في رمز عامل التظليل ونقلها إلى وحدة معالجة الرسومات للاستفادة إلى أقصى حدّ من الموارد.

ماذا يحدث عندما يكون رمز أداة التظليل نفسه بطيئًا ويكون وحدة معالجة الرسومات مرهقة؟ ماذا لو أزلنا العمل غير الضروري من وحدة المعالجة المركزية (CPU) وأضفنا بعض الأعمال بدلاً من ذلك في رمز أداة تظليل الأجزاء. إليك أداة تظليل الأجزاء باهظة الثمن:

#ifdef GL_ES
precision highp float;
#endif
void main(void) {
  for(int i=0; i<9999; i++) {
    gl_FragColor = vec4(1.0, 0, 0, 1.0);
  }
}

كيف يبدو تتبع التعليمة البرمجية باستخدام أداة التظليل هذه؟

عمليات تتبُّع وحدة معالجة الرسومات ووحدة المعالجة المركزية (CPU) عند استخدام رمز وحدة معالجة رسومات بطيء
عمليات تتبُّع وحدة معالجة الرسومات ووحدة المعالجة المركزية (CPU) عند استخدام رمز وحدة معالجة رسومات بطيء

لاحظ مرة أخرى مدة الإطار. في هذه الحالة، ينتقل نمط التكرار من 2750 ملي ثانية إلى 2950 ملي ثانية، وتبلغ مدته 200 ملي ثانية (يبلغ معدّل عرض الإطارات 5 هرتز تقريبًا). يكون خط CrRendererMain فارغًا تمامًا تقريبًا، مما يعني أن وحدة المعالجة المركزية (CPU) تكون غير نشِطة معظم الوقت، بينما تكون وحدة معالجة الرسومات زائدة التحميل. هذه علامة مؤكدة على أن أدوات التظليل ثقيلة جدًا.

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

أمثلة حقيقية

لنتحقق الآن من شكل بيانات التتبع من لعبة حقيقية. أحد الأشياء الرائعة حول الألعاب التي تم إنشاؤها باستخدام تقنيات الويب المفتوحة هو أنه يمكنك معرفة ما يجري في منتجاتك المفضلة. إذا كنت تريد اختبار أدوات التحليل، فيمكنك اختيار عنوان WebGL المفضل لديك من سوق Chrome الإلكتروني وملفه الشخصي باستخدام about:tracing. وهذا مثال مأخوذ من لعبة Skid Racer الممتازة في ألعاب WebGL.

تتبُّع لعبة حقيقية
ممارسة رياضة حقيقية

يبدو أن كل إطار يستغرق 20 ملي ثانية تقريبًا، مما يعني أن عدد اللقطات في الثانية يبلغ 50 لقطة في الثانية تقريبًا. يمكنك ملاحظة أنّ العمل متوازن بين وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات، لكن وحدة معالجة الرسومات هي المورد الأكثر طلبًا. إذا كنت تريد أن ترى كيف يبدو تقديم أمثلة حقيقية لألعاب WebGL، جرب تجربة بعض الألعاب في سوق Chrome الإلكتروني التي تم تصميمها باستخدام WebGL بما في ذلك:

الخلاصة

إذا كنت تريد تشغيل اللعبة في 60 هرتز، يجب أن تتناسب جميع عملياتك مع 16 ملي ثانية من وحدة المعالجة المركزية (CPU) و16 ملي ثانية من وقت وحدة معالجة الرسومات، وذلك لكل لقطة. لديك موردان يمكن استخدامهما بالتوازي، ويمكنك تحويل العمل بينهما لتحقيق أفضل أداء. يعتبر Chrome's about:tracing view (عرض التعقب) في Chrome أداة لا تقدر بثمن للحصول على إحصاءات حول ما تفعله التعليمات البرمجية بالفعل وستساعدك على زيادة وقت التطوير من خلال معالجة المشكلات الصحيحة.

ما هي الخطوات التالية؟

بالإضافة إلى وحدة معالجة الرسومات، يمكنك أيضًا تتبُّع أجزاء أخرى من وقت تشغيل Chrome. تم إعداد Chrome Canary، وهو الإصدار المبكر من Chrome، لتتبع IO وIndexedDB والعديد من الأنشطة الأخرى. يجب قراءة مقالة Chromium هذه للتعرّف بشكل أفضل على حالة تتبّع الأحداث في الوقت الحالي.

إذا كنت مطوّر ألعاب ويب، احرص على مشاهدة الفيديو أدناه. إنه عرض تقديمي من فريق Game Developer Advocate في مؤتمر مطوّري الألعاب (GDC) 2012 حول تحسين أداء ألعاب Chrome: