NoIndex
date
Jan 8, 2025
icon
password
Featured
Evergreen
LastVerified
synced
synced
paired_with
3551d487-a2a1-81fc-ad17-d951139050a0
RedirectFrom
UpdateType
NotificationPolicy
TechVersions
type
Post
Series
category
技术分享
NotificationVersion
source_hash
md:v1:ff29e29d5e9444f0c2556111bc160b9dcdc4a2f14b522b0d049adb7668cf4dc6
slug
图形api与着色器
RelatedProjects
translation_locked
translation_locked
status
Published
summary
Shader、GPU 机器码、图形 API 和缓存失效。为什么过程怎么慢?
tags
开发
工具
游戏
NotifyAt
ContentType
玩游戏时大家是不是经常看到"正在编译着色器"的进度条?尤其是 Steam 上下完一个新游戏第一次启动,那个进度条能转好几分钟。这玩意儿到底在编译什么、为什么要编译、为什么不能"早就编好"?
着色器是什么
游戏画面里你看到的每一个像素,都是显卡跑了一段叫"着色器" (shader) 的小程序计算出来的。光照、阴影、水面反射、皮肤质感、粒子效果,全是着色器干的活。
着色器是写给显卡运行的程序。和给 CPU 写的 C 不一样,它是给 GPU 写的。最常见的两种是顶点着色器(决定每个顶点画在屏幕的哪里)和片段着色器(决定每个像素是什么颜色)。
为什么要"编译"
游戏开发者写的着色器是高级语言(HLSL、GLSL)。要让显卡执行,需要把它翻译成显卡能直接跑的"机器码"。这一步就是着色器编译。
但显卡的机器码不像 CPU 的 x86 那样是一个标准。NVIDIA 的卡、AMD 的卡、Intel 集显,每家的指令集都不一样。同一段着色器代码,在不同的卡上要编译成不同的机器码。
这就是为什么代码不能在游戏发布前就编好。开发者不知道你用的是哪张卡、哪个驱动版本。所以游戏只能带着源码(或者一种中间格式)发给你,第一次启动时让你的驱动在你的现场翻译给你的显卡。
图形 API 简史
着色器编译的背后是图形 API 的演化。简单梳理一下。
OpenGL (1992) 一开始由 Silicon Graphics 开源,跨平台。它曾经是 PC 游戏的事实标准,后来逐渐被替代。它的版本历程大致是
- 1992 年 OpenGL 1.0
- 2004 年 OpenGL 2.0,引入了着色器语言 GLSL
- 2010 年 OpenGL 4.0
- 2017 年 OpenGL 4.6,至今没大版本更新
DirectX 作为微软的图形API,绑定 Windows。Direct3D 12 把控制权下放到开发者手里,可以直接接管 GPU 内存、自己排队渲染命令,性能上限更高(也更难写)。今天 PC 上 3A 游戏几乎都跑在 DX12 上。
Vulkan 算是 OpenGL 的接班人,由 Khronos 在 2016 年推出。设计思路和 DX12 类似:显式控制、低开销、跨平台、开源。《毁灭战士·永恒》或者《无人深空》这种重度游戏都用它。
Metal 是苹果的图形API,2014 年被推出,是 Mac 和 iPhone 上唯一的图形 API。苹果的开发团队针对自己的 GPU 做了优化,特别是在 M 系列芯片上效率非常高。
为什么编译要那么久
顺着上面的思路就能理解,一个现代 3A 游戏可能有上千个不同的着色器组合(不同光源数量、不同材质、不同特效叠加),而每一个组合都要在你的卡上编译一次。再叠上 DX12、Vulkan 这些"显式 API"对编译时机更严格的要求,第一次启动时就需要把以后会用到的着色器都先编一遍,免得游戏中途卡顿。
这就是你看到那个进度条的全部原因。它在一次性把后面会用到的所有着色器都翻译成你显卡的机器码,再缓存到本地。下次启动直接读缓存,就不用再编译了。
但缓存会失效。换显卡、升级驱动、游戏打补丁,缓存就作废,得重新编一遍。这就是为什么有时候你只是更新了一下显卡驱动,进游戏又看到那个烦人的进度条。
📎 参考文章
相关文章
继续关注
通过开放 Feed 获取新文章、研究和重要更新。
- 作者:LeoQin
- 链接:https://leoqin.com/article/%E5%9B%BE%E5%BD%A2api%E4%B8%8E%E7%9D%80%E8%89%B2%E5%99%A8
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。