الشبكة العربية لمطوري الألعاب

موهوب  16mofed84 مشاركة 1

السلام عليكم
لا اعلم ان كان هذا الموضوع يحتاج الطرح موضوع جديد كنت اتمنى ان يوجد علبه دردشه لان الموضوع لا يستاهل ان يوضع في موضوع جديد ..على كل حال :

عند استخدام الStopWatch او Environment.TicksCount او DateTime.Now  لقياس وقت Draw لمجموعه مثلثات فأنه يعطي قيمه صفر
مع العلم انه كل ما زاد عدد المثلثات قلت سرعه العبه (FramesPerSecond) اعلم ان FPS يعطي عدد الرسم في كل ثانيه لكن ماذا اذا كنت اريد قياس فقط مجموعه من المثلثات ؟
وشكراً

خبير مدير وسام البهنسي مشاركة 2

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

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

لقياس زمن الرسم على معالج الرسوميات، يجب أن تستخدم مؤقتاً يعمل على ذلك المعالج. دايركت ثري دي 9 وما فوق يقدم هذه الخدمة لك عن طريق ما يدعى بالاستفسارات Queries:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb147308(v=vs.85).aspx

ستحتاج لاستخدام نوعين من الاستسفسارات: TimeStamp و TimeStampFreq. الأول يعطيك الزمن الحالي على معالج الرسوميات، والثاني يعطيك تردد مقياس الزمن (للتحويل إلى ثواني).

لاستخدام هذه الميزة لقياس الزمن، ستحتاج لمعرفة أن أوامر التوقيت مثلها مثل أوامر الرسم، تعمل على التوازي مع المعالج المركزي. أي بمعنى آخر، أنت لا تعلم متى سيتم تنفيذها بالضبط. لذلك تصبح خوارزمية العمل كالآتي:

1- إنشاء Query من نوع TimeStampFreq واستخدامه لمعرفة تردد مقياس الزمن من معالج الرسوميات.


IDirect3DQuery9 *pFreq = nullptr;
HRESULT hRetval = pD3DDevice->CreateQuery(D3DQUERYTYPE_TIMESTAMPFREQ,&pFreq);

if (FAILED(hRetval))
{
   m_dGPUTimeFrequency = 0.0; 
   // Not available
   return false;
}
 

pFreq->Issue(D3DISSUE_END); // Issue query
 

// Get response
UINT64 uFreq = 0;

while (S_FALSE == pFreq->GetData(&uFreq,sizeof(uFreq),D3DGETDATA_FLUSH));


 
pFreq->Release();

 



if (!uFreq)
   return false;
 
m_dGPUTimeFrequency = 1.0 / (double)uFreq * 1000.0; // To milliseconds

 
2- إنشاء queries لقياس الزمن. ستحتاج لاثنين لكل لقطة. واحد لتسجيل لحظة البداية، والآخر لتسجيل لحظة النهاية. معالج الرسوميات له الحق بتكديس أوامر رسم 3 لقطات على الأكثر، لذلك ستضطر لإنشاء 6 استفسارات. 3 للبداية و3 للنهاية لكل لقطة. وتعيد استخدام الاستفسارات كل ثلاث لقطات.


for (DWORD i=0;i<3;i++)
{
   pD3DDevice->CreateQuery(D3DQUERYTYPE_TIMESTAMP,&m_aStartTimers[i]);
   pD3DDevice->CreateQuery(D3DQUERYTYPE_TIMESTAMP,&m_aEndTimers[i]);
}
 
 
3- فلنقل أنك تريد قياس زمن تنفيذ أمر رسم بعض المثلثات، قبل نداء أمر الرسم، نقوم بإضافة طلب استفسار للحظة البداية:
 

m_aStartTimers[dwIssueSlot]->Issue(D3DISSUE_END);
 
ثم نقوم بنداء أمر الرسم، يليه فوراً طلب استفسار لحظة النهاية:

m_aEndTimers[dwIssueSlot]->Issue(D3DISSUE_END);
 
 
4- في وقت لاحق، يجب أن نسأل معالج الرسوميات إن كان قد أنهى معالجة طلب استفسار لحظة النهاية، فعندها تكون النتيجة قد أصبحت جاهزة للاسترداد:
 

const DWORD dwReadSlot = m_dwCurrentReadSlot%kMaxBufferedFrames;
 
UINT64 uEndTime = 0;

if (m_aEndTimers[dwReadSlot]->GetData(&uEndTime,sizeof(uEndTime),0) != S_OK)
   return false; // No new reading yet
 
UINT64 uStartTime = 0;
m_aStartTimers[dwReadSlot]->GetData(&uStartTime,sizeof(uStartTime),0);
 
UINT64 uTimeDiff = uEndTime - uStartTime;
m_fLastFrameTime = (float)((double)uTimeDiff * m_dGPUTimeFrequency);
 
هذه هي الخطوات العامة لإجراء التوقيت. أنا أقترح إنشاء صنف مخصص للقيام بهذه العملية، تضع فيه التعليمات المذكورة بحيث تبسط عملية القياس إلى شيء مثل:


gpuTimer.Begin();
pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, ...);
gpuTimer.End();
 
وعند الرغبة في الحصول على الزمن تفعل:


float fTimeMS = 0;
bool valueIsReady = gpuTimer.GetValueIfReady(fTimeMS);
 
 
وأنا جاهز للإجابة عن أية تفاصيل غير واضحة في شرحي هذا...

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

موهوب  16mofed84 مشاركة 3

مشكور جداً وجدت كود للxna مشابه وهو occlusion queriesوهو يعطي عدد ال pixels. المرسومة وهو مشابه لما ذكرته فيه begin endلكن هل يمكن قياس الوقت به ؟اعتذر ان كنت قد خيبت ظنك في كتابه كذلك الرد الذي كتبته انت فانا استخدم ال xna مشكور جدا

خبير  سلوان الهلالي مشاركة 4

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

هذه اول مرة ارى فيها طريقة لقياس أداء المعالج الرسومي بشكل مباشر 😲
كنت اعتقد ان Queries تأتي بمعلومات حول الـ driver فقط..
😋

خبير  أحمد عزالدين مشاركة 5

السلام عليكم
جزاكم الله خيراً أخي وسام على المعلومات القيمة 😄

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

خبير مدير وسام البهنسي مشاركة 6

وفي 06/جمادى الثانية/1433 02:51 ص، أعرب 16mofed84 عن رأيه بالموقف كالآتي:

مشكور جداً وجدت كود للxna مشابه وهو occlusion queriesوهو يعطي عدد ال pixels. المرسومة وهو مشابه لما ذكرته فيه begin endلكن هل يمكن قياس الوقت به ؟

كلا بالطبع. استفسارات التغطية (occlusion queries) تخبرك بعدد البكسلات الظاهرة من جسم ما قمت برسمه، ولا علاقة لها بالتوقيت. جدير بالذكر أن الاستفسارات لها استخدامات عدة. لكن ما يهمنا هو خاصية التوقيت. تستطيع إنجاز نفس العملية باستخدام إكس إن إيه. فقط عدل الكود الذي حصلت عليه ليقوم بإنشاء استفسارات توقيت بدلاً من استفسارات إحصاء عدد البكسلات، واتبع الخطوات التي قمت بذكرها في ردي الأول.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير مدير وسام البهنسي مشاركة 7

في 06/جمادى الثانية/1433 06:13 ص، قال سلوان الهلالي بهدوء وتؤدة:

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

هذه اول مرة ارى فيها طريقة لقياس أداء المعالج الرسومي بشكل مباشر 😲
كنت اعتقد ان Queries تأتي بمعلومات حول الـ driver فقط..
😋

هي كذلك، إضافة لبعض الإمكانيات المفيدة كالتوقيت وإحصاء عدد البكسلات المرسومة والرؤوس المحسوبة... الخ.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز