Datawhale干货
作者:Eternity,Datawhale成员

不少玩家在玩游戏时总会遇到这样的问题:明明我电脑的配置已经足够高了,为什么需要等待这么久?
遇到这个问题大家不用急着排查故障,首先观看一下游戏界面是否有“着色器正在编译中”这行字,或许这就是导致进入游戏需要等待的“罪魁祸首”之一。
《黑神话:悟空》在 PC 端打开游戏的时候,进度条就会显示:正在进行着色器编译...
「着色器编译」究竟是干什么的?
要回答这个问题,首先要解释:着色器是什么;其次要解释:为什么需要在启动时编译,而不能提前打包到安装包中?
省流版:着色器决定了你在打游戏时能够看到的画面,而着色器又与系统、显卡非常相关,所以通常在启动时(这个时候程序已经知道你的设备是什么操作系统、什么显卡)进行编译(变成一种底层硬件能够认识的格式)。

什么是着色器?
在这之前,先说下 GPU、CPU:CPU 有着比较少的计算核心,但每个核心能干的事情比较多;而 GPU 虽然每个核心能干得不多,可它数量多。比如你有一张图像,需要修改每个像素,在 CPU 上通常需要逐个像素的使用 for 循环;而在 GPU 上,可以为每个像素分配一个核同时进行计算(假设像素个数少于核的数量)。对于图像处理或者图形相关的任务,GPU 就存在一些优势,尤其是需要为每个像素进行大量运算时。


为什么需要在启动时编译,而不能提前打包到安装包中?
下面尝试回答第二个问题:众所周知(如果不知道,现在也不晚),我们写的大部分代码都是机器不能直接使用的(高级语言是为了方便人们理解),需要变为机器所能理解的语言——二进制文件。
市面上有不同硬件厂商生产的不同显卡,首先游戏开发者与硬件厂商约定了一些标准/规范(OpenGL、Vulkan、DX 等),硬件厂商基于这些规范去实现相应硬件功能,游戏开发者根据这些规范编写代码,而编译过程则是将这些代码变为显卡所能理解的二进制文件,比如在 OpenGL 可通过以下代码进行编译并链接:
// compile shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// link shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glLinkProgram(shaderProgram);
对于不同显卡、不同平台/操作系统,编译产物往往不同。此外,着色器中通常存在不同的变体(对应不同行为),比如在一些高性能显卡上,可以开启一些比较消耗性能的特性,而对于相对低端的硬件设备,往往会进行一些降级(这也是为什么在不同机器上,游戏画质不同的原因)。

现代的一些图形接口,如 Metal、Vulkan、DX12,允许编译得到中间格式的结果(比如 Metal IR、SPIR-V 等),一些引擎会选择将这些中间格式结果打入安装包中,但仍然避免不了在首次加载时编译得到最终产物。
这里补充下Raymond Fei老师的回答:
“补充一下,不仅是不同家 GPU 上不统一,同一家的不同代 GPU 都是不统一的,甚至同一款 GPU 用了不同驱动都可能不一样,而且不一定向前兼容,就是说旧 GPU/驱动上编译出来的机器码有的在新 GPU 下面就不存在了。所以唯一能做的就是在用户机器上编译。这就是极致优化所需要的代价:之所以这么搞,除了不同厂商无法统一指令集之外,还因为跑着色器的都是高度实时的程序,用户对性能(比 CPU 上跑的程序)要敏感很多。”
以上也就是游戏在启动时编译着色器的过程,通常在首次加载游戏时需要对着色器进行编译,一些规范允许对编译产物进行缓存,之后能够更加快速地进入游戏。
笔者对着色器编译的理解也非常肤浅,编译的一些底层细节以及如何跨平台进行编译,也留给我去慢慢学习,如有不对还请批评指教。
如果对图形学感兴趣,可以移步:
https://www.zhihu.com/question/41468803/answer/1811541335 https://www.zhihu.com/question/349302834/answer/2311690807 https://www.zhihu.com/column/ComputerGraphics
内容中包含的图片若涉及版权问题,请及时与我们联系删除
评论
沙发等你来抢