无用的前言
很久前我观察过DDC的一些代码了解了些浅显的知识。
 最近我遇到个DDC相关的问题,于是将之前写的东西又复习了一遍。同时我也将记录下我最近研究这个问题时,一些重要的部分以作备忘。
目标
观察一个StaticMesh加载其对应DDC文件的流程,记录其中的一些要点。
(注意:此处引擎版本是4.26,UE5里DDC相关代码已有较大调整)
0. 代码入手处
DDC接口的GetSynchronous和GetAsynchronous函数是获得DDC数据的统一接口,断点在这里一定能触发到。
不过,我现在只想观察一个特定StaticMesh的DDC,那么如果我一开始就将断点打在GetSynchronous和GetAsynchronous内部,则会断到很多其他资源。
 所以,我选择首先将断点打在StaticMesh调用DDC接口的地方,然后,双击加载一个我想观察的StaticMesh资源比如这个:
 
 然后断点就能触发:(可以通过Owner的值确认下触发的StaticMesh资源是自己想要观察的那个)
 
 现在,即是这个StaticMesh准备获取DDC数据的时刻。
随后,再在 GetSynchronous 函数内部下断点来触发:(可以通过CacheKey值确认是这个资源)
 
这里调用的函数是 GetSynchronous,即同步的方式,所以接下来会立即执行这个任务(而不是放在另一个线程中执行)。
GetSynchronous函数核心是创建一个 FAsyncTask<FBuildAsyncWorker> 来执行任务。
1. FBuildAsyncWorker
FAsyncTask安排了一个可异步执行的任务(也可同步执行),其基础用法可见上一篇。
 而任务的具体内容则由FBuildAsyncWorker定义,它本身有些成员变量,可以参考下注释对他们的解释:
/** true in the case of a cache hit, otherwise the result of the deriver build call **/
bool							bSuccess;
/** true if we should record the timing **/
bool							bSynchronousForStats;
/** true if we had to build the data */
bool							bDataWasBuilt;
/** Data dervier we are operating on **/
FDerivedDataPluginInterface*	DataDeriver;
/** Cache key associated with this build **/
FString							CacheKey;
/** Data to return to caller, later **/
TArray<uint8>					Data;
 
而执行的逻辑,则在 DoWork()函数中,其中最重要的是通过FDerivedDataBackend获得DDC数据。
- 如果成功则局部变量
bGetResult会记为真,那么FBuildAsyncWorker本身的成员bSuccess会变为真,且成员Data也是有值的。 - 如果失败,那么
bSuccess为假,Data也是空的。 

 这样,当任务结束后,就可以通过FBuildAsyncWorker本身的成员变量bSuccess和Data的值,来明白DDC是否获取成功和DDC数据是什么。
接下来,需要看FDerivedDataBackend::Get().GetRoot().GetCachedData
2. 众多的 DerivedDataBackend?
正如 《观察DerivedDataBackendInterface各个子类的关系》 所看到,DerivedDataBackend 由多个子类,且之间并非并列,而是嵌套的关系。
所以,尽管这里作为 Root 的 DerivedDataBackend 是 DerivedDataLimitKeyLengthWrapper:
 
 但随后它又会调用其他的 DerivedDataBackend 来尝试获得DDC数据。
UE设计成这样众多Backend的结构肯定是有理由的,不过我还没理解清楚,所以不做讨论。
但有一点可以肯定:如果最终通过文件获得DDC数据,则一定是通过FFileSystemDerivedDataBackend。
3. 在FFileSystemDerivedDataBackend::GetCachedData 下断点可以获得对应文件的路径
随后,在 FFileSystemDerivedDataBackend::GetCachedData 下断点,可以通过Filename的值知道对应的DDC文件路径:
 
 可以直接在磁盘上找到这个文件:
 
4*. 缓存缺失重新构建的情况
也可以在之后手动删除这个文件,来模拟DDC文件没有找到的情况。
 此时可以看到FBuildAsyncWorker的DoWork()会走到这里,结束后bSuccess为假,Data也是空的。
 
 GetSynchronous由于返回的是PendingTask.GetTask().bSuccess,所以返回的是假。
那么StaticMesh调用后就会走向另一个分支
 
随后,DDC的数据会被重建。
最后,在GetDerivedDataCacheRef().Put调用后:
 
DDC文件会被重新保存(由于这次调用应该是异步的,所以这个函数返回后不会立即看到文件的产生)
总结
- StaticMesh 调用DDC的通用接口
GetSynchronous获得DDC数据 GetSynchronous内部创建一个FAsyncTask<FBuildAsyncWorker>来执行任务。FBuildAsyncWorker的DoWork()是其核心,可以看到它通过DerivedDataBackend获得DDC数据。- 众多的 DerivedDataBackend 相互连接,定义了找DDC数据的逻辑。
 - 可在
FFileSystemDerivedDataBackend::GetCachedData下断点知道资源加载的DDC文件路径。 
小记最近的问题
最近遇到的问题从堆栈上看是,StaticMesh加载的DDC数据中一个数组的元素大小不匹配现在的定义。一般来说,DDC的问题首先要尝试的肯定是清除一遍DDC让它重新生成一遍。但是我(自以为)清除了之后,还是同样的错误。
最后,通过找到那个StaticMesh对应的DDC文件的具体路径才发现——其获得数据的文件并没有清除,而是一个共享的位置,不在本地。而且这个位置是很久前已经弃用的,之所以找到这个位置是因为之前配置了一个环境变量。









![[JVM] 3. 类加载子系统(2)-- 类加载器、双亲委派机制(JDK1.8及之前)及其他](https://img-blog.csdnimg.cn/8efc20a12a2d4aa79ed4eaf157f338dd.png)








