perf(pump): slice 6 — break the 15.6ms sleep quantum (the REAL ceiling behind takes 7-9)
Take 9's numbers were decisive: paste cache moved work to ~25ms/frame but the period stayed ~37ms. The missing ~12ms per tick is Task.Delay rounding every sub-tick request up to the Windows system-clock tick (~15.6ms default — documented: learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.delay). A frame finishing 3ms early requested 3ms and slept 15.6. Producer capped at ~27fps no matter how fast the compositor got — which is why two real render fixes read as 'zero change' in playback. Game-loop/OBS canon for this (stackoverflow.com/questions/5441464; learn.microsoft.com/en-us/windows/win32/ api/timeapi/nf-timeapi-timebeginperiod): raise the timer resolution for the session, sleep only the bulk of the remainder, and SPIN the last ~2ms across the deadline. - FramePump: timeBeginPeriod(1) on entering the pump loop, timeEndPeriod(1) in the finally; pacing = bulk _pacingDelay(ahead - 2ms) + bounded Thread.SpinWait tail; blown deadlines rebase unchanged (never burst). - Stats now report avg wait: render+submit+wait must equal the period — the accounting is closed, no stage can hide in an unmeasured gap again. - Webcam dropped its IsOpaque paste-cache bypass: it re-sampled ~156k px every tick even between identical device frames; cached paste beats the sampler on hits, costs the same on misses. 52/52 per-class green (pacing + pixel suites unchanged — output byte-stable), clean build 0 warnings. Docs same commit (ai.md slice 6, TASKS.md take-10 gate, MyMistakes #3 + renumber, HANDOFF). User's top-bar spec remains next in queue (Unit B) — re-sent many times, captured, no open questions.
This commit is contained in:
@@ -763,6 +763,20 @@ seam:** `Func<Scene?>`, `Func<SceneElement, VideoFrame?>` resolver, `Func<Compos
|
||||
existing pixel suite guards sampler semantics. Take 9 must show `≈300/300, avg render ≤ ~8ms`;
|
||||
if it lands there, the recording saga closes. Follow-ups unchanged: vertical-tier scale alloc,
|
||||
debounced chat re-render under message bursts.
|
||||
- **Slice 6 — the sleep quantum WAS the ceiling (take 9, 2026-09-04):** period measured ~37ms while
|
||||
work (render+submit) was ~25 — the missing ~12ms is `Task.Delay` rounding EVERY request up to the
|
||||
Windows clock tick (documented ~15.6ms default; learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.delay).
|
||||
A frame finishing 3ms early requests a 3ms wait and sleeps a full 15.6 — capping the producer at
|
||||
~27fps NO MATTER how fast the compositor ran. This is why takes 7→9 showed zero playback change
|
||||
despite real render wins: the sleep floor dominated everything above it. Established fix (game-loop
|
||||
canon — stackoverflow.com/questions/5441464; timeBeginPeriod — learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod):
|
||||
`timeBeginPeriod(1)` for the pump's lifetime (paired `timeEndPeriod` in the finally), sleep only the
|
||||
BULK of the remainder (request minus 2ms), `Thread.SpinWait` the last ~2ms across the deadline;
|
||||
blown deadlines still rebase. Stats gained `avg wait Xms` so render+submit+wait must ≈ period —
|
||||
the accounting is closed, no stage can hide again. Same slice: the webcam's `IsOpaque` paste-cache
|
||||
bypass was removed (it re-sampled ~156k px every tick even between identical device frames; the
|
||||
cached paste beats the sampler on hits and costs the same on misses). Take 10 verdict: `≈300/300`
|
||||
honest fps — if short, the wait/render split names the remaining term with no ambiguity left.
|
||||
- **Stop ordering matters:** `StopAsync` stops the encoder (closes stdin → EOF → ffmpeg finalizes+exits)
|
||||
**before** awaiting the loop, because closing stdin unblocks a write stuck on pipe backpressure — the
|
||||
reverse order would deadlock. `ProcessFailed` self-stops the pump. `Failed` while live flips
|
||||
|
||||
Reference in New Issue
Block a user