我们如何引入X-Ray x64

前言


美好的一天,我们将谈论X射线游戏引擎,或更确切地说是关于X射线氧气的分支。2016年12月,发布了X射线氧气项目。 然后我独自开发了它,没有梦到现在它已经变成了什么。


三月份,我想到了一个主意:“为什么不将其全部转移到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] } } 

这段代码的美在于优化,但是x64中的MSBuilder不支持它,仍然不支持它。 大部分代码都可以用std类似物代替,有些地方可以很容易地更改为Intrinsics ,例如:


 __asm pause; 

可以安全地替换为:


 _mm_pause(); 

同样在引擎中,有时在本机代码上有类似的功能(称赞CPUID系统)。 但是有些地方您只是要摆脱。 例如, MMX指令已被遗忘。 幸运的是,它们在任何地方都没有被调用,而只是被编译并闲置着。


可操作性


在装配上进行所有编辑之后,下一阶段开始了:如何开始所有这些工作?


第一个叛徒是LuaJIT 。 不幸的是,只有在2.0.5版中,LuaJIT才能在x64中正常运行(很好,几乎...)。 这些都是小数字分配内存的小问题。 但是,那时我还不知道,所以我发现的第一件事是LuaJIT和滚动的香草Lua 5.1。 是的,这解决了问题,但是速度很快。记住,我们感到悲伤。 后来在论坛上,我得知可以尝试使用LuaJIT 2.0.4。 是的,它帮助了我开始游戏,并能够进入主菜单!


但是……幸福是短暂的……您好要构造偏移量,数据类型和xrCDB。 游戏没有加载关卡,材料飞向了物体,引擎也不太喜欢它。 几天后,我完全绝望了,决定以昵称Giperion的身份向经验丰富的程序员寻求帮助。 我没有指望他参与该项目,我的梦想只是建议。 但是,通过这种方式,我吸引了一位经验丰富的开发人员加入该项目。 从那时起,一个团队就形成了。


下一个问题是OPCODE和数据类型。 我必须将所有udword(无符号int)转换为uqwords(无符号long long)。 为了理解这一点,我不得不在调试器上花费大约4个小时。


但是,那仅仅是问题的一部分。 轮到材料了。 我们有什么:


 union { u32 dummy; // 4b struct { u32 material : 14; // u32 suppress_shadows : 1; // u32 suppress_wm : 1; // u32 sector : 16; // }; }; 

x32中的此类代码由魔术#pragma pack(4)保存,但由于某些原因,对于x64,它未保存。 对齐的时候到了,通过调试我们发现在某些情况下结构中的数据是有效的,而在其他情况下则无效。 我们重新构造该结构,并使其成为转换器验证器。 该结构具有以下形式:


 union { size_t dummy; struct { size_t material:14; // size_t suppress_shadows:1; // size_t suppress_wm:1; // size_t sector:16; // size_t dumb : 32; // ,     x64. }; 

验证器是这样的:


 ... 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))); 

此代码不会导致原始粒子出现问题。 它们太简单了,无法安静地容纳在为它们分配的内存中。 但是,随着modmaker制作的更加复杂和丰富多彩的细节,记忆的背离而来了。 x64并从内存崩溃,怎么回事? 重做了代码,离开了:


 particles = alloc<Particle>(max_particles); 

游戏问题


同样,第一个问题是LuaJIT


...


智能封面的用户数据飞涨。 这个问题几乎已经解决了。 只需从发布的LuaJIT 2.0.5中传输编辑内容即可。


下一个问题:浮点数的物理和计算。 用于在x64中计算infinity control87_controlfp被阻止...物品掉落有一个很大的问题,一到三个物品就正确掉了。 有时它们飞入太空,有时在地底下。 问题仅在于一个变量,该变量的值是无穷大。 这种情况由FLT_MAX修复,所有平台都相同。


 surface.mu = dInfinty // x32 surface.mu = FLT_MAX // x64 

最后一个问题是粒子的速度。 请注意以下代码:


 DWORD angle = 0xFFFFFFFF; ... if (angle != *((DWORD*)&m.rot.x)) { angle = *((DWORD*)&m.rot.x); fsincos(angle, sina, cosa); } 

一切似乎井井有条。 但是,x64中的0xFFFFFFFF转换为浮点类型时具有不同的含义。 事实是fsincos具有Double副本,而x64更喜欢double数据。 而且,双重价值更为重要。 转换为浮点保存了这种情况。


 DWORD angle = 0xFFFFFFFF; ... if (angle != *((DWORD*)&m.rot.x)) { angle = *((DWORD*)&m.rot.x); // fsincos(angle, sina, cosa); fsincos(*(float*)&angle, sina, cosa); } 

结论


总之,我只想说一件事:x64中的端口带来了很多新知识,这些知识将来会有用。 我告诉过您许多移植问题。 然后,如果您决定在任何OpenSource项目中执行此操作,那么一切都将取决于您。


感谢您的阅读!

Source: https://habr.com/ru/post/zh-CN421823/


All Articles