G4.3 idle-skip + no fixed-tick render: idle CPU ~0, latency 9.8ms

This commit is contained in:
2026-08-16 11:44:56 +07:00
parent 9436e66835
commit 9fa3076359
2 changed files with 36 additions and 8 deletions
+9 -1
View File
@@ -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.
+27 -7
View File
@@ -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