Skip to content

Commit a3f8e13

Browse files
committed
better change tracking, and don't mutate the cache key...
1 parent aaeecc2 commit a3f8e13

File tree

3 files changed

+58
-33
lines changed

3 files changed

+58
-33
lines changed

Source/DynamicProperties/CompiledProps.cs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal class MpbCacheEntry
99
{
1010
internal readonly MaterialPropertyBlock Mpb = new();
1111
internal readonly Dictionary<Props, List<int>> ManagedIds = [];
12+
internal bool Changed = true;
1213
}
1314

1415
internal class CompiledProps
@@ -17,65 +18,82 @@ internal class CompiledProps
1718
SortedSet<Props>.CreateSetComparer();
1819

1920
// FIXME: clear old entries...
20-
private static readonly Dictionary<SortedSet<Props>, MpbCacheEntry> mpbCache =
21+
private static readonly Dictionary<SortedSet<Props>, MpbCacheEntry> MpbCache =
2122
new(CascadeKeyComparer);
2223

23-
internal static void Clear() => mpbCache.Clear();
24+
internal static void Clear() => MpbCache.Clear();
2425

2526
private readonly SortedSet<Props> cascade = new(Props.PriorityComparer);
2627

2728
internal bool Add(Props props)
2829
{
29-
cachedMpb = null;
30-
return cascade.Add(props);
30+
var added = cascade.Add(props);
31+
if (added) cacheEntry = null;
32+
return added;
3133
}
3234

33-
private MaterialPropertyBlock? cachedMpb = null;
35+
private MpbCacheEntry? cacheEntry = null;
3436

3537
// Should this be a hashset?
36-
private static readonly List<Props> _dirtyProps = [];
38+
private static readonly List<Props> _changedProps = [];
3739

38-
internal static void UpdateDirtyProps()
40+
internal static void RefreshChangedProps()
3941
{
40-
foreach (var (cascade, cache) in mpbCache) {
42+
foreach (var (cascade, cache) in MpbCache) {
43+
cache.Changed = false;
4144
foreach (var props in cascade) {
42-
if (!props.Dirty) continue;
43-
_dirtyProps.Add(props);
45+
if (!props.Changed) continue;
46+
cache.Changed = true;
47+
_changedProps.Add(props);
4448
foreach (var managedId in cache.ManagedIds[props]) {
4549
props.Write(managedId, cache.Mpb);
4650
}
4751
}
4852
}
4953

50-
foreach (var props in _dirtyProps) props.Dirty = false;
51-
_dirtyProps.Clear();
54+
foreach (var props in _changedProps) props.Changed = false;
55+
_changedProps.Clear();
5256
}
5357

54-
internal MaterialPropertyBlock Get()
58+
private static MpbCacheEntry BuildCacheEntry(SortedSet<Props> cascade)
5559
{
56-
if (cachedMpb != null) return cachedMpb;
60+
var clonedCascade = new SortedSet<Props>(cascade, Props.PriorityComparer);
61+
var entry = MpbCache[clonedCascade] = new MpbCacheEntry();
5762

58-
if (!mpbCache.TryGetValue(cascade, out var cacheEntry)) {
59-
mpbCache[cascade] = cacheEntry = new MpbCacheEntry();
63+
Dictionary<int, Props> idManagers = [];
64+
foreach (var props in cascade) {
65+
foreach (var id in props.ManagedIds) {
66+
idManagers[id] = props;
67+
}
68+
}
6069

61-
Dictionary<int, Props> idManagers = [];
62-
foreach (var props in cascade) {
63-
foreach (var id in props.ManagedIds) {
64-
idManagers[id] = props;
65-
}
70+
foreach (var (id, props) in idManagers) {
71+
if (!entry.ManagedIds.TryGetValue(props, out var ids)) {
72+
entry.ManagedIds[props] = ids = [];
6673
}
6774

68-
foreach (var (id, props) in idManagers) {
69-
if (!cacheEntry.ManagedIds.TryGetValue(props, out var ids)) {
70-
cacheEntry.ManagedIds[props] = ids = [];
71-
}
75+
ids.Add(id);
76+
props.Write(id, entry.Mpb);
77+
}
7278

73-
ids.Add(id);
74-
props.Write(id, cacheEntry.Mpb);
75-
}
79+
return entry;
80+
}
81+
82+
internal bool GetIfChanged(out MaterialPropertyBlock? mpb)
83+
{
84+
if (cacheEntry != null) {
85+
mpb = cacheEntry.Changed ? cacheEntry.Mpb : null;
86+
return cacheEntry.Changed;
87+
}
88+
89+
if (!MpbCache.TryGetValue(cascade, out cacheEntry)) {
90+
Debug.Log("cache not hit");
91+
cacheEntry = BuildCacheEntry(cascade);
92+
} else {
93+
Debug.Log("cache hit!");
7694
}
7795

78-
cachedMpb = cacheEntry.Mpb;
79-
return cachedMpb;
96+
mpb = cacheEntry.Mpb;
97+
return true;
8098
}
8199
}

Source/DynamicProperties/MaterialPropertyManager.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,17 @@ public void Set(Renderer renderer, Props props)
4747

4848
private void Refresh()
4949
{
50-
CompiledProps.UpdateDirtyProps();
50+
CompiledProps.RefreshChangedProps();
5151

5252
foreach (var (renderer, compiledProps) in compiledProperties) {
5353
if (renderer == null) {
5454
_deadRenderers.Add(renderer);
5555
continue;
5656
}
5757

58-
renderer.SetPropertyBlock(compiledProps.Get());
58+
if (compiledProps.GetIfChanged(out var mpb)) {
59+
renderer.SetPropertyBlock(mpb);
60+
}
5961
}
6062

6163
foreach (var dead in _deadRenderers) {

Source/DynamicProperties/Props.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public sealed class Props(int priority)
2020

2121
private readonly Dictionary<int, Prop> _props = [];
2222

23-
internal bool Dirty = false;
23+
internal bool Changed = false;
2424

2525
private static uint _idCounter = 0;
2626
private static uint _nextId() => _idCounter++;
@@ -44,17 +44,22 @@ private void _internalSet<T>(int id, T value)
4444

4545
if (!_props.TryGetValue(id, out var prop)) {
4646
_props[id] = new Prop<T>(value);
47+
Changed = true;
4748
return;
4849
}
4950

5051
if (prop is not Prop<T> propT) {
5152
MaterialPropertyManager.Instance.LogWarning(
5253
$"property {id} has mismatched type; overwriting with {typeof(T).Name}!");
5354
_props[id] = new Prop<T>(value);
55+
Changed = true;
5456
return;
5557
}
5658

59+
if (EqualityComparer<T>.Default.Equals(value, propT.Value)) return;
60+
5761
propT.Value = value;
62+
Changed = true;
5863
}
5964

6065
[MethodImpl(MethodImplOptions.AggressiveInlining)]

0 commit comments

Comments
 (0)