UE5.8 着色器编译极慢,CPU 只有 20%?小心 Incredibuild / XGE 这个坑
GSWXY
Archivist & Author

最近在折腾 Unreal Engine 5.8 源码版时,遇到了一个相当折磨人的问题:
UE 编辑器启动非常慢,一直卡在“正在编译着色器”,但奇怪的是 CPU 并没有跑满。
一开始我还以为纯粹是电脑太老。
我的开发机配置大致是:
- CPU:Intel Core i7-8700,6 核 12 线程
- 内存:64GB
- 显卡:RTX 2080
- 系统:Windows
- 引擎:Unreal Engine 5.8 源码版
i7-8700 确实已经是很多年前的 CPU,用现在的眼光来看,拿它编译 UE5 源码和 Shader 肯定不算快。
问题是,事情没这么简单。
一、最开始 CPU 明明能跑到 100%
第一次启动项目时,我打开任务管理器看了一眼:
CPU 直接 100%。
这看起来很正常。
UE 在第一次启动源码项目、生成 Derived Data Cache、处理 Shader、加载模块的时候,本来就会产生大量 CPU 工作。
于是我的第一反应是:
i7-8700 不行了,该换 CPU 了。
但过了一段时间以后,情况开始变得不对。
UE 依然停留在着色器处理阶段,可 CPU 却从 100% 掉到了大约:
20% 左右。
与此同时:
- 内存只用了约 34%
- 磁盘只有约 1%
- GPU 也只有约 10%
也就是说:
CPU 不忙、内存没满、磁盘没满、GPU 也没满,但 UE 就是在那里慢慢磨。
这时候就不能简单用“CPU 老了”来解释了。
二、12 线程 CPU,只用了大约一两个线程
我的 i7-8700 是:
6 核 12 线程。
Windows 任务管理器显示整机 CPU 20% 左右,而 UnrealEditor 本身大约只有 12%。
粗略计算:
一个逻辑线程完全跑满,大约就是:
100% ÷ 12 ≈ 8.3%
也就是说,这个时候 UnrealEditor 实际上可能只有一两个线程在比较忙。
这就出现了一个很典型的现象:
界面显示还在处理 Shader,但整颗 CPU 根本没有充分参与工作。
换一颗更快的 CPU 当然可以提高单线程性能,但如果 UE 本身只让一两个线程干活,那么即便直接换 16 核、24 核,效果也不可能达到正常并行编译时的水平。
所以我决定先查 Shader 编译进程。
三、检查 ShaderCompileWorker,结果居然是 0
Unreal Engine 正常进行大量 Shader 编译时,会启动多个:
ShaderCompileWorker.exe
这些 Worker 才是 UE 用来并行处理 Shader 编译任务的重要组成部分。
我在 PowerShell 中执行:
Get-Process ShaderCompileWorker -ErrorAction SilentlyContinue |
Format-Table Id, CPU, WorkingSet, Path -AutoSize
然后统计数量:
(Get-Process ShaderCompileWorker -ErrorAction SilentlyContinue).Count
结果:
0
也就是说:
当时一个 ShaderCompileWorker 都没有运行。
这里需要说明一点:
看到 0 并不意味着 UE 一定坏了。
UE 新版本的一部分 Shader 预处理工作可以直接发生在 Editor 进程中,所以在某些阶段暂时没有 ShaderCompileWorker 是可能出现的。
但是,如果长时间显示 Shader 处理非常慢,同时 CPU 长时间只有十几个百分点,那么这个现象就值得继续查了。
Epic 在 UE 5.8 的控制台变量文档中也明确提供了:
r.ForceAllCoresForShaderCompiling
当它设置为 1 时,会忽略部分 INI 中关于保留线程的设置,尽可能启动与可用核心数量相匹配的 ShaderCompileWorker,以提高 Shader 吞吐量。需要注意的是,高核心数、低内存机器可能因此增加内存压力。
我的机器有 64GB 内存,因此这方面并不是主要问题。
四、先确认 ShaderCompileWorker 到底有没有编出来
因为我用的是 UE 5.8 源码版,所以另一个可能性是:
ShaderCompileWorker 根本没有正确编译。
于是继续检查:
Test-Path "D:\Unreal\UE5.8-Source\Engine\Binaries\Win64\ShaderCompileWorker.exe"
结果:
True
再检查整个目录:
Get-ChildItem "D:\Unreal\UE5.8-Source\Engine\Binaries\Win64\ShaderCompileWorker*" |
Format-Table Name,Length,LastWriteTime
可以看到:
ShaderCompileWorker.exe
ShaderCompileWorker.pdb
ShaderCompileWorker.modules
ShaderCompileWorker.target
ShaderCompileWorker.version
以及大量对应模块 DLL。
所以可以排除:
ShaderCompileWorker 没编译出来。
程序明明存在,但 UE 卡顿时 Worker 数量却是 0。
那就继续往下查。
五、真正引起注意的是:Incredibuild
在任务管理器里,我注意到了两个后台服务:
Incredibuild Endpoint Service
Coordinator Service
这就值得警惕了。
Incredibuild 本质上是一套分布式构建加速工具,而 Unreal Engine 本身支持通过 XGE 使用类似的分布式执行方式。
UE 5.8 官方的 BuildConfiguration 文档中,仍然存在:
bAllowXGE
其含义就是:
如果 XGE 可用,是否允许 UnrealBuildTool 使用 XGE。
默认值为 true。
UE 5.8 还存在:
r.XGEController.Enabled
官方说明:
0 = 仅使用本地构建
1 = 使用 XGE 分发构建
默认值同样为 1。
理论上,这东西本来是为了加速。
但对于一台普通个人开发电脑,如果没有真正搭建好多台机器组成的 Incredibuild/XGE 分布式环境,或者 Incredibuild 的安装、授权、服务状态出现异常,就有可能产生一个非常反直觉的结果:
本来想加速,最后反而把本地并行编译拖慢了。
六、这并不是孤例
继续查资料以后发现,这个问题实际上很多年前就有人踩过。
Epic Developer Community 上曾有开发者遇到类似现象:
- Shader 编译一段时间以后停止;
- Incredibuild 参与 Shader 编译;
- ShaderCompileWorker 数量异常;
- CPU 无法充分利用;
- 禁用或卸载 Incredibuild 后恢复本地并行编译。
有开发者反馈,移除 Incredibuild 后,原本只有少量 ShaderCompileWorker 工作的情况恢复为多个 Worker,同时 CPU 可以充分利用。
也有人遇到 UE 编辑器启动卡在 Shader 阶段,最后确认干扰来源就是 Incredibuild。
甚至到较新的 UE5 版本,Epic 社区中仍然可以找到 Incredibuild 与 ShaderCompileWorker/XGE 组合出现异常的案例。
所以这不是一个简单的:
“你的 CPU 太垃圾。”
而是应该先判断:
CPU 到底有没有被 UE 正确利用。
七、我的处理方案:单机开发直接关闭 XGE
我的使用场景很明确:
一台 Windows 开发机,本地编译 UE,没有 Incredibuild 分布式编译集群。
既然如此,与其让 UE 尝试调用 XGE/Incredibuild,不如干脆:
彻底使用本地 ShaderCompileWorker + UE 自己的本地编译能力。
我的处理原则是:
- 禁止 XGE Shader 分布式编译;
- Shader 编译尽量使用全部 CPU 核心;
- UnrealBuildTool 禁止使用 XGE;
- 保留 UE5 自己的 UBA 本地编译;
- 禁用 Incredibuild 后台服务。
八、永久关闭 XGE Shader
可以在工程的:
Config\DefaultEngine.ini
中加入:
[SystemSettings]
r.XGEShaderCompile=0
r.ForceAllCoresForShaderCompiling=1
其中:
r.XGEShaderCompile=0
目的是不再让 Shader 编译走 XGE 路径。
而:
r.ForceAllCoresForShaderCompiling=1
则让 UE 在 Shader 编译时尽量使用全部 CPU 核心。
Epic 对 r.ForceAllCoresForShaderCompiling 的官方描述就是:设置为 1 后,忽略相关 INI 设置并尽可能按照可用核心数量启动 ShaderCompileWorker。
九、UnrealBuildTool 也禁掉 XGE
Shader 是一部分,C++ 编译又是另一部分。
UnrealBuildTool 在 Windows 下会读取:
%APPDATA%\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml
Epic 的 UE 5.8 官方文档确认,该目录属于 UBT 正式支持的全局配置位置。
可以配置:
<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowXGE>false</bAllowXGE>
<bAllowUBAExecutor>true</bAllowUBAExecutor>
<bAllowUBALocalExecutor>true</bAllowUBALocalExecutor>
<bAllCores>true</bAllCores>
</BuildConfiguration>
</Configuration>
几个参数的意思分别是:
bAllowXGE=false
禁止 UnrealBuildTool 使用 XGE。
Epic UE 5.8 文档中明确说明:
bAllowXGE
控制 XGE 在可用时是否允许被使用,默认值是 true。
bAllowUBAExecutor=true
允许使用 Unreal Build Accelerator。
bAllowUBALocalExecutor=true
允许使用 UBA 的本地执行模式。
bAllCores=true
在计算可以使用多少 CPU 资源时,把逻辑核心也考虑进去。
换句话说:
不是关闭所有加速,而是不再使用不适合我当前单机环境的 XGE,把编译交回 UE 自己的本地执行体系。
十、Incredibuild 服务也可以直接禁用
如果自己本来就不使用 Incredibuild,那么仅仅修改 UE 配置以后,让它的 Endpoint、Coordinator 等后台服务继续常驻也没有什么意义。
可以在:
services.msc
中找到与:
Incredibuild
Xoreax
有关的服务,停止并设置为禁用。
如果完全用不到 Incredibuild,另外一个更彻底的办法就是:
直接通过 Visual Studio Installer 或 Windows 应用管理将其卸载。
对于单机 UE 开发而言,我个人更倾向于这种方式:
不使用,就不要让它参与构建链路。
十一、一个容易犯的错误:不要关闭 ShaderCompileWorker
排查这种问题时,网上还能搜到一些类似配置:
r.Shaders.AllowCompilingThroughWorkers=0
这东西不要随便加。
我们的目标恰恰相反:
我要让 UE 正常启动多个 ShaderCompileWorker,而不是把 Worker 全关掉。
否则很可能让原本能够并行的 Shader 编译退化成更加低效的执行方式。
所以:
关闭 XGE ≠ 关闭 ShaderCompileWorker
正确方向应该是:
关闭异常的 XGE / Incredibuild 路径
↓
恢复 UE 本地 ShaderCompileWorker
↓
尽量充分使用 CPU 核心
十二、也不要动不动就删 DDC
还有一种网上很常见的“万能解决方案”:
删 DerivedDataCache。
这个操作确实可以解决某些缓存损坏问题。
但是:
如果 DDC 本身没有坏,就不要为了优化编译速度随便删。
因为删除以后意味着大量 Shader、纹理和其他 Derived Data 需要重新生成。
最终结果很可能是:
本来只是第一次慢,现在被自己手动变成再慢一次。
所以我的处理顺序是:
先看 CPU 利用率
↓
再看 ShaderCompileWorker 数量
↓
确认 ShaderCompileWorker.exe 是否存在
↓
检查 Incredibuild / XGE
↓
调整编译链路
↓
最后才考虑 DDC 是否异常
而不是上来就:
删 Intermediate
删 Saved
删 DDC
重新编译整个 UE
那样往往只会制造更多等待时间。
十三、怎么判断是不是同一个问题
如果你的 UE5 也出现以下组合,可以重点检查这一项。
症状 1
启动 UE:
Compiling Shaders...
长时间不结束。
症状 2
CPU 一开始可能达到:
80%~100%
后来却长期掉到:
10%~30%
症状 3
同时:
内存不满
SSD 不忙
GPU 不忙
症状 4
运行:
(Get-Process ShaderCompileWorker -ErrorAction SilentlyContinue).Count
发现大量 Shader 编译阶段:
0
或者只有极少数 Worker。
症状 5
任务管理器里又恰好存在:
Incredibuild Endpoint Service
Coordinator Service
那就非常值得检查:
是不是 XGE/Incredibuild 把 UE 的本地 Shader 编译链路接管了。
十四、别急着因为 Shader 慢就换电脑
这个坑给我最大的提醒其实不是 Incredibuild 本身,而是:
看到 UE 慢,不要立刻认为硬件性能不够。
我的 i7-8700 确实老了。
6 核 12 线程拿来开发 UE 5.8 源码项目,和现在的 16 核、24 核 CPU 相比,Shader 编译、C++ 编译、引擎源码编译肯定都会慢很多。
升级 CPU 依然是有价值的。
但:
CPU 100% 编译太慢
和:
CPU 只有 20%,UE 却在那里慢慢编
是两个完全不同的问题。
前一种可以通过升级硬件解决。
后一种首先应该解决的是:
为什么软件没有把现有硬件用起来。
如果一个 12 线程 CPU 实际只工作一两个线程,就算直接换一颗 32 线程 CPU,也只是把问题藏起来,而不是解决问题。
十五、总结
这次 UE5.8 Shader 编译问题的排查路径可以归纳成一句话:
UE 编译 Shader 很慢时,先别只看进度条,要打开任务管理器看看 CPU 到底有没有真正工作。
如果:
Shader 很慢
+ CPU 占用异常低
+ ShaderCompileWorker 很少甚至没有
+ 安装了 Incredibuild
那么 XGE/Incredibuild 就应该进入重点排查范围。
对于我这种:
单台 Windows PC + UE5 源码开发 + 不使用分布式编译集群
的环境,我更倾向于:
禁用 XGE
+
禁用 Incredibuild
+
使用本地 ShaderCompileWorker
+
使用 UBA Local
+
Shader 编译尽量使用全部 CPU 核心
简单、直接,也更符合个人开发机的实际情况。
最后
Unreal Engine 的很多“卡死”,其实并不是真的死了。
Shader、DDC、Cook、C++ 编译这些东西本身就很耗时间。
但正常的慢应该表现为:
电脑很忙,所以我在等它。
而不是:
电脑闲着,UE 却让我等它。
后一种情况,往往才是真正值得排查的坑。
这篇记录留在这里,希望以后再遇到类似问题时,不至于第一反应就是:
换 CPU。
Conversation