Skip to content

Commit c47e108

Browse files
committed
feat(BAG): process-wide SignatureCache so multiple controls rendering the same geometry share one bake's signature table
1 parent c62f787 commit c47e108

1 file changed

Lines changed: 60 additions & 0 deletions

File tree

src/Combobulate/Rendering/SignatureBake.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,37 @@ public sealed class Signature
5656
public required string Key { get; init; }
5757
}
5858

59+
/// <summary>
60+
/// Process-wide signature cache. Painter-sort signatures depend only
61+
/// on the input geometry + sort algorithm + camera-distance + cull-margin
62+
/// — not on the AST, axes' ScalarNode identities, or the consumer's
63+
/// host control. So multiple Combobulate instances rendering the same
64+
/// model (e.g. a grid of book thumbnails sharing one cached
65+
/// <see cref="ObjGeometry"/>) can reuse a single bake's signature
66+
/// table; only materialisation of the per-cell sprite trees is
67+
/// per-instance work.
68+
///
69+
/// <para>Keyed by reference-identity on the geometry, plus the
70+
/// algorithm and float parameters. Geometry caching lives in
71+
/// <see cref="ObjCache"/>; same parsed model always yields the same
72+
/// <see cref="ObjGeometry"/> instance, so reference-equality is the
73+
/// right invariant here.</para>
74+
/// </summary>
75+
private static readonly System.Collections.Generic.Dictionary<CacheKey, Signature[]> s_cache = new();
76+
private static readonly object s_cacheLock = new();
77+
78+
private readonly record struct CacheKey(ObjGeometry Geom, SortAlgorithm Sort, int CameraDistanceBits, int CullMarginCosBits);
79+
80+
/// <summary>Number of distinct cached signature tables; for diagnostics.</summary>
81+
public static int CacheCount { get { lock (s_cacheLock) return s_cache.Count; } }
82+
83+
/// <summary>Drop all cached signature tables. For tests and explicit
84+
/// invalidation when a sorter implementation changes.</summary>
85+
public static void ClearCache()
86+
{
87+
lock (s_cacheLock) s_cache.Clear();
88+
}
89+
5990
/// <summary>Run the bake.</summary>
6091
public static Signature[] Bake(
6192
Matrix4x4Node transformNode,
@@ -65,6 +96,35 @@ public static Signature[] Bake(
6596
float cameraDistance,
6697
float cullMarginCos,
6798
CancellationToken ct)
99+
{
100+
var key = new CacheKey(geometry, sortAlgorithm,
101+
BitConverter.SingleToInt32Bits(cameraDistance),
102+
BitConverter.SingleToInt32Bits(cullMarginCos));
103+
lock (s_cacheLock)
104+
{
105+
if (s_cache.TryGetValue(key, out var cached)) return cached;
106+
}
107+
108+
var fresh = BakeInternal(transformNode, axes, geometry, sortAlgorithm, cameraDistance, cullMarginCos, ct);
109+
110+
lock (s_cacheLock)
111+
{
112+
// Two threads may race a miss; the second writer's insert is
113+
// a harmless overwrite (signatures are deterministic for a
114+
// given geometry).
115+
s_cache[key] = fresh;
116+
}
117+
return fresh;
118+
}
119+
120+
private static Signature[] BakeInternal(
121+
Matrix4x4Node transformNode,
122+
TransformAnimationAxis[] axes,
123+
ObjGeometry geometry,
124+
SortAlgorithm sortAlgorithm,
125+
float cameraDistance,
126+
float cullMarginCos,
127+
CancellationToken ct)
68128
{
69129
if (axes is null || axes.Length == 0)
70130
throw new ArgumentException("At least one axis required.", nameof(axes));

0 commit comments

Comments
 (0)