標籤:

從零開始手敲次世代遊戲引擎(MacOS特別篇)

雙十一快到了,不知道大家今年準備剁手購買什麼。作者一直心念著之前提出想要在MacOS上面學習本教程的同學,雙十一剁手入了一個MacPro本,摸了2天來更新這篇文章。

(在此之前作者從來未使用過Mac。所以寫的不對請不吝賜教)

根據資料(*1)顯示,MacOS從版本10開始基於一個被稱為XNU的內核(Darwin達爾文系統)。這個內核是基於BSD系統的。BSD是一種Unix系統,而Linux是模仿Unix系統開發的。所以我們之前寫的Linux版本應該是可以比較容易地在MacOS上面編譯的。是不是這樣,有多容易,我們實際來試一下。

首先我們需要安裝編譯環境。之前我們說過,MacOSX之後標準是使用clang。但是這個環境預設是沒有安裝到系統當中的。最為簡單的安裝方法是安裝Xcode。我們可以在Apple Store裡面方便的找到它並安裝。

因為我們是要工作在命令行,安裝完成之後,我們需要打開命令行窗口安裝Xcode的命令行工具。在MacOS打開一個程序最快的方法是使用Spotlight搜索,我們按下(?)鍵和空格鍵,然後輸入term,按下回車鍵。

然後輸入

xcode-select --installn

然後根據屏幕提示完成安裝。

接下來是安裝git/cmake等工具。在Mac上面安裝GNU軟體似乎一般有兩個方式,一個是通過一個叫做HomeBrew的包管理,而另外一個則是使用MacPort。我首先是試了一下HomeBrew,這個好像是基於ruby的,裡面可以用的包很少,比如找不到libxcb。所以後來我又換成了MacPort。MacPort就是Port的Mac版,而Port是BSD系統當中的包管理器,就相當於Linux系統當中的APT或者YUM工具。只不過Port是下載源代碼到本地進行編譯安裝的,而不是直接下載二進位包進行安裝。(所以我們需要先安裝Xcode命令行工具)

MacPort的安裝方法在MacPort的網站上有詳細介紹。需要注意的就是必須根據Mac系統的版本下載對應的包。不同的版本之間是不兼容的。

裝好MacPort之後,在命令行運行

sudo port selfupdatensudo port install git git-lfs cmaken

就可以完成相關工具的安裝。

然後我們就可以嘗試checkout我們的代碼並進行編譯了。首先我們嘗試編譯我們的第三方依賴庫,crossguid。運行項目根目錄下的build_crossguid.sh,直接編譯成功了。

接下來我們嘗試編譯OpenGEX,運行項目根目錄下的build_opengex.sh,也是一次成功。

然後我們編譯我們的引擎。這裡主要有幾個問題:

  1. 首先是找不到ispc。這主要是由於我們CMake文件的寫法導致的(CMAKE_SYSTEM_NAME變成了Darwin而不是Linux)。調整之後找到。可以直接使用Linux下編譯的ispc的二進位文件,也可以在MacOS下面重新編譯一個。編譯的腳本在External/src/ispc下面(如果是空目錄請在項目根目錄執行

    git submodule update --init External/src/ispc

    獲取源代碼和編譯腳本)只不過需要注意的是Xcode預設安裝的toolchain似乎並不包括llvm相關的庫,而編譯ispc需要這些庫。我的做法是直接用項目根目錄下的build_llvm_clang.sh腳本重新完整編譯了一套llvm工具出來。應該也可以在Xcode裡面安裝這些庫,只不過我沒有去調查了;

  2. 提示找不到xcb、X11等庫。這個問題又可以分兩個方面:
    1. 沒有X11 Server。MacOS是使用的自己的圖形伺服器(Quartz)和圖形庫(Cocoa)。在老的版本當中也同時提供了X11 Server,但是好像從OS X開始X11被剝離到一個名為XQuartz的項目當中,並在GitHub開源了(*2)。所以需要安裝XQuartz。安裝過程請參考GitHub相關項目頁面;
    2. 沒有xcb、X11等庫。這個通過port安裝就可以了。需要安裝的包名稱為:

      xorg-libxcb

      xorg-libX11

好了。進行了這些準備之後,代碼就可以完成編譯了(執行build.sh)。跑一下Test目錄下的測試,大部分沒有問題,但是GeomMathTest的部分結果出現問題。如果在cmake之後指定『-G 「Xcode」』,則最後的可執行文件的鏈接會出錯,提示libGeomMath.a的符號表有問題。經過調試發現是我們生成libGeomMath.a的時候只是使用ar工具將obj文件壓成了庫,但是沒有執行ranlib工具來更新庫當中的符號表。如下修改Framework/Geommath/ispc/CMakeLists.txt之後問題得到解決:

add_custom_command(OUTPUT ${GEOMMATH_LIB_FILE}n COMMAND ${CMAKE_AR} ${ISPC_LIBRARIAN_OPTIONS} ${OBJECTS}n+ COMMAND ${CMAKE_RANLIB} ${GEOMMATH_LIB_FILE}n COMMAND rm -v ${OBJECTS}n DEPENDS ${OBJECTS}n )n

最後的問題是編譯雖然通過了,但是執行./build/Platform/Darwin/MyGameEngineOpenGL會直接發生段錯誤而強行關閉。如下:

chenwenlideMacBook-Pro:GameEngineFromScratchPrivate chenwenli$ ./build/Platform/Darwin/MyGameEngineOpenGL nSegmentation fault: 11n

使用lldb工具進行調試,發現錯誤信息如下:

Process 51269 stoppedn* thread #1, queue = com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)n frame #0: 0x0000000000000000nerror: memory read failed for 0x0nTarget 0: (MyGameEngineOpenGL) stopped.n(lldb) btn* thread #1, queue = com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)n * frame #0: 0x0000000000000000n frame #1: 0x0000000100002a9e MyGameEngineOpenGL`My::OpenGLApplication::Initialize(this=0x0000000100701410) at OpenGLApplication.cpp:106n frame #2: 0x0000000100043a29 MyGameEngineOpenGL`main(argc=1, argv=0x00007ffeefbffa48) at main.cpp:7n frame #3: 0x00007fff6ad71145 libdyld.dylib`start + 1n frame #4: 0x00007fff6ad71145 libdyld.dylib`start + 1n

出錯位置是在OpenGLApplication.cpp:106,使用l命令打出代碼:

(lldb) l OpenGLApplication.cpp:106n 96 t {n 97 t fprintf(stderr, "Cant open displayn");n 98 t return -1;n 99 t }n 100 tn 101 t default_screen = DefaultScreen(m_pDisplay);n 102 tn 103 t gladLoadGLX(m_pDisplay, default_screen);n 104 tn 105 t /* Query framebuffer configurations */n 106 t fb_configs = glXChooseFBConfig(m_pDisplay, default_screen, visual_attribs, &num_fb_configs);n

繼續跟蹤可以發現是相關的glX函數指針未被glad正確載入造成的。這個問題的解決方法現在還在摸索當中。。。(已經解決了,看更新2)

# 對了,代碼在「mac」分支(branch)當中。

#更新1: 有進展了,從零開始手敲次世代遊戲引擎(十二)當中的代碼可以正常出圖了:

在MacOS下編譯的命令與Linux下基本相同,但是需要加入頭文件和庫的查找路徑。

分兩種情況:

  • 如果是直接安裝的XQuartz的dmg的包,那麼是下面這樣:

chenwenlideMBP:GameEngineFromScratchPrivate chenwenli$ clang -I /usr/X11/include/ -L /usr/X11/lib -o helloengine_opengl helloengine_opengl.cpp -lxcb -lX11 -lX11-xcb -lGL -lGLUn

  • 如果是通過macports安裝的XQuatz(xorg-server),那麼還需要安裝libxcb, libX11, libX11-xcb, libGL, libGLU等幾個包。這裡面需要注意的是libGL是包括在mesa驅動包里的。一個簡單的方法是直接用 sudo ports install glxgears這個命令安裝glxgears這個測試程序,會自動安裝GLX相關的包,然後再用sudo ports install libGLU補上libGLU就好了 。

仔細觀察繪製出的圖形,會發現與從零開始手敲次世代遊戲引擎(十二)題圖有一些區別。(有一條明顯的對角線)

根據參考引用(*4),這是因為在Mac OS當中,通過XQuartz能夠支持的OpenGL只能到版本2.1。所以,對於四邊形的繪製,其實是拆成兩個三角形繪製的,形成了明顯的對角線。如果要使用版本3之上的OpenGL,必須使用Mac OS原生的圖形服務(Quartz,注意少個X)和圖形API(Cocoa)創建繪圖Context,而不是X11 + GLX。其實在上面的過程當中也可以看到,MacPorts安裝的X11是基於mesa驅動的,這個是一個開源社區的驅動,不是A/N卡原廠驅動(好吧,我的Mac其實是15年產品是I卡...但是能支持4.1),所以能夠支持的功能是有限的。Mac OS原生的圖形API叫Cocoa,是一個基於Object-C語言的庫。另外最近還有一個新的Swift語言可以用。這部分等我自己搞明白了更新上來。

#更新2 Segmentation Fault的問題解決了。原因是External/glad/src/glad_glx.c當中,當操作系統為MacOS的時候,是按照如下的順序查找OpenGL的動態庫的(其中綠色的兩行是我加上的):

diff --git a/External/src/glad/src/glad_glx.c b/External/src/glad/src/glad_glx.cnindex 9551033..af0f61e 100644n--- a/External/src/glad/src/glad_glx.cn+++ b/External/src/glad/src/glad_glx.cn@@ -127,6 +127,8 @@ staticn int open_gl(void) {n #ifdef __APPLE__n static const char *NAMES[] = {n+ "/opt/local/lib/libGL.1.dylib",n+ "/opt/local/lib/libGL.dylib",n "../Frameworks/OpenGL.framework/OpenGL",n "/Library/Frameworks/OpenGL.framework/OpenGL",n "/System/Library/Frameworks/OpenGL.framework/OpenGL",n

原來的3行實際上載入的都是MacOS自帶的OpenGL庫。這個庫是沒有GLX相關的介面的。查看的方法是使用nm命令,如下:

nm -gU "/System/Library/Frameworks/OpenGL.framework/OpenGL"n00000000000053c8 T _CGLAreContextsSharedn000000000000546b T _CGLBackDispatchn0000000000008b68 T _CGLChoosePixelFormatn0000000000006b46 T _CGLClearDrawablen00000000000052e3 T _CGLCopyContextn0000000000004620 T _CGLCreateContextn000000000000aa9e T _CGLCreatePBuffern000000000000ad12 T _CGLDescribePBuffern000000000000a584 T _CGLDescribePixelFormatn000000000000b6cd T _CGLDescribeRenderern0000000000004381 T _CGLDestroyContextn000000000000adba T _CGLDestroyPBuffern000000000000a0e8 T _CGLDestroyPixelFormatn000000000000b69e T _CGLDestroyRendererInfon000000000000ccd2 T _CGLDisablen000000000000cc47 T _CGLEnablen0000000000008b2b T _CGLErrorStringn000000000000d058 T _CGLFlushDrawablen0000000000005461 T _CGLFrontDispatchn000000000000528d T _CGLGetContextRetainCountn0000000000006a95 T _CGLGetDeviceFromGLRenderern000000000000a8c8 T _CGLGetGlobalOptionn0000000000005436 T _CGLGetNextContextn00000000000085a0 T _CGLGetOffScreenn000000000000aa45 T _CGLGetOptionn00000000000077ef T _CGLGetPBuffern000000000000af7c T _CGLGetPBufferRetainCountn000000000000cb70 T _CGLGetParametern00000000000052b5 T _CGLGetPixelFormatn000000000000a1b4 T _CGLGetPixelFormatRetainCountn0000000000006a2d T _CGLGetShareGroupn0000000000007c21 T _CGLGetSurfacen000000000000d0b3 T _CGLGetVersionn00000000000044b8 T _CGLGetVirtualScreenn000000000000ca68 T _CGLIsEnabledn000000000000a5d6 T _CGLLockContextn0000000000006024 T _CGLOpenCLMuxLockDownn000000000000b30c T _CGLQueryRendererInfon00000000000050ef T _CGLReleaseContextn000000000000af4a T _CGLReleasePBuffern000000000000a182 T _CGLReleasePixelFormatn0000000000005533 T _CGLRestoreDispatchn000000000000556a T _CGLRestoreDispatchFunctionn0000000000005263 T _CGLRetainContextn000000000000af6a T _CGLRetainPBuffern000000000000a1a2 T _CGLRetainPixelFormatn0000000000005480 T _CGLSelectDispatchn00000000000054ba T _CGLSelectDispatchBoundedn0000000000005501 T _CGLSelectDispatchFunctionn0000000000007cda T _CGLSetFullScreenn00000000000082e0 T _CGLSetFullScreenOnDisplayn000000000000a6a0 T _CGLSetGlobalOptionn0000000000008403 T _CGLSetOffScreenn000000000000a9e0 T _CGLSetOptionn0000000000006c25 T _CGLSetPBuffern000000000000b24c T _CGLSetPBufferVolatileStaten000000000000cd5d T _CGLSetParametern000000000000788f T _CGLSetSurfacen000000000000455c T _CGLSetVirtualScreenn000000000000b095 T _CGLTexImageIOSurface2Dn000000000000af8e T _CGLTexImagePBuffern000000000000a608 T _CGLUnlockContextn0000000000008671 T _CGLUpdateContextn000000000000a1c6 T _GLCDescribePixelFormatn0000000000007758 T _GLCGetPBuffern000000000000ce49 T _GLCGetParametern0000000000001fae T _GLCGetProfilerStoragen0000000000007be2 T _GLCGetSurfacen0000000000004405 T _GLCGetVirtualScreenn0000000000001f83 T _GLCSetProfilerStoragen0000000000006050 T _cglBadApplicationNotMuxAwareLockDownn0000000000001870 T _glcDebugListenern00000000000029c1 T _glcGetIOAccelServicen0000000000001fe8 T _glcNoopn00000000000027b1 T _glcPluginConnectn0000000000001ff0 T _glcPluginCountn00000000000027e1 T _glcPluginDisconnectn0000000000008b5f T _glcRecordErrorn

可以看到很多CGL的介面,這個才是macOS原生的OpenGL介面。

所以通過追加(也可以替換)到我們通過MacPorts安裝的庫的路徑,就可以解決這個問題。注意如果是直接安裝的XQurtz的dmg包而不是通過MacPorts安裝的庫,那麼路徑應該是/usr/X11而不是/opt/local

本作品採用知識共享署名 4.0 國際許可協議進行許可。

參考引用

  1. macOS - Wikipedia
  2. XQuartz
  3. The MacPorts Project
  4. Overview of OpenGL Support on OS X
  5. CMake, OpenGL, and GLX on OS X

推薦閱讀:

TAG:游戏开发 |