fix(web): add CropBounds to PasteKey — stale raster cache was corrupting transparency

This commit is contained in:
2026-09-08 13:28:42 -07:00
parent 5175c1c8c9
commit 081e4c1bf8
+8 -6
View File
@@ -19,11 +19,13 @@ public sealed class SceneCompositor
// chat/web/image layers are identical frame after frame. Same shape as OBS (the // 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 // 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. // 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 // Keyed by the SOURCE FRAME IDENTITY (array + Epoch + CropBounds) — producers hand
// immutable byte[], or recycled-ring arrays whose monotonic Epoch distinguishes // out fresh immutable byte[], or recycled-ring arrays whose monotonic Epoch distinguishes
// each generation (screen capture, take-11 fix), so "same array+epoch + same // each generation (screen capture, take-11 fix). CropBounds must be in the key because
// target rect" ⇒ same raster. Bounds-checked eviction clears wholesale. // the same frame (same Pixels/Epoch/Width/Height) can have different CropBounds as
private sealed record PasteKey(byte[] Pixels, long Epoch, int SrcW, int SrcH, int DstW, int DstH, bool Round, bool Mirror); // 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<PasteKey, VideoFrame> _pasteCache = new(); private static readonly Dictionary<PasteKey, VideoFrame> _pasteCache = new();
private static readonly Dictionary<SceneElement, VideoFrame> _elementRasters = new(ReferenceEqualityComparer.Instance); private static readonly Dictionary<SceneElement, VideoFrame> _elementRasters = new(ReferenceEqualityComparer.Instance);
private static readonly object _pasteGate = new(); private static readonly object _pasteGate = new();
@@ -286,7 +288,7 @@ public sealed class SceneCompositor
VideoFrame? raster = null; VideoFrame? raster = null;
lock (_pasteGate) 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); _pasteCache.TryGetValue(key, out raster);
} }