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

6.1 KiB
Raw Blame History

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.