Files

7.5 KiB
Raw Permalink Blame History

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.