them big_tasks.md (roadmap G0-G4, danh dau G1.1-G1.5 hoan thanh) + debug scripts/probes cua phien lam viec G0.1-G1.5 (test_g13/test_g12/test_g15_stress, patch_g1x, deploy/build bridge, probe_g0x)

This commit is contained in:
2026-08-15 21:59:17 +07:00
parent c813481357
commit 3664ffbc47
46 changed files with 4614 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
# BÁO CÁO BUG: Mở VST GUI khi đang PLAY → dự án câm, GUI không load
Ngày: 2026-08-15 · Repo: `C:/Users/locpham/SonicForgeStudio` · git status sạch
## 1. Tóm tắt
Nguyên nhân gốc: **nhiều thread cùng vào 1 plugin DLL trong lúc `view->attached()`**.
Khi đang PLAY, audio loop `process()` chạy trên các instance Nexus (ch0-4) cùng lúc
worker thread của ch5 chạy `attachView()`. Nexus throw exception trong
`createView/attached` → attach FAILED → frontend retry ~30 lần (0.3s/lần) →
`bridgeWriteIndex` stall 3s → Tauri watchdog kill+respawn bridge → **mất hết
instrument → câm toàn bộ**. GUI không bao giờ load được.
Không phải "instrument track khác bị reset" (giả thuyết 1 sai ở bước này — reload
reset state chỉ xảy ra khi GUI ĐÃ mở thành công rồi đóng rồi mở lại, không phải lần
attach đầu).
## 2. Chuỗi bug đầy đủ (xác nhận live, bridge.log)
```
... 6x Nexus loaded ch0-5 + EZkeys ch6 ...
[NativeBridge] transport PLAY playhead=0
[NativeBridge] GUI attach EXCEPTION ch=5 — plugin threw (createView/attached)
[NativeBridge] GUI attach FAILED hwnd=0000000002250F36 plugin=Nexus
[tauri] bridge restarted (attempt 1) ← watchdog: writeIndex stall 3s
[NativeBridge] Starting DAW Host Bridge Engine... ← restart, mất hết instrument
```
1. `handleOpenGui` (main.cpp ~943): OPEN_GUI ch5 → silence mọi channel cùng
plugin path → `g_loadMutex` → `try { reloadForGUI(); attachView(hwnd); } catch(...)`.
2. `attachView` (Vst3Instrument.cpp:550): `controller->createView(kEditor)` →
`view->attached(hwnd, kPlatformTypeHWND)` → **Nexus throw** khi đang PLAY
(audio loop process() cùng DLL đang chạy trên instance khác).
3. catch → `ok=false` → log EXCEPTION + FAILED → PostMessage WM_CLOSE (GUI không load).
4. Frontend `openVstGuiRetry` spam lại ~30 lần; mỗi lần chạy lại job (silence +
mutex + reload/attach) → main loop bị nghẽn → `bridgeWriteIndex` stall 3s.
5. `lib.rs`: stall >3s → `kill_bridge` + `spawn_bridge` (restart_attempts==0) →
mất toàn bộ instrument state → **CÂM**.
## 3. Bằng chứng thực nghiệm
### 3.1 Probe kiến trúc chính xác (bridge_two: 2 worker STA riêng + main pump)
`gui_probe.exe <plugin> bridge_two <secs>` — kết quả 76 VST3 hệ thống (sweep):
`native_bridge/debug/sweep_bridge_two_8s.json`, `probe_attach_detail.py` (bổ sung).
⚠️ **Hiệu chỉnh sweep cũ**: exit code 0xC0000409 của TẤT CẢ 76 plugin là
**artifact của probe** — `ProbeWorker::th` không join → `~std::thread` trên
joinable thread → `std::terminate` → fail-fast. RESULT line in ra nhưng không flush
kịp. Không phải 76 plugin cùng crash teardown.
Phân loại đúng dựa trên output per-attach:
| Nhóm | Số plugin | Bằng chứng | Ý nghĩa |
|---|---|---|---|
| **HANG attach inst2** | 30 | `workerA openGUI inst1=1` rồi `workerB openGUI inst2` in ra, **không có kết quả** — Nexus 25s không về | 2 instance cùng DLL, 2 apartment → attach instance 2 HANG trong `view->attached()` |
| **BOTH-OK** | 20 | `workerB openGUI inst2=1` — Kontakt, BlueARP, Ozone 11 (18) | Attach 2 instance OK; chỉ crash teardown artifact |
| **HANG/crash attach inst1** | 18 | `workerA hwnd=` in ra, không in `openGUI inst1` — RX 11 (18) + ACTP | Chết ngay instance 1 |
| **CRASH EARLY** | 8 | exit 1.2-4.6s, 0xC0000005 (AV): Melodyne, Orb Arpeggios/Melody, smartEQ3/live, smartreverb, DUNE3, ACDD | Crash thật trong attach |
Không plugin nào HANG >18s (không WATCHDOG-KILL) — main pump cứu được các trường
hợp cần pump; vấn đề là **conflict thread/apartment khi 2 instance cùng DLL**.
### 3.2 Nexus: hang khi 2 instance — OK khi 1 thread
- `bridge_two` (2 worker STA, 2 instance Nexus): inst1 attach OK, **inst2 HANG 25s+** (xác nhận bằng run 25s).
- `same_thread` (load + attach cùng 1 thread): **OK** (returned=1).
- `main_own` (load+attach main thread): **OK** (RESULT done=1 attached_ok=1).
- `two_workers_close` (đóng editor inst1 trước khi attach inst2, có closeGUI + attach qua temp thread): Nexus **HANG ngay attach inst1**; Kontakt chạy hết, OK cả 2.
→ 2 thread/apartment cùng 1 DLL = conflict thật (không riêng Nexus: 30 plugin hang
inst2, 8 crash AV). Kontakt/Ozone/BlueARP chịu được.
### 3.3 Giả thuyết (a) reload mất state — XÁC NHẬN từ code
`Vst3Instrument::reload()` (Vst3Instrument.cpp:615): `closeGUI()` + terminate +
`delete state_` + `loadPlugin(path_)` **MỚI** → preset/state mất khi mở lại GUI
(reopen). Kích hoạt bởi `reloadForGUI()` khi `hasAttachedOnce_ && !guiAttached_`
(đã từng mở GUI rồi đóng). Không phải nguyên nhân của bug câm lần này (attach đầu
chưa thành công nên chưa tới reload), nhưng là bug phụ: **mở GUI → đóng → mở lại =
mất preset/state của track**.
## 4. Kết luận nguyên nhân
1. **Root cause**: attach GUI chạy worker thread trong khi audio loop `process()`
đang chạy instance cùng DLL (2+ thread 1 DLL). Nexus throw → attach fail.
Probe không audio: biểu hiện hang (inst2); app thật có audio: biểu hiện
exception (ch5). Cùng gốc.
2. **Khuếch đại**: frontend retry spam 30× → stall 3s → watchdog tưởng bridge chết
→ restart → mất hết instrument → câm. Đây là lý do dự án chết hoàn toàn, không
chỉ mất GUI.
3. `g_loadMutex` + silence hiện có chưa đủ: không đợi renderAll thoát hẳn instance
cùng DLL trước khi `createView`, và Nexus vẫn throw ngay cả khi đã silence
(silence chỉ ngăn process gọi tiếp, không chặn instance đang nằm trong process()).
## 5. Đề xuất fix (xếp theo ưu tiên)
1. **Chặn/defer OPEN_GUI khi transport PLAY** — đơn giản nhất, an toàn nhất:
nếu đang PLAY, đợi transport STOP (hoặc tự tạm dừng) trước khi attach, resume
sau. Loại bỏ hoàn toàn conflict audio-loop × attachView. (`ponytail:` người dùng
muốn mở GUI khi đang chơi thì nâng cấp thành pause-resume tự động, hoặc đợi
`setProcessing(false)` + đợi renderAll thoát.)
2. **Giới hạn retry**: bỏ `openVstGuiRetry` spam; thử tối đa 2-3 lần có backoff.
3. **Watchdog đừng giết khi đang attach GUI**: phân biệt stall do GUI job dài (có
attach-in-flight flag) với stall do bridge chết thật; nâng ngưỡng stall khi có
attach đang chạy.
4. **Fix state reset**: `reload()` nên save/restore state (`getState`/`setState`)
thay vì load instance mới mất preset. (Bug phụ, độc lập.)
5. (Dài hạn) 1 apartment chung cho mọi instance cùng DLL (`shared_worker` pattern
đã có trong probe) — loại conflict nhiều-thread-1-DLL triệt để.
## 6. Files liên quan
- `native_bridge/src/main.cpp` — handleOpenGui (943), silence/close-other (1015-1090), attach job + catch (1132-1160)
- `native_bridge/src/Vst3Instrument.cpp` — reload() (615), reloadForGUI() (528), attachView() (550), closeGUI() (640)
- `native_bridge/src/gui_probe.cpp` — probe bridge_two (243), two_workers_close (318)
- `native_bridge/debug/` — sweep_all_gui.py, sweep_bridge_two_8s.json, probe_attach_detail.py, probe_app4_live.py, probe_app4.py
- Bridge log: `%AppData%/SonicForgeDAW/logs/bridge.log`
Trạng thái live hiện tại: app câm (bridge 21932 sống, không instrument, transport
STOP). Test thêm bằng `probe_app4_live.py` sẽ lại làm hư session — cần xác nhận
trước khi chạy tiếp.