双十一快到了,不知道大家今年准备剁手购买什么。作者一直心念着之前提出想要在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 --install
然后根据屏幕提示完成安装。
接下来是安装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 selfupdate sudo port install git git-lfs cmake
就可以完成相关工具的安装。
然后我们就可以尝试checkout我们的代码并进行编译了。首先我们尝试编译我们的第三方依赖库,crossguid。运行项目根目录下的build_crossguid.sh,直接编译成功了。
接下来我们尝试编译OpenGEX,运行项目根目录下的build_opengex.sh,也是一次成功。
然后我们编译我们的引擎。这里主要有几个问题:
- 首先是找不到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里面安装这些库,只不过我没有去调查了; - 提示找不到xcb、X11等库。这个问题又可以分两个方面:
- 没有X11 Server。MacOS是使用的自己的图形服务器(Quartz)和图形库(Cocoa)。在老的版本当中也同时提供了X11 Server,但是好像从OS X开始X11被剥离到一个名为XQuartz的项目当中,并在GitHub开源了(*2)。所以需要安装XQuartz。安装过程请参考GitHub相关项目页面;
- 没有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} COMMAND ${CMAKE_AR} ${ISPC_LIBRARIAN_OPTIONS} ${OBJECTS} + COMMAND ${CMAKE_RANLIB} ${GEOMMATH_LIB_FILE} COMMAND rm -v ${OBJECTS} DEPENDS ${OBJECTS} )
最后的问题是编译虽然通过了,但是执行./build/Platform/Darwin/MyGameEngineOpenGL会直接发生段错误而强行关闭。如下:
chenwenlideMacBook-Pro:GameEngineFromScratchPrivate chenwenli$ ./build/Platform/Darwin/MyGameEngineOpenGL Segmentation fault: 11
使用lldb工具进行调试,发现错误信息如下:
Process 51269 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x0000000000000000 error: memory read failed for 0x0 Target 0: (MyGameEngineOpenGL) stopped. (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) * frame #0: 0x0000000000000000 frame #1: 0x0000000100002a9e MyGameEngineOpenGL`My::OpenGLApplication::Initialize(this=0x0000000100701410) at OpenGLApplication.cpp:106 frame #2: 0x0000000100043a29 MyGameEngineOpenGL`main(argc=1, argv=0x00007ffeefbffa48) at main.cpp:7 frame #3: 0x00007fff6ad71145 libdyld.dylib`start + 1 frame #4: 0x00007fff6ad71145 libdyld.dylib`start + 1
出错位置是在OpenGLApplication.cpp:106,使用l命令打出代码:
(lldb) l OpenGLApplication.cpp:106 96 { 97 fprintf(stderr, "Can't open displayn"); 98 return -1; 99 } 100 101 default_screen = DefaultScreen(m_pDisplay); 102 103 gladLoadGLX(m_pDisplay, default_screen); 104 105 /* Query framebuffer configurations */ 106 fb_configs = glXChooseFBConfig(m_pDisplay, default_screen, visual_attribs, &num_fb_configs);
继续跟踪可以发现是相关的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 -lGLU
- 如果是通过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.c index 9551033..af0f61e 100644 --- a/External/src/glad/src/glad_glx.c +++ b/External/src/glad/src/glad_glx.c @@ -127,6 +127,8 @@ static int open_gl(void) { #ifdef __APPLE__ static const char *NAMES[] = { + "/opt/local/lib/libGL.1.dylib", + "/opt/local/lib/libGL.dylib", "../Frameworks/OpenGL.framework/OpenGL", "/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/OpenGL",
原来的3行实际上加载的都是MacOS自带的OpenGL库。这个库是没有GLX相关的接口的。查看的方法是使用nm命令,如下:
nm -gU "/System/Library/Frameworks/OpenGL.framework/OpenGL" 00000000000053c8 T _CGLAreContextsShared 000000000000546b T _CGLBackDispatch 0000000000008b68 T _CGLChoosePixelFormat 0000000000006b46 T _CGLClearDrawable 00000000000052e3 T _CGLCopyContext 0000000000004620 T _CGLCreateContext 000000000000aa9e T _CGLCreatePBuffer 000000000000ad12 T _CGLDescribePBuffer 000000000000a584 T _CGLDescribePixelFormat 000000000000b6cd T _CGLDescribeRenderer 0000000000004381 T _CGLDestroyContext 000000000000adba T _CGLDestroyPBuffer 000000000000a0e8 T _CGLDestroyPixelFormat 000000000000b69e T _CGLDestroyRendererInfo 000000000000ccd2 T _CGLDisable 000000000000cc47 T _CGLEnable 0000000000008b2b T _CGLErrorString 000000000000d058 T _CGLFlushDrawable 0000000000005461 T _CGLFrontDispatch 000000000000528d T _CGLGetContextRetainCount 0000000000006a95 T _CGLGetDeviceFromGLRenderer 000000000000a8c8 T _CGLGetGlobalOption 0000000000005436 T _CGLGetNextContext 00000000000085a0 T _CGLGetOffScreen 000000000000aa45 T _CGLGetOption 00000000000077ef T _CGLGetPBuffer 000000000000af7c T _CGLGetPBufferRetainCount 000000000000cb70 T _CGLGetParameter 00000000000052b5 T _CGLGetPixelFormat 000000000000a1b4 T _CGLGetPixelFormatRetainCount 0000000000006a2d T _CGLGetShareGroup 0000000000007c21 T _CGLGetSurface 000000000000d0b3 T _CGLGetVersion 00000000000044b8 T _CGLGetVirtualScreen 000000000000ca68 T _CGLIsEnabled 000000000000a5d6 T _CGLLockContext 0000000000006024 T _CGLOpenCLMuxLockDown 000000000000b30c T _CGLQueryRendererInfo 00000000000050ef T _CGLReleaseContext 000000000000af4a T _CGLReleasePBuffer 000000000000a182 T _CGLReleasePixelFormat 0000000000005533 T _CGLRestoreDispatch 000000000000556a T _CGLRestoreDispatchFunction 0000000000005263 T _CGLRetainContext 000000000000af6a T _CGLRetainPBuffer 000000000000a1a2 T _CGLRetainPixelFormat 0000000000005480 T _CGLSelectDispatch 00000000000054ba T _CGLSelectDispatchBounded 0000000000005501 T _CGLSelectDispatchFunction 0000000000007cda T _CGLSetFullScreen 00000000000082e0 T _CGLSetFullScreenOnDisplay 000000000000a6a0 T _CGLSetGlobalOption 0000000000008403 T _CGLSetOffScreen 000000000000a9e0 T _CGLSetOption 0000000000006c25 T _CGLSetPBuffer 000000000000b24c T _CGLSetPBufferVolatileState 000000000000cd5d T _CGLSetParameter 000000000000788f T _CGLSetSurface 000000000000455c T _CGLSetVirtualScreen 000000000000b095 T _CGLTexImageIOSurface2D 000000000000af8e T _CGLTexImagePBuffer 000000000000a608 T _CGLUnlockContext 0000000000008671 T _CGLUpdateContext 000000000000a1c6 T _GLCDescribePixelFormat 0000000000007758 T _GLCGetPBuffer 000000000000ce49 T _GLCGetParameter 0000000000001fae T _GLCGetProfilerStorage 0000000000007be2 T _GLCGetSurface 0000000000004405 T _GLCGetVirtualScreen 0000000000001f83 T _GLCSetProfilerStorage 0000000000006050 T _cglBadApplicationNotMuxAwareLockDown 0000000000001870 T _glcDebugListener 00000000000029c1 T _glcGetIOAccelService 0000000000001fe8 T _glcNoop 00000000000027b1 T _glcPluginConnect 0000000000001ff0 T _glcPluginCount 00000000000027e1 T _glcPluginDisconnect 0000000000008b5f T _glcRecordError
可以看到很多CGL的接口,这个才是macOS原生的OpenGL接口。
所以通过追加(也可以替换)到我们通过MacPorts安装的库的路径,就可以解决这个问题。注意如果是直接安装的XQurtz的dmg包而不是通过MacPorts安装的库,那么路径应该是/usr/X11而不是/opt/local
参考引用