Files
SonicForgeStudio/native_bridge/debug/BUG_REPORT.md

123 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.