DC优化
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# 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` 改为配置指纹:
|
||||
|
||||
```csharp
|
||||
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 显著下降(受益程度取决于面板内同配置特效数量)。
|
||||
|
||||
## 六、注意事项与局限
|
||||
|
||||
1. **配置不同的特效无法互相合批**——每种不同配置组合仍各占一个材质/DC,属预期行为;
|
||||
2. `configId` 在画布重建时计算,字段遍历存在少量装箱开销,已通过静态缓存降到最低(普通 UI 不会每帧触发);
|
||||
3. 新增 `UIEffectContext` 公共字段会自动纳入指纹(遍历 public instance 字段),**无需手动维护字段清单**;
|
||||
4. 验证方式:打开 Frame Debugger / Profiler,确认同配置 UI 元素合并为同一批次;确认控制台不再出现 `Non-matching Profiler.EndSample`。
|
||||
Reference in New Issue
Block a user