432adfdaef
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).
30 lines
1.2 KiB
C#
30 lines
1.2 KiB
C#
namespace ytLive.Services;
|
|
|
|
/// <summary>
|
|
/// A normalized CPU frame (32bpp BGRA, tightly packed). The capture path hands
|
|
/// these to the UI thread, which copies them into the shared WriteableBitmap.
|
|
/// Deliberately the only pixel type the rest of the app knows about — every
|
|
/// future capture source (screen, background-removed webcam) feeds the same seam.
|
|
/// </summary>
|
|
public sealed class VideoFrame
|
|
{
|
|
public int Width { get; }
|
|
public int Height { get; }
|
|
public byte[] BgraPixels { get; }
|
|
public int Stride => Width * 4;
|
|
|
|
/// <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
|
|
/// layer instead of 2M per-pixel blends. NEVER set it for paths whose pixels can
|
|
/// be transparent (static art, web overlays, chat, the social-bar strip).</summary>
|
|
public bool IsOpaque { get; init; }
|
|
|
|
public VideoFrame(int width, int height, byte[] bgraPixels)
|
|
{
|
|
Width = width;
|
|
Height = height;
|
|
BgraPixels = bgraPixels;
|
|
}
|
|
}
|