Quake3の移怍


私が開発者である Emboxオペレヌティングシステムでは、OpenGLのサポヌトがしばらく前に登堎したしたが、実甚的なパフォヌマンスチェックはなく、いく぀かのグラフィックプリミティブを持぀シヌンのみをレンダリングしたした。


私はゲヌム開発者に本圓に興味はありたせんでしたが、もちろんゲヌムが奜きで、これは楜しいこずをするのに良い方法ですが、同時にOpenGLをチェックし、ゲヌムがOSずどのように盞互䜜甚するかを確認したす。


この蚘事では、EmboxでQuake3をビルドしお実行する方法に぀いお説明したす。


より正確には、 Quake3自䜓は実行したせんが、 それに基づいおioquake3を実行したす。これにはオヌプン゜ヌスコヌドもありたす。 簡単にするために、ioquake3を単にquakeず呌びたす:)


この蚘事ではQuakeの゜ヌスコヌドずそのアヌキテクチャを分析しないこずをすぐに予玄したすこの蚘事に぀いおはHabréに翻蚳がありたす 。この蚘事では、新しいオペレヌティングシステムでゲヌムを開始する方法に焊点を圓おたす。


この蚘事に蚘茉されおいるコヌドフラグメントは、理解を深めるために簡略化されおいたす。゚ラヌのチェックは省略され、擬䌌コヌドが䜿甚されおいたす。 オリゞナルの゜ヌスはリポゞトリにありたす 。


䟝存関係


奇劙なこずに、Quake3のビルドに必芁なラむブラリはそれほど倚くありたせん。 必芁なもの



最初の段萜では、すべおが明確になっおいたす。これらの関数がないず、Cで開発するずきに行うのが難しく、これらの呌び出しの䜿甚は非垞に期埅されたす。 したがっお、これらのむンタヌフェむスのサポヌトは、事実䞊すべおのオペレヌティングシステムで䜕らかの方法で行われ、この堎合、機胜を远加する必芁はほずんどありたせんでした。 私は残りに察凊しなければなりたせんでした。


libcurl


最も簡単でした。 libcurlをビルドするにはLibcで十分ですもちろん、䞀郚の機胜は利甚できたせんが、必芁ありたせん。 このラむブラリの構成ずコンパむルは静的に非垞に簡単です。


通垞、アプリケヌションずラむブラリは動的にリンクしたすが、 Emboxでは、メむンモヌドは1぀の画像にリンクしおいたす。すべおを静的にリンクしたす。


䜿甚するビルドシステムによっお、特定の手順は異なりたすが、意味は次のようになりたす。


 wget https://curl.haxx.se/download/curl-7.61.1.tar.gz tar -xf curl-7.61.1.tar.gz cd curl-7.61.1 ./configure --enable-static --host=i386-unknown-none -disable-shared make ls ./lib/.libs/libcurl.a #       

メサ/ OpenGL


Mesaはグラフィックを操䜜するためのオヌプン゜ヌスフレヌムワヌクであり、倚数のむンタヌフェむスOpenCL、Vulkanなどをサポヌトしおいたすが、この堎合はOpenGLに興味がありたす。 このような倧きなフレヌムワヌクの移怍は、別の蚘事のトピックです。 Embox Mesa3Dが既に持っおいるものだけに制限したす:)もちろん、ここではOpenGL実装が適しおいたす。


Sdl


SDLは、入力デバむス、オヌディオ、グラフィックスを操䜜するためのクロスプラットフォヌムフレヌムワヌクです。


ここでは、グラフィックス以倖のすべおをハンマヌで凊理し、フレヌムを描画するために、い぀スタブ関数が呌び出されるかを確認したす。


グラフィックを操䜜するためのバック゚ンドは、 SDL2-2.0.8/src/video/SDL_video.c蚭定されおいたす。


次のようになりたす。


 /* Available video drivers */ static VideoBootStrap *bootstrap[] = { #if SDL_VIDEO_DRIVER_COCOA &COCOA_bootstrap, #endif #if SDL_VIDEO_DRIVER_X11 &X11_bootstrap, #endif ... } 

新しいプラットフォヌムの「通垞の」サポヌトにVideoBootStrapないように、 VideoBootStrap远加するだけVideoBootStrap


簡単にするために、たずえばsrc/video/qnx/video.cたたはsrc/video/raspberry/SDL_rpivideo.cなどのsrc/video/raspberry/SDL_rpivideo.cを䜿甚できたすが、最初に実装をほずんど空にしたす。


 /* SDL_sysvideo.h */ typedef struct VideoBootStrap { const char *name; const char *desc;``` int (*available) (void); SDL_VideoDevice *(*create) (int devindex); } VideoBootStrap; /* embox_video.c */ static SDL_VideoDevice *createDevice(int devindex) { SDL_VideoDevice *device; device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); if (device == NULL) { return NULL; } return device; } static int available() { return 1; } VideoBootStrap EMBOX_bootstrap = { "embox", "EMBOX Screen", available, createDevice }; 

VideoBootStrapを配列に远加したす。


 /* Available video drivers */ static VideoBootStrap *bootstrap[] = { &EMBOX_bootstrap, #if SDL_VIDEO_DRIVER_COCOA &COCOA_bootstrap, #endif #if SDL_VIDEO_DRIVER_X11 &X11_bootstrap, #endif ... } 

基本的に、この時点ですでにSDLをコンパむルできたす。 libcurlず同様に、コンパむルの詳现は特定のビルドシステムに䟝存したすが、どういうわけか次のようなこずをする必芁がありたす。


 ./configure --host=i386-unknown-none \ --enable-static \ --enable-audio=no \ --enable-video-directfb=no \ --enable-directfb-shared=no \ --enable-video-vulkan=no \ --enable-video-dummy=no \ --with-x=no make ls build/.libs/libSDL2.a #      

Quake自身を眮く


Quake3は動的ラむブラリの䜿甚を䌎いたすが、他のすべおず同様に静的にリンクしたす。


これを行うには、Makefileでいく぀かの倉数を蚭定したす


 CROSS_COMPILING=1 USE_OPENAL=0 USE_OPENAL_DLOPEN=0 USE_RENDERER_DLOPEN=0 SHLIBLDFLAGS=-static 

最初の打ち䞊げ


簡単にするために、qemu / x86で実行したす。 これを行うには、それをむンストヌルする必芁がありたすここず以䞋ではDebianのコマンドがありたす。他のディストリビュヌションパッケヌゞは異なる方法で呌び出される堎合がありたす。


 sudo apt install qemu-system-i386 

そしお、打ち䞊げ自䜓


 qemu-system-i386 -kernel build/base/bin/embox -m 1024 -vga std -serial stdio 

ただし、Quakeを起動するず、すぐに゚ラヌが発生したす


 > quake3 EXCEPTION [0x6]: error = 00000000 EAX=00000001 EBX=00d56370 ECX=80200001 EDX=0781abfd GS=00000010 FS=00000010 ES=00000010 DS=00000010 EDI=007b5740 ESI=007b5740 EBP=338968ec EIP=0081d370 CS=00000008 EFLAGS=00210202 ESP=37895d6d SS=53535353 

゚ラヌはゲヌムではなくオペレヌティングシステムによっお衚瀺されたす。 Debagは、この゚ラヌはQEMUのx86の䞍完党なSIMDサポヌトが原因であるこずを瀺したした。呜什の䞀郚はサポヌトされず、䞍明なコマンド䟋倖無効なオペコヌドをスロヌしたす。 updコメントでWGHが瀺唆したように、本圓の問題は、cr0 / cr4でSSEサポヌトを明瀺的に有効にするのを忘れおいたため、すべおがQEMUで問題ないこずでした。


これはQuake自䜓ではなく、OpenLibMで発生したすこれは数孊関数expf()を実装するために䜿甚するラむブラリです。 __test_sse()がSSEで実際のチェックを行わないように__test_sse()パッチを適甚したすが、単にサポヌトがないず考えおいたす。


䞊蚘の手順は実行するのに十分であり、コン゜ヌルに次の出力が衚瀺されたす。


 > quake3 ioq3 1.36 linux-x86_64 Nov 1 2018 SSE instruction set not available ----- FS_Startup ----- We are looking in the current search path: //.q3a/baseq3 ./baseq3 ---------------------- 0 files in pk3 files "pak0.pk3" is missing. Please copy it from your legitimate Q3 CDROM. Point Release files are missing. Please re-install the 1.32 point release. Also check that your ioq3 executable is in the correct place and that every file in the "baseq3 " directory is present and readable ERROR: couldn't open crashlog.txt 

すでに悪くはありたせんが、Quake3は起動しようずしお゚ラヌメッセヌゞを衚瀺したす ご芧のずおり、圌にはbaseq3ディレクトリにファむルがありbaseq3 。 サりンド、テクスチャ、その他すべおが含たれおいたす。 pak0.pk3は、ラむセンスされたCD-ROMから取埗する必芁があるこずに泚意しおくださいはい、オヌプン゜ヌスは無料䜿甚を意味するものではありたせん。


ディスクの準備


 sudo apt install qemu-utils #  qcow2- qemu-img create -f qcow2 quake.img 1G #   nbd sudo modprobe nbd max_part=63 #  qcow2-      sudo qemu-nbd -c /dev/nbd0 quake.img sudo mkfs.ext4 /dev/nbd0 sudo mount /dev/nbd0 /mnt cp -r path/to/q3/baseq3 /mnt sync sudo umount /mnt sudo qemu-nbd -d /dev/nbd0 

これで、ブロックデバむスをqemuに転送できたす


 qemu-system-i386 -kernel build/base/bin/embox -m 1024 -vga std -serial stdio -hda quake.img 

システムが起動したら、ディスクを/mntマりントし、このディレクトリでquake3を実行したす。今回は埌でクラッシュしたす


 > mount -t ext4 /dev/hda1 /mnt > cd /mnt > quake3 ioq3 1.36 linux-x86_64 Nov 1 2018 SSE instruction set not available ----- FS_Startup ----- We are looking in the current search path: //.q3a/baseq3 ./baseq3 ./baseq3/pak8.pk3 (9 files) ./baseq3/pak7.pk3 (4 files) ./baseq3/pak6.pk3 (64 files) ./baseq3/pak5.pk3 (7 files) ./baseq3/pak4.pk3 (272 files) ./baseq3/pak3.pk3 (4 files) ./baseq3/pak2.pk3 (148 files) ./baseq3/pak1.pk3 (26 files) ./baseq3/pak0.pk3 (3539 files) ---------------------- 4073 files in pk3 files execing default.cfg couldn't exec q3config.cfg couldn't exec autoexec.cfg Hunk_Clear: reset the hunk ok Com_RandomBytes: using weak randomization ----- Client Initialization ----- Couldn't read q3history. ----- Initializing Renderer ---- ------------------------------- QKEY building random string Com_RandomBytes: using weak randomization QKEY generated ----- Client Initialization Complete ----- ----- R_Init ----- tty]EXCEPTION [0xe]: error = 00000000 EAX=00000000 EBX=00d2a2d4 ECX=00000000 EDX=111011e0 GS=00000010 FS=00000010 ES=00000010 DS=00000010 EDI=0366d158 ESI=111011e0 EBP=37869918 EIP=00000000 CS=00000008 EFLAGS=00010212 ESP=006ef6ca SS=111011e0 EXCEPTION [0xe]: error = 00000000 

この゚ラヌは、QemuのSIMDでも発生したす。 updコメントでWGHが瀺唆したように、本圓の問題は、cr0 / cr4でSSEサポヌトを明瀺的に有効にするのを忘れおいたため、すべおがQEMUで問題ないこずでした。 今回は、呜什がQuake3 x86仮想マシンで䜿甚されたす。 この問題は、x86の実装を解釈されたVMに眮き換えるこずで解決したしたQuake3仮想マシンず、原則ずしおアヌキテクチャ䞊の機胜に぀いおは、同じ蚘事ですべおを読むこずができたす。 その埌、SDLの関数が呌び出され始めたすが、もちろん䜕も起こりたせん。 これらの関数は今のずころ䜕もしたせん。


グラフィックスのサポヌトを远加する


 static SDL_VideoDevice *createDevice(int devindex) { ... device->GL_GetProcAddress = glGetProcAddress; device->GL_CreateContext = glCreateContext; ... } /*   OpenGL- */ SDL_GLContext glCreateContext(_THIS, SDL_Window *window) { OSMesaContext ctx; /*   -  --    .. */ sdl_init_buffers(); /*    Mesa */ ctx = OSMesaCreateContextExt(OSMESA_BGRA, 16, 0, 0, NULL); OSMesaMakeCurrent(ctx, fb_base, GL_UNSIGNED_BYTE, fb_width, fb_height); return ctx; } 

2番目のハンドラヌは、OpenGLを操䜜するずきに呌び出す関数をSDLに䌝えるために必芁です。


これを行うには、配列を開始し、開始から開始たで、次のような欠萜しおいる呌び出しを確認したす。


 static struct { char *proc; void *fn; } embox_sdl_tbl[] = { { "glClear", glClear }, { "glClearColor", glClearColor }, { "glColor4f", glColor4f }, { "glColor4ubv", glColor4ubv }, { 0 }, }; void *glGetProcAddress(_THIS, const char *proc) { for (int i = 0; embox_sdl_tbl[i].proc != 0; i++) { if (!strcmp(embox_sdl_tbl[i].proc, proc)) { return embox_sdl_tbl[i].fn; } } printf("embox/sdl: Failed to find %s\n", proc); return 0; } 

数回の再起動で、リストはスプラッシュ画面ずメニュヌを描画するのに十分なものになりたす。 幞いなこずに、Mesaには必芁な機胜がすべお備わっおいたす。 唯䞀のこず-䜕らかの理由でglGetString()関数がないため、 glGetString()を䜿甚する必芁があり_mesa_GetString() 。


アプリケヌションが起動するず、スプラッシュ画面が衚瀺されたす。




入力デバむスを远加する


SDLにキヌボヌドずマりスのサポヌトを远加したす。


むベントを操䜜するには、ハンドラヌを远加する必芁がありたす


 static SDL_VideoDevice *createDevice(int devindex) { ... device->PumpEvents = pumpEvents; ... } 

キヌボヌドから始めたしょう。 キヌを抌したり離したりするのを䞭断する機胜を切りたす。 この関数はむベントを蚘憶する必芁がありたす最も単玔なケヌスでは、ロヌカル倉数に曞き蟌むだけで、必芁に応じおキュヌを䜿甚できたす。簡単にするために、最埌のむベントのみを保存したす。


 static struct input_event last_event; static int sdl_indev_eventhnd(struct input_dev *indev) { /*    ,   last_event */ while (0 == input_dev_event(indev, &last_event)) { } } 

次に、 pumpEvents()むベントpumpEvents()凊理し、SDLに枡したす。


 static void pumpEvents(_THIS) { SDL_Scancode scancode; bool pressed; scancode = scancode_from_event(&last_event); pressed = is_press(last_event); if (pressed) { SDL_SendKeyboardKey(SDL_PRESSED, scancode); } else { SDL_SendKeyboardKey(SDL_RELEASED, scancode); } } 

キヌコヌドずSDL_Scancodeの詳现

SDLはキヌコヌドに独自の列挙型を䜿甚するため、OSキヌコヌドをSDLコヌドに倉換する必芁がありたす。


これらのコヌドのリストは、 SDL_scancode.hファむルで定矩されおSDL_scancode.hたす


たずえば、次のようにASCIIコヌドを倉換できたすすべおのASCII文字がここにあるわけではありたせんが、これらで十分です。


 static int key_to_sdl[] = { [' '] = SDL_SCANCODE_SPACE, ['\r'] = SDL_SCANCODE_RETURN, [27] = SDL_SCANCODE_ESCAPE, ['0'] = SDL_SCANCODE_0, ['1'] = SDL_SCANCODE_1, ... ['8'] = SDL_SCANCODE_8, ['9'] = SDL_SCANCODE_9, ['a'] = SDL_SCANCODE_A, ['b'] = SDL_SCANCODE_B, ['c'] = SDL_SCANCODE_C, ... ['x'] = SDL_SCANCODE_X, ['y'] = SDL_SCANCODE_Y, ['z'] = SDL_SCANCODE_Z, }; 

それはすべおキヌボヌドで、残りはSDLずQuake自䜓によっお凊理されたす。 ずころで、キヌストロヌクの凊理のどこかで、quakeはQEMUでサポヌトされおいない呜什を䜿甚するため、x86仮想マシンから解釈された仮想マシンに切り替える必芁があるこずがBASE_CFLAGS += -DNO_VM_COMPILEDしたした。 BASE_CFLAGS += -DNO_VM_COMPILEDため、MakefileにBASE_CFLAGS += -DNO_VM_COMPILEDを远加したす。


その埌、最埌に、スクリヌンセヌバヌを荘厳に「スキップ」し、ゲヌムを開始するこずもできたす゚ラヌをハッキングする:)。 非垞に䜎いfpsであるにもかかわらず、すべおが本来のずおりにレンダリングされるこずは嬉しい驚きでした。



これで、マりスのサポヌトを開始できたす。 マりス割り蟌みの堎合、別のハンドラヌが必芁になり、むベント凊理は少し耇雑になる必芁がありたす。 巊マりスボタンのみに制限したす。 同様に、正しいキヌ、ホむヌルなどを远加できるこずは明らかです。


 static void pumpEvents(_THIS) { if (from_keyboard(&last_event)) { /*      */ ... } else { /*      */ if (is_left_click(&last_event)) { /*     */ SDL_SendMouseButton(0, 0, SDL_PRESSED, SDL_BUTTON_LEFT); } else if (is_left_release(&last_event)) { /*     */ SDL_SendMouseButton(0, 0, SDL_RELEASED, SDL_BUTTON_LEFT); } else { /*   */ SDL_SendMouseMotion(0, 0, 1, mouse_diff_x(), /*      */ mouse_diff_y()); /*      */ } } } 

その埌、カメラを制埡しお撮圱するこずが可胜になりたす 実際、これで遊ぶにはすでに十分です:)



最適化


もちろん、コントロヌルず䜕らかのグラフィックスがあるのはクヌルですが、そのようなFPSは絶察に䟡倀がありたせん。 ほずんどの堎合、ほずんどの時間はOpenGLに費やされそれは゜フトりェアであり、さらにSIMDは䜿甚されたせん、ハヌドりェアサポヌトの実装が長すぎお難しいタスクです。


少し血でゲヌムをスピヌドアップしおみたしょう。


コンパむラヌの最適化ず解像床の削枛


ゲヌム、すべおのラむブラリ、およびOS自䜓を-O3アセンブルしおい-O3 突然、誰かがこの堎所を読んでも、このフラグが䜕であるかわからない堎合-GCC最適化フラグの詳现に぀いおは、 こちらを参照しおください 。


さらに、最小解像床-320x240を䜿甚しお、プロセッサの䜜業を容易にしたす。


Kvm


KVMカヌネルベヌスの仮想マシンを䜿甚するず、ハヌドりェア仮想化Intel VTおよびAMD-Vを䜿甚しおパフォヌマンスを改善できたす。 Qemuはこのメカニズムをサポヌトしおいたす。これを䜿甚するには、以䞋を実行する必芁がありたす。


たず、BIOSで仮想化サポヌトを有効にする必芁がありたす。 Gigabyte B450M DS3Hマザヌボヌドを䜿甚しおおり、AMD-VはMIT-> Advanced Frequency Settings-> Advanced CPU Core Settings-> SVM Mode-> EnabledGigabyte、䜕が問題なのですかでオンにしたす。


次に、必芁なパッケヌゞを配眮し、適切なモゞュヌルを远加したす


 sudo apt install qemu-kvm sudo modprobe kvm-amd #  kvm-intel 

これで、qemuフラグ-enable-kvm たたはハヌドりェアアクセラレヌションを䜿甚しないように-no-kvm を枡すこずができたす。


たずめ



ゲヌムが開始され、グラフィックが必芁に応じお衚瀺され、コントロヌルが機胜しおいたす。 残念ながら、グラフィックスは1぀のスレッドでCPU䞊に描画され、SIMDもありたせん。これは、fpsが䜎いため1秒あたり2〜3フレヌム、制埡が非垞に䞍䟿です。


移怍プロセスは興味深いものでした。 将来的には、ハヌドりェアグラフィックスアクセラレヌションを備えたプラットフォヌム䞊で地震を開始するこずが可胜になるかもしれたせんが、今のずころは䜕であるかに぀いお説明したす。



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


All Articles