From 081e4c1bf823fa9a172ad89737aa7c970f623cfe Mon Sep 17 00:00:00 2001 From: gramps Date: Tue, 8 Sep 2026 13:28:42 -0700 Subject: [PATCH] =?UTF-8?q?fix(web):=20add=20CropBounds=20to=20PasteKey=20?= =?UTF-8?q?=E2=80=94=20stale=20raster=20cache=20was=20corrupting=20transpa?= =?UTF-8?q?rency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Services/Compositor/SceneCompositor.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Services/Compositor/SceneCompositor.cs b/Services/Compositor/SceneCompositor.cs index 711ec53..06f6dab 100644 --- a/Services/Compositor/SceneCompositor.cs +++ b/Services/Compositor/SceneCompositor.cs @@ -19,11 +19,13 @@ public sealed class SceneCompositor // chat/web/image layers are identical frame after frame. Same shape as OBS (the // source surface is cached, the compositor blits): a scaled/round-clipped/mirrored // layer rasterizes once and is pasted (row-copy/blend) on every later tick. - // Keyed by the SOURCE FRAME IDENTITY (array + Epoch) — producers hand out fresh - // immutable byte[], or recycled-ring arrays whose monotonic Epoch distinguishes - // each generation (screen capture, take-11 fix), so "same array+epoch + same - // target rect" ⇒ same raster. Bounds-checked eviction clears wholesale. - private sealed record PasteKey(byte[] Pixels, long Epoch, int SrcW, int SrcH, int DstW, int DstH, bool Round, bool Mirror); + // Keyed by the SOURCE FRAME IDENTITY (array + Epoch + CropBounds) — producers hand + // out fresh immutable byte[], or recycled-ring arrays whose monotonic Epoch distinguishes + // each generation (screen capture, take-11 fix). CropBounds must be in the key because + // the same frame (same Pixels/Epoch/Width/Height) can have different CropBounds as + // the element is resized — a stale cached raster with wrong crop pixels would corrupt + // the output. Bounds-checked eviction clears wholesale. + private sealed record PasteKey(byte[] Pixels, long Epoch, int SrcW, int SrcH, int DstW, int DstH, bool Round, bool Mirror, (int X, int Y, int W, int H)? CropBounds); private static readonly Dictionary _pasteCache = new(); private static readonly Dictionary _elementRasters = new(ReferenceEqualityComparer.Instance); private static readonly object _pasteGate = new(); @@ -286,7 +288,7 @@ public sealed class SceneCompositor VideoFrame? raster = null; lock (_pasteGate) { - key = new PasteKey(frame.BgraPixels, frame.Epoch, frame.Width, frame.Height, dw, dh, isRound, mirror); + key = new PasteKey(frame.BgraPixels, frame.Epoch, frame.Width, frame.Height, dw, dh, isRound, mirror, frame.CropBounds); _pasteCache.TryGetValue(key, out raster); }