The cadence work ended with a question the log could not answer: the compositor
manages 6.8 composites/s at ~141ms while the 10:03 take did 29-34ms on the "same
scene shape" (full-render split=0 elements=6 dynamic=5). Nothing in the stats line
said WHICH of the six elements cost what, and nothing recorded the scene itself, so
the two takes could only be compared by eye. This closes that gap and nothing else:
no fix is attempted until a real take names the culprit.
- Services/Compositor/CompositeTimings.cs (new): per-window collector.
Add(stage, ticks) accumulates; Describe(renders, minMs = 0.1) prints the averaged
breakdown slowest-first, dropping sub-0.1ms noise.
- SceneCompositor.Render / CompositeLayers / BakeStaticBase / BlitBakedElement /
BlitCachedLayer + SceneGraph.GetBakedBase: trailing optional CompositeTimings?
Every stage is timed (backdrop, base, flash, social, ticker, zoom, scale, overlay,
bake backdrop, bake overlays) and EVERY element gets its own entry keyed by
z-index + name ("#2 Webcam"), with the nested "raster" sub-stage recording the
paste-cache rasterize miss - the split between "re-samples every tick" and "slow
paste" without a second session. Bake-pass elements are prefixed "bake #..." so a
one-off rebake cannot masquerade as a per-composite cost. Callers that pass
nothing (thumbnails, existing tests) are unaffected and pay nothing.
- FramePump: owns the collector, threads it through RenderScene/RenderFull/
GetBakedBase/CompositeLayers, and logs "FramePump parts (N renders/5s): ..." once
per 5s window beside the stats line (averaged per render, so it compares directly
with "avg render"), then resets it. Also dumps the scene - "FramePump scene dump:"
header plus one line per element (z-index, name, kind, source type, visibility,
rect, opacity, clip) - at pump start and on every scene change.
- One Good Dog integration test (ytLive.Tests/CompositeTimingTests.cs): a real
composite of a full-canvas element plus a 64x64 one through the real collector
must rank the full-canvas element first, carry the raster sub-stage and the fixed
stages, and the dump must name both elements with z-index/type/rect and echo the
split point.
Derivative work (AGENTS.md): OBS's answer to "which source is slow" is the Source
Profiler - average execution time of each source's tick and render functions
(https://docs.obsproject.com/reference-libobs-util-source-profiler, util/profiler.h).
Same shape here: one timer per element per composite, reported as an average, plus
the stage split this case needs.
Docs in the same commit: ai.md (new subsection "Per-element composite timing + scene
dump", the closed gap corrected in "Frame cadence"), Services/index.md (new file row
+ FramePump/SceneCompositor rows), TASKS.md (unit marked shipped, next = creator
take), HANDOFF.md (rewritten: unpushed commit, next step, verify.sh whitelist hole).
Also recorded this session (from the creator): the launch command is
`dotnet run -c Release` - Release is the DEFAULT, Debug only when explicitly named,
and perf takes must read Release. ai.md -> Run updated.
Full suite 428 tests, 0 warnings; the only failures are the known audio env flakes
(both pass in isolation). Scope check passed.
71 lines
5.2 KiB
Markdown
71 lines
5.2 KiB
Markdown
# HANDOFF — 2026-10-16 (work committed locally; NOT pushed — push at the next milestone)
|
||
|
||
## At a glance
|
||
- **Branch** main, tracking `origin/main`. **origin/main = `931ab46`; HEAD = the tip commit "Per-element
|
||
composite timing + scene dump (measurement only)"** — **2 ahead, 0 behind** (nothing pushed this
|
||
session; per the commit/push policy, push only at sub-milestones/milestones, when the creator says
|
||
so). This file cannot record its own commit's hash — amending a commit changes that hash — so the tip
|
||
is named by subject.
|
||
- The two local-only commits are `3cb29ea` (**the previous session's HANDOFF refresh — the old
|
||
handoff claimed it was pushed; it never was**) and the tip. So "0 ahead" in the 2026-10-14 handoff
|
||
was wrong — check `git log origin/main..HEAD`, never the handoff's own claim.
|
||
- Working tree **clean** after the commit.
|
||
- No app run, recording, or test session happened 2026-10-11 → 10-16 (verified against
|
||
`%APPDATA%\ytLlive\startup.log`: last activity 2026-10-10 13:26). The creator was away for a family
|
||
emergency and has only just returned — **there is still no take of the instrumented build.**
|
||
|
||
## This session's unit (committed, unpushed — the tip commit)
|
||
- **Per-element composite timing + scene dump — MEASUREMENT ONLY (2026-10-16).** Closes the gap the
|
||
cadence work left: the 141ms take and the 30ms take could not be compared because the log named no
|
||
element and recorded no scene.
|
||
- `Services/Compositor/CompositeTimings.cs` (new): collector; `Describe(renders, minMs = 0.1)`
|
||
prints the averaged breakdown slowest-first.
|
||
- `SceneCompositor.Render` / `CompositeLayers` / `BakeStaticBase` / `BlitBakedElement` /
|
||
`BlitCachedLayer` + `SceneGraph.GetBakedBase`: trailing optional `CompositeTimings?` — every stage
|
||
and every element timed (`#<i> <name>`, nested `raster` = paste-cache miss). Existing callers
|
||
unaffected.
|
||
- `FramePump`: `FramePump parts (N renders/5s): …` per stats window; `FramePump scene dump:` block
|
||
(header + one line per element) at pump start and on scene change.
|
||
- Gate test: `ytLive.Tests/CompositeTimingTests.cs` (one `[Fact]`, real composite through the real
|
||
collector + dump formatter). Full suite 428 tests; 0 warnings; scope check passed.
|
||
- Derivative citation: OBS Source Profiler (per-source tick/render average) — see `ai.md`
|
||
"Per-element composite timing + scene dump".
|
||
|
||
## Next step
|
||
- **Creator take on the instrumented build, Release** (`"/mnt/c/Program Files/dotnet/dotnet.exe" run
|
||
-c Release`). Read the two new lines in `%APPDATA%\ytLlive\startup.log`:
|
||
`FramePump parts (N renders/5s): …` (which element / which stage eats the budget) and the
|
||
`FramePump scene dump:` block (the shape the take was made from). **No fix is to be attempted until
|
||
the parts line names the element** — the 10:03 → 12:01 regression (29–34ms → 143–155ms) is still
|
||
unexplained, and the competing explanations in the map (21:9 source rect vs. same-shape regression)
|
||
are unresolved on purpose until a reading decides it.
|
||
|
||
## Also still open (operational)
|
||
- **verify.sh's known-failure whitelist is too loose (found 2026-10-16):** it counts matching names and
|
||
passes if `KNOWN > 0`, so a NEW third failure rides along unnoticed. This session's full run showed
|
||
`AudioPipelineTests.MasterLimiter_CapsTheLiveMix_OnThePipe` failing beside the two known ones — it
|
||
passes in isolation (29/29), so it is an audio-device env flake like
|
||
`Mix_HonorsProviderGains…` (system audio playing). **Follow-up: tighten the script** (require EVERY
|
||
failure line to be in the whitelist) and add `MasterLimiter_CapsTheLiveMix_OnThePipe` to the list.
|
||
Not fixed in this unit — out of scope (`scripts/verify.sh` was not in the declared file list).
|
||
- **Vertical-format regression (STALE, not fixed):** 1080p60-vertical selection no longer shrinks the
|
||
capture border in the preview. Mechanism mapped in `Controls/PreviewPane.xaml:319-322` (crop Rectangle
|
||
bound to `OutputRectX/Y/W/H` + `IsOutputCropped`, driven by `MainViewModel.Streaming.cs`
|
||
`ApplyStreamQuality`; `DimRects` in `MainViewModel.cs:91-94`). Root cause unknown — paused.
|
||
- ⛔ Sudo password rotation pending (user; then update both password lines in `CREDENTIALS.md`).
|
||
- TASK 56 secret scan before `gramps/LlamaCasty` goes public.
|
||
- TASK 55 disk hygiene queued (`startup.log` uncapped — now 22MB and growing with the new lines).
|
||
|
||
## Tests
|
||
- Full suite **426 passed / 2 failed / 0 skipped / 0 warnings** (2026-10-16, 428 total incl. the new
|
||
test). Both failures are the audio env flake (see above) and pass in isolation.
|
||
- Pre-commit gate: `./scripts/verify.sh "<file1>" "<file2>" ...` (clean build + full suite + scope check).
|
||
**Read its output — it can pass with an unlisted failure present (see the landmine above).**
|
||
|
||
## Secrets / paths (do not re-hunt)
|
||
- Layout DB: `%APPDATA%\ytLlive\ytLlive.db` (SQLite, full-rewrite per save). Auth: `%APPDATA%\ytLlive\ytLlive.auth`
|
||
(DPAPI). Startup log: `%APPDATA%\ytLlive\startup.log`.
|
||
- LAN credentials: `CREDENTIALS.md` (gitignored). Google Cloud OAuth project number `198374416003`,
|
||
owned by `llamachile.shop@gmail.com` — verify at
|
||
`https://console.cloud.google.com/apis/credentials?project=198374416003`.
|