perf(chat): slice 3 — raster-on-change cache in ChatOverlayLayer (take-5 fix)
Take 5: render 58.9 -> 25.5ms (138/300, still ~2.2x). The blits were fixed; the resolver was not: ResolveOutputFrame -> RenderChatBox ran a FULL WPF raster (FormattedText + RenderTargetBitmap + CopyPixels + channel swap) EVERY tick whenever the chat buffer was non-empty — and the buffer survives sessions, so even a signed-out record-only take paid it. Established answer (OBS text sources): re-render on change, blit the cache every tick. ChatOverlayLayer: content version bumped from Messages.CollectionChanged (covers adds, the 500-cap removal, the fade Clear from any caller) + a config key (size + all Chat* appearance props); RenderFrame returns the cached VideoFrame by identity until either changes (compositor only reads cached frames). Conservative ordering (version latched BEFORE render) makes a mid-render message re-render next tick, never serve stale. ONE integration test: ChatOverlayLayerCacheTests (RealApp, real renderer): Same() for unchanged inputs, NotSame() on message/config change, null on empty. Full regression green (62 across touched classes), clean build 0 warnings. Accepted cost pending take 6: one ~15-25ms tick per arriving message; if live-chat bursts sag n/300, next slice = debounced off-tick re-render. Docs same commit: ai.md pipeline section, TASKS.md TASK 18, MyMistakes recipe (raster-on-change + session-surviving-buffer trap), HANDOFF (take 6 -> then Unit B, spec unchanged).
This commit is contained in:
+13
-7
@@ -37,12 +37,18 @@ RealApp boot-smoke. Scope-check passed.
|
||||
|
||||
## OPEN — next, in order
|
||||
|
||||
1. **Take 5 (user, ~30s record-only):** read `%APPDATA%\ytLlive\startup.log` — expect
|
||||
`FramePump stats: ≈300/300 frames per 5s, avg render ≤ ~10ms, avg submit ~1ms` + ffprobe frames ≈
|
||||
60×duration. If render is under budget but `n/300` still short → suspect capture-side (the 1440p
|
||||
`DownscaleBgra` per-frame alloc loop on the capture thread) or residual GC — next slice.
|
||||
Also for the user to eyeball: webcam now at true 60fps, audio still in sync.
|
||||
2. **Unit B — the top bar + session logic (user spec 2026-09-04, decisions settled):**
|
||||
1. **Take 5 happened (2026-09-04 10:49):** `138/300 frames per 5s, avg render 25.5ms` — blits
|
||||
fixed but the resolver's `RenderChatBox` full-rasterized the chat box EVERY tick whenever the
|
||||
message buffer was non-empty (the buffer survives sessions — a signed-out record-only take paid
|
||||
chat render cost!). **Slice 3 shipped same day:** `ChatOverlayLayer` content-versioned cache —
|
||||
raster on message/config change, blit the cached frame every tick (OBS text-source pattern).
|
||||
Tests: `ChatOverlayLayerCacheTests` (the ONE, RealApp) + full regression green (62 across
|
||||
touched classes), clean build 0 warnings.
|
||||
2. **Take 6 (user, ~30s record-only):** expect `≈300/300 frames per 5s, avg render ≤ ~10ms`. If
|
||||
chat-burst drops one frame per message (avg fine but `n/300` sags during live chat): debounced
|
||||
off-tick re-render is the next slice. If steady at ~300 with render <10 → recording saga
|
||||
CLOSED, go Unit B (below).
|
||||
3. **Unit B — the top bar + session logic (user spec 2026-09-04, decisions settled):**
|
||||
- Two-line top bar. Line 1: center = REC + **LIVE** pills (text renamed from ON-AIR; pills become
|
||||
mutually-exclusive RADIOS — record-OR-stream ruling), right = avatar + **Login/Logout** button
|
||||
(no account status light). Line 2: centered primary **Start** (grayed while NO pill armed —
|
||||
@@ -59,7 +65,7 @@ RealApp boot-smoke. Scope-check passed.
|
||||
- LIVE+Start → **Go-Live dialog stays** as preflight: prefilled from Text-drawer `Broadcast.*`,
|
||||
unfilled fields visibly prompted, explicit confirm → `PrepareAndStartLiveAsync` (user: "going
|
||||
live is scary — confirmation allows back-out + testing up to go-live").
|
||||
- Bottom-bar metrics init/maintain: `ResetHealth` + `HealthUpdated` exist — verify on take 5/6.
|
||||
- Bottom-bar metrics init/maintain: `ResetHealth` + `HealthUpdated` exist — verify on take 6.
|
||||
- F6 "start/end" hotkey routes through `HandleHotkey` — check it honors the new grayed-Start gate.
|
||||
- ONE integration test (hermetic): pills↔button state machine + record-path seam (an
|
||||
`internal static Func<SaveFileDialog-ish prompt>` override seam mirroring `RegistrarOverride`
|
||||
|
||||
Reference in New Issue
Block a user