As we in x-ray x64 delivered

Foreword


Good day, we'll talk about the X-Ray game engine, or rather, about its fork, X-Ray Oxygen In December 2016, the draft X-Ray Oxygen was published. Then I developed it alone and did not dream about what he has become at the moment.


In March, I got the idea: "Why not transfer it all to x64?". As you understand, it is about this idea, or rather its implementation, that will be discussed.


Build project


The first step was to transfer the code to collect the whole thing under the x64 platform. After setting up the projects, I ran into the first problem ... No, not Ptr functions, but assembly inserts ...


__forceinline voidfsincos( constfloat 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]
 } }

The beauty of this code was to optimize, but MSBuilder in x64 did not support it and does not support it until now. Most of this code could be replaced by std counterparts, there were places that could be easily changed to Intrinsics , for example, like:


__asm pause;

It could be safely replaced by:


_mm_pause();

Also in the engine, sometimes there were analogs of functions on the native code (Praise to the CPUID system). But there were places that you just had to get rid of. For example, the MMX instructions have sunk into oblivion. Fortunately, they were not called anywhere, they just compiled and were lying around without any work.


Performance


After all the edits to the assembly, the next stage came: How to start all this?


The first traitor was LuaJIT . Unfortunately, LuaJIT began to work fine (well, almost ...) in x64 only from version 2.0.5. And then there were small problems with memory allocation from small digits. But, then I did not know about it and first of all I sawed out LuaJIT and rolled vanilla Lua 5.1. Yes, it fixed the problem, but speed ... Remember, grieve. Later on the forum I was told that you can try to use LuaJIT 2.0.4. And yes, it helped, I started the game and was able to go to the main menu!


But ... Happiness was short-lived ... Hi structure shifts, data types and xrCDB. The game did not load the level, the materials on the objects flew and the engine did not like it very much. After a couple of days, I finally despaired and decided to ask for help from a more experienced programmer under the nickname Giperion. I did not count on his participation in the project, my dream was just advice. But, in this way, I got an experienced developer in the project. From this moment a team was formed.


The next problem was OPCODE and data types. I had to translate all udword'y (unsigned int) to uqword'y (unsigned long long). Just to understand this, I had to spend about 4 hours under the debugger.


But, this was only part of the problem. It is the turn of materials. What we have:


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

Such code in x32 was saved by magic #pragma pack(4), but for some reason in x64 it did not save. The alignment queue has come, we have found out through the debbag that for some cases the data in the structure were valid and for others not. Altered the structure and made a converter-validator. The structure received the following form:


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

And the validator was:


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

Thus, we had to change part of the calls due to the rebuildTrisRequired flag, but the game was able to start.


But, over time, the problem with the parts has come:


real_ptr = malloc( sizeof( Particle ) * ( max_particles + 1 ) );
particles = (Particle*)((DWORD)real_ptr + (64 - ((DWORD)real_ptr & 63)));

This code did not cause problems with the original parts. They were too simple and quietly fit into the memory allocated for them. But with more complex and colorful bands that made modmakers, memory departures came. x64 and memory crashes, how is that ?! The code has been redone, departures are gone:


particles = alloc<Particle>(max_particles);

Game problems


The first problem was, again, LuaJIT


...


Flew userdata for smart cover'ov. This problem has been fixed almost the latest. Simply transferring edits from a reverted LuaJIT 2.0.5.


Next problem: Physics and calculation of floats. control87and _controlfpfor the calculation infinityin x64 were blocked ... There was a huge problem with the drop of objects, once to three they fell correctly. Sometimes they flew into space, sometimes under the terrane. The problem was only one variable, which was given the value of infinity. The situation is corrected FLT_MAX, the same for all platforms.


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

The last problem was the speed of the particle. Pay attention to the following code:


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

It seems to be all right. But, 0xFFFFFFFF in x64 has a different meaning when converting to a floating point type. The fact is that fsincos has Double analog, and x64 prefers double data. And this value in double matters much more. The situation was saved by the conversion to float.


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

Conclusion


In conclusion, I want to say just one thing: the port in x64 brought a lot of new knowledge that will be useful in the future. I told you about the many problems with porting. And then everything will depend on you, if you decide to do this in any OpenSource projects.


Thanks for reading!


Also popular now: