UE4.27 保留 ShaderDebugInfo 导致 Shader 编译 OOM
March 24, 2025About 1 min
UE4.27 保留 ShaderDebugInfo 导致 Shader 编译 OOM
为了用 RenderDoc 调试和分析 shader,需要在 \Engine\Config\ConsoleVariables.ini 里开启保留 shader 调试信息。但在 UE4.27 上,一旦要编译的 shader 数量很大,编译过程会把内存吃光——极端情况下 128GB 甚至 270GB 内存都会被撑爆而崩溃。根因是一个经典的循环引用导致的内存泄漏,UE5.0.2 有对应 hotfix。
前提
用 RenderDoc 调 shader 必须保留 shader 调试信息,配置在:
\Engine\Config\ConsoleVariables.ini
开了它之后,如果工程 shader 编译量大,内存占用会失控。
根因:ShaderJobCache 里的循环引用
分析下来是 shader 编译作业缓存(FShaderJobCache)里的经典循环引用导致内存泄漏——缓存的作业输出没有被正确释放,编译量一大就线性堆积到 OOM。
修复:UE5.0.2 的 hotfix
修复在 \Engine\Source\Runtime\Engine\Private\ShaderCompiler\ShaderCompiler.cpp。核心是清理缓存时,把命中数达到最小值的那些 output 显式 delete 掉,而不是只从容器里移除引用:
void FShaderJobCache::Add(const FJobInputHash& Hash,
const FJobCachedOutput& Contents, int32 InitialHitCount)
{
// ...
// 一直移除命中数等于最小值的项,直到腾出足够内存
for (TMap<...>::TIterator Iter(Outputs); Iter; ++Iter)
{
if (Iter.Value()->NumHits == MinHits)
{
MemoryThatWillBeUsed -= static_cast<int64>(Iter.Value()->JobOutput.Num());
// ------------------- HOTFIX -------------------
delete Iter.Value();
// ----------------------------------------------
// ...
}
}
}
同时在作业释放路径上也要确保 PendingShaderMap.SafeRelease()、标记 Job->bReleased = true 并从 AllJobs 里移除。
处理建议
- 如果卡在 UE4.27 又必须保留 shader 调试信息,把 UE5.0.2 的这个 hotfix backport 过去。
- 如果只是偶尔需要调 shader,可以临时开启调试信息、编译目标 shader 后再关掉,避免一次性全量编译撞上 OOM。