Take 4: pacing held (sync perfect) but avg render stayed 58.9ms — 2M managed row-walk iterations + a fresh 8.3MB buffer every tick (LOH churn into GC stalls inside the render measurement). - VideoFrame.IsOpaque: producer-contract flag (screen capture + webcam — DWM/MF fill alpha 255; media/chat/web/static NOT flagged). Full-cover aligned opaque backdrop = ONE Buffer.BlockCopy; black pre-fill skipped when it covers. - General BlitContent: integer 8.8 fixed-point bilinear + blend, row invariants hoisted, no per-pixel division/Math.Round. Within ±1 of the float reference (pixel tests allow ±2). Research per derivative-work rule: libyuv row/scale kernels (chromium.googlesource.com/libyuv/libyuv). - FramePump scratch pool (max 4, length-keyed, owned-by-reference): release strictly AFTER SubmitFrameAsync returns (stdin write copies); Contains-guard makes the transition Cut alias safe. - Removed the dead per-tick fromScene render + fromSceneProvider seam — BlendFrame consumes TransitionService.FromFrame captured at Start; the pump's render fed nothing. MainViewModel call site updated (signature). Bugs caught by the pixel probes pre-ship (recorded MyMistakes): first Bilinear double-shifted both stages (solid-255 sampled to ~1 -> general path drew nothing); sentinel 0xAB collided with an x+y pixel. FakeEncoder snapshots submitted frames (mirrors real copy semantics under recycling). ONE integration test: Pump_Pools_ScratchBuffers_Across_Frames_Without_ Stale_Pixels (alternating backdrops + repeated backing identity). Direct pin: Composite_OpaqueFullCover_Backdrop_CopiesEveryPixel_Into_Scratch. Clean build 0 warnings; 59/59 per-class + RealApp boot-smoke. take 5 verdict: expect avg render <= ~10ms, ~300/300 frames. Docs same commit: ai.md pipeline section, TASKS.md TASK 18, HANDOFF rewritten (Unit B spec + settled decisions queued).
5.9 KiB
HANDOFF — Session State
Branch / Commit State
main — Unit A (render starvation, slice 2) committed locally this session; push on the user's word
(his pattern: says "push" explicitly). Slice 1 (deadline pacing + row-blit, take-3 fix) is already on
origin/main as 716a77f.
What just happened (2026-09-04, Unit A slice 2)
Take 4 verdict: pacing HELD (no stall cliff, sync intact — user confirmed game+webcam in-sync) but render stayed 58.9ms (budget 16.7) → file still ~3.4x time-lapse. Root: the 2M-iteration managed row walk + 8.3MB fresh buffer every tick. Shipped:
VideoFrame.IsOpaqueproducer-contract flag — set ONLY byScreenCaptureFrameSource+MediaCaptureFrameSource(DWM/MF fill alpha 255). Full-canvas aligned blit of an opaque frame = ONEBuffer.BlockCopy; black pre-fill skipped when the backdrop covers.- Integer fixed-point bilinear in
BlitContentgeneral path (webcam: round/mirror/scaled) — no divisions, noMath.Round; ±1 of the float reference (tests allow ±2). - Pump scratch pool —
AcquireScratch/ReleaseScratch(max 4, length-keyed, owned-by-reference so bake-cache/social-bar/static-art arrays can never be captured). Release strictly AFTERSubmitFrameAsyncreturns (stdin write copies).FakeEncodernow snapshots frames like the real encoder (holds would race legitimate recycling). - Dead code kill: the per-tick
fromScenerender in the transition branch fed NOTHING (BlendFrameusesTransitionService.FromFramecaptured atStart) — removed with thefromSceneProviderseam +MainViewModel.cscall site. Transition cost halves as a side effect.
Bugs caught by the pixel probes before shipping (see MyMistakes take-4 follow-ups): first Bilinear
double-shifted (both stages scaled → solid-255 sampled to ~1 → general path drew nothing); sentinel
0xAB collided with a legitimate x+y value; pacing-fake synchronous completion hangs vstest (known,
re-trod).
Verification: clean build 0 warnings (both projects); per-class vstest 59/59 (FramePump 11 incl. the
new pooling test, SceneCompositor incl. NEW Composite_OpaqueFullCover..., SceneGraph, SocialBar, StretchMath,
Camera/ScreenCapture/MediaVideoSource producers, WebcamOutputKey, SessionTeardown) + SourceNaming
RealApp boot-smoke. Scope-check passed.
OPEN — next, in order
- Take 5 (user, ~30s record-only): read
%APPDATA%\ytLlive\startup.log— expectFramePump stats: ≈300/300 frames per 5s, avg render ≤ ~10ms, avg submit ~1ms+ ffprobe frames ≈ 60×duration. If render is under budget butn/300still short → suspect capture-side (the 1440pDownscaleBgraper-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. - 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 — INVERTS the 2026-09-01 "unarmed Start records" rule; fix the map when landing) that becomes the Stop/End button while active.
- Avatar right-click → Change Account (creator: "standard google thing ... on a portrait
right-click"). Login =
SignInCommanddirect (context menu on Start dies; "Choose Record Folder" lives in gear → App Settings only). - LIVE pill stays login-gated (
CanToggleOnAirexists ✓ 3a.viii). - REC+Start →
Microsoft.Win32.SaveFileDialog(InitialDirectory = settings folder, default namety-…-0000.mp4, NATIVE overwrite prompt covers exists/validate, Enter confirms). Cancel → abort + disarm pill (lit pill with no session is a lie). Up-front naming RETIRES the stop-time rename modal (assumption stated; user's dialog answer was about Go-Live confirmation). - 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+HealthUpdatedexist — verify on take 5/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 mirroringRegistrarOverride— never pop real dialogs in tests).
- Follow-ups recorded (don't fix opportunistically): vertical-tier
BilinearScaleper-frame alloc; cosmeticFramePump: encoder stop failed: No process is associateddouble-stop race; 1440p capture downscale alloc.
Landmines
- testhost shares startup.log with the app — filter by time when triaging.
- Stale testhost/exe locks the DLL (MSB3027):
taskkill /F /IM testhost.exe/ytLive.exefirst. - Do NOT run full-suite vstest (WASAPI hang, pre-existing); flow = clean build + per-class + scope-check.
- Pacing-seam fakes MUST await/yield (sync-completed task → pump runs inline on StartAsync → hang).
FakeEncodersnapshots submitted frames — keep any new fake encoder honest about buffer recycling.- Multi-stage fixed-point: shift only at the end (MyMistakes 2026-09-04).
- Real-
MainWindowtests:LayoutPathOverride+ temp DB mandatory;VolumePushOverridefor volume. - ffmpeg: month-end pinned build;
Startup.log"Recording saved:" lines show the real final path.
@ User note
Recording fix FIRST (his order), UX queue right behind — spec + settled decisions above, don't re-ask. Keep responses SHORT; one integration test per change; commit every unit; push on his word only.