perf(capture): slice 8 — buffer ring + paste-cache Epoch; gen2 visibility (take-11 spikes)
Take 11 (c10ce06c) validated the off-UI architecture: typical frames land work ~10ms + wait ~6.8ms = 16.7 exactly on the deadline; 212/300 best yet. The ENTIRE remaining gap is periodic 35-65ms render spikes that WORSENED across the take (189 -> 147) — the signature of gen2 GC pauses. Biggest churner is structural: the screen capture minted a fresh ~8.3MB byte[] per DWM frame (~500MB/s of LOH), a producer OBS never does (it owns fixed surface pools). - ScreenCaptureFrameSource: 4-deep buffer ring with size-matched slots (a <=17ms consumer cannot be lapped at 60Hz) + reused downscale row scratch. - VideoFrame.Epoch: monotonic per producer frame. The paste cache keys on array IDENTITY, so recycled arrays MUST be distinguished — epoch joins the PasteKey. Producers handing fresh arrays leave it 0 (key unchanged effect). - Stats print 'gen2 +N' per 5s window: next take acquits or convicts GC without another guess (rule: prove the stage). - Test (the ONE): PasteCache_RecycledArrayWithNewEpoch_ReRasterizes_NotStaleHits — same array, new content, bumped epoch; fails on the old key by construction. 37/37 compositor/pump, clean build. - Next suspect if gen2 stays hot: the 10Hz WebView2 capture (full-canvas PNG decode + fresh arrays on the UI thread) — recorded, untouched. Creator audio ask queued in the same working session (+40% post-mix master gain before the -1dBFS limiter) lands as its own commit next.
This commit is contained in:
@@ -13,6 +13,13 @@ public sealed class VideoFrame
|
||||
public byte[] BgraPixels { get; }
|
||||
public int Stride => Width * 4;
|
||||
|
||||
/// <summary>Producer frame epoch — a monotonically increasing number stamped by
|
||||
/// sources that RECYCLE their pixel buffers (the screen-capture ring, take-11 spike
|
||||
/// fix). Consumers that key caches on buffer identity (the compositor's paste cache)
|
||||
/// MUST include it, or a recycled array false-hits with stale content. Producers
|
||||
/// that hand out fresh arrays per frame leave it 0 — identity alone is then enough.</summary>
|
||||
public long Epoch { get; init; }
|
||||
|
||||
/// <summary>Producer contract: every alpha byte in <see cref="BgraPixels"/> is 255
|
||||
/// (DWM capture surfaces and MediaCapture video carry no alpha — the OS fills 255).
|
||||
/// Lets the compositor take a straight-copy fast path for a full-canvas opaque
|
||||
|
||||
Reference in New Issue
Block a user