Wie wir X-Ray x64 eingeführt haben

Vorwort


Guten Tag, wir werden über die X-Ray-Game-Engine bzw. über die Gabelung von X-Ray Oxygen sprechen. Im Dezember 2016 wurde das X-Ray Oxygen-Projekt veröffentlicht. Dann habe ich es alleine entwickelt und nicht davon geträumt, was es im Moment geworden ist.


Im März kam mir die Idee: "Warum nicht alles auf x64 übertragen?" Wie Sie verstehen, wird diese Idee bzw. deren Umsetzung erörtert.


Projektmontage


Der erste Schritt bestand darin, den Code zu portieren, um das Ganze unter die x64-Plattform zu stellen. Nach dem Einrichten von Projekten stieß ich auf das erste Problem ... Nein, nicht Ptr-Funktionen, sondern Assembler-Einfügungen ...


__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] } } 

Das Schöne an diesem Code war die Optimierung, aber MSBuilder in x64 hat ihn nicht unterstützt und unterstützt ihn immer noch nicht. Der größte Teil dieses Codes konnte durch Standardanaloga ersetzt werden. Es gab Stellen, die leicht in Intrinsics geändert werden konnten, z. B.:


 __asm pause; 

Es könnte sicher ersetzt werden durch:


 _mm_pause(); 

Auch in der Engine gab es manchmal Analoga von Funktionen im nativen Code (Lob an das CPUID-System). Aber es gab Orte, die man einfach loswerden musste. Zum Beispiel sind MMX-Anweisungen in Vergessenheit geraten. Zum Glück wurden sie nirgendwo angerufen, sondern einfach zusammengestellt und lagen untätig herum.


Bedienbarkeit


Nach all den Änderungen an der Baugruppe hat die nächste Phase begonnen: Wie fange ich mit all dem an?


Der erste Verräter war LuaJIT . Leider begann LuaJIT in x64 nur mit Version 2.0.5 einwandfrei zu funktionieren (na ja, fast ...). Und das waren kleine Probleme mit der Speicherzuordnung von kleinen Ziffern. Aber dann wusste ich nichts davon und das erste, was ich heraussah, war LuaJIT und rollte Vanille Lua 5.1. Ja, das hat das Problem behoben, aber Geschwindigkeit ... Denken Sie daran, wir trauern. Später im Forum wurde mir mitgeteilt, dass Sie LuaJIT 2.0.4 verwenden können. Und ja, es hat geholfen, ich habe das Spiel gestartet und konnte zum Hauptmenü gehen!


Aber ... Glück war von kurzer Dauer ... Hallo, um Offsets, Datentypen und xrCDB zu strukturieren. Das Spiel hat das Level nicht geladen, Materialien flogen auf die Objekte und die Engine mochte es nicht sehr. Nach ein paar Tagen war ich völlig verzweifelt und beschloss, einen erfahreneren Programmierer unter dem Spitznamen Giperion um Hilfe zu bitten. Ich habe nicht mit seiner Teilnahme an dem Projekt gerechnet, mein Traum war nur ein Ratschlag. Aber auf diese Weise habe ich einen erfahrenen Entwickler in das Projekt aufgenommen. Von diesem Moment an bildete sich ein Team.


Das nächste Problem war OPCODE und Datentypen. Ich musste alle udwords (unsigned int) in uqwords (unsigned long long) übersetzen. Nur um dies zu verstehen, musste ich ungefähr 4 Stunden unter dem Debugger verbringen.


Das war aber nur ein Teil des Problems. Es war an der Reihe der Materialien. Was haben wir:


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

Ein solcher Code in x32 wurde von magic #pragma pack(4) gespeichert, aber für x64 wurde er aus irgendeinem Grund nicht gespeichert. Die Wende der Ausrichtung erfolgte durch ein Debugging. Wir stellten fest, dass in einigen Fällen die Daten in der Struktur gültig waren, in anderen jedoch nicht. Wir haben die Struktur überarbeitet und den Konverter validiert. Die Struktur hat folgende 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. }; 

Und der Validator war so:


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

Daher musste ein Teil der Aufrufe aufgrund des Flags "RebuildTrisRequired" geändert werden, aber das Spiel konnte gestartet werden.


Aber im Laufe der Zeit kam das Problem mit Partikeln:


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

Dieser Code verursachte keine Probleme mit den ursprünglichen Partikeln. Sie waren zu einfach und passten leise in den ihnen zugewiesenen Speicher. Aber mit komplexeren und farbenfroheren Details, die von Modmakers gemacht wurden, kam es zu Abweichungen vom Gedächtnis. x64 und stürzt aus dem Speicher ab, wie so ?! Der Code wurde überarbeitet, Abfahrten sind weg:


 particles = alloc<Particle>(max_particles); 

Spielprobleme


Das erste Problem war wieder LuaJIT


...


Benutzerdaten für Smart Cover flogen. Dieses Problem wurde fast zuletzt behoben. Übertragen Sie einfach die Änderungen aus dem veröffentlichten LuaJIT 2.0.5.


Nächstes Problem: Physik und Berechnung von Schwimmern. control87 und _controlfp für die Berechnung der infinity in x64 wurden blockiert ... Es gab ein großes Problem mit dem Ablegen von Gegenständen, einmal bis drei fielen sie korrekt. Manchmal flogen sie in den Weltraum, manchmal unter Terranen. Das Problem lag nur in einer Variablen, die den Wert unendlich erhielt. Die Situation wurde von FLT_MAX behoben, für alle Plattformen gleich.


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

Das letzte Problem war die Geschwindigkeit der Partikel. Beachten Sie den folgenden Code:


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

Alles scheint in Ordnung zu sein. 0xFFFFFFFF in x64 hat jedoch eine andere Bedeutung bei der Konvertierung in einen Gleitkommatyp. Tatsache ist, dass fsincos ein doppeltes Gegenstück hat und x64 doppelte Daten bevorzugt. Und dieser doppelte Wert ist viel wichtiger. Die Umstellung auf Float rettete die Situation.


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

Fazit


Abschließend möchte ich nur eines sagen: Der Port in x64 brachte viele neue Erkenntnisse, die in Zukunft nützlich sein werden. Ich habe Ihnen über viele Portierungsprobleme erzählt. Und dann hängt alles von Ihnen ab, ob Sie dies in einem OpenSource-Projekt tun.


Danke fürs Lesen!

Source: https://habr.com/ru/post/de421823/


All Articles