Files
LlamaCasty/HANDOFF.md
T
gramps 5e78065c2d fix(web): web-layer capture cadence 10Hz → ~30Hz while recording — widget animations no longer play ~1/6 speed
The recording is 60fps but WebView2 capture was a blind 100ms DispatcherTimer = 10Hz;
each captured web frame repeated ~6x into the file caps web animation at the capture
rate, not the page's (user: 'the animation appears to be too slow').

- New CaptureScheduler (Services/CaptureScheduler.cs): per-session dispatcher timer
  that DROPS a tick while a capture is in flight (latest-wins, never queues) — the
  guard that makes a higher cadence safe: concurrent full-HD PNG CapturePreviewAsync
  calls (~10-30ms each, slow per WebView2Feedback#20) would stack CPU and publish
  stale-after-fresh. Effective cadence = max(interval, capture duration).
- Cadence: SetCaptureInterval(33) on record/stream start, (200) idle — applied via
  MainViewModel.Streaming.Operations.cs.
- De-throttle the hidden page: shared CoreWebView2Environment created BEFORE
  EnsureCoreWebView2Async with --disable-backgrounding-occluded-windows
  --disable-renderer-backgrounding --disable-features=CalculateNativeWinOcclusion.
  Off-screen WebView2 is a hidden page when the host window is unfocused/covered and
  Chromium then parks rAF and clamps timers to ~1s (WebView2Feedback#1172/#3070,
  Chrome-88 timer-throttling blog).
- Telemetry: first 30 captures per session log elapsed ms (PNG encode + decode) to
  startup.log — that decides whether ~30Hz stays or drops to ~20Hz; the FramePump
  drops frames (never time-lapses, slice 10) if UI-thread GC churn starves it.
- ONE test: CaptureScheduler_Drops_Ticks_While_Capture_InFlight_And_Resumes
  (deterministic TCS-driven, no WebView2 runtime). Suite 290/291 — sole failure the
  pre-existing compositor pixel test.
- Docs same-commit: ai.md slice 11, MyMistakes.md, HANDOFF.

References: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1172
https://github.com/MicrosoftEdge/WebView2Feedback/issues/3070
https://github.com/MicrosoftEdge/WebView2Feedback/issues/20
https://developer.chrome.com/blog/timer-throttling-in-chrome-88
2026-09-10 10:25:41 -07:00

85 lines
5.4 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-10 (evening)
## Branch / Commit State
`main` HEAD currently = `c45cbc9` (slice 10). **Slice 11 is uncommitted** in the working tree
(WebView2 capture cadence + de-throttle; see below). **ahead of origin by 16, NOT pushing** —
user ruling (2026-09-10): do not push until the web-overlay **transparency AND audio-silence**
issues are addressed. Both are open (see Still Open). Milestone tag `milestone-recording-timing`
(annotated) on `c45cbc9` — rollback point: `git reset --hard milestone-recording-timing`.
## Timing saga — CLOSED (verified)
- slice 9 `bd396e4`: duration == wall time (count-based CFR, deadline never rebased, `-re` removed).
- slice 10 `c45cbc9`: bounded `Channel` encoder queue + drop-newest policy + burned-in dot-matrix
frame counter (bottom-right). Verified on two takes (`ty-20260910-0949…-2.mp4`, 2383 frames 39.92s;
`ty-20260910-0957…-2.mp4`, 2270 frames 38.05s): **every frame decodes, counter advances +1/frame,
zero gaps/dups**. Content cadence both: median 2 frames, max gap 9, longest frozen run 8 (133ms).
User: "I think we're good on this issue" → **timing CLOSED.**
## The web-overlay threads (the active push-gate work) — Slice 11 in tree
Two user-reframed symptoms converged on ONE root cause and one change is in the tree (uncommitted):
1. "The widget is an animated resource — the animation looks **too slow**."
2. (earlier) "image renders but the **transparent pixels are black**."
Root cause of #1 (code-proven): the recording is 60fps, but the WebView2 capture loop was a blind
100ms `DispatcherTimer` = 10Hz → the widget's motion is sampled 6× under rate → repeated-frame
slow-mo. PLUS Chromium hidden-page throttling (rAF parked, timers→1s; WebView2Feedback#1172/#3070)
when the off-screen page's host window is unfocused/covered. #2 remains UNVERIFIED (the diagnostic
PNG+alpha log is bound to the FIRST capture ever = the initial about:blank doc, so it can never see
the widget — 5/5 sessions logged alpha=0 blank; instrument is blind, not the capture).
**Slice 11 (uncommitted, files below):**
- `Services/CaptureScheduler.cs` (new): per-session dispatcher timer that DROPS ticks while a
capture is in flight (latest-wins, never queues). Interval owner-configurable: 33ms recording /
200ms idle.
- `Services/WebView2Manager.cs`: scheduler replaces the raw timer; shared `CoreWebView2Environment`
created BEFORE `EnsureCoreWebView2Async` with `--disable-backgrounding-occluded-windows
--disable-renderer-backgrounding --disable-features=CalculateNativeWinOcclusion`
(`GetEnvironmentAsync`); `SetCaptureInterval(int)`; capture-cost telemetry (first 30 captures/
session → startup.log).
- `ViewModels/MainViewModel.Streaming.Operations.cs`: `SetCaptureInterval(33)` on record/stream
start, `(200)` on stop.
- `ytLive.Tests/WebView2ManagerTests.cs`: ONE test
`CaptureScheduler_Drops_Ticks_While_Capture_InFlight_And_Resumes` (deterministic TCS-driven, no
WebView2 runtime).
- Docs same commit: `ai.md` slice 11, `MyMistakes.md` recipe "web widget captured at 10Hz…".
Suite: 290/291 pass — sole failure the PRE-EXISTING `Composite_FullScene_MasterPixels` pixel
(1380,700) cyan-vs-magenta (out of scope; fails with any fix stashed).
## NEXT STEP (the take that closes the web animation speed)
1. Commit slice 11 (scope-check first; commit cites WebView2Feedback#1172/#3070/#20 + Chrome-88 blog).
2. Run a normal recording with the animated widget on screen for ~20s. Expected:
- startup.log: `capture #1..#30 … took Nms` lines (tell us the per-capture cost) — **this decides
whether 30Hz stays or drops to ~20Hz**; also `FramePump stats:` should show no new dropped-frame
growth vs before (GC-churn check).
- Widget animation ~2-3× closer to real-time (10→30Hz).
- If it only became jittery instead of fast: check the FramePump dropped-frame counters.
3. Then, separately, the TRANSPARENCY verification (still open): re-point the first-capture dump/
alpha-log at a POST-PAINT capture of the real widget document (currently bound to about:blank) —
the change that makes "are transparent margins real?" answerable. Intended next change.
## Still Open
- **Web transparency UNVERIFIED** — the diagnostic instrument is blind (fires on about:blank); last
known visual state take-25 black box; user's "transparent pixels black" may be the opacity bug OR
the preview surface. Needs the post-paint instrument fix (above) then a take. Push gate reason #1.
- **Audio silence** — silent audio (−91dB full-length) in take-15; named-pipe audio delivers ~nothing.
Queued follow-up. Push gate reason #2.
- Pre-existing compositor pixel test failure (never in scope).
## Landmines
- testhost shares startup.log with app — filter by time when triaging.
- Locked DLLs: `taskkill //F //IM testhost.exe //IM ytLive.exe` before rebuild.
- Build/tests via `/mnt/c/Program Files/dotnet/dotnet.exe build …` / `… vstest
"C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.Tests\bin\Debug\net8.0-windows10.0.19041.0\ytLive.Tests.dll"`.
- Probing recordings: `/mnt/c/Program Files/Krita (x64)/bin/ffmpeg.exe` / `ffprobe.exe` (Windows-form paths).
- `tools/ticker`: constant ~+982ms offset on the FIRST line is cosmetic.
- WebView2Manager Ran into LOH churn worry: each capture allocs a BitmapImage raster (~8.3MB) that
is garbage per capture; at 30Hz that's ~250MB/s LOH on the UI thread → potential gen2 pauses
showing as FramePump dropped frames. Slice 12 candidate if the take's stats show it.