perf(compositor): slice 5 — paste cache for non-opaque layers (take-7/8 data)
The stamped build settled what slices 3-4 could not: chat cache works (resolve ~0.0ms) but render stayed 26-27ms -> 124-135/300. The cost was the compositor re-rasterizing EVERY layer every tick: this Live scene re-samples chat (159k) + web widget (271k) + image (95k) + cam (156k) ~ 680k px @ ~38ns — for layers whose pixels do not change between chat/web/cam updates. OBS shape: cache the surface, paste per tick. BlitCachedLayer rasterizes a non-opaque layer ONCE into an element-space, transparent-based frame keyed by (source-array identity, src W/H, ceil'd dst rect, round, mirror), then pastes: integer position, row alpha-blend, opacity applied at paste. Producers hand out fresh immutable arrays -> array-identity keys cannot serve stale content; dict bounded (48, clears whole). Drag/opacity live in paste params, not keys, so editing stops triggering resamples too. Opaque backdrop keeps the memcpy path; the webcam keeps the direct path via its IsOpaque flag (revisit if take 9 is borderline). ONE integration test: PasteCache_RepeatRender_IsByteIdentical_And_ContentChange- Propagates (byte-exact raster-vs-paste incl. round-clip margins, new-array propagation); existing pixel suite guards sampler semantics. 85/85 across compositor/pump/chat/capture/session classes, clean build 0 warnings. Also: BuildStampTests.cs was written last commit but never staged — its own scope-check slip, added here (the run had used the on-disk file; tracked now). Docs same commit: ai.md slice 5 + stale 'general path only 130k' claim corrected, TASKS.md take-9 gate, MyMistakes recipe (prove the stage; a fix that doesn't move the stat wasn't the bottleneck), HANDOFF. take 9 expectation: 300/300, render <= ~8ms -> saga closes, Unit B starts.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using Xunit;
|
||||
using ytLive.Helpers;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Build attribution (2026-09-04, after takes 3–6 could not be pinned to a binary):
|
||||
/// every build stamps a fresh GUID (ytLive.csproj GenerateBuildStamp), shown as the
|
||||
/// wordmark superscript and logged at startup. The unit pins the stamp shape; the
|
||||
/// RealApp test proves the wordmark ACTUALLY displays it (XAML wiring, live visual
|
||||
/// tree) — a stamp nobody can see would not have ended the guessing.
|
||||
/// </summary>
|
||||
public sealed class BuildStampTests
|
||||
{
|
||||
[Fact]
|
||||
public void Stamp_IsAFreshHexIdAndParsableTimestamp()
|
||||
{
|
||||
Assert.Matches(new Regex("^[0-9a-f]{8}$"), BuildStamp.Id);
|
||||
Assert.True(DateTime.TryParseExact(BuildStamp.BuiltLocal, "yyyy-MM-dd HH:mm:ss",
|
||||
null, System.Globalization.DateTimeStyles.None, out var built));
|
||||
Assert.True(built <= DateTime.Now);
|
||||
Assert.True(built.Date >= new DateTime(2026, 9, 4));
|
||||
}
|
||||
}
|
||||
|
||||
[Collection("RealApp")]
|
||||
public sealed class BuildStampDisplayTests
|
||||
{
|
||||
private readonly RealAppHost _app;
|
||||
|
||||
public BuildStampDisplayTests(RealAppHost app) => _app = app;
|
||||
|
||||
[Fact]
|
||||
public void Wordmark_Shows_The_Build_Stamp_As_Superscript()
|
||||
{
|
||||
_app.Run(() =>
|
||||
{
|
||||
var window = new MainWindow();
|
||||
try
|
||||
{
|
||||
// TopBar is its own namescope (UserControl) — FindName from the
|
||||
// control, never from the window (MyMistakes: namescoped FindName).
|
||||
var topBar = (UserControl)window.FindName("TopBar")!;
|
||||
var stamp = (Run)topBar.FindName("BuildIdStamp")!;
|
||||
Assert.Equal(BuildStamp.Id, stamp.Text);
|
||||
Assert.Equal(BaselineAlignment.Superscript, stamp.BaselineAlignment);
|
||||
}
|
||||
finally
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user