8.6 KiB
方案:加载性能优化(冷启动 / 静默下载 / 点击优先)
日期:2026-09-11
关联:docs/性能打点说明.md、assets/mx/module/res/*
打开 / 引用 / 分包所有权 以方案-资源与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. 原则
- 真正进内存的加载按需:进首页只 load 首页能画出来的资源;点哪个页面就 load 哪个。
- 静默 = 下载分包 /
bundle.preload:不addRef、不进战斗资源包内存、可随时丢弃。 - 静默与主循环解耦:用
setTimeout泵 silent,不走Director.EVENT_AFTER_UPDATE。 - 玩家打开 / 自动弹窗 / 进战 = 最高优先级:
beginUserLoad立刻停发新的 silent;进行中的原生loadBundle无法取消,但不继续排队。 - 分档只调激进度与表现,不 fork 业务:下载并发/间隔用
MemoryProfile;战斗特效用已有DeviceProfile。 - 不恢复战斗资源包的内存预热(
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。