You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
8.6 KiB
144 lines
8.6 KiB
# 方案:加载性能优化(冷启动 / 静默下载 / 点击优先)
|
|
|
|
> 日期:2026-09-11
|
|
> 关联:`docs/性能打点说明.md`、`assets/mx/module/res/*`
|
|
> **打开 / 引用 / 分包所有权** 以 [`方案-资源与UI生命周期.md`](./方案-资源与UI生命周期.md) 为准(2026-09-14)。
|
|
|
|
## 1. 问题与证据
|
|
|
|
现有启动链已具备 Phase / LoadScheduler / PreloadPredictor,但和「最快进首页 + 后台静默下载」目标不一致:
|
|
|
|
| 现状 | 后果 |
|
|
|------|------|
|
|
| Boot 并行拉 `icons`、`popCommon` | 与 `home` 抢带宽,首进/冷启动变慢 |
|
|
| `preLoadResPakg` 对弹窗做 `bundle.load` + addRef | 静默路径占内存,且曾与进战 `clearResPkg` 并发导致贴图失效(战斗 silent warm 已关闭,保留) |
|
|
| LoadScheduler silent 挂在 `AFTER_UPDATE` | 后台 IO 吃主循环帧预算 |
|
|
| `openPanel` 不暂停 silent | 玩家点页面仍可能被后台下载拖慢 |
|
|
|
|
## 2. 原则
|
|
|
|
1. **真正进内存的加载按需**:进首页只 load 首页能画出来的资源;点哪个页面就 load 哪个。
|
|
2. **静默 = 下载分包 / `bundle.preload`**:不 `addRef`、不进战斗资源包内存、可随时丢弃。
|
|
3. **静默与主循环解耦**:用 `setTimeout` 泵 silent,不走 `Director.EVENT_AFTER_UPDATE`。
|
|
4. **玩家打开 / 自动弹窗 / 进战 = 最高优先级**:`beginUserLoad` 立刻停发新的 silent;进行中的原生 `loadBundle` 无法取消,但不继续排队。
|
|
5. **分档只调激进度与表现,不 fork 业务**:下载并发/间隔用 `MemoryProfile`;战斗特效用已有 `DeviceProfile`。
|
|
6. **不恢复战斗资源包的内存预热**(`scheduleBattleWarm('silent')` / `preLoadLevelRes` 保持关闭),避免贴图生命周期回归。
|
|
|
|
## 3. 阶段
|
|
|
|
```
|
|
Boot(最小)
|
|
First + Comm + icons_boot + home + BootRes
|
|
表:Const / Item / Rank / Level(首页展示与点开始)
|
|
│
|
|
▼
|
|
first/comm/home 分包落地(BootRes 仍在本包 load)
|
|
│ 立刻静默 loadBundle(icons),不阻塞、不 acquire
|
|
▼
|
|
Hall 可交互(Home 已打开)
|
|
│ 延迟 2 帧,避免和首页首帧抢 IO
|
|
▼
|
|
Silent A:icons 优先(已在下则跳过),再战斗分包(loadBundle,不 acquire、不 load 资源)
|
|
heros / battleGame / battleMap / battleGameMonster / battleHero / popBattle
|
|
(不含已删除的 role / mechanismEffect;大厅名单只含磁盘上有预制的页面)
|
|
│
|
|
▼
|
|
Silent B:下载页面分包 + preload 弹窗预制
|
|
match 优先,再大厅页/弹窗
|
|
│
|
|
▼
|
|
玩家点击 / 自动弹窗 / 进战
|
|
beginUserLoad → 暂停 silent → 按需 loadPrefab / loadResPakg(进战最小包)
|
|
│
|
|
▼
|
|
BattleGame 已打开
|
|
│
|
|
▼
|
|
Silent C:局内弹窗 preload(暂停优先)+ 受击/死亡/武器目录 load
|
|
不 addRef;点开弹窗仍走 openPanel 最高优先
|
|
```
|
|
|
|
新玩家与老玩家冷启动同一条最小 Boot;老号缓存命中后 `loadBundle` 更快,但仍不在 Boot 里拉战斗包和全量弹窗。
|
|
|
|
进战最小包:地图、出怪口、模板/字块/营地、本关血条与开场怪、状态特效、飘字、怪物皮肤目录、英雄体/攻效/buff 目录。
|
|
进局后再补:`popBattle` 弹窗、受击/死亡、组合武器目录。BGM 由 `playBgmBattle` 按需拉。
|
|
|
|
## 4. 五维影响
|
|
|
|
| 加载 | 内存 | FPS | CPU | 发热 |
|
|
|------|------|-----|-----|------|
|
|
| 首进/冷启动少下 `icons`+`popCommon`,进首页更快 | 大厅不再把 HallWarm 整包 addRef | silent 离开主循环,首帧更稳 | 后台 IO 错峰 | 低档拉长 silent 间隔 |
|
|
|
|
补偿:段位图标走 `SpriteLoad` 按需拉 `icons`;点设置/体力等首次需等 `popCommon`(随后 silent 会补下)。进战 `loadResPakg` 只解最小包;局内弹窗/受击未预完时按需 load。
|
|
|
|
## 5. 资源所有权(不可违反)
|
|
|
|
- Silent `loadBundle` **不** `BundleLifecycle.acquire`,避免 `owner=res` 钉死战斗包,导致退战卸不掉。
|
|
- 进战 `loadResPakg` / `openPanel` 仍按原 owner 登记;释放顺序不变:teardown → onDestroy → waitFrames → clearResPkg / releaseBattlePhase。
|
|
- 进战 `abortHallSilentDownload` + `clearSilentQueue`,禁止大厅 silent 与 `loadResPakg(clear)` 并发。
|
|
- 局内 silent 不 `acquire`、不进 `BattleRes`;退战先 abort 再 teardown → `clearResPkg` / `releaseBattlePhase`。
|
|
- 战斗中允许 silent(`BattlePhysicsGate` 不再暂停);切后台 / 内存降档仍暂停。
|
|
|
|
## 6. 回归
|
|
|
|
- 清缓存冷启动:新号 / 老号都能进 Home,段位图可晚一拍出现。
|
|
- 进 Home 后立刻点开始:匹配页优先加载,不受 silent 阻塞。
|
|
- 进 Home 后立刻点神将/设置:该页优先,silent 停发。
|
|
- 战斗 ↔ 大厅 ≥3 次:无缺贴图 / Spine;退战战斗包仍可卸。
|
|
- 进战立刻点暂停/结算/Boss:未预完也能按需打开。
|
|
- 首召 / 首波:英雄体、怪物皮肤、状态特效仍在进战包内。
|
|
- 弱网:silent 失败只打日志,不 `recoverHome`。
|
|
|
|
## 7. 线上卡 loading / 失败后打不开页面
|
|
|
|
老项目就有:`assetManager.loadBundle` / `bundle.load` **无超时、同名可并发、回调可能永不回来**。
|
|
昨天把 `popCommon` / 局内弹窗 / 战斗包改成按需后,这条路径变成线上主路径,缺陷被放大。
|
|
|
|
| 缺陷 | 后果 |
|
|
|------|------|
|
|
| 静默 `loadBundle` 与玩家 `loadBundle` 同名并发 | 小游戏上第二回调可能永不触发,loading 一直转 |
|
|
| `openPanel` 用 `_panelOpenInflight` 等第一次打开 | 第一次挂死后,同页面后续点击永久等待 |
|
|
| `recoverHome` 只 `endUserLoad` 一次、再 `enterMain` | 门闩泄漏;Home 再挂则死循环卡 loading |
|
|
| `closeAllPanel` 清 inflight 却不 resolve | 已 await 的打开再也结束不了 |
|
|
|
|
修复:`BundleLifecycle.ensureLoaded` 同名合并 + 20s 超时;加载中禁止 `removeBundle`;`openPanel` 超时失败并 `abortInflightOpens`;`recoverHome` 重置用户加载门闩,连续失败改重启提示。
|
|
|
|
**2026-09-18:** 根因是 `openPanel` 与静默预载对同一预制双飞原生 load,回调丢失后失效缓存被当成永久失败。预制必须走 `ResourcesManager.loadRes` 合并。进战仍释放 HallWarm **预制**;`popCommon` zip 常驻;回大厅先 decode 再静默战斗包。
|
|
|
|
## 8. 新用户首局缺资源(2026-09-12)
|
|
|
|
首进失败、再进正常:不是贴图坏了,是冷启动优化把「齐套」拆成后台补齐,首局用到时还没齐。
|
|
|
|
| 缺陷 | 后果 |
|
|
|------|------|
|
|
| Hero/Weapon/Text/Monster1 延后加载,`getTableData` 只点火不阻塞 | 首局组装进战包漏血条/特效;AI 武器全 miss |
|
|
| `loadBundle` 超时 `finish(null)` 并清 inflight | 新用户首下未完就二次并发,回调丢失 |
|
|
| 静默超时把分包打进 `_silentMissBundles` | 后续永远跳过该包 |
|
|
| 预载 evict 后 `loadResPakg(fresh=false)` 复用已释放 `bundle.get` | 血条/皮肤 getNode 空,再进因缓存命中又好了 |
|
|
| `preLoadResPakg` 失败仍标 warm 完成 | 进战以为已预热,跳过强制重载 |
|
|
|
|
修复:开战前 `ensureBattleTables` 再组包;超时不拆原生 inflight;warm 失败才 `fresh` 重载;血条三种预制固定进最小包。
|
|
|
|
## 9. 匹配页卡死(2026-09-12)
|
|
|
|
抖音首包 `preLoadResPakg` 常超过 30s。匹配门闩 `awaitBattleWarm` 超时抛错后仍进战,进战再次 `await` 同一 Promise,超时走 `_failBattleEnter`,首页已关、匹配页不关。
|
|
|
|
修复:匹配页只点火 warm,门闩只等表 / 神将包 / BattleGame 预制;`awaitBattleWarm` 超时返回 false 不抛;进战超时改 `loadResPakg`,不失败离场。`battleWarmTimeoutMs` 与 `dirTimeoutMs` 对齐为 60s。
|
|
|
|
## 10. 首局加载过久(2026-09-12)
|
|
|
|
抖音新包点开始要 30s+,不是网单独慢,是下载被自己掐掉再重来:
|
|
|
|
| 缺陷 | 后果 |
|
|
|------|------|
|
|
| 战斗分包走 silent 队列,tier0 并行 1、间隔 120ms | heros→battleGame→地图…串行下 |
|
|
| 点开始 `abortHallSilentDownload` + `beginUserLoad` | 没开始的战斗包出队,匹配再从头下 |
|
|
| warm 超时 `abort` + `loadResPakg(fresh)` | 已下到一半的目录被 evict,再下一遍 |
|
|
|
|
修复:BootRes 完成后并行 `ensureLoaded` 战斗分包(不进 silent、点开始不停);进战 warm 仍在飞则 `loadResPakg(fresh=false)` 合并,禁止 evict 重来。
|
|
|
|
| 加载 | 内存 | FPS | CPU | 发热 |
|
|
|------|------|-----|-----|------|
|
|
| 首页展示后战斗包已在下,首局少等一轮 zip | 与原 silent 同包,不 addRef | 首页首帧后才开火,不抢 BootRes | 并行 loadBundle 峰值略升 | 低档可接受 |
|
|
|
|
回归:清缓存冷启动立刻点开始;在首页停 5s 再点;战斗↔大厅 ×3。
|
|
|