fix(pump+compositor): take-3 starvation — deadline pacing + row-blit fast paths
Two defects made the producer 17x slow (37s record -> 2.1s/127-frame file, rawvideo stamps by arrival): FramePump slept the FULL interval after each render (period = render+submit+interval) and SceneCompositor did per-pixel float sampling + Math.Round blends over all 2.07M master pixels, scanning the whole destination per overlay (258ms avg render vs 1.5ms submit). Both solutions are established, not invented — researched before coding per the derivative-work rule: - deadline pacing: OBS libobs/media-io/video-io.c video_thread (nextTick += intervalTicks, sleep only the remainder, rebase on overrun, never burst) - row blits: libyuv pattern (BSD-3, chromium.googlesource.com/libyuv/libyuv) — 1:1 aligned identity fast path, per-pixel alpha branch, integer fixed-point blend, overlay clipped to the intersection rect, skip the dead black pre-fill when the backdrop covers ONE integration test: Pump_Paces_To_The_Deadline_Compensating_Render_Cost (lands after a fake-seam lesson: pacing fakes must await, not complete synchronously, or the pump loop runs inline on StartAsync and hangs vstest). Clean build 0 warnings; FramePumpTests 10/10, SceneCompositor/SceneGraph/ SocialBar/StretchMath 20/20. Docs same commit: ai.md pipeline section, TASKS.md TASK 18 (webcam-in-output + rename modal verified from take 3), MyMistakes recipe, HANDOFF rewritten. Take 4 pending on the user's machine.
This commit is contained in:
@@ -47,6 +47,36 @@ packages, high-quality downscale via `TransformedBitmap`. Screenshots compress
|
||||
|
||||
---
|
||||
|
||||
### Feeding a rawvideo pipe at 60fps: deadline pacing + row-blit budget
|
||||
|
||||
Derived 2026-09-03 (take-3 diagnosis — the stats seam from `97ffc42` named the stage
|
||||
in one line: `17/300 frames per 5s, avg render 258.1ms, avg submit 1.5ms`).
|
||||
Both halves were solved by OBS/libyuv long ago; do not re-derive:
|
||||
|
||||
1. **Pacing is a DEADLINE, never a post-render sleep.** `sleep(interval)` after each
|
||||
frame makes the period `render + submit + interval` — the producer can hit ≤ half
|
||||
the declared rate even with a free render. OBS's `video_thread`
|
||||
(`libobs/media-io/video-io.c`) advances an absolute `nextTick += intervalTicks` and
|
||||
sleeps only the remainder; if the deadline blew, skip the wait AND the missed ticks
|
||||
(rebase, no catch-up burst — a burst queues stale frames). Critical with rawvideo:
|
||||
pts is stamped by ARRIVAL, so a starved producer silently time-lapses the file.
|
||||
2. **A 1080p frame is ~2.07M pixels — the hot path must be row-simple.** Per-pixel
|
||||
`Math.Round` + float source-over in managed code costs ~100ns/px = the whole 258ms.
|
||||
libyuv's pattern (https://chromium.googlesource.com/libyuv/libyuv/): branch per
|
||||
pixel on source alpha (opaque → 4-byte copy, transparent → skip), integer
|
||||
fixed-point blend `(s*a + d*(255-a) + 127)/255` otherwise; and ALWAYS clip the loop
|
||||
to the intersection rect (our social-bar overlay scanned all 2M dst px for a 64px
|
||||
strip). A full-cover 1:1 blit also obsoletes the opaque-black pre-fill — skip dead
|
||||
writes.
|
||||
3. **Hermetic pacing test:** inject the delay seam to RECORD the requested TimeSpan and
|
||||
genuinely await it (`Task.Delay(d, ct)`) — a fake that returns
|
||||
`Task.CompletedTask` synchronously makes the whole pump loop run on `StartAsync`'s
|
||||
sync continuation and hang the test run (hit this 2026-09-03; the existing fakes all
|
||||
yield for exactly this reason). Assert the REQUESTED wait (< interval with a
|
||||
≥cost-ms fake render) — never wall-clock rate, which flakes on loaded machines.
|
||||
|
||||
---
|
||||
|
||||
## Splitting a large file into partials — NEVER `awk … > SRC` while awking SRC
|
||||
|
||||
(2026-08-31, Commit D) Tried to split `SocialsDialogViewModel.cs` in one line:
|
||||
|
||||
Reference in New Issue
Block a user