From 9fa307635943383b19985b895f8b126eebfcba88 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Sun, 16 Aug 2026 11:44:56 +0700 Subject: [PATCH] G4.3 idle-skip + no fixed-tick render: idle CPU ~0, latency 9.8ms --- 20260815-tasks-g4.md | 10 +++++++- native_bridge/src/plugin_host_main.cpp | 34 ++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/20260815-tasks-g4.md b/20260815-tasks-g4.md index 42d8604..01ca7a9 100644 --- a/20260815-tasks-g4.md +++ b/20260815-tasks-g4.md @@ -128,4 +128,12 @@ G4.1 → G4.2 → G4.3. - Build Release PASS (daw_vst_bridge.exe + plugin_host.exe). Deploy shutil.copy2 (kill proc giu file truoc). - Verify (probe_g43.py, bridge standalone env `SF_SANDBOX_VST3=1` + state file rong): LOAD Nexus ch0+ch1 -> 2 plugin_host [con song song]; heartbeat ca 2 OK; note-on ch0 C4 + ch1 E4 -> main audio peak 0.71835 (tong 2 channel); writeSlot flip 64 blocks/s = latency 15.6ms < 20ms (khong yeu cau 172/s thuc - Nexus render ~10ms/block that, CPU-bound); kill child0 -> child1 van phat 0.36972, bridge song + heartbeat + child0 respawn - PASS. - Regression: G1.5 stress 3 iter PASS (writeIndex delta 286); G2.5 stress 2 iter PASS (kill bridge -> respawn -> preset exact + LOAD-skip OK). App deploy (install/sonicforge-daw.exe) dang chay, bridge pid moi. -- ponytail: child render lien tuc ke ca im lang (CPU cao khi idle) - toi uu idle-skip khi can. +- FIX idle-skip (G4.3.1): plugin_host_main.cpp them `activeNotes` dem (note-on ++, note-off --, clamp >= 0) + + `idle` flag. Khi khong con note dang giu va peak block < 0.0005f -> idle = true, + loop Sleep(15) bo qua render (van PeekMessage + parent-alive check). Bo `Sleep(1)` + hot loop -> `Sleep(0)` yield. KET QUA: writeSlot flip 0 blocks/s khi im lang (truoc day + render lien tuc ~100/s, CPU cao); khi dang giu note flip 101.6 blocks/s = latency 9.8ms + (truoc 64/s = 15.6ms) - ca 2 loi G4.3 xac nhan da het. +- Verify lai: probe_g43.py PASS (2 children song song, peak 0.67273, kill child0 -> child1 + 0.36972, isolation OK); dbg_rate.py idle 0 flips/3s; G1.5 3 iter PASS (delta 287); + G2.5 2 iter PASS. Deploy install/plugin_host.exe moi. App dang chay pid 21396. diff --git a/native_bridge/src/plugin_host_main.cpp b/native_bridge/src/plugin_host_main.cpp index 526892d..affd070 100644 --- a/native_bridge/src/plugin_host_main.cpp +++ b/native_bridge/src/plugin_host_main.cpp @@ -98,6 +98,8 @@ int main(int argc, char* argv[]) { } }).detach(); + int activeNotes = 0; + bool idle = false; for (;;) { MSG msg; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { @@ -105,14 +107,15 @@ int main(int argc, char* argv[]) { DispatchMessageW(&msg); } uint32_t n = ipc->base.midiQueueCount > 64 ? 64 : ipc->base.midiQueueCount; + bool midiActivity = n > 0; for (uint32_t i = 0; i < n; ++i) { const SharedAudioBufferIPC::MidiEventIPC& e = ipc->base.midiQueue[i]; switch (e.command) { case 0x9: - if (e.velocity > 0) inst.noteOn(e.channel, e.pitch, e.velocity / 127.0f, 0); - else inst.noteOff(e.channel, e.pitch, 0); + if (e.velocity > 0) { inst.noteOn(e.channel, e.pitch, e.velocity / 127.0f, 0); ++activeNotes; } + else { inst.noteOff(e.channel, e.pitch, 0); if (activeNotes > 0) --activeNotes; } break; - case 0x8: inst.noteOff(e.channel, e.pitch, 0); break; + case 0x8: inst.noteOff(e.channel, e.pitch, 0); if (activeNotes > 0) --activeNotes; break; case 0xB: inst.controlChange(e.channel, e.pitch, e.data2); break; case 0xC: inst.programChange(e.channel, e.data2); break; case 0xE: inst.pitchBend(e.channel, e.data2 | (uint32_t(e.data3) << 7)); break; @@ -120,6 +123,15 @@ int main(int argc, char* argv[]) { } } ipc->base.midiQueueCount = 0; + if (midiActivity) idle = false; + if (parentPid && !parent_alive(parentPid)) { + printf("[plugin_host] parent gone - exiting\n"); + break; + } + if (idle) { + Sleep(15); + continue; + } // G4.3 double buffer: render into the slot we are NOT publishing, then // publish. The bridge keeps the last published block, so a slow child // never tears audio (it just repeats the previous block). @@ -127,11 +139,19 @@ int main(int argc, char* argv[]) { inst.processAudioBlock(ipc->audioLeft[slot], ipc->audioRight[slot], block); ipc->writeSlot = slot; ipc->base.bridgeWriteIndex++; - if (parentPid && !parent_alive(parentPid)) { - printf("[plugin_host] parent gone - exiting\n"); - break; + if (activeNotes == 0) { + float peak = 0.0f; + for (uint32_t i = 0; i < block; ++i) { + float l = ipc->audioLeft[slot][i]; + if (l < 0.0f) l = -l; + float r = ipc->audioRight[slot][i]; + if (r < 0.0f) r = -r; + if (l > peak) peak = l; + if (r > peak) peak = r; + } + if (peak < 0.0005f) idle = true; } - Sleep(1); + Sleep(0); } inst.closeGUI(); #ifdef _WIN32