Parallel Animation
February 18, 2024About 2 min
Parallel Animation
UE 可以把动画计算放到多个线程上并行跑,减轻主线程负担。能并行的主要是三块:
- Skinning Decompression:解压、准备网格数据以供蒙皮,可多线程并行加速。
- Animation Graph Evaluation:动画图求值,含 IK、动画混合、状态转换。
- Bone Transforms Calculation:算最终骨骼位置供渲染用。
这套并行的关键是 UAnimInstance 和 FAnimInstanceProxy 的分工。UAnimInstance 跑在主线程,管动画蓝图逻辑——状态机、变量、事件这些和游戏逻辑挂钩的部分。FAnimInstanceProxy 是它的轻量代理(一个 struct),把动画计算要用的数据打包,拿到工作线程上独立算,和游戏逻辑解耦。
一个更新周期里三步走:
- 数据同步:周期开始时,
UAnimInstance把动画参数、状态机状态、骨骼变换等复制进FAnimInstanceProxy。 - 并行计算:数据齐了,
FAnimInstanceProxy在工作线程上独立算骨骼变换、动画混合、IK。 - 数据回传:算完把结果(最终骨骼变换)传回
UAnimInstance,在主线程应用到 Mesh 上渲染。
动画蓝图的线程安全更新(Blueprint Thread Safe Update Animation)
上面讲的是引擎内部的并行机制。落到动画蓝图作者这边,对应的做法是把更新逻辑从 Event Blueprint Update Animation 换成 Blueprint Thread Safe Update Animation。
区别在于跑在哪个线程:Event Graph 里的 Event Blueprint Update Animation 跑在 Game Thread,一个角色一个角色顺序处理;而 Blueprint Thread Safe Update Animation 里的逻辑跑在 Work Thread,多个角色的动画蓝图可以并行算。角色一多、核心数一多,后者的性能优势就出来了——一个「纯算 1000 次」的对照测试里,线程安全版明显更快。
它主要用来频繁获取角色状态(移动速度、方向等)。代价是:里面只能用线程安全的节点和函数,否则直接编译报错。
属性访问(Property Access)
拿数据的推荐方式是属性访问。它能以线程安全的方式自动把 gameplay 数据喂给动画图表,同时减少「获取属性」的中间实例、降低蓝图复杂度。
- 如果要在属性访问里用自定义函数,该函数必须设为 Pure,且输出参数名必须正好是
ReturnValue(中间不能有空格),否则属性访问里选不到它。 - AnimGraph 里节点的属性可以直接绑定属性访问,比默认的取值方式快约 1.2~3 倍。
- 属性访问还能指定在哪个 CPU 线程上取值。
这也是 Distance Matching 要求 On Update 里只用线程安全函数的原因——它的更新就是挂在这条线程安全的更新路径上。