Read the first instrumented take: the judder did not reproduce, no element is slow
The measurement unit existed to name which of the six scene elements eats ~140ms per composite. The creator's Release recording (ty-20261016-1841-559-0002.mp4, 174.5s, 1920x1080@60) names none of them — the whole composite costs ~1ms: composites 301 per 5s = 60/s, every slot, 0 dropped, 0 stalls after warm-up avg render 1.0-1.1ms (worst 2.5ms) = ~7% of the 16.67ms budget parts "#5 HD Pro Webcam C920 0.4ms, #5 ... raster 0.2ms, backdrop 0.2ms" - the other four elements never reach the 0.1ms print floor C4 cache 151R/150H: half the ticks were a whole-frame BlockCopy webcam 30.0/s at the resolver, longest gap 50.0ms (one 60Hz frame of sampling phase, not a drop) the FILE 29.6 distinct webcam images/s, 82% of images held exactly 2 frames, longest hold 267ms (the healthy 30fps-in-60fps signature) The two stalls are not composite cost: 124ms on the very first render (warm-up) and 53ms entering the "Chat" scene (a rebake). So the historic slow numbers are not a slow scene but different configurations: - Oct 9 19:48 (only bad take still on disk) was the VERTICAL tier: "FramePump started (1080x1920 @ 60 fps) scene='Live' comp=608x1080" - the 607x1080 centre crop upscaled to 2M px, 92 renders/5s, avg render 45.0ms, worst 278ms. - Oct 5 12:01 (141ms) logged no scene=/comp= at all - the suffix did not exist yet, so the map's "21:9 -> 1920x803" explanation stays a hypothesis, not a reading. - Every slow window shows "cache xR/0H" (the whole-frame cache never reusing) while this take shows 151R/150H, and one Oct 5 take latches mid-take (1.5ms -> 3.0ms -> 45.2ms in 15s, then stays). That latch is the lead for the next take; it is a hypothesis and the parts line is what will decide it. Corrections to the map (same commit, stale facts replaced not appended): - "It was never Debug-vs-Release" is WITHDRAWN: same 1920x1080 tier, Oct 9 = 10.4ms vs this Release take's 1.0ms, and Build <hash> carries no configuration - every pre-2026-10-16 render number is config-unknown. - The "same scene shape" 10:01/12:01 comparison is superseded: it was an eye comparison of takes whose geometry was never logged. New: scripts/webcam-cadence.py - the ground-truth probe for what the FILE holds, beside what the pump says it did. It took three wrong readings to get right (H.264 re-encoding defeats exact-pixel comparison; a round-clip crop's corners leak 60fps background; a fixed crop across a scene switch reads background), all recorded in MyMistakes.md as a recipe, including an ffmpeg-native cross-check (tblend=difference + signalstats.YAVG - the same metric, the technique already in this repo's A/V-sync recipe) which agreed: 52.2% duplicates / 28.6 distinct/s vs 50.7% / 29.6/s on different windows of the same take. Next: no fix is allowed yet - the creator makes a Release take in the failing geometry (vertical 1080p60 tier first), and that take's parts line names the stage. Docs in the same commit: ai.md (new "What the first instrumented take said" section, the two corrections, the parts-line denominators, the tool in the inventory), TASKS.md (reading outcome), HANDOFF.md (rewritten), MyMistakes.md (recipe). Gates: clean build 0 warnings, full suite 428/428, scope check passed.
This commit is contained in:
@@ -382,6 +382,52 @@ delay + ~100ms pipeline = exactly the measured 2.11–2.22s. The lag ALSO scales
|
||||
"time since app launch" up to the ring depth — a take right after a restart reads as
|
||||
~370ms while a fully-warmed app reads ~2.2s. Same code, wildly different numbers.
|
||||
|
||||
### Measuring webcam CAMERA cadence in a recording — the MAD method (RECIPE, 2026-10-16)
|
||||
|
||||
Derived while reading the first instrumented take. Answers "did the camera's cadence reach the
|
||||
FILE?" — the ground truth beside the pump's own counters. Tool: **`scripts/webcam-cadence.py`**
|
||||
(`python3 scripts/webcam-cadence.py TAKE.mp4 --x X --y Y --size 180 --start SECONDS`).
|
||||
Three wrong readings were produced before it worked; each is a trap to skip:
|
||||
|
||||
1. **Exact pixel equality is useless.** H.264 re-encodes even a *fully static* region slightly
|
||||
differently every frame, so `cur == prev` reported **57.9 distinct images/s** on a take whose
|
||||
camera delivers 30/s — every frame "new". Use a THRESHOLD on the mean absolute difference
|
||||
(MAD) between consecutive frames: the distribution is bimodal (codec noise < 0.1, real camera
|
||||
motion > 1), so read the threshold off the histogram (0.5 for a moving subject) instead of
|
||||
guessing.
|
||||
2. **The crop must sit INSIDE a round clip.** The webcam is `clip=Round`, radius `r = min(w,h)/2`
|
||||
= 145 for a 526×290 box. A square crop's corners lie at `s/√2` from the centre, so `s = 240`
|
||||
→ 170 > 145 → the corners sampled the 60fps BACKGROUND behind the circle and the probe counted
|
||||
60/s. Keep `s ≤ 1.41·r` (180 is safe for r=145). **The crop rect comes from the pump's
|
||||
`FramePump scene dump:` line** (`rect=1425,748 526x290` → centre 1688,893).
|
||||
3. **One scene at a time.** Element rects differ per scene, so a crop carried across a scene
|
||||
switch sits on background and reports a multi-second "hold" that never happened (a 13.8s
|
||||
"freeze" in the first pass was just the `Chat` scene). Pick a window from the dump timestamps
|
||||
with `--start`.
|
||||
4. **A still subject also produces long holds.** Threshold counting cannot distinguish "the
|
||||
camera had nothing new to send" from "we repeated a frame" — read it BESIDE the pump's
|
||||
`FramePump stats` lines (`N renders/5s` = composites/s, `webcam N/s` = arrivals), which do not
|
||||
depend on subject motion.
|
||||
|
||||
**Healthy shape:** the hold histogram peaks at **2 frames** (30fps camera in a 60fps container:
|
||||
every image twice → ~50% duplicates), distinct/s ≈ the camera's own rate, longest holds in the
|
||||
low hundreds of ms. Oct 16 Release take: 29.6/s, **82% of images held exactly 2 frames**, longest
|
||||
267ms. A peak at 4+ frames, or distinct/s approaching the composites/s figure, is the judder.
|
||||
|
||||
**Cross-check with ffmpeg's own filters (no numpy, uses this file's A/V-sync technique):** the metric
|
||||
above is the mean absolute frame-to-frame difference, which is exactly what
|
||||
`tblend=all_mode=difference,signalstats,metadata=print` reports as `YAVG`. Same crop, 40s window:
|
||||
|
||||
```bash
|
||||
ffmpeg -v error -ss 45 -t 40 -i TAKE.mp4 \
|
||||
-vf "crop=180:180:1598:803,scale=64:64,format=gray,tblend=all_mode=difference,signalstats,metadata=print:file=-" \
|
||||
-f null - | grep YAVG | sed 's/.*YAVG=//' # count values <= threshold
|
||||
```
|
||||
|
||||
It agreed with the script on the same take (52.2% duplicates / 28.6 distinct/s vs 50.7% / 29.6/s —
|
||||
the windows differ slightly), so either path is trustworthy; use the script when the full take and
|
||||
the hold histogram matter, this pipeline for a fast spot check.
|
||||
|
||||
### 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
|
||||
|
||||
Reference in New Issue
Block a user