feat(scoreboard): unit 5 — tile selection and the cached strip presenter
ScoreboardTiles decides which of the eight facts appear and shapes their text (money uses the
session's dominant currency and flags a second one rather than converting; watch hours carry
'as of {date}' because Analytics is a daily aggregate). Tiles are OPT-IN, so 'None' really means the
layer draws nothing, and 'Default' is the six session facts.
ScoreboardPresenter owns the raster cache: ScoreboardRecorder.Changed fires only on a visible change,
so the strip rebuilds only then — a per-tick raster would be the compositor's most expensive layer
for identical pixels.
Guard: ScoreboardPresenterTests asserts the cache identity (same instance when nothing moved, a new
frame when a value moved, reuse when a recipient-side gift event arrives) and that an empty selection
returns no frame at all.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using ytLive.Models;
|
||||
|
||||
namespace ytLive.Services.Compositor;
|
||||
|
||||
/// <summary>Turns the session's tallies into the strip frame, and keeps the raster until a displayed
|
||||
/// value actually moves.
|
||||
///
|
||||
/// <para>The cache is the point: a strip re-rasterised every tick would be the most expensive layer
|
||||
/// the compositor carries, for pixels that did not change. `ScoreboardRecorder.Changed` only fires
|
||||
/// on a visible change, so the presenter rebuilds only then.</para></summary>
|
||||
public sealed class ScoreboardPresenter
|
||||
{
|
||||
private readonly ScoreboardRecorder _recorder;
|
||||
private VideoFrame? _frame;
|
||||
private string? _tilesFingerprint;
|
||||
private ScoreboardStyle _style;
|
||||
private ScoreboardTiles _tiles = ScoreboardTiles.None;
|
||||
|
||||
public ScoreboardPresenter(ScoreboardRecorder recorder, ScoreboardStyle style)
|
||||
{
|
||||
_recorder = recorder;
|
||||
_style = style;
|
||||
_recorder.Changed += Invalidate;
|
||||
}
|
||||
|
||||
/// <summary>The strip frame for the current tallies, or null when nothing is enabled/shown.</summary>
|
||||
public VideoFrame? Frame
|
||||
{
|
||||
get
|
||||
{
|
||||
var tiles = _tiles.Build(_recorder.State);
|
||||
var fingerprint = $"{_tiles.CacheKey}|{string.Join('|', tiles)}|{_style}";
|
||||
if (_frame != null && fingerprint == _tilesFingerprint) return _frame;
|
||||
|
||||
_tilesFingerprint = fingerprint;
|
||||
_frame = ScoreboardRenderer.Render(tiles, _style);
|
||||
return _frame;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTiles(ScoreboardTiles tiles)
|
||||
{
|
||||
_tiles = tiles;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void SetStyle(ScoreboardStyle style)
|
||||
{
|
||||
_style = style;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>Drops the cached raster so the next read rebuilds it.</summary>
|
||||
public void Invalidate()
|
||||
{
|
||||
_frame = null;
|
||||
_tilesFingerprint = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user