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
This commit is contained in:
@@ -214,9 +214,41 @@ Both halves were solved by OBS/libyuv long ago; do not re-derive:
|
||||
that HOLDS submitted frames now must snapshot them (`Clone`) once the producer
|
||||
legitimately recycles buffers — mirror the real consumer's copy semantics in the fake.
|
||||
|
||||
---
|
||||
|
||||
|
||||
### A web widget captured at 10Hz inside a 60fps recording plays at ~1/6 speed
|
||||
|
||||
Derived 2026-09-10 (the "widget animation too slow" report). The recording is 60fps and the
|
||||
WebView2 capture loop was a blind 100ms `DispatcherTimer` = 10Hz — each captured web frame gets
|
||||
repeated ~6× in the file, so whatever the page animates at, the OUTPUT is capped at the *capture*
|
||||
cadence, not the page's. Two stacked throttles, both real:
|
||||
|
||||
1. **The capture rate is the hard ceiling.** web-layer motion in the recording can never beat
|
||||
`CapturePreviewAsync` frequency. But you can't just raise the timer: full-HD PNG capture costs
|
||||
~10-30ms (WebView2Feedback#20: "CapturePreviewAsync … produces PNG/JPG and is very slow"), so
|
||||
concurrent captures stack CPU AND can publish stale-after-fresh. The mandatory shape is a
|
||||
**latest-wins drop**: at most one capture in flight per session; a tick during the in-flight
|
||||
window is DROPPED, never queued; effective cadence = max(interval, capture duration). Do this
|
||||
before anyone touches the interval constant.
|
||||
2. **Chromium throttles hidden pages.** An off-screen WebView2 (we place it at (-5000,-5000)) is a
|
||||
hidden page the moment the host window is unfocused or covered: `requestAnimationFrame` parks and
|
||||
JS timers clamp to ~1s (WebView2Feedback#1172 — background-throttled rAF; #3070 — a WebView2 with
|
||||
`Visibility.Collapsed` slows timers to 1s; Chrome-88 blog — heavy timer throttling of hidden tabs).
|
||||
There is NO supported per-page opt-out (#5250 still open). The embedder answer is browser args on
|
||||
the `CoreWebView2EnvironmentOptions` created BEFORE `EnsureCoreWebView2Async`:
|
||||
`--disable-backgrounding-occluded-windows --disable-renderer-backgrounding
|
||||
--disable-features=CalculateNativeWinOcclusion` (the Electron/Streamlabs-class fix for
|
||||
occluded-window animation throttling). Share ONE environment across sessions (one browser process).
|
||||
3. **Measure before picking the cadence.** Log the first ~30 captures' elapsed ms on the first
|
||||
recording run; PNG encode + WPF decode of 1920×1080 is the per-capture cost that decides whether
|
||||
~30Hz is affordable or it must drop to ~20Hz. The FramePump drops frames (never time-lapses) if
|
||||
UI-thread GC churn starves it, so cost shows up as dropped-frame stats — read them.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Splitting a large file into partials — NEVER `awk … > SRC` while awking SRC
|
||||
|
||||
(2026-08-31, Commit D) Tried to split `SocialsDialogViewModel.cs` in one line:
|
||||
|
||||
Reference in New Issue
Block a user