diff --git a/native_bridge/src/RealtimeFxLoop.cpp b/native_bridge/src/RealtimeFxLoop.cpp index aaf9c4c..dfeca71 100644 --- a/native_bridge/src/RealtimeFxLoop.cpp +++ b/native_bridge/src/RealtimeFxLoop.cpp @@ -205,7 +205,7 @@ int run_realtime_fx_loop(const std::string& jobPath, const std::string& shmName, uint64_t processed = 0; uint64_t lastGen = 0; // chain generation đã báo latency // ---- FXRT_PERF instrumentation (tạm, đo crackle) ---- - uint64_t perfIter = 0, perfProc = 0, perfIdle = 0; + uint64_t perfIter = 0, perfProc = 0, perfIdle = 0, perfOutFull = 0; double perfProcSum = 0.0, perfProcMax = 0.0, perfProcMin = 1e9; double perfLoopSum = 0.0, perfLoopMax = 0.0; uint32_t perfTakeSum = 0; @@ -264,6 +264,21 @@ int run_realtime_fx_loop(const std::string& jobPath, const std::string& shmName, std::cerr << "[RealtimeFxLoop] latency reported: " << lats.size() << " slot(s)" << std::endl; } + // OUT-ring backpressure (fix crackle "wobble" — pointer collision): + // kiểm tra chỗ trống TRƯỚC khi consume input. Nếu out ring đầy (engine + // pump chậm/stall: WS send bị block khi main-thread browser busy) mà + // bridge vẫn ghi → ghi đè slot engine đang đọc = read/write pointer + // giẫm nhau → mẫu torn → méo "wobble" kéo dài tới khi reset. Khi đầy: + // KHÔNG xử lí batch này (nhường engine drain), input backlog dồn về + // engine → engine drop batch cũ nhất (drop sạch, worklet hold+fade che + // gap) — không bao giờ ghi đè out slot chưa đọc. + const uint32_t outFree = ipc->h.outSlots - (ipc->h.outWrite - ipc->h.outRead); + if (outFree < std::min(ipc->h.inWrite - ipc->h.inRead, FXRT_IN_SLOTS)) { + ++perfOutFull; + std::this_thread::sleep_until(std::chrono::steady_clock::now() + + std::chrono::milliseconds(1)); + continue; + } uint32_t avail = ipc->h.inWrite - ipc->h.inRead; if (avail == 0) { ++perfIdle; @@ -315,12 +330,13 @@ int run_realtime_fx_loop(const std::string& jobPath, const std::string& shmName, << " loopAvg=" << (perfIter ? perfLoopSum / perfIter : 0.0) << "ms" << " loopMax=" << perfLoopMax << "ms" << " idle=" << perfIdle + << " outFull=" << perfOutFull << " takeAvg=" << (perfProc ? (double)perfTakeSum / perfProc : 0.0) << " procBlocks=" << processed << " rate=" << (runSec > 0 ? processed / runSec : 0.0) << "blk/s" << " outDepth=" << (ipc->h.outWrite - ipc->h.outRead) << std::endl; - perfProc = 0; perfProcSum = 0.0; perfTakeSum = 0; perfIdle = 0; + perfProc = 0; perfProcSum = 0.0; perfTakeSum = 0; perfIdle = 0; perfOutFull = 0; } } ipc->h.state = FXRT_STATE_STARTING; // đã dừng diff --git a/src-tauri/binaries/fx_vst_bridge-x86_64-pc-windows-msvc.exe b/src-tauri/binaries/fx_vst_bridge-x86_64-pc-windows-msvc.exe index 4a72fa3..e8a9aab 100644 Binary files a/src-tauri/binaries/fx_vst_bridge-x86_64-pc-windows-msvc.exe and b/src-tauri/binaries/fx_vst_bridge-x86_64-pc-windows-msvc.exe differ diff --git a/src-tauri/binaries/fx_vst_bridge.exe b/src-tauri/binaries/fx_vst_bridge.exe index 4a72fa3..e8a9aab 100644 Binary files a/src-tauri/binaries/fx_vst_bridge.exe and b/src-tauri/binaries/fx_vst_bridge.exe differ