perf(ui): scope cooldown heartbeat to visible items

This commit is contained in:
Erik 2026-07-25 05:12:58 +02:00
parent f2a015be8e
commit 6b56f4bef2
5 changed files with 132 additions and 3 deletions

View file

@ -49,14 +49,22 @@ public sealed class ItemCooldownUiController
public void Tick()
{
double now = _currentTime();
_stepByItemId.Clear();
if (!_spellbook.HasCooldownEnchantments)
return;
double now = _currentTime();
// Retail updates cooldown visibility from UIElement_UIItem::DoHeartbeat,
// not while drawing. Query each represented item once so an expired
// registry node can be removed safely before the render walk.
// registry node can be removed safely before the render walk. A hidden
// retained subtree receives no heartbeat in retail, so only lists whose
// complete ancestry is visible participate.
foreach (UiItemList list in _lists)
{
if (!IsEffectivelyVisible(list))
continue;
for (int index = 0; index < list.GetNumUIItems(); index++)
{
UiItemSlot? cell = list.GetItem(index);
@ -83,6 +91,17 @@ public sealed class ItemCooldownUiController
}
}
internal static bool IsEffectivelyVisible(UiElement element)
{
ArgumentNullException.ThrowIfNull(element);
for (UiElement? current = element; current is not null; current = current.Parent)
{
if (!current.Visible)
return false;
}
return true;
}
internal int GetOverlayStep(uint itemId)
=> _stepByItemId.TryGetValue(itemId, out int step) ? step : 0;

View file

@ -156,6 +156,11 @@ public sealed class Spellbook
public int LearnedCount => _learnedSpells.Count;
public int ActiveCount => _activeById.Count;
public bool HasCooldownEnchantments =>
_enchantmentOrderByBucket.TryGetValue(
CooldownBucket,
out List<uint>? order)
&& order.Count != 0;
public bool Knows(uint spellId) => _learnedSpells.ContainsKey(spellId);