مقدمة
يوم جيد ، سنتحدث عن محرك لعبة X-Ray ، أو بالأحرى ، عن شوكة أكسجين X-Ray في ديسمبر 2016 ، تم نشر مشروع X-Ray Oxygen. ثم طورته بمفردي ولم أحلم بما أصبح عليه الآن.
في شهر مارس ، أتتني الفكرة: "لماذا لا يتم نقلها كلها إلى x64؟" كما تفهم ، سيتم مناقشة هذه الفكرة ، أو بالأحرى تنفيذها ،.
تجميع المشروع
كانت الخطوة الأولى هي ترقية الكود لوضع كل شيء تحت منصة x64. بعد إعداد المشاريع ، واجهت المشكلة الأولى ... لا ، ليس وظائف Ptr ، ولكن إدراجات المجمع ...
__forceinline void fsincos( const float angle , float &sine , float &cosine ) { __asm { fld DWORD PTR [angle] fsincos mov eax , DWORD PTR [cosine] fstp DWORD PTR [eax] mov eax , DWORD PTR [sine] fstp DWORD PTR [eax] } }
كان جمال هذا الرمز هو التحسين ، لكن MSBuilder في الإصدار x64 لم يدعمه ولا يزال لا يدعمه. يمكن استبدال معظم هذا الرمز بنظائر قياسية ، وكانت هناك أماكن يمكن تغييرها بسهولة إلى Intrinsics ، على سبيل المثال ، مثل:
__asm pause;
يمكن استبداله بأمان بـ:
_mm_pause();
أيضا في المحرك ، في بعض الأحيان كانت هناك نظائر للوظائف على الكود الأصلي (الحمد لنظام CPUID). ولكن كان هناك أماكن كان عليك التخلص منها للتو. على سبيل المثال ، غرقت تعليمات MMX في النسيان. لحسن الحظ ، لم يتم استدعاؤهم في أي مكان ، ولكن تم تجميعهم ببساطة ووضعهم في وضع الخمول.
قابلية التشغيل
بعد كل التعديلات على التجميع ، بدأت المرحلة التالية: كيف تبدأ كل هذا؟
كان الخائن الأول LuaJIT . لسوء الحظ ، بدأ LuaJIT يعمل بشكل جيد (جيدًا تقريبًا ...) في الإصدار x64 فقط مع الإصدار 2.0.5. وكانت هذه مشاكل صغيرة في تخصيص الذاكرة من أرقام صغيرة. ولكن ، بعد ذلك لم أكن أعرف عن ذلك وأول شيء رأيته كان LuaJIT وتدحرجت الفانيليا Lua 5.1. نعم ، هذا حل المشكلة ، لكن السرعة ... تذكر ، نحن نحزن. في وقت لاحق من المنتدى ، تم إخباري أنه يمكنك محاولة استخدام LuaJIT 2.0.4. ونعم ، لقد ساعدتني ، بدأت اللعبة وتمكنت من الذهاب إلى القائمة الرئيسية!
لكن ... السعادة لم تدم طويلاً ... مرحبًا ببناء التعويضات وأنواع البيانات و xrCDB. لم تقم اللعبة بتحميل المستوى ، وحلقت المواد على الأشياء ولم يعجبها المحرك كثيرًا. بعد يومين ، شعرت باليأس تمامًا وقررت طلب المساعدة من مبرمج أكثر خبرة تحت اسم Giperion. لم أعول على مشاركته في المشروع ، حلمي كان مجرد نصيحة. ولكن ، بهذه الطريقة ، حصلت على مطور متمرس في المشروع. من تلك اللحظة ، شكل فريق.
كانت المشكلة التالية OPCODE وأنواع البيانات. كان علي أن أترجم جميع udwords (int غير موقعة) إلى uqwords (طويلة غير موقعة). فقط لفهم هذا ، كان علي أن أقضي حوالي 4 ساعات تحت المصحح.
ولكن ، كان ذلك مجرد جزء من المشكلة. لقد كان دور المواد. ماذا لدينا:
union { u32 dummy;
تم حفظ هذا الرمز في x32 عن طريق #pragma pack(4)
السحرية #pragma pack(4)
، ولكن لـ x64 لسبب ما لم يتم حفظه. جاء دور المحاذاة ، عن طريق تصحيح اكتشفنا أنه في بعض الحالات كانت البيانات في الهيكل صالحة ، ولكن في حالات أخرى لم تكن كذلك. قمنا بإعادة تصميم الهيكل وجعلنا أداة التحقق من المحول. يحتوي الهيكل على الشكل التالي:
union { size_t dummy; struct { size_t material:14;
وكان المصدق على هذا النحو:
... if (rebuildTrisRequired) { TRI_DEPRECATED* realT = reinterpret_cast<TRI_DEPRECATED*> (T); for (int triIter = 0; triIter < tris_count; ++triIter) { TRI_DEPRECATED& oldTri = realT[triIter]; TRI& newTri = tris[triIter]; newTri = oldTri; } } else { std::memcpy(tris, T, tris_count * sizeof(TRI)); } ...
وبالتالي ، كان يجب تغيير جزء من المكالمات بسبب علامة rebuildTrisRequired ، لكن اللعبة كانت قادرة على البدء.
ولكن بمرور الوقت ، ظهرت مشكلة الجسيمات:
real_ptr = malloc( sizeof( Particle ) * ( max_particles + 1 ) ); particles = (Particle*)((DWORD)real_ptr + (64 - ((DWORD)real_ptr & 63)));
لم يسبب هذا الرمز مشاكل مع الجسيمات الأصلية. كانت بسيطة للغاية وتناسب بهدوء الذاكرة المخصصة لهم. ولكن مع تفاصيل أكثر تعقيدًا وملونة ، والتي تم إجراؤها من قبل صانعي النماذج ، جاء خروج الذاكرة. إلى x64 وتعطل من الذاكرة ، كيف ذلك ؟! تم إعادة تصميم الرمز ، واختفى المغادرة:
particles = alloc<Particle>(max_particles);
مشاكل اللعبة
كانت المشكلة الأولى ، مرة أخرى ، لوجيت

طار Userdata للأغطية الذكية. تم إصلاح هذه المشكلة تقريبًا. مجرد نقل التعديلات من الإصدار LuaJIT 2.0.5 الذي تم إصداره.
المشكلة التالية: الفيزياء وحساب العوامات. control87
و _controlfp
لحساب infinity
في x64 ... كانت هناك مشكلة كبيرة مع انخفاض العناصر ، مرة واحدة إلى ثلاثة سقطوا بشكل صحيح. في بعض الأحيان طاروا إلى الفضاء ، وأحيانًا تحت الأرض. تكمن المشكلة في متغير واحد فقط ، تم إعطاؤه القيمة اللانهائية. تم إصلاح الموقف بواسطة FLT_MAX ، وهو نفس الشيء لجميع الأنظمة الأساسية.
surface.mu = dInfinty
كانت المشكلة الأخيرة سرعة الجسيمات. انتبه للرمز التالي:
DWORD angle = 0xFFFFFFFF; ... if (angle != *((DWORD*)&m.rot.x)) { angle = *((DWORD*)&m.rot.x); fsincos(angle, sina, cosa); }
يبدو أن كل شيء على ما يرام. ولكن ، 0xFFFFFFFF في x64 له معنى مختلف عند التحويل إلى نوع نقطة عائمة. والحقيقة أن fsincos لها نظير مزدوج ، ويفضل x64 البيانات المزدوجة. وهذه القيمة في الأمور المزدوجة أكثر من ذلك بكثير. تحويل إلى تعويم أنقذ الوضع.
DWORD angle = 0xFFFFFFFF; ... if (angle != *((DWORD*)&m.rot.x)) { angle = *((DWORD*)&m.rot.x);
الخلاصة
في الختام ، أود أن أقول شيئًا واحدًا فقط: جلب المنفذ في x64 الكثير من المعرفة الجديدة التي ستكون مفيدة في المستقبل. أخبرتك عن العديد من مشاكل النقل. ثم يعتمد كل شيء عليك إذا قررت القيام بذلك في أي مشروع مفتوح المصدر.
شكرا للقراءة!