The ~30Hz CapturePreviewAsync PNG poll capped real cadence at ~20Hz
(35–165ms full-HD encode+decode), so a 60fps widget still juddered at ~1/6
speed. Replaced polling with frame-driven capture of the composition
controller's root visual — the mechanism WebView2CompositionControl and
Flutter's webview_windows use (graphics_context.cc captures the root
surface_ visual via CreateGraphicsCaptureItemFromVisual; reference:
github.com/microsoft/Windows.UI.Composition.WinUI / flutter-internal
webview_windows). Frames now arrive at the renderer's own pace; capture
memory is epoch'd ring reuse + one crop-sized shared WriteableBitmap.
New Services/WebCaptureFrameSource.cs owns GraphicsCaptureItem + free-
threaded Direct3D11CaptureFramePool + session (Straight alpha readback,
per-frame FindContentBounds → CropBounds). WebView2Manager reworked around
per-session composition controllers + one UI-thread Compositor created via
the CoreMessaging CreateDispatcherQueueController P/Invoke (the 19041
projection lacks CreateOnCurrentThread); internal seam ctor
(Dispatcher, Func<string,IScreenCaptureSource>?) for hermetic tests.
CaptureScheduler.cs deleted; the three SetCaptureInterval cadence hooks
removed; InitWebView2() moved from MainWindow ctor to Loaded (a parent
HWND must exist for the composition controller); the hidden WebViewHostPanel
overlay deleted. TransparentBackgroundScript unchanged.
Tests: WebView2ManagerTests reworked — 4 control-size + scheduler tests
dropped, FindContentBounds tests moved to WebCaptureFrameSource, ONE
integration test (Frames_PublishCroppedPreview_And_CoalesceToLatest_CarryingCropBounds)
drives the seam with a FakeWebSource + background-STA DispatcherPump.
Suite 293/293, 0 warnings.
NOTE: composition path NOT yet verified on a device — the take is the
next step. Web work committed locally only (no push per standing rule).
Docs same-commit: ai.md Slice 14 + supersede marker on Slice 11, HANDOFF,
MyMistakes (CoreMessaging DQ + namespace-landmine recipe), TASK 17,
Controls/ViewModels/Services indexes.
User launch (2026-09-01 19:07) NRE'd in MainViewModel ctor:
1. SceneGraph (TASK 31) was 'null!'-declared, assigned mid-ctor, but Scenes is touched
~120 lines earlier — field-initialized now.
2. LeftPanel extraction (c9fd1bd) moved StaticResource users (EyeButton/EyeIconStyle)
into a UserControl while the styles stayed window-scope — invisible at parse time;
moved to Themes/Controls.xaml (app scope, the existing rule). Full audit: these were
the only two offenders (grep of Controls/*.xaml StaticResource keys vs app dictionary).
3. RoundClipInteractionTests — the second 'known failure' the map never explained: it
was two stale-test layers (window.FindName across the new UserControl namescope +
VisualTreeHelper.HitTest, which returned the IsHitTestVisible=False WebViewHostPanel
overlay for EVERY point; UIElement.InputHitTest — the real input pipeline — shows the
corner IS grabbable in both Traditional and Round). Test fixed, no product bug.
Verified: clean rebuild 0 warnings; app boots (log shows full MainWindow loaded; user
clicked + closed, zero new exceptions; real DB Webcam row = the genuine C920, untouched);
RoundClip + 6 RealApp classes pass natively per-class.
Docs: ai.md known-failure note → 246/247 (audio only); TASK 31 verification paragraph
corrected ('cannot run headless' overstated — per-class Windows-host vstest runs them);
MyMistakes: InputHitTest-vs-VTH recipe + namescope/app-style + shared-log facts.
Spin-guard citations: WPF Visual Tree Overview (InputHitTest vs VisualTreeHelper hit
semantics) + XAML namescope docs, learn.microsoft.com.