4.1 KiB
4.1 KiB
UIEffect 降 DC 改动总结
提交:
fa5f0cdb 降低DC的操作(2026-08-26) 涉及目录:unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/
一、背景与目标
UI_Effect 插件原本每个 UIEffect 组件都会生成独立的材质实例(缓存 key 使用 effectId,即组件的 GetInstanceID(),每个实例唯一),导致:
- 相同配置的特效无法共享材质 → 无法参与动态合批 → Draw Call 偏高;
- 批量刷 UI 时材质创建开销大,伴随帧率波动。
目标:让相同配置的 UIEffect 共享同一材质,从而参与合批,降低 DC。
二、方案选型
| 方案 | 说明 | 结论 |
|---|---|---|
| UIEffectReplica + Preset | 业务代码逐个改为副本组件,指向共享 preset | 需要改动大量业务文件、创建 preset 资产,侵入性大 |
| configId 配置指纹(最终采用) | 在插件内部按"配置内容哈希"共享材质 | 业务代码零改动,同配置自动共享合批 |
三、具体改动
1. UIEffectContext.cs(新增约 121 行)
核心:新增 configId 属性——特效配置的"稳定指纹"。
- 反射字段缓存:
s_ConfigFields为static readonly FieldInfo[],GetFields只在类型加载时执行一次,运行时无反射类型发现开销,避免批量刷新时的 GC 尖峰(这是此前"帧率不稳"问题的根因与修复点)。 - 内容哈希
HashValue:null→ 0;UnityEngine.Object→GetInstanceID()(引用同一资产即同哈希);Gradient→GradientHash按 colorKeys / alphaKeys 逐 key 做内容哈希(不同实例但内容相同的渐变得到相同指纹);- 基元/枚举 →
GetHashCode();嵌套值类型(Color、Vector、MinMax01 等)→HashFields逐字段展开(s_FieldCache按类型缓存)。
- 雪崩混淆:FNV-1a 混合后追加 MurmurHash3 终混,让相近配置散列开,降低哈希碰撞概率。
2. UIEffectBase.cs(GetModifiedMaterial)
材质缓存 key 由 effectId 改为配置指纹:
var configId = context.configId;
var rootId = context.useViewMatrix && transitionRoot ? (uint)transitionRoot.GetInstanceID() : 0u;
var hash = new Hash128((uint)baseMaterial.GetInstanceID(), configId, samplingScaleId, rootId);
要点:
rootId仅在特效需要视图矩阵时(transition / gradation / detail / shiny)才参与哈希,普通灰度(ToneFilter)等不受 root 影响,可共享范围更大;Hash128(baseMaterial, configId, samplingScaleId, rootId)相同 → 命中MaterialRepository中同一材质实例 → 参与动态合批。
3. ShaderVariantRegistry.cs(Profiler 采样修复)
RegisterVariant / GetVariant 的所有提前 return 分支均补上 Profiler.EndSample(),保证 BeginSample/EndSample 严格配对,修复控制台的:
Non-matching Profiler.EndSample (Every EndSample call must have a preceding BeginSample call...)
四、业务侧影响
- 业务文件零改动:
EquipBuffItem、MapBlockItem、PetItem、V_RechargeItem、FightLinkHeroItem、AvatarItem等继续正常使用UIEffect控制灰度,无需迁移到UIEffectReplica。 - 同屏所有同配置灰度特效(如大量
ToneFilter.Grayscale的图标)自动共享一个材质并合批。
五、效果
- N 个相同配置的 UIEffect:N 个材质实例 → 1 个共享材质,可被 UGUI 动态合批;
- DC 显著下降(受益程度取决于面板内同配置特效数量)。
六、注意事项与局限
- 配置不同的特效无法互相合批——每种不同配置组合仍各占一个材质/DC,属预期行为;
configId在画布重建时计算,字段遍历存在少量装箱开销,已通过静态缓存降到最低(普通 UI 不会每帧触发);- 新增
UIEffectContext公共字段会自动纳入指纹(遍历 public instance 字段),无需手动维护字段清单; - 验证方式:打开 Frame Debugger / Profiler,确认同配置 UI 元素合并为同一批次;确认控制台不再出现
Non-matching Profiler.EndSample。