虚幻引擎派生数据缓存(DDC)原理机制源码剖析

Viewed 16

首发于:虚幻引擎派生数据缓存(DDC)原理机制源码剖析

说明:本文写于3年前,基于虚幻引擎 UE4.27.2 。

概述

  • UE4 的 DerivedDataCache (以下简称 DDC ) 机制能把资源提前准备好,避免用到再临时去生成时的等待耗时。
  • 此处以 Shader 相关的 MATSM (和 GSM )DDC 资源为例,从源码角度,解析 DDC 模块的运行流程、DDC 资源的读取和生成机制与原理及 DDC 资源的命名规则机制等,以便更好理解 DDC 。

DDC 模块的结构和运行流程

源码

模块路径

  • Engine\Source\Developer\DerivedDataCache\

核心类

  • FDerivedDataCache
  • FMaterialShaderMap

类图

  • 本图为使用 Visual Studio 自动生成,功能限制,较之正式类图有些欠缺,请酌情参阅。

流程解析

模块加载和初始化

  • 简要
  • 模块实现 IModuleInterface 接口,在 ShaderCompiler 的 CompileGlobalShaderMap 时获取和启动模块。
  • 时序图
  • 本文所有时序图使用 PlantUML 编写生成,因功能限制,方法内调用另一个方法无法很好地表现出来,请注意识别,带来的不便,敬请谅解。

Shader 缓存的触发、DDC 的获取和生成、存储。

  • 简要
  • 在工程启动或打开场景等依赖 Shader 的资源时,触发 UMaterial 的 CacheResourceShadersForRendering ,去缓存 shader 用于渲染。
  • 根据 shader 类型、是否编辑器、使用模式等,决定是直接从 DDC 中寻找还是重新编译。
  • 如能从 DDC 缓存中找到对应资源,则返回之。
  • 如找不到 DDC 资源,则编译生成并存储。
  • 时序图

DDC 资源的存取机制和原理

读取机制

  • 流程
  • 启动时或打开新场景、打开材质编辑器等各种时机下触发 Shader 的编译~~~~
  • 读取和解析 DDC 配置
  • 把 DDC 资源读取到内存中
  • 读取指定 DDC 资源
  • 时序图

存储(生成)机制

  • 流程
  • shader 等资源编译完毕后存储到 DDC 内存中
  • 把内存中的 DDC 数据序列化到本地磁盘
  • 时序图

DDC 资源的命名规则和机制

概述

  • DDC 资源(udd 后缀)的名字与资源的平台、对应 DDC 类型的版本号 、 资源的版本号 、 资源的各种属性和依赖的资源等息息相关,为这些变量的组合和简化。
  • 对应的,相关 DDC 资源如不存在时,会触发 shader 的编译,重新生成对应的 DDC 资源。

核心方法

  • MaterialShader::GetMaterialShaderMapKeyString
  • DerivedDataLimitKeyLengthWrapper::ShortenKey

名字拆解分析

  • 以此为例:MATSM_EEC7EF19B1AF435984C9604C80FF9350_PCD3D_SM5_8__BC5N_NOCCBN_NOIRIS_DEV_SL___20D7E8E819B60428C579756BD5851EC239EE63E5.udd
  • MATSM
  • 释义
  • DDC 资源的类型
  • 官方释义
  • PluginName,Name of the derived data type
  • 核心方法
  • FDerivedDataCacheInterface::BuildCacheKey
  • FDerivedDataCacheInterface::SanitizeCacheKey
  • EEC7Ef19B1AF435984C9604C80FF9350
  • 释义
  • DDC 资源的版本号
  • 官方释义
  • In case of merge conflicts with DDC versions, you MUST generate a new GUID and set this new guid as version
  • 核心方法
  • 见文件:ShaderDerivedDataVersion
  • 如 UE 4.26.2 和 4.27.2 此文件有如下变化:

  • PCD3D_SM5_8__BC5N_NoCCBN_NoIris_DEV_SL_
  • 本为:PCD3D_SM5_8__BC5N_NoCCBN_NoIris_DEV_SL_PreExp_DBuf_UnInt_VFO_SKYATM_gs1_VT-0-0-1-16
  • PCD3D_SM5
  • 释义
  • 平台标识
  • 核心方法
  • ShaderPlatformToShaderFormatName
  • Platform = SP_PCD3D_SM5 (0)
  • 8
  • 释义
  • Shader 格式版本号
  • 核心方法
  • GetTargetPlatformManagerRef().ShaderFormatVersion(Format)
  • Format = PCD3D_SM5
  • _BC5N_NoCCBN_NoIris_DEV_SL_PreExp_DBuf_UnInt_VFO_SKYATM_gs1_VT-0-0-1-16
  • 释义
  • 一些控制台变量相关的字符
  • 官方释义
  • Globals that should cause all shaders to recompile when changed must be appended to the key here。 Key should be kept as short as possible while being somewhat human readable for debugging
  • 核心方法
  • ShaderMapAppendKeyString::FindTConsoleVariableDataInt

  • 16CE89424241C3C0C6A603CB9C0C80259F_High_SM5__0__EFDB0A46F0F802F9F778A3A8088F280F4636B6FC___TTranslucentLightingInjectPSLightType_Pointtruetruetrue685D2C1B605DABD2D13EA15923A8...

  • 释义
  • 在最终名字出现前,完整的资源名字其实是这样的,很长。
  • 一些材质相关属性参数和材质属性标签组合而来,包括:QualityLevel、FeatureLevel level、StaticSwitchParameters、StaticComponentMaskParameters、TerrainLayerWeightParameters、MaterialLayersParameterIDs、ReferencedFunctions、ReferencedParameterCollections、ShaderTypeDependencies、ShaderPipelineTypeDependencies、VertexFactoryTypeDependencies、TextureReferencesHash、BasePropertyOverridesHash 等及GMaterialPropertyAttributesMap.AttributeMap、GMaterialPropertyAttributesMap.CustomAttributes等。
  • 核心方法
  • ShaderMapId.AppendKeyString
  • FMaterialAttributeDefinitionMap::AppendDDCKeyString
  • 20D7E8E819B60428C579756BD5851EC239EE63E5
  • 释义
  • 通过对整个字符串加以 MemCrc32、GetHash 和 BytesToHex 等操作简化,最后再截取优化以便整个字符串保持最大长度120。
  • 官方释义
  • Shorten the cache key and return true if shortening was required
  • 核心方法
  • DerivedDataLimitKeyLengthWrapper::ShortenKey

Shader 的 Debug 推荐实践

启用中间着色器的转储

  • 若要开始对引擎安装进行调试,你需要在 Engine/Config 文件夹中找到 ConsoleVariables.ini 配置文件,然后在其中启用部分预定义控制台变量。在 [Startup] 分段下,会发现以下控制台变量列表,这些变量应如下所示:
[Startup]

; 取消注释以获得有关着色器编译的详细日志以及发生错误后的重试机会
r.ShaderDevelopmentMode=1
; 取消注释以在保存文件夹中转储着色器
; 警告:将此变量保持开启一段时间,会让硬盘充满许多小文件和文件夹
r.DumpShaderDebugInfo=1
; 启用此变量后,SCW崩溃将输出当前工作程序中的作业列表
r.ShaderCompiler.DumpQueuedJobs=1
; 启用此变量后,转储着色器时,将生成一个与ShaderCompilerWorker -direct模式一起使用的额外文件
r.DumpShaderDebugWorkerCommandLine=1
; 启用此变量后,着色器编译器的警告将在所有着色器加载时发送到日志中(从DDC或者着色器编译任务发送)。
r.ShaderCompiler.EmitWarningsOnLoad=1

  • 也可以通过控制台命令修改上述参数

在游戏工程路径下会生成编译时对应的所有 shader

目录

GameDir\Saved\ShaderDebugInfo\

转储着色器的文件夹结构

转储着色器所生成的文件夹路径包含了相关信息。让我们来看看转储着色器路径示例并分析其各个部分:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

  • 首先是项目的根路径。在本例中,它指向名为Tappy Chicken的项目。
  • D:\UE4\Samples\Games\TappyChicken\
  • 路径的下一个部分表示着色器转储后的保存路径。
  • D:\UE4\Samples\Games\TappyChicken\ Saved\ShaderDebugInfo\
  • 对于每个着色器格式和/或平台,路径都将创建一个子文件夹。此路径将为 PC D3D着色器模型5 创建一个子文件夹。
  • D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\ PCD3D_SM5\
  • 每个材质都将有一个文件夹,且路径将创建一个名为 Global 的特殊文件夹。这里看到的调试着色器用于 M_Egg 材质。
  • D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\ M_Egg\
  • 着色器在按顶点工厂排序的贴图中进行分组,此类顶点工厂通常对应于网格体/组件类型。此路径指向 LocalVF,它表示 本地顶点工厂(Local Vertex Factory)。
  • D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\ LocalVF\
  • 路径的最后一个部分表示用于材质的各种功能集合。
  • D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\ BPPSFNoLMPolicy\
  • 由于 ConsoleVariables.ini 文件中设置了控制台变量 r.DumpShaderDebugShortNames=1,因此名称已经过压缩,以便缩短文件长度。
    例如,如果之前未启用此控制台变量,则路径将为:
  • D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\FLocalVertexFactory\TBasePassPSFNoLightMapPolicy\
  • 着色器转储文件夹包含生成的批处理文件、文本文件和usf文件。
  • usf文件是转至平台编译器的最终着色器代码,编译器在预处理器之后运行。
  • 批处理文件用于调用平台编译器以查看中间代码。
  • 名为 DirectCompile.txt 的文本文件包含使用ShaderCompileWorker进行调试的命令行。

参考

Debugging the Shader Compile Process

答疑

  • 已有对应的 gsm 或 matsm DDC 时,重新打开工程会不会需要编译或重新生成 DDC ?
  • 不会
  • 删除某个 DDC 资源后,重新打开工程生成的 DDC 资源名字会不会不一样?
  • 不会
  • 删除 DDC 资源后,重新生成 DDC 资源,其名字会不会不一样?
  • 不会
  • 不删除 DDC 资源,重新编译 DDC 时,DDC 资源是否会重新生成?新成的 DDC 资源名字会不会不一样?
  • 会重新生成,名字不会变,但是文件属性会有修改。
  • 不删除 DDC 资源,重新编译 DDC 后,重新生成 DDC 资源会不会速度快很多?
  • 会快一些,但是快多少跟 DDC 资源的变动比例有关。
  • 如 X21 140M 的 DDC 资源,不改动 shader 时,如删除原来的相关的 DDC 资源重新生成,需要13分钟,如不删除直接重新生成,只需要 3 分钟(有个别 DDC 资源新增)。
  • gsm 和 matsm DDC 资源(udd文件) 跟 uasset 、源文件的区别和联系?
  • DDC存储了一个资源的版本,这个版本是UE在目标平台上所用的格式。与此相对的是Artist所创建的原始格式的资源,那些资源被导入到UE4编辑器中存储成了.uasset文件。
  • 为什么需要编译shader
  • 渲染需要缓存 shader(CacheResourceShadersForRendering),ddc 中又没有。
  • Shader compilation
  • UE4 compiles shaders asynchronously using a streaming system. Compile requests are enqueued when materials load that do not have a cached shader map, and compile results are applied as they become available, without blocking the engine. This is optimal in terms of load time and compile throughout, but it does mean that there are quite a few layers between the actual platform shader compile and the material that requested it.
  • UE4 使用流式系统以异步方式编译着色器。编译请求在没有高速缓存的着色器贴图的材质加载时排入队列,编译结果将在它们变为可用时应用,而不会阻塞引擎。这可在装入时间和编译吞吐量方面实现最佳结果,但这意味着实际平台着色器编译与请求编译的材质之间存在相当多的层。
  • ShaderDevelopment Shader compilation
  • 引用:许多 虚幻引擎 资产都需要额外的"派生数据"才能使用(比如包含着色器的材质就是一个简单示例)。材质在渲染之前,必须为运行编辑器的平台编译着色器。
  • DDC 的读取方式是怎么样的?Engine\DerivedDataCache 、 GameProject\DerivedDataCache 及 共享目录中都存在同名udd时读取哪一个?
  • 根据 DDC 资源的命名规则,原则上同名的 DDC 资源必定是同一个资源(的复制品)。
  • 如确实存在同一份资源存在于多个 DDC 目录,会按照 ddc 配置解析时得到的顺序(一般为 Boot.ddc、local、shared 等)来读取,读取到后不再继续读取同名资源。
    ; Configure the hierarchy entry. This uses multiple nodes that are used in order until a read is found (writes go to all writable entries) Hierarchy=(Type=Hierarchical, Inner=Boot, Inner=Pak, Inner=EnginePak, Inner=Local, Inner=Shared)

引用:DDC的存储位置

  • 根据项目和系统的配置方式,可以存在多个DDC缓存(按照快慢顺序)。在评估派生数据时,你的系统将执行以下操作来确定访问速度:
  • 当需要某条派生数据时,将首先检查最快的缓存,然后检查速度次之的缓存,依此类推,直到找到数据为止。
  • 找到数据后,会将其复制到最快的本地缓存中,以便下次更快地访问。
  • 如果未找到数据,则将首先生成数据,然后将其异步复制到缓存中,以便供你(甚至可能是你的团队)将来使用。
  • 存储在DDC中的内容允许使用后即丢弃,因为它可以随时使用保存在.uasset文件中的数据重新生成。通过将这些派生格式存储在外部,我们可以轻松地添加或更改引擎使用的格式,而不需要修改源资产文件。
  • DerivedDataCache
  • 为什么生成了ddc打开某些场景还会触发shader编译生成matsm
  • 根据 DDC 的生成逻辑,部分类型的 Shader (如 on-the-life 即时型)会无视 DDC 存在与否,在用到时即时生成。
  • 什么样的修改会导致 DDC 文件失效或者说需要重新生成?
  • DDC 资源(udd 后缀)的名字与资源的平台、对应 DDC 类型的版本号 、 资源的版本号 、 资源的各种属性和依赖的资源等息息相关,为这些变量的组合和简化。
  • 见本文的 “DDC 资源的命名规则和机制” 部分。
  • 故而相关参数的变动都会导致 DDC 文件的重新生成。
  • 哪些资源的变动会需要重编shader
  • 引擎、游戏工程插件等下的 usf 文件的变动会导致 shader 的编译
  • 有被引用的 material 的变动会导致 shader 的编译
  • 另外
  • 每次打开材质编辑器,都会编译对应材质的编辑器下的shader,其不会cook和存储ddc。
  • 材质编辑器中对节点和配置的大部分修改都会触发shader编译(如挪动节点位置、修改 two side 属性等)
  • 有什么减少 Shader 编译的方法

**声明:**本文来自公众号:GameDevLearning,转载请附上原文链接(https://mp.weixin.qq.com/s/x5OkQefG8SlGnb2KStiOlA)及本声明。

0 Answers