Files
HomeAgent/cmd/gui
JianFeeeee 04c5883f4b fix(gui): 星图几何体共享 + 重建时 dispose,修聊天页长时间挂起后卡死
## 实测故障

聊天页长时间挂着之后 GUI 卡死。CDP 抓生产实例(2588 节点):

    Mesh/SphereGeometry    2588 个   geometry 数据合计 22.67MB
    Sprite/BufferGeometry  1294 个   label 纹理,256×64 RGBA ≈ 64KB/个

进程常驻内存反复冲上 GB 级、被 GC 拉回后又再度暴涨(实测 986MB ↔ 3.8GB
震荡),CPU 稳定 30%(多线程耗在 GC 与对象构建),句柄数稳定 490 ⇒
不是句柄泄漏,而是持续的 GPU 资源分配。

## 两个叠加缺陷

1. **几何体从不共享**。每个节点 `new THREE.SphereGeometry(rad, 16, 12)`、
   每个光晕球 `new THREE.SphereGeometry(gr, 16, 12)` —— 半径各异看起来
   「只能各自 new」。但 three.js 里大小应由 mesh.scale 表达,几何体应共享。

2. **重建时不 dispose**。buildChatStarmapGraph 只 `starmapScene.remove(m)`,
   从不 dispose()。remove 只把对象从场景树摘下来,geometry/material/texture
   仍占着 WebGL 资源。全代码搜不到 dispose() 就是铁证。星图每次全量重建
   泄漏一整轮,节点数还随记忆增长 ⇒ 泄漏只增不减。

## 修法

- `starmapSphereGeometry()`:建一次半径 0.5 的球,全场景共用。
  **半径必须仍是 0.5** —— 节点缩放基准 `baseScale = rad / 0.5` 的分母
  就是它,改成 1 会让所有节点缩放翻倍,且与脉冲结束时的
  `setScalar(baseScale)` 复原逻辑不自洽。
- `disposeStarmapObject()`:递归 traverse 子树(节点下挂着 glow sphere
  与 label sprite,只 dispose 顶层会漏),释放 geometry/material/texture;
  用 seen 集合防重复释放,并**排除共享几何体**(释放它会让仍在渲染的
  mesh 变黑/报错)。
- 重建时节点与边都走 dispose。

几何体数据从 2588 份降到 1 份(22.67MB → 9KB),label 纹理由
「只增不减」变成「每轮释放」。

## 判据(cmd/gui/starmap-gpu-lifecycle.test.mjs)

13 条。变异测试验证非假红:
- 还原 `new SphereGeometry(rad,…)` ⇒ 「节点循环用共享几何体」+
  「代码里只有一处构造」转红
- `disposeStarmapObject(m)` 换回 `starmapScene.remove(m)` ⇒
  「重建时释放旧节点资源」转红

排障中判据自身报过 3 次假红,根因都写进注释:
  - 注释里也写着 `starmapNodeMeshes.forEach` 与 `disposeStarmapObject`,
    裸正则从注释开始匹配 ⇒ 改为先剥注释
  - 匹配窗口 200 太大:节点那处换成 remove 后,**边**的
    `disposeStarmapObject(l)`(相距 84 字符)仍落在窗口内 ⇒ 收到 40
  - 摘要里 `new SphereGeometry` 计数包含注释里的引用,显示 2 次而真实
    只有 1 次 ⇒ 计数前先剥注释

## 验证

6 个门禁全绿:starmap-gpu-lifecycle / window-liveness /
device-bridge-liveness / endpoint-align / device-align /
device-gateway-discovery。
2026-10-06 10:29:58 +08:00
..