perf(runtime): remove measured steady allocations

Eliminate boxed production surface-override enumeration, retain vital modifier projections until the enchantment registry mutates, and measure DAT font widths without allocating a captured delegate. Preserve exact hashes, spell stacking, and glyph advances with warmed zero-allocation tests.

Validated by the focused rendering, UI, and spell suites, a zero-error Release build, and 8,409 passing Release tests with five pre-existing skips.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-25 17:50:30 +02:00
parent 574a6c454f
commit feb80a67d9
6 changed files with 188 additions and 14 deletions

View file

@ -848,18 +848,33 @@ internal sealed class CurrentRenderSceneOracle :
ulong xorHigh = 0; ulong xorHigh = 0;
ulong sumLow = 0; ulong sumLow = 0;
ulong sumHigh = 0; ulong sumHigh = 0;
foreach ((uint surfaceId, uint replacementId) in overrides) if (overrides is Dictionary<uint, uint> dictionary)
{ {
StableRenderHash128 pair = StableRenderHash128.Create(); // Production hydration owns Dictionary instances. Enumerating
pair.Add(surfaceId); // through IReadOnlyDictionary boxes Dictionary.Enumerator for
pair.Add(replacementId); // every live mesh on both projection-sync phases.
RenderSceneHash128 value = pair.Finish(); foreach (KeyValuePair<uint, uint> pair in dictionary)
xorLow ^= value.Low;
xorHigh ^= value.High;
unchecked
{ {
sumLow += value.Low; Accumulate(
sumHigh += value.High; pair.Key,
pair.Value,
ref xorLow,
ref xorHigh,
ref sumLow,
ref sumHigh);
}
}
else
{
foreach ((uint surfaceId, uint replacementId) in overrides)
{
Accumulate(
surfaceId,
replacementId,
ref xorLow,
ref xorHigh,
ref sumLow,
ref sumHigh);
} }
} }
@ -870,6 +885,27 @@ internal sealed class CurrentRenderSceneOracle :
geometry.Add(sumLow); geometry.Add(sumLow);
geometry.Add(sumHigh); geometry.Add(sumHigh);
return geometry.Finish(); return geometry.Finish();
static void Accumulate(
uint surfaceId,
uint replacementId,
ref ulong xorLow,
ref ulong xorHigh,
ref ulong sumLow,
ref ulong sumHigh)
{
StableRenderHash128 pairHash = StableRenderHash128.Create();
pairHash.Add(surfaceId);
pairHash.Add(replacementId);
RenderSceneHash128 value = pairHash.Finish();
xorLow ^= value.Low;
xorHigh ^= value.High;
unchecked
{
sumLow += value.Low;
sumHigh += value.High;
}
}
} }
internal static RenderSceneHash128 FingerprintSelectionGeometry( internal static RenderSceneHash128 FingerprintSelectionGeometry(

View file

@ -62,7 +62,7 @@ public sealed class UiDatFont
private readonly Dictionary<char, FontCharDesc> _glyphs; private readonly Dictionary<char, FontCharDesc> _glyphs;
private UiDatFont( internal UiDatFont(
uint fgTex, int fgW, int fgH, uint fgTex, int fgW, int fgH,
uint bgTex, int bgW, int bgH, uint bgTex, int bgW, int bgH,
float lineHeight, float baselineOffset, float lineHeight, float baselineOffset,
@ -131,7 +131,18 @@ public sealed class UiDatFont
/// glyph's retail advance. Characters not in the font contribute nothing. /// glyph's retail advance. Characters not in the font contribute nothing.
/// </summary> /// </summary>
public float MeasureWidth(string text) public float MeasureWidth(string text)
=> MeasureWidth(text, c => _glyphs.TryGetValue(c, out var g) ? g : null); {
if (string.IsNullOrEmpty(text)) return 0f;
float width = 0f;
for (int index = 0; index < text.Length; index++)
{
if (_glyphs.TryGetValue(text[index], out FontCharDesc? glyph))
width += GlyphAdvance(glyph);
}
return width;
}
/// <summary> /// <summary>
/// Pure pen-advance summation seam: total width of <paramref name="text"/> /// Pure pen-advance summation seam: total width of <paramref name="text"/>

View file

@ -23,6 +23,7 @@ public sealed class Spellbook
private readonly Dictionary<uint, float> _learnedSpells = new(); private readonly Dictionary<uint, float> _learnedSpells = new();
private readonly Dictionary<uint, ActiveEnchantmentRecord> _activeById = new(); private readonly Dictionary<uint, ActiveEnchantmentRecord> _activeById = new();
private readonly Dictionary<uint, List<uint>> _enchantmentOrderByBucket = new(); private readonly Dictionary<uint, List<uint>> _enchantmentOrderByBucket = new();
private readonly Dictionary<uint, EnchantmentMath.VitalMod> _vitalModCache = new();
private readonly List<uint>[] _favoriteSpells = Enumerable.Range(0, 8) private readonly List<uint>[] _favoriteSpells = Enumerable.Range(0, 8)
.Select(_ => new List<uint>()).ToArray(); .Select(_ => new List<uint>()).ToArray();
private readonly Dictionary<uint, uint> _desiredComponents = new(); private readonly Dictionary<uint, uint> _desiredComponents = new();
@ -86,8 +87,20 @@ public sealed class Spellbook
/// <see cref="EnchantmentMath.VitalMod.Identity"/> when no buffs /// <see cref="EnchantmentMath.VitalMod.Identity"/> when no buffs
/// apply (or when no SpellTable was wired). /// apply (or when no SpellTable was wired).
/// </summary> /// </summary>
public EnchantmentMath.VitalMod GetVitalMod(uint statKey) => public EnchantmentMath.VitalMod GetVitalMod(uint statKey)
EnchantmentMath.GetMod(ActiveEnchantments, _table, statKey); {
if (_vitalModCache.TryGetValue(
statKey,
out EnchantmentMath.VitalMod cached))
{
return cached;
}
EnchantmentMath.VitalMod calculated =
EnchantmentMath.GetMod(ActiveEnchantments, _table, statKey);
_vitalModCache.Add(statKey, calculated);
return calculated;
}
/// <summary>Fires when a spell is added to the player's spellbook.</summary> /// <summary>Fires when a spell is added to the player's spellbook.</summary>
public event Action<uint>? SpellLearned; public event Action<uint>? SpellLearned;
@ -342,6 +355,7 @@ public sealed class Spellbook
_activeById.Clear(); _activeById.Clear();
_enchantmentOrderByBucket.Clear(); _enchantmentOrderByBucket.Clear();
_vitalModCache.Clear();
foreach (ActiveEnchantmentRecord enchantment in enchantments) foreach (ActiveEnchantmentRecord enchantment in enchantments)
UpsertManifestEnchantment(enchantment); UpsertManifestEnchantment(enchantment);
@ -401,6 +415,7 @@ public sealed class Spellbook
_learnedSpells.Clear(); _learnedSpells.Clear();
_activeById.Clear(); _activeById.Clear();
_enchantmentOrderByBucket.Clear(); _enchantmentOrderByBucket.Clear();
_vitalModCache.Clear();
foreach (List<uint> tab in _favoriteSpells) tab.Clear(); foreach (List<uint> tab in _favoriteSpells) tab.Clear();
_desiredComponents.Clear(); _desiredComponents.Clear();
_spellbookFilters = 0x3FFFu; _spellbookFilters = 0x3FFFu;
@ -418,6 +433,7 @@ public sealed class Spellbook
private void NotifyEnchantmentsChanged() private void NotifyEnchantmentsChanged()
{ {
_vitalModCache.Clear();
EnchantmentsChanged?.Invoke(); EnchantmentsChanged?.Invoke();
StateChanged?.Invoke(); StateChanged?.Invoke();
} }

View file

@ -472,6 +472,33 @@ public sealed class CurrentRenderSceneOracleTests
CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint(null)); CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint(null));
} }
[Fact]
public void SurfaceOverrideFingerprint_DictionaryHotPathAllocatesNothing()
{
var overrides = new Dictionary<uint, uint>
{
[0x0800_0001] = 0x0500_0011,
[0x0800_0002] = 0x0500_0012,
[0x0800_0003] = 0x0500_0013,
};
RenderSceneHash128 expected =
CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint(
overrides);
RenderSceneHash128 actual = default;
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 10_000; iteration++)
{
actual =
CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint(
overrides);
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(expected, actual);
Assert.Equal(0, allocated);
}
private static CurrentRenderProjectionFingerprint CaptureSingle( private static CurrentRenderProjectionFingerprint CaptureSingle(
WorldEntity entity, WorldEntity entity,
uint landblockId, uint landblockId,

View file

@ -81,4 +81,35 @@ public class UiDatFontTests
Assert.Equal(0f, UiDatFont.MeasureWidth("", Lookup)); Assert.Equal(0f, UiDatFont.MeasureWidth("", Lookup));
Assert.Equal(0f, UiDatFont.MeasureWidth(null, Lookup)); Assert.Equal(0f, UiDatFont.MeasureWidth(null, Lookup));
} }
[Fact]
public void InstanceMeasureWidth_ReusesGlyphTableWithoutAllocating()
{
var glyphs = new Dictionary<char, FontCharDesc>
{
['A'] = Glyph('A', width: 8, before: 1, after: 2),
['B'] = Glyph('B', width: 7, before: 1, after: 1),
};
var font = new UiDatFont(
fgTex: 0,
fgW: 0,
fgH: 0,
bgTex: 0,
bgW: 0,
bgH: 0,
lineHeight: 16f,
baselineOffset: 12f,
glyphs);
const string Text = "ABBA";
float expected = font.MeasureWidth(Text);
float actual = 0f;
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 10_000; iteration++)
actual = font.MeasureWidth(Text);
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(expected, actual);
Assert.Equal(0, allocated);
}
} }

View file

@ -90,6 +90,59 @@ public sealed class SpellbookTests
Assert.Equal(2, book.ActiveCount); Assert.Equal(2, book.ActiveCount);
} }
[Fact]
public void GetVitalMod_ReusesCachedValueUntilEnchantmentsChange()
{
SpellTable table = SpellTable.Create(
[
TestSpell(42u) with { Family = 100u },
]);
var book = new Spellbook(table);
book.OnEnchantmentAdded(new ActiveEnchantmentRecord(
SpellId: 42u,
LayerId: 7u,
Duration: 300f,
CasterGuid: 0u,
StatModKey: EnchantmentMath.StatKey.MaxHealth,
StatModValue: 1.5f,
Bucket: 1u));
Assert.Equal(
1.5f,
book.GetVitalMod(EnchantmentMath.StatKey.MaxHealth).Multiplier);
EnchantmentMath.VitalMod actual = default;
// Cross the tiered-PGO call threshold before measuring the retained
// lookup itself. Otherwise a full parallel solution run can attribute
// runtime tier-promotion bookkeeping to this thread.
for (int iteration = 0; iteration < 10_000; iteration++)
actual = book.GetVitalMod(EnchantmentMath.StatKey.MaxHealth);
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 10_000; iteration++)
{
actual =
book.GetVitalMod(EnchantmentMath.StatKey.MaxHealth);
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(1.5f, actual.Multiplier);
Assert.Equal(0, allocated);
book.OnEnchantmentAdded(new ActiveEnchantmentRecord(
SpellId: 42u,
LayerId: 7u,
Duration: 300f,
CasterGuid: 0u,
StatModKey: EnchantmentMath.StatKey.MaxHealth,
StatModValue: 1.25f,
Bucket: 1u));
Assert.Equal(
1.25f,
book.GetVitalMod(EnchantmentMath.StatKey.MaxHealth).Multiplier);
}
[Fact] [Fact]
public void LiveEnchantments_NewIdentityInsertsAtRetailListHead() public void LiveEnchantments_NewIdentityInsertsAtRetailListHead()
{ {