Files
LlamaCasty/HANDOFF.md
T
gramps 64a5a6d06f perf(pump): slice 18 — C4 blit-on-change composite cache
ty-1841: capture fixed (band ~20 updates/s, no tears) but FramePump stalls
on EVERY iteration (totalMs 21-44, render=full-render split=0 elements=6
dynamic=4) — the render ceiling, ~22-28 composites/s, caps the desktop in a
60fps file. SceneGraph can't help: the live backdrop is element 0 and cannot
be baked (a cached capture goes stale), so the cache lives at the pump.

RenderFull wraps both full-render call sites: BuildFullRenderSignature hashes
the full input identity (options rect, social bar, per-element layout/visual
bits + the frame each element would resolve through the SAME resolver seam,
using array identity + Epoch + CropBounds); unchanged identity reuses the last
composite with one Buffer.BlockCopy (~3ms) instead of a ~30ms re-composite.
Cache buffer is a separate long-lived array, written pre-burn/pre-recycle
(the caller burns the frame counter and recycles scratch AFTER render).
Engagement gated on the 1:1 config (the only deployed tier). Telemetry
surfaces `cache NR/WH` on the 5s stats line.

Same shape as OBS (sources cache their surface, the scene blits on update) —
docs.obsproject.com/backend-design, the pattern this repo cites since the
2026-09-04 paste-cache slice.

Good Dog: FullRenderCache_StaticInputs_RenderOnce_Then_Reuse_UntilInputChanges
(static scene renders ONCE + byte-identical reuse; new frame+Epoch invalidates).
297/297 green, 0 warnings, verify.sh scope-locked (FramePump.cs, FramePumpTests.cs
+ ai.md/HANDOFF/MyMistakes). No push — device re-verify next.
2026-09-15 07:57:09 -07:00

90 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HANDOFF — 2026-09-15 (slice-18 C4 composite cache committed locally — device verify next)
## Branch / Commit State
`main` HEAD = **slice-18 commit** (C4 blit-on-change composite cache — committed LOCALLY, **NOT
pushed**; web/A/V work stays commit-local until greenlight). Before it: slice-17 overlapping
capture readbacks, slice-16 capture conversion fix, slice-15 pacing fix (FramePump), `c01206f`
(composition capture), `b22d08e` (signed audio-sync, pushed). Working tree clean.
## ⚠️ Branding (2026-09-14, creator-corrected): product = **llamacasty**, internals = ytLive
The product is **llamacasty**; repo path, csproj `AssemblyName`/`RootNamespace`, DB/log paths
(`%APPDATA%\ytLlive\...`), and most code names are the legacy **ytLive/ytLlive**. User-facing
language says "llamacasty"; code/assembly/repo names stay ytLive. See `ai.md` → Brand.
## ✅ Committed locally — slice 18: C4 = FramePump blit-on-change composite cache
The ty-1841 take (slice-17 build) proved capture fixed (band ~20 fresh updates/s, no tears, pacing
clean) but the render is STILL the wall: **FramePump stall on EVERY iteration** (`totalMs 21-44`,
`render=full-render split=0 elements=6 dynamic=4`, worst render 166ms startup spike) — only ~22-28
composites/s. The SceneGraph split can't fix it: `GetSplitPoint` returns **0** because the
live-capture backdrop is element 0 and CANNOT be baked (a cached capture goes stale).
**What** (`Services/Encoder/FramePump.cs`, + new Good Dog test in `ytLive.Tests/FramePumpTests.cs`):
the full-render path now caches the last composite + its INPUT IDENTITY. `BuildFullRenderSignature`
mirrors the compositor's own resolution (same resolver seam: element ref + layout/visual bits +
resolved frame's array identity + Epoch + CropBounds + options + social bar) — unchanged identity →
ONE `Buffer.BlockCopy` (~3ms) instead of the full re-composite (~30ms); changed identity → re-render.
Cache buffer is a separate long-lived array, written pre-burn/pre-recycle (never the scratch pool).
Gated on the 1:1 config (the only deployed tier). Telemetry: `cache {renders}R/{hits}H` on the 5s
stats line + internal `CacheHits`/`CacheRenders`/`OutputIndex`.
**Good Dog test:** `FullRenderCache_StaticInputs_RenderOnce_Then_Reuse_UntilInputChanges` — static
scene renders ONCE then hits (byte-identical above the burn strip), a new frame (new array + Epoch)
invalidates + propagates. Existing `Pump_Pools_...` test now passes a STABLE scene (like production)
and keys alternation on `OutputIndex` (the two resolver passes per tick double-advanced a call-count
flip). **297/297 green, app + tests build 0 warnings.** Scope-locked (2 code files + ai.md +
HANDOFF): `Services/Encoder/FramePump.cs`, `ytLive.Tests/FramePumpTests.cs`.
## ⚠️ Open items (before PUSHABLE)
- **Device re-verify (next step):** creator records the SAME tv-show scenario on the slice-18
build. Judge numerically:
- startup.log telemetry: `cache` line shows hits dominating on TV holds (`e.g. cache 1R/250H`),
`avg render` drops toward the ~3ms BlockCopy, FramePump **stalls disappear** (the per-iteration
stall was the C4 signature).
- Decode + `/tmp/opencode/freeze_audit.py` / `band_timeline.py`: desktop-band fresh updates/s up
toward ~60 (was ~20 first-11s; the render cap was the bind), no mid-frame splits.
- ffprobe: video ≈ audio ≈ wall (pacing already healthy at slice 17 — unchanged expected).
- If the desktop layer STILL reads choppy after cache hits dominate every static hold, the residual
is the **24fps TV → 60fps container pulldown** (inherent 3:2-ish repeats; the 1841 gap histogram
was 89×2-slot + 84×3-slot holds) — that's content, not the pipeline; decide with the creator
whether it needs an adaptive cadence or is acceptable.
- **No push yet** — commit-locally-until-greenlight for web/A/V work. After the take verdict, also
re-measure the clap offset (`/tmp/opencode/avsync.py`), then decide push with the user.
## Open threads (carried)
- Audio-silence verification — fixed (`724af14`); creator heard real audio.
- Webcam MJPG missing / ~10–14Hz, layer SortOrder, truncation-with-dynamic-scenes — queued.
- Sync control user-doc tutorial — REQUIRED before 1.0 (creator directive; TASK 22).
- Signed A/V sync: verify the negative (advance) direction on device.
- Focus-loss capture lag — closed as NOT the cause (1824: delivery healthy ~60-100/s).
## Landmines
- testhost shares startup.log — filter by time.
- `cmd.exe /c "taskkill /F /IM ytLive.exe"` (WSL double-slashes mangle) before rebuilds — a live
app process locks `ytLive.exe` and the apphost copy fails (MSB3021).
- Build/tests: **Windows dotnet host** (`/mnt/c/Program Files/dotnet/dotnet.exe`). 0 warnings —
only `./scripts/verify.sh "<files>"`'s clean build counts. Building `ytLive.csproj` alone does
NOT rebuild `ytLive.Tests.dll` — run the Tests csproj before `vstest`.
- FramePump tests that assert per-frame CONTENT must pass a STABLE scene (`() => scene`) — the
default NewPump scene is fresh-per-tick (ok for pacing tests, but it churns the C4 render
signature and hides the cache). Cache-sensitive assertions also can't use a call-count resolver
flip (the tick resolves twice: signature + render) — key alternation on `OutputIndex`.
- ffmpeg/ffprobe: `/mnt/c/Program Files/Krita (x64)/bin/` with Windows paths.
- `MyMistakes.md` has the **freeze-audit RECIPE**, the **A/V sync measurement recipe**, the
**deadline-pacing** lessons, the **CoreMessaging DQ recipe**, and the **WGC-CLIP** + slice
blocks — grep before re-deriving.
- sqlite3 at `/home/gramps/android-sdk/platform-tools/sqlite3`.
- `C:\tmpout` is for ffmpeg evidence artifacts (raw decodes / PNGs); keep them out of the repo.
## Next step
Creator records a tv-show take on the slice-18 build → read the startup.log telemetry: shift-stall
frequency and the `cache NR/WH` line (hits must dominate on TV holds) + the band audit + ffprobe
durations. If the desktop now tracks ~60 updates/s and stalls are gone: re-measure the clap offset,
then decide push with the user. If the layer is still choppy on fully-static holds, the pulldown
(readme) is the residual and it's a content decision, not a pipeline bug.