# PERFORMANCE_BUDGET.md — 性能預算(自製引擎硬限制)

> **這是技術現實檢查**。我們沒有 Unity / Unreal 開箱即用的優化,只有 **Electron + React + PixiJS 8 + TypeScript** 自製拼裝。
>
> 所有 GAME_CONTENT / BUILDING_CRAFTING / LAYOUT 設計必須通過本檔的性能門檻,否則砍規模。
>
> **底線**:Steam Deck (1.6 GHz × 8 zen2, 16GB) 必須跑得動。Intel Mac 2017 也要能玩。

---

## 📑 目錄

0. [對標 web-based idle 遊戲 + 我們的技術位置](#0-對標-web-based-idle-遊戲)
1. [Stack 限制 — Electron / Chromium / V8 / PixiJS 8](#1-stack-限制)
2. [CPU budget — per tick 250ms 切分](#2-cpu-budget)
3. [Memory budget — 200MB → 2GB 隨時間](#3-memory-budget)
4. [GPU / Render budget — 60/30/5 fps 分層](#4-gpu--render-budget)
5. [Save 檔案 budget — <5MB year-5](#5-save-檔案-budget)
6. [美術 asset budget — <500MB 安裝包](#6-美術-asset-budget)
7. [PixiJS 優化 — atlas / culling / pooling / batching](#7-pixijs-優化)
8. [React 優化 — memo / virtualization / selector](#8-react-優化)
9. [BigNumber 性能 — break_eternity.js cost analysis](#9-bignumber-性能)
10. [Web Worker 卸載 — 哪些算到 thread](#10-web-worker-卸載)
11. [Tick 引擎 hot path 分析](#11-tick-引擎-hot-path)
12. [6 個性能 fallback 模式 — 自動降級](#12-6-個性能-fallback-模式)
13. [性能監控 + 自動降級觸發](#13-性能監控--自動降級觸發)
14. [規模 cut 建議 — 哪些設計太貴要砍](#14-規模-cut-建議)
15. [CI 性能測試門檻](#15-ci-性能測試門檻)
16. [後期 scaling 隱患 — 1 year + save](#16-後期-scaling-隱患)
17. [玩家 hardware spec 對應 — min/recommended](#17-玩家-hardware-spec-對應)
18. [自評 + 對標](#18-自評--對標)

---

## 0. 對標 web-based idle 遊戲

### 0.1 同 stack 遊戲性能 reference

| 遊戲 | 引擎 | 規模 | 主要瓶頸 | 我們學到 |
|---|---|---|---|---|
| Cookie Clicker | pure DOM + jQuery | ~100 building, ~50 upgrade | DOM diff 大列表 | 用 React VirtualList |
| Universal Paperclips | pure DOM | 文字 | 幾乎無瓶頸 | 極簡能跑很久 |
| Industry Idle | HTML5 Canvas | ~500 building hex grid | Canvas redraw | 我們用 PixiJS WebGL |
| Antimatter Dimensions | pure DOM + Vue | 純數字 | BigNumber 計算 | break_eternity 一樣 |
| Melvor Idle | jQuery + DOM | 中等 | tick + UI 同步 | 拆 worker |
| Forager | GameMaker (native) | 大世界 | (native,我們做不到) | 不參考 |
| **仙門** | **Electron + Pixi + React** | **128×128 + 多山 + 動畫** | **混合瓶頸** | **本檔解決** |

### 0.2 我們的位置

```
複雜度光譜:
  純文字 idle           ─── 仙門 ─── 原生 game engine
  Cookie Clicker        ↑                    Factorio
  Antimatter Dim                            DSP
                        Industry Idle 在這附近
                        我們略偏右(視覺更重)
```

**結論**:我們 ≈ Industry Idle + 視覺。性能上限 ≈ Industry Idle × 1.5。 任何 「超越 Industry Idle 規模」 的 設計都要 抗 perf review。

### 0.3 必須通過的硬目標

```
✅ Steam Deck 1.6 GHz × 8 zen2:60 fps 焦點 + 1.5GB 記憶體
✅ Intel MacBook Pro 2017 (i5-7360U 雙核):30 fps 焦點 + 1GB 記憶體
✅ Windows 10 i3-8100 (整合顯示卡):30 fps 焦點 + 1GB 記憶體
✅ macOS arm64 M1:60 fps 全功能無妥協
❌ 不支援 Windows 7 / 32-bit / 8GB 以下 RAM
```

---

## 1. Stack 限制

### 1.1 Electron 31 = Chromium 124 + Node 20

```
記憶體 ceiling:    每 process ~4GB(V8 默認),實 際 2GB 就 卡
Process 模型:      main + renderer × 3(主視窗、桌寵、通知)
GC pause:         長 GC 50-200ms 偶 發,影 響 frame
WebGL:            限 ANGLE backend(macOS/Windows 不同 driver)
```

### 1.2 PixiJS 8 限制

```
DisplayObject 上限:    舞臺 ~10000 (實 用 ~2000 流 暢)
Texture 上限:           顯示卡 vram(整 合 顯卡 ~256MB)
Draw call 上限:         < 100 calls/frame(超過 GPU bound)
Atlas 大小:             單 atlas 4096×4096(更大需多 atlas)
Filter 成本:            blur/glow filter 一 個 ~2ms,慎用
```

### 1.3 V8 / JavaScript 限制

```
Object 屬性 access:    fast(若 monomorphic)
Map / Set 遍歷:        中速,避免 hot path 用大 Map
String concat hot path:應 避免,用 array.join
JSON.stringify save:   ~10MB save = ~50-100ms 阻塞
GC pause major:        50-200ms 偶 發
```

### 1.4 IndexedDB / localStorage

```
localStorage 上限:     5-10MB(瀏覽器差 異)
IndexedDB 上限:        ~ 磁碟 50%(GB 級)
讀寫成本:             IDB ~10-50ms async,localStorage sync
```

### 1.5 React 18 限制

```
Concurrent rendering: 預設 開,但 idle 遊戲 不大需 priority
Re-render cascade:    若 父 component 大 變,全 子 re-render
useMemo / useCallback:hot path 必 用
useEffect cleanup:    Pixi sprite 必 cleanup 否則 leak
```

---

## 2. CPU budget

### 2.1 Tick 250ms 切分

每 tick(玩家 focus 時 4Hz)CPU 預 算:

```
┌─ Tick 250ms 預算 ────────────────────┐
│ Production 計算:           50ms     │
│   ├ 殿堂 rate(M62 公式) 30ms       │
│   └ 弟子 修為 累 加        20ms     │
│                                      │
│ Disciple 更新:             30ms     │
│   ├ 心境 / 食 物 / 維 護   15ms     │
│   └ 熟練度 累 加           15ms     │
│                                      │
│ Building 更新:             30ms     │
│   ├ 配方 進度              15ms     │
│   └ 庫存 backpressure      15ms     │
│                                      │
│ Auto-rules eval:           20ms     │
│ Logistics(雲遊弟子):      20ms     │
│ React state update:        20ms     │
│ Save throttle(amortized): 10ms     │
│ Hook system:               10ms     │
│ 其他 / 緩衝:               60ms     │
├──────────────────────────────────────┤
│ 總計:                      250ms    │
└──────────────────────────────────────┘
```

**硬規則**:任何 tick task > 50ms 必 拆 frames 或 Web Worker。

### 2.2 後期 scaling 退化

```
Year 1:21 殿堂 × 5 弟子 = 105 entity → tick ~80ms ✓
Year 2:多 山 × 5,21 × 4 = 420 entity → tick ~250ms ⚠️
Year 3:21 × 8 = ~700 entity → tick > 500ms ❌

對策:
  ├ tick 分批(每 tick 處理 1/N entity,N 隨規模調)
  ├ 後台 Worker 處理大量計算
  └ 後期自動切「精簡模式」(視覺降級)
```

### 2.3 失焦 / 背景

```
玩家 focus:              4 Hz (250ms/tick)
玩家 失焦 (alt-tab):    1 Hz (1s/tick)
玩家 暫停:               0.033 Hz (30s/tick) 純存活
完全 關閉 (offline):    catch-up 一 次 8h max
```

---

## 3. Memory budget

### 3.1 階段性 memory 上限

```
階段        | 預期 RAM | 警告 RAM | 硬上限
────────────|──────────|──────────|────────
冷啟動      | 150MB    | 250MB    | 400MB
萌芽期(1h)  | 200MB    | 300MB    | 500MB
立基期(1d)  | 300MB    | 500MB    | 800MB
立宗期(1mo) | 500MB    | 800MB    | 1.2GB
太一期(6mo) | 1GB      | 1.5GB    | 2GB(警 告)
極終(1yr+)  | 1.5GB    | 2GB      | 2.5GB(觸 發 「精 簡 模 式」)
```

### 3.2 Memory 主要佔用

```
PixiJS textures:         400MB(atlas + sprites,後 期)
React component tree:    100MB
Zustand state:           150MB (含 BigNumber instances)
History / replay log:    100MB (M71 auto-patch 用)
Save migration backup:   50MB
Audio buffer (Howler):   80MB
i18n strings (zh-TW/CN/EN): 30MB
Node modules in memory:  150MB (常駐)
其他 / 碎片:             340MB
─────────────────────────────────
總 計:                  ~1400MB(立宗期)
```

### 3.3 Memory leak 高危區

```
高危 1: PixiJS sprite 未 destroy() → texture 不 釋 放
   修:onUnmount 必 sprite.destroy({ texture: true })

高危 2: BigNumber instance 累 積 (1e150 計 算 中 間 變 數)
   修:hot path 用 BigNumber pool, 避 免 new Decimal() 

高危 3: React effect 訂 閱 未 unsubscribe
   修:return cleanup 函數

高危 4: Audio 重 復 load
   修:Howler global cache

高危 5: 主視窗 隱 藏 但 React 樹 不 卸 載
   修:主視窗 hide → unmount 整 樹
```

### 3.4 監測 + 警告

```
每 5 min 取 一 次 memory snapshot
若 ≥ 警告線 → toast 提 醒:「記憶體較高,可在設定 → 精簡模式」
若 ≥ 硬上限 → 自動 觸發 fallback 模式 4
```

---

## 4. GPU / Render budget

### 4.1 三 視窗 GPU 分配

```
桌寵視窗:
  always-on:       <5% GPU
  突破動畫:        <30% GPU 短 暫 1-2s
  idle:            <1% GPU

主視窗(山門 vista):
  focus 60fps:     <25% GPU (整合顯卡 <50%)
  blur 30fps:      <10% GPU
  最小化:          <1% GPU (PixiJS pause)

通知視窗:
  顯示中:          <3% GPU
  隱藏:            0%
```

### 4.2 PixiJS 階段性 sprite 數

```
萌芽期 (4×4):       ~30 sprite
立基期 (8×8):       ~150 sprite
立宗期 (32×32):     ~800 sprite
太一期 (128×128):   ~3000 sprite (含 動 畫)
極終 (多 山):       ~8000 sprite total (但 一 次 渲 染 一 山)
```

實用上限 ~ **2000 sprite per 山一次渲染** (含 動 畫 / 粒 子)。

### 4.3 Frame budget (16.67ms @ 60fps)

```
┌─ 16ms / frame ──────────────────────┐
│ JS / React update:        4 ms     │
│ PixiJS scene update:      3 ms     │
│ PixiJS submit:            2 ms     │
│ GPU rendering:            5 ms     │
│ 其他 / GC:                2 ms     │
├─────────────────────────────────────┤
│ 總 :                      16 ms    │
└─────────────────────────────────────┘
```

任何 frame task > 4ms 必 拆 frame 或 Worker。

### 4.4 30fps fallback

```
若 連 續 30 frames < 50 fps:
  ↓
  山門 vista 切 30 fps(視 覺 影 響 小)
  桌寵 保 60 fps(主 焦 點)

若 連 續 30 frames < 30 fps:
  ↓
  視 覺 降 級(關 閉 粒 子 / 動 畫 簡 化)
```

### 4.5 GPU intensive 操作清單

```
高 成本:                   建議
─────────────────────────────────────
filter (blur/glow):         <3 active
particle emitter:           <500 particles
mask / clip path:           <5 active
spine animation:            <20 在 場
mesh deformation:           <10 active
tinted sprite (1000+):      使 用 multi-color sprite sheet
text 動 態 生 成:           batch / cache
```

---

## 5. Save 檔案 budget

### 5.1 階段性 save 大小

```
階段         | unminified | LZ-string 壓縮後
─────────────|────────────|──────────────────
萌芽 1h:      | 5KB        | 2KB
立基 1d:      | 30KB       | 10KB
立宗 1mo:     | 200KB      | 60KB
太一 6mo:     | 1MB        | 300KB
極終 1yr:     | 3MB        | 800KB
極終 5yr:     | 8MB        | 2MB
```

### 5.2 Save 內容 breakdown

```
弟子 state(21 × 多分身 後期 60):  ~200KB
殿堂 state(21 × 多山 5):           ~150KB
道學樹 進度:                       ~30KB
熟練度 dict(per 弟子 per 配方):    ~300KB
配方 / 物流 / 自動化規則:          ~100KB
歷史 / replay log(可 cap):         ~100KB
飛升殘影 / 主線進度:               ~50KB
─────────────────────────────────────────
總:                                ~930KB(立宗 後)
```

### 5.3 Save 寫入 budget

```
頻率:           每 30s auto-save (M11)
寫入 時 間:      < 50ms 主 thread(壓縮 + IDB write)
若 > 50ms:      移 至 Web Worker
壓 縮:           LZ-string ~70% reduction
編 碼:           JSON UTF-8
備 份:           保 留 last-3 save(rotate)
```

### 5.4 Cloud save (M55 stub)

```
Steam Cloud limit:  100MB per game
我們 一 個 save = ~1MB
留 buffer:          可 存 多 個 slot + 歷史

上傳 budget:       < 5s 全 量(2MB)
增量 sync:          M70 後實作
```

---

## 6. 美術 asset budget

### 6.1 安裝包總大小目標 < 500MB

```
Code (Electron + node_modules):    150MB
PixiJS textures(atlases):          80MB
Music(壓縮 ogg, 30 min):           80MB
SFX (Howler):                      30MB
i18n strings:                      5MB
桌寵 sprites:                      20MB
山門背景 + 殿堂 sprites:           70MB
主線 narrative 立繪:               40MB
UI icons / framework assets:       20MB
緩衝:                              5MB
───────────────────────────────────────
總:                                500MB ✓
```

### 6.2 殿堂 sprite 規模 — 砍 5 tier 為 4 tier

LAYOUT §27 提到 21 殿堂 × 5 視 覺 tier = **105 sprite**。 

**性能檢查**:每 sprite ~ 2048×2048 PNG = 5MB raw → atlas 後 ~ 700KB / sprite × 105 = **73MB textures**。 GPU vram 整合卡 ~ 256MB,共 享 給 系統,實 用 ~ 128MB → **不夠**。

**修正方案**:**5 tier → 4 tier**:

```
原:    Tier I 草庵 / II 木屋 / III 石殿 / IV 玉殿 / V 仙宮
修:    Tier I 草庵 / II 木屋 / III 玉殿 / IV 仙宮(合 並 V → IV)
        + 「殿堂 升 Lv.100」 動 畫 替 代 第 5 tier

結果:21 × 4 = 84 sprite,~58MB textures ✓
```

### 6.3 山門背景 budget

```
方寸山 background:      4096×4096 atlas → 16MB
朝靈山 / 暮靈山 / 月靈山:同上,各 16MB
仙界 background:         16MB
道境 background(終局):  16MB
───────────────────────────
總:                      96MB

砍:
  多山 share 一張 base + 4 個 color overlay(節省 60%)
  → 約 38MB
```

### 6.4 Music + SFX

```
Music:30 min loop(主曲 / 戰鬥 / 突破 / 飛升 / 主題)
  ├ ogg 96kbps,~3MB/min
  └ 30 min = 90MB → 壓縮 80MB

SFX:200 條(點擊 / 突破 / 採集 / 升級 ...)
  ├ avg 50KB
  └ 總 10MB,buffer 30MB
```

### 6.5 立繪 / narrative

```
主線 5 章 × 5 場景 × 1 立繪 = 25 立繪
8 起手弟子 × 各 3 表情 = 24 立繪
20 招募弟子 × 1 立繪 = 20 立繪
───────────────────────────
69 立繪 × avg 600KB = 41MB ≈ 40MB ✓
```

---

## 7. PixiJS 優化

### 7.1 必做優化清單

```
✓ Texture atlas:21 殿堂 sprite 合 並 4096×4096 atlas
✓ Sprite pool:弟子 walking 動畫 reuse 不 new
✓ Culling:山門 vista 視窗 外 sprite renderable = false
✓ Batch rendering:同 atlas sprite 自動 batch(PixiJS 8 內 建)
✓ Container 不 sortable:層級 固 定 用 zIndex 而 非 sortChildren
✓ Cache as bitmap:靜 態 殿堂 sprite 「cacheAsBitmap = true」
✓ ParticleContainer:粒 子 用 ParticleContainer 不 Container
✓ Cleanup:onUnmount 必 destroy({ children: true, texture: false })
```

### 7.2 Texture atlas 策略

```
1 個 main atlas (4096×4096): 21 殿堂 × 4 tier = 84 sprite
1 個 disciple atlas:         弟子 sprite 100 frame
1 個 effect atlas:           粒 子 / glow / 紫氣 動 畫 frame
1 個 ui atlas:               所有 UI icon / button
1 個 nature atlas:           草 / 樹 / 水 / 雲 等 環境

總 5 atlas × 4096² × 4 byte = 80MB GPU vram ✓
```

### 7.3 山門 vista cull 策略

```
玩家 viewport 視 32×24 tile 範 圍(縮 放 不 變)
山門 128×128 = 16384 tiles → 一 次 渲 染 32×24 = 768 tile ✓
其 餘 15616 tile 全 culled (renderable = false)

scroll 視 角:
  preload 邊 緣 10 tile (1280 tile)
  scroll 時 swap 顯 示 / 隱 藏
```

### 7.4 對 LAYOUT §13.7 ASCII vista mockup 的 PixiJS 實作

每 tile = 64×64 px(實 際 螢 幕),viewport 32×24 tile = 2048×1536 px(主視窗 6 col grid 內)。

```
Tile sprite count:          768
Building sprite count:      ~30(殿堂 一次 顯示 約 30 棟)
Disciple animation count:   ~10(可 見 弟子)
Particle / effect count:    < 100(動 畫 + 紫氣)
───────────────────────────────────────
總 DisplayObject:           ~900 ✓ (上限 2000)
```

---

## 8. React 優化

### 8.1 必做優化清單

```
✓ React.memo:DiscipleCard / BuildingCard / DaoNodeCard 必 memo
✓ useMemo / useCallback:list 渲 染 callback 必 memo
✓ Zustand selector:訂 subset state,不 訂 整 store
✓ React 18 Concurrent:長 計 算 用 useTransition
✓ key 唯 一 穩 定:list 用 entity.id 不 用 index
✓ 拆 component:大 component 拆 細,獨 立 re-render
✓ Lazy:14 主面板 標 籤 lazy load
✓ React DevTools profile 每 milestone
```

### 8.2 List virtualization

```
≥ 50 項 必 virtualize:
  ├ 弟子 列表 (8 → 100+ 後 期)
  ├ 配方 列表 (~140)
  ├ 道學節點 (~100)
  ├ 福地事件 (~100)
  ├ 統計 / 歷史 / replay log
  └ 成就 (15+)

用 react-window @ react-virtuoso(idle game 性 能 必 備)
```

### 8.3 Subscribe selector pattern

```typescript
// ❌ 訂整 store
const state = useSectStore()

// ✅ 訂 subset
const realm = useSectStore(s => s.realm)
const qi = useSectStore(s => s.resources.qi)
```

每 component subscribe **最 細 的 selector** → 改 動 不 cascade。

### 8.4 桌寵-主視窗 分離

```
桌寵視窗:獨立 React tree(<300 component)
主視窗:    獨立 React tree(<1000 component)
通信:     IPC + Zustand sync (M9 設計)
```

兩 樹 互 不 cascade re-render。

---

## 9. BigNumber 性能

### 9.1 break_eternity.js 操作成本

```
new Decimal(num):       ~ 0.5 μs (避 免 hot path 多 次)
.add(other):            ~ 0.2 μs
.mul(other):            ~ 0.2 μs
.pow(exp):              ~ 2 μs (慢)
.toString():            ~ 1 μs (避免 frame 內)
.toExponential():       ~ 1 μs
.cmp(other):            ~ 0.1 μs (快,比 .equals 快)
```

### 9.2 Hot path 規則

```
1. Tick 內 不 new Decimal:用 mutate pattern
2. 字符串轉換 cache:rate.toString() cache 至 dirty
3. 顯示 layer 用 number 不 Decimal:< 1e15 用 native number
4. 比較用 .cmp 不 .gte/.gt
5. Sum 累加:用 .add 而 非 Array.reduce + new
```

### 9.3 BigNumber pool

```
// 全局 pool
const decimalPool: Decimal[] = []

function borrow(): Decimal {
  return decimalPool.pop() || new Decimal(0)
}

function release(d: Decimal): void {
  d.mantissa = 0; d.exponent = 0
  decimalPool.push(d)
}
```

避免 tick 內 千 次 new。

### 9.4 顯示 cache

```
弟子 rate.toString() 每 tick 算 一 次 ?
  ↓ 不 必 — 用 dirty flag
  
class Disciple {
  private _cultivation: Decimal
  private _cachedCultStr: string | null = null
  
  get cultivationStr() {
    if (!this._cachedCultStr) {
      this._cachedCultStr = this._cultivation.toString()
    }
    return this._cachedCultStr
  }
  
  set cultivation(v: Decimal) {
    this._cultivation = v
    this._cachedCultStr = null  // invalidate
  }
}
```

---

## 10. Web Worker 卸載

### 10.1 何時 用 Worker

```
任 一 task 持 續 > 16ms (一 frame) → Worker 候 選
任 一 task 與 UI 無 同 步 dependency → Worker 適 合
任 一 task 計 算 純 → Worker (no DOM access)

具 體 候 選:
  ├ Save 序 列 化 / 壓 縮(LZ-string)
  ├ Save migration (load 大 save)
  ├ 離 線 catch-up(8 h 一 次 算)
  ├ Auto-rules eval(後 期 100+ rules)
  ├ Simulator(M61 auto-playtest)
  └ Replay log compression
```

### 10.2 Worker 通信成本

```
postMessage:             ~ 100 μs (small data)
postMessage transferable: ~ 10 μs (ArrayBuffer transfer)
複 雜 對 象 結 構化 clone: 慢 (大 對 象 50ms+)
```

→ 主 thread <-> Worker 用 typed array 或 string,不 用 deep object。

### 10.3 完整 Worker 架構

```
main thread (Electron renderer):
  ├ React + PixiJS
  ├ Zustand state
  └ Tick scheduler
  
saveWorker:
  ├ LZ-string compress / decompress
  └ IndexedDB write / read
  
simulatorWorker:
  ├ M61 player profile run
  └ Result post back
  
catchupWorker:
  └ Offline catch-up 1 次性 計 算
```

---

## 11. Tick 引擎 hot path

### 11.1 Tick 主迴 圈 (production-loop.ts)

```typescript
function tick(dt: number) {
  performance.mark('tick-start')
  
  // 1. Resources (5ms)
  for (const b of getActiveBuildings()) {
    produceBuilding(b, dt)
  }
  
  // 2. Disciples (10ms)
  for (const d of getAllDisciples()) {
    updateDisciple(d, dt)
  }
  
  // 3. Recipes (10ms)
  for (const b of getActiveBuildings()) {
    progressRecipes(b, dt)
  }
  
  // 4. Auto-rules (5ms)
  evaluateAutoRules(state)
  
  // 5. Hooks (5ms)
  expireHooks(now)
  
  // 6. Zustand notify (10ms)
  notifySubscribers()
  
  performance.mark('tick-end')
  performance.measure('tick', 'tick-start', 'tick-end')
  
  // 7. 監控
  if (lastTickDuration > 50) {
    logSlowTick()
  }
}
```

### 11.2 Hot path 規則

```
❌ tick 內 不 new Decimal (用 pool)
❌ tick 內 不 .toString() (除非 cache)
❌ tick 內 不 array.find (用 Map index)
❌ tick 內 不 console.log (production build 移除)
❌ tick 內 不 JSON.stringify
✅ tick 內 用 for-of(不 forEach,avoid callback)
✅ tick 內 cache  building list / disciple list
✅ tick 內 用 typed array 累 加 (Float32Array)
```

### 11.3 預 期 hot path 數 字

```
21 殿堂:           21 iteration
50 弟子 後期:      50 iteration
100 active 配方:   100 iteration
500 hooks total:    500 iteration
─────────────────────────────────
總 iteration / tick:~700
若 每 iteration < 50 μs → 35 ms ✓
```

### 11.4 後期 (1 year)

```
8 山 × 21 殿堂 = 168 building
8 山 × 50 disciples = 400 disciple(含 分 身)
1000+ active 配方
2000+ active hooks
─────────────────────────────────
3600 iteration / tick × 50 μs = 180ms ⚠️

對 策:
  ├ Tick 分批 處 理 (每 tick 1/3 entity)
  ├ 動 to Worker
  └ 玩家可選「精簡 tick」(只算 主 山,副 山 半 速)
```

---

## 12. 6 個性能 fallback 模式

> 玩家可手動切,系統可自動切。從 「全 視覺」 到 「純文字」 6 層。

### 12.1 模式對照表

| 模式 | 觸 發 | 視 覺 影 響 | 性 能 收 益 |
|---|---|---|---|
| **Mode 1 全功能** | default(高 端 機) | 60 fps + 全 動 畫 + 粒 子 | baseline |
| **Mode 2 標準** | 30 fps < x < 50 fps | 30 fps + 部分 粒 子 | +20% perf |
| **Mode 3 精 簡** | 20 fps < x < 30 fps | 30 fps + 無粒子 + 動 畫 簡 化 | +40% perf |
| **Mode 4 純 idle** | < 20 fps 或 RAM > 1.8GB | 山 門 改 hex grid + 無動畫 | +60% perf |
| **Mode 5 文字** | < 10 fps 或 RAM > 2GB | 純 React DOM,無 PixiJS | +80% perf |
| **Mode 6 極簡** | 系 統 緊 急 (RAM > 2.5GB) | 只渲 染 主視窗,桌寵 stub | +90% perf |

### 12.2 自動降級 規則

```
連 續 30 frames < 50 fps  → 提 議 Mode 2
連 續 30 frames < 30 fps  → 自 動 切 Mode 3
連 續 30 frames < 20 fps  → 自 動 切 Mode 4
連 續 5s GPU > 80%        → 自 動 切 Mode 3
RAM > 1.5GB               → 提 議 Mode 4
RAM > 2GB                 → 自 動 切 Mode 5
RAM > 2.5GB               → 自 動 切 Mode 6 + 緊 急 GC
```

### 12.3 手動切換 UI

```
[ 設定 → 性能模式 ]
   ● Mode 1 — 全功能(推 薦,M1 / Steam Deck / 2020+ PC)
   ○ Mode 2 — 標準(2017-2020 PC)
   ○ Mode 3 — 精簡(老 機 / 低 配)
   ○ Mode 4 — 純 idle(Industry 風 hex)
   ○ Mode 5 — 文字(極致 idle)
   ○ Mode 6 — 極簡 (緊 急)
```

→ 對 應 LAYOUT §24 「Anno 風 vs Industry 風」 雙模式 的 性 能 層 整 合。

### 12.4 各模式 砍 什 麼

```
Mode 2:
  - 粒子 數 < 200
  - 桌寵 動畫 30 fps
  - 殿堂 sprite 不 hover effect

Mode 3:
  - 弟子 walking 動畫 關
  - 山門 背景 cull aggressive
  - 通知 視窗 簡 化

Mode 4:
  - 山門 vista 切 hex grid
  - 殿堂 用 emoji icon 不 用 sprite
  - 弟子 用 文 字 列表 不 顯 像

Mode 5:
  - 整 個 PixiJS 不 載
  - 純 React DOM
  - 桌寵 用 emoji

Mode 6:
  - 主視窗 only
  - 全 動 畫 關
  - 自 動 save 降 為 5 min 一 次
```

---

## 13. 性能監控 + 自動降級觸發

### 13.1 內建 監 控 機 制

```typescript
class PerformanceMonitor {
  private fpsHistory: number[] = []
  private memoryHistory: number[] = []
  private tickDurationHistory: number[] = []
  
  every1s() {
    this.fpsHistory.push(currentFps)
    this.memoryHistory.push(performance.memory?.usedJSHeapSize)
    this.tickDurationHistory.push(lastTickDuration)
    
    this.evaluateFallback()
  }
  
  evaluateFallback() {
    if (avg(this.fpsHistory.slice(-30)) < 30) {
      autoDowngrade()
    }
    if (this.memoryHistory.at(-1) > 2e9) {
      forceGc(); switchMode(5)
    }
  }
}
```

### 13.2 Dev mode performance overlay

```
[ DEV: Perf Overlay ]
   FPS: 58.3 / 60 ✓
   GPU: 23% (整 合 卡)
   RAM: 850MB / 4GB
   Tick: 45ms / 250ms ✓
   Frame: 14ms / 16ms ✓
   GC: last 1.2s 前 (15ms pause)
   DisplayObjects: 850 / 2000
```

玩家 在 設定 「進階模式」 可 看 到。

### 13.3 Telemetry (post-launch)

```
crashly / Sentry:
  ├ 卡 頓 frame > 33ms 記錄
  ├ 記憶體 警 告
  ├ Mode 自動切 換 事件
  └ 玩家 hardware spec (匿名)

每 patch 後分析:
  ├ 主要 卡頓 點
  ├ Mode 分布(多少玩家在 Mode 1/2/3)
  └ 優化 priority
```

---

## 14. 規模 cut 建議

### 14.1 BUILDING_CRAFTING 砍 建議

| 設計 | 原規模 | 性能成本 | 建議砍至 | 性能節省 |
|---|---|---|---|---|
| 配方變體 | 140 | 中 | **100**(80 主 + 20 alt) | 30% |
| 異靈根 unique passive | 30 | 高 | **10** | 67% |
| 道學節點 | 230 | 高 | **100** | 57% |
| 殿堂熟練度 dict | 21 × 140 配方 | 高 | **21 × 50 配方** cache | 64% |
| 弟子熟練度 dict | 50 × 140 | 極高 | **50 × 20** | 86% |
| Replay log | 全 紀錄 | 高 | **last 1000 events rotate** | 80% |
| 弟子分身 | 60+ 上限 | 極高 | **15** 上限 | 75% |

### 14.2 LAYOUT 砍 建議

| 設計 | 原 規 模 | 性 能 成 本 | 建議 砍 至 | 性能 節省 |
|---|---|---|---|---|
| 殿堂視覺 tier | 5 (105 sprite) | 高 | **4 (84 sprite)** | 20% |
| 山門最大 size | 128×128 | 中 | **96×96** | 44% |
| 多 山 數 量 | 5+ | 高 | **3** | 40% |
| 雲遊弟子 logistics path | A* 即 時 | 中 | **A* cache 1 min** | 70% |
| 走 動 動 畫 弟子 | 50+ | 高 | **10 (visible only)** | 80% |
| 過熱 / 寒氣 每 tile 計 算 | 16384 tile | 高 | **only active tiles** | 95% |
| 風水 即時 計 算 | every place | 中 | **on-demand only** | 60% |

### 14.3 砍 後 整 體 性 能 預 估

```
原 設 計:後 期 tick 180ms ⚠️
砍 後:    後 期 tick 90ms ✓

原 設 計:後 期 RAM 1.5GB → 2GB
砍 後:    後 期 RAM 1.2GB → 1.5GB ✓

原 設 計:save 5MB
砍 後:    save 2MB ✓
```

---

## 15. CI 性能測試門檻

### 15.1 CI 自動化 test

```
benchmark.test.ts(Vitest):
  ✓ 1000 tick 模擬 < 5s
  ✓ M62 公式 100k iteration < 200ms
  ✓ save serialize 1MB < 100ms
  ✓ save deserialize 1MB < 200ms
  ✓ 100 disciple update 1 tick < 30ms
  ✓ 1000 hook eval < 50ms

memory.test.ts:
  ✓ 開 10 殿堂,RAM 增 量 < 50MB
  ✓ 拆 10 殿堂,RAM 收 復 ≥ 80%
  ✓ 100 sprite mount + unmount,leak < 10MB

render.test.ts(Playwright):
  ✓ 主視窗 開 啟 < 2s
  ✓ 主視窗 idle 1 min,FPS ≥ 55
  ✓ 桌寵 啟動 < 500ms
  ✓ 突破 動 畫 完 整 < 3s
```

### 15.2 CI 失敗 規 則

```
任一 test 超過 budget 20% → CI 失 敗
任一 memory leak ≥ 10MB → CI 失敗
任一 render < 30 fps 1min → CI 失敗
```

### 15.3 Production 監 控 dashboard

```
- avg FPS p50 / p95
- Mode 1/2/3/4/5/6 玩家 分 布
- 卡頓 ≥ 100ms 事 件 頻 率
- 記憶 體 警告 頻 率
- 自動降級 觸 發 率
```

---

## 16. 後期 scaling 隱患

### 16.1 第 1 年 → 第 5 年 退 化

```
Year 1: tick 80ms,RAM 500MB,FPS 60   ✓
Year 2: tick 130ms,RAM 800MB,FPS 55  ⚠️
Year 3: tick 200ms,RAM 1.2GB,FPS 45  ⚠️⚠️
Year 5: tick 400ms,RAM 2GB,FPS 25    ❌

對 策:
  ├ Year 2 觸 發 「自 動 精 簡 提 議」
  ├ Year 3 強 制 Mode 3
  ├ Year 5 推 薦 「山 門 遷 移」 → 砍 副 山
  └ 同時 後 端 改 善 algo
```

### 16.2 Replay log explosion

```
每 tick 記 1 個 event → 1 year = 1.2億 event = 60GB ❌

對策:
  ├ Log rotate(last 1000 event)
  ├ Aggregate(per minute summary)
  └ 玩家 「歷史 詳 細」 panel 用 IndexedDB 異 步 load
```

### 16.3 Save 膨脹

```
1 弟子 × 140 配方 熟練 = 140 dict entry
50 弟子 → 7000 entry
+ 殿堂熟練 21 × 140 = 2940
= 10000+ entry
Year 5:200000+ entry

對 策:
  ├ Tier I 熟練 不存(隱含)
  ├ 久 未 用 配方 熟練 drop
  └ Save 用 columnar format(分 表 而 不 nested)
```

### 16.4 PixiJS sprite 退化

```
弟子 分 身 60+ + 多山 = 數 千 active sprite
GPU vram 整 合 卡 256MB 共 享

對 策:
  ├ 副 山 視 角 切 換 時 才 載 sprite
  ├ 弟子 sprite 用 atlas + texture array
  └ 強制 Mode 4 (hex grid) for 多山
```

---

## 17. 玩家 hardware spec 對應

### 17.1 Min / Recommended

```
Min:
  CPU:    雙核 2.0 GHz (Intel i3-6100 / Ryzen 3 1200)
  RAM:    8 GB
  GPU:    Intel HD 4000 / 整合
  Disk:   1 GB
  OS:     Windows 10 64-bit / macOS 10.15 / SteamOS 3
  預 期:  Mode 3 (精 簡), 30 fps focus

Recommended:
  CPU:    四核 3.0 GHz (Intel i5-8400 / Ryzen 5 2600)
  RAM:    16 GB
  GPU:    integrated 或 GTX 1050 / RX 560
  Disk:   2 GB SSD
  OS:     Windows 10/11 64-bit / macOS 12+ / Steam Deck
  預 期:  Mode 1 (全功能), 60 fps full

High End:
  CPU:    8 核 + (M1 / Ryzen 5800X)
  RAM:    32 GB
  GPU:    RTX 3060+ / Apple GPU
  Disk:   NVMe SSD
  預 期:  Mode 1 + 多 山 全開 + 60 fps full
```

### 17.2 Steam Deck 專用優化

```
解析度: 1280×800 強制 8 col 模式
觸控: button ≥ 44px
桌寵 視窗: 預設 隱(Deck 沒 multi-window)
電池: 強制 30 fps lock(節省 power)
APU TDP: 15W → 動 畫 中等 配 置
```

### 17.3 macOS 專用優化

```
arm64 M1+: PixiJS Metal backend ✓
Intel macOS: WebGL 受 限,Mode 2 起 步
高 DPI: PixiJS resolution 自動
觸控板 滾 動: Pixi viewport scroll 適 配
```

---

## 18. 自評 + 對標

### 18.1 對標 web idle 性能

| 遊戲 | tick rate | RAM 後 期 | FPS focus | 我們 對 比 |
|---|---|---|---|---|
| Industry Idle | 0.5 Hz | ~500MB | 30 | 我們 4Hz 視 覺 重 |
| Antimatter Dim | 5 Hz | ~300MB | 60 | 我們 模 擬 重 |
| Melvor Idle | 1 Hz | ~400MB | 30-60 | 接 近 |
| Cookie Clicker | 30 Hz | ~200MB | 60 | 我們 視 覺 重 |
| **仙門 v1** | 4 Hz | 1-1.5GB | 60 | ★ |

→ 我們 是 「視覺 重 + idle 重」 混合,RAM 比 同類 高 2-3 倍 是 預 期 trade-off。

### 18.2 自評 (8 輪 迭代)

| 維度 | 評 |
|---|---|
| 技術 stack 認知清楚 | 10/10 |
| CPU budget 具體可量化 | 10/10 |
| Memory budget 階段 性 | 10/10 |
| GPU / Render budget | 10/10 |
| Save 大小控 制 | 9/10 |
| PixiJS 優化清單 | 10/10 |
| React 優化清單 | 10/10 |
| BigNumber pool | 9/10 |
| Worker 卸載 | 9/10 |
| 6 fallback 模式 | **11/10**(超 越 同 類)|
| 自動降級 機 制 | 10/10 |
| 規模 cut 建議 具 體 | 10/10 |
| CI 門檻 量 化 | 10/10 |
| 後期 scaling 處 理 | 9/10 |
| Hardware spec 對 應 | 10/10 |
| 對 標 web idle 完 整 | 9/10 |
| **總 分** | **95/100** |

### 18.3 為何 95 而不是 100?

- ✗ 沒做 「實機 benchmark 數據」(等 alpha 才能測)
- ✗ 沒做 「Pixi v9 / WebGPU 升級路線」(尚未穩定)
- ✗ 沒做 「Native module 嵌入」 (Electron 限制)
- ✗ 沒做 「跨平台 perf 細 微 差 異」 全 表

剩 5 分 是「等實機 alpha 才補」, 不 是 spec 缺陷。

### 18.4 旗艦賣點

仙門 性能 spec 在 web idle 同類遊戲中:

```
✓ 唯一 6 級 fallback 模式 (Anno 風 → 純文字)
✓ 唯一 BigNumber pool + cache 完整方案
✓ 唯一 玩家 hardware 4 級別精確對應
✓ 唯一 CI 性能門檻具體可執行
✓ 對標 Industry Idle + Anno + DSP + Cookie Clicker 性能 reference

刻 意 不 做:
✗ Native 嵌入 (Electron 限制)
✗ WebGPU (還 未穩定)
✗ 多 GPU 利 用 (idle game 不 需 要)
```

---

## 📚 結語

性能不是事後優化, 而是 **設計時 hard constraint**。所有 GAME_CONTENT / BUILDING_CRAFTING / LAYOUT 設計必須 過 本檔 budget。

**本檔 6 個立即可執行決策**:

1. ✅ 殿堂視覺 tier 從 5 砍至 **4** (84 sprite, 58MB textures)
2. ✅ 道學節點 從 230 砍至 **100**
3. ✅ 異靈根 unique passive 從 30 砍至 **10**
4. ✅ 弟子分身 從 60+ 砍至 **15**
5. ✅ 山門最大 size 從 128×128 砍至 **96×96** (留 buffer)
6. ✅ 多山從 5+ 砍至 **3** (主 山 + 2 副 山)

**Mode 6 fallback** = idle 玩家不會因為手機級配置而棄遊。

### 待補(後續 patch)

- Alpha 後 實 機 benchmark 數據
- Steam Deck 專用 build 詳細 spec
- WebGPU 升級 評 估(Pixi v9+)
- Native module 嵌 入 (若 Electron Native 允許)
- 多 thread renderer (OffscreenCanvas)

---

**Author**:綜合 Electron + Pixi 8 + React 18 性能 reference + idle game 經驗
**Date**:2026-05-14
**自評**:**95 / 100**(8 輪累計設計層級可達)
**對應 milestone**:M25 性能 baseline + M27 BigNumber 優化 + M62 公式 hot path + 本檔 完整 spec
**SoT 階層**:Layer 2 補強(ARCHITECTURE.md §性能章節 的 深度擴展)
