performance: tooltip caching
This commit is contained in:
@@ -172,6 +172,12 @@ function Main:SetupSlashCommands()
|
|||||||
addon:Print("Category Cache: %d hits, %d misses (%.1f%% hit rate)",
|
addon:Print("Category Cache: %d hits, %d misses (%.1f%% hit rate)",
|
||||||
stats.hits, stats.misses, stats.hitRate)
|
stats.hits, stats.misses, stats.hitRate)
|
||||||
end
|
end
|
||||||
|
-- Show tooltip cache stats
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.GetTooltipCacheStats then
|
||||||
|
local stats = addon.Modules.Utils:GetTooltipCacheStats()
|
||||||
|
addon:Print("Tooltip Cache: %d hits, %d misses (%.1f%% hit rate)",
|
||||||
|
stats.hits, stats.misses, stats.hitRate)
|
||||||
|
end
|
||||||
|
|
||||||
elseif msg == "perfreset" then
|
elseif msg == "perfreset" then
|
||||||
-- Reset performance statistics
|
-- Reset performance statistics
|
||||||
@@ -183,6 +189,10 @@ function Main:SetupSlashCommands()
|
|||||||
if addon.Modules.CategoryManager and addon.Modules.CategoryManager.ClearCache then
|
if addon.Modules.CategoryManager and addon.Modules.CategoryManager.ClearCache then
|
||||||
addon.Modules.CategoryManager:ClearCache()
|
addon.Modules.CategoryManager:ClearCache()
|
||||||
end
|
end
|
||||||
|
-- Clear tooltip cache
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.ClearTooltipCache then
|
||||||
|
addon.Modules.Utils:ClearTooltipCache()
|
||||||
|
end
|
||||||
|
|
||||||
elseif msg == "help" then
|
elseif msg == "help" then
|
||||||
-- Show help
|
-- Show help
|
||||||
|
|||||||
+160
-13
@@ -5,6 +5,54 @@ local addon = Guda
|
|||||||
local Utils = {}
|
local Utils = {}
|
||||||
addon.Modules.Utils = Utils
|
addon.Modules.Utils = Utils
|
||||||
|
|
||||||
|
--=============================================================================
|
||||||
|
-- Tooltip Scan Caching
|
||||||
|
-- Caches results of expensive tooltip scanning operations
|
||||||
|
--=============================================================================
|
||||||
|
local tooltipCache = {
|
||||||
|
questItem = {}, -- IsQuestItem results
|
||||||
|
specialText = {}, -- HasSpecialTooltipText results
|
||||||
|
bindOnEquip = {}, -- IsBindOnEquip results
|
||||||
|
uniqueItem = {}, -- IsUniqueItem results
|
||||||
|
restoreTag = {}, -- GetConsumableRestoreTag results
|
||||||
|
}
|
||||||
|
local tooltipCacheStats = {
|
||||||
|
hits = 0,
|
||||||
|
misses = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Clear all tooltip caches
|
||||||
|
function Utils:ClearTooltipCache()
|
||||||
|
tooltipCache.questItem = {}
|
||||||
|
tooltipCache.specialText = {}
|
||||||
|
tooltipCache.bindOnEquip = {}
|
||||||
|
tooltipCache.uniqueItem = {}
|
||||||
|
tooltipCache.restoreTag = {}
|
||||||
|
tooltipCacheStats.hits = 0
|
||||||
|
tooltipCacheStats.misses = 0
|
||||||
|
addon:Debug("Tooltip cache cleared")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get tooltip cache statistics
|
||||||
|
function Utils:GetTooltipCacheStats()
|
||||||
|
local total = tooltipCacheStats.hits + tooltipCacheStats.misses
|
||||||
|
local hitRate = total > 0 and (tooltipCacheStats.hits / total * 100) or 0
|
||||||
|
return {
|
||||||
|
hits = tooltipCacheStats.hits,
|
||||||
|
misses = tooltipCacheStats.misses,
|
||||||
|
total = total,
|
||||||
|
hitRate = hitRate,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generate cache key from item link
|
||||||
|
local function GetTooltipCacheKey(itemLink)
|
||||||
|
if not itemLink then return nil end
|
||||||
|
-- Extract item ID from link for stable caching
|
||||||
|
local _, _, itemID = string.find(itemLink, "item:(%d+)")
|
||||||
|
return itemID
|
||||||
|
end
|
||||||
|
|
||||||
--=============================================================================
|
--=============================================================================
|
||||||
-- Frame Budget System (Baganator-inspired performance optimization)
|
-- Frame Budget System (Baganator-inspired performance optimization)
|
||||||
-- Prevents any single operation from causing frame lag by spreading work
|
-- Prevents any single operation from causing frame lag by spreading work
|
||||||
@@ -534,6 +582,21 @@ function Utils:HasSpecialTooltipText(bagID, slotID, itemLink)
|
|||||||
bagID = tonumber(bagID)
|
bagID = tonumber(bagID)
|
||||||
slotID = tonumber(slotID)
|
slotID = tonumber(slotID)
|
||||||
|
|
||||||
|
-- Get item link for cache key if not provided
|
||||||
|
local cacheLink = itemLink
|
||||||
|
if not cacheLink and bagID and slotID then
|
||||||
|
cacheLink = GetContainerItemLink(bagID, slotID)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check cache first
|
||||||
|
local cacheKey = GetTooltipCacheKey(cacheLink)
|
||||||
|
if cacheKey and tooltipCache.specialText[cacheKey] ~= nil then
|
||||||
|
tooltipCacheStats.hits = tooltipCacheStats.hits + 1
|
||||||
|
local cached = tooltipCache.specialText[cacheKey]
|
||||||
|
return cached.hasSpecial, cached.textType
|
||||||
|
end
|
||||||
|
tooltipCacheStats.misses = tooltipCacheStats.misses + 1
|
||||||
|
|
||||||
local tooltip = GetScanTooltip()
|
local tooltip = GetScanTooltip()
|
||||||
if not tooltip then return false, nil end
|
if not tooltip then return false, nil end
|
||||||
|
|
||||||
@@ -554,7 +617,13 @@ function Utils:HasSpecialTooltipText(bagID, slotID, itemLink)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local numLines = tooltip:NumLines() or 0
|
local numLines = tooltip:NumLines() or 0
|
||||||
if numLines == 0 then return false, nil end
|
if numLines == 0 then
|
||||||
|
-- Cache negative result
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.specialText[cacheKey] = { hasSpecial = false, textType = nil }
|
||||||
|
end
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
-- Scan tooltip lines for yellow or green text
|
-- Scan tooltip lines for yellow or green text
|
||||||
for i = 2, numLines do -- Start from line 2 (skip item name on line 1)
|
for i = 2, numLines do -- Start from line 2 (skip item name on line 1)
|
||||||
@@ -572,6 +641,10 @@ function Utils:HasSpecialTooltipText(bagID, slotID, itemLink)
|
|||||||
for _, pattern in ipairs(SPECIAL_TEXT_PATTERNS) do
|
for _, pattern in ipairs(SPECIAL_TEXT_PATTERNS) do
|
||||||
if string.find(textLower, pattern) then
|
if string.find(textLower, pattern) then
|
||||||
addon:Debug("HasSpecialTooltipText: YELLOW match '%s' in: %s", pattern, text)
|
addon:Debug("HasSpecialTooltipText: YELLOW match '%s' in: %s", pattern, text)
|
||||||
|
-- Cache positive result
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.specialText[cacheKey] = { hasSpecial = true, textType = "yellow" }
|
||||||
|
end
|
||||||
return true, "yellow"
|
return true, "yellow"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -581,12 +654,20 @@ function Utils:HasSpecialTooltipText(bagID, slotID, itemLink)
|
|||||||
-- Green text is ALWAYS considered special (no pattern check needed)
|
-- Green text is ALWAYS considered special (no pattern check needed)
|
||||||
if IsGreenColor(r, g, b) then
|
if IsGreenColor(r, g, b) then
|
||||||
addon:Debug("HasSpecialTooltipText: Found GREEN text: %s (r=%.2f g=%.2f b=%.2f)", text, r, g, b)
|
addon:Debug("HasSpecialTooltipText: Found GREEN text: %s (r=%.2f g=%.2f b=%.2f)", text, r, g, b)
|
||||||
|
-- Cache positive result
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.specialText[cacheKey] = { hasSpecial = true, textType = "green" }
|
||||||
|
end
|
||||||
return true, "green"
|
return true, "green"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Also check for "Use:" or "Equip:" regardless of color (some items may have different colors)
|
-- Also check for "Use:" or "Equip:" regardless of color (some items may have different colors)
|
||||||
if string.find(textLower, "^use:") or string.find(textLower, "^equip:") then
|
if string.find(textLower, "^use:") or string.find(textLower, "^equip:") then
|
||||||
addon:Debug("HasSpecialTooltipText: Found Use/Equip text: %s", text)
|
addon:Debug("HasSpecialTooltipText: Found Use/Equip text: %s", text)
|
||||||
|
-- Cache positive result
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.specialText[cacheKey] = { hasSpecial = true, textType = "yellow" }
|
||||||
|
end
|
||||||
return true, "yellow"
|
return true, "yellow"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -601,12 +682,21 @@ function Utils:HasSpecialTooltipText(bagID, slotID, itemLink)
|
|||||||
if text and r and g and b then
|
if text and r and g and b then
|
||||||
if IsYellowColor(r, g, b) or IsGreenColor(r, g, b) then
|
if IsYellowColor(r, g, b) or IsGreenColor(r, g, b) then
|
||||||
addon:Debug("HasSpecialTooltipText: Found special text (right): %s", text)
|
addon:Debug("HasSpecialTooltipText: Found special text (right): %s", text)
|
||||||
return true, IsYellowColor(r, g, b) and "yellow" or "green"
|
local textType = IsYellowColor(r, g, b) and "yellow" or "green"
|
||||||
|
-- Cache positive result
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.specialText[cacheKey] = { hasSpecial = true, textType = textType }
|
||||||
|
end
|
||||||
|
return true, textType
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cache negative result
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.specialText[cacheKey] = { hasSpecial = false, textType = nil }
|
||||||
|
end
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -934,8 +1024,22 @@ local function ExtractHyperlink(itemLink)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Detect if a consumable has 'Use: Restores' or mentions 'while eating'/'while drinking'
|
-- Detect if a consumable has 'Use: Restores' or mentions 'while eating'/'while drinking'
|
||||||
function Utils:GetConsumableRestoreTag(bagID, slotID)
|
function Utils:GetConsumableRestoreTag(bagID, slotID, itemLink)
|
||||||
if not bagID or not slotID then return nil end
|
if not bagID or not slotID then return nil end
|
||||||
|
|
||||||
|
-- Get item link for cache key if not provided
|
||||||
|
local cacheLink = itemLink or GetContainerItemLink(bagID, slotID)
|
||||||
|
|
||||||
|
-- Check cache first
|
||||||
|
local cacheKey = GetTooltipCacheKey(cacheLink)
|
||||||
|
if cacheKey and tooltipCache.restoreTag[cacheKey] ~= nil then
|
||||||
|
tooltipCacheStats.hits = tooltipCacheStats.hits + 1
|
||||||
|
local cached = tooltipCache.restoreTag[cacheKey]
|
||||||
|
-- Return nil if cached as false, otherwise return the tag
|
||||||
|
return cached ~= false and cached or nil
|
||||||
|
end
|
||||||
|
tooltipCacheStats.misses = tooltipCacheStats.misses + 1
|
||||||
|
|
||||||
local tooltip = GetScanTooltip()
|
local tooltip = GetScanTooltip()
|
||||||
tooltip:ClearLines()
|
tooltip:ClearLines()
|
||||||
tooltip:SetBagItem(bagID, slotID)
|
tooltip:SetBagItem(bagID, slotID)
|
||||||
@@ -958,6 +1062,12 @@ function Utils:GetConsumableRestoreTag(bagID, slotID)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cache the result (nil becomes false for cache check purposes)
|
||||||
|
if cacheKey then
|
||||||
|
tooltipCache.restoreTag[cacheKey] = tag or false
|
||||||
|
end
|
||||||
|
|
||||||
return tag
|
return tag
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1017,17 +1127,28 @@ end
|
|||||||
-- For bank items, use itemLink since SetBagItem may not work for bank slots
|
-- For bank items, use itemLink since SetBagItem may not work for bank slots
|
||||||
function Utils:IsBindOnEquip(bagID, slotID, itemLink)
|
function Utils:IsBindOnEquip(bagID, slotID, itemLink)
|
||||||
if not bagID or not slotID then return false end
|
if not bagID or not slotID then return false end
|
||||||
|
|
||||||
|
-- Get item link for cache key if not provided
|
||||||
|
local cacheLink = itemLink or GetContainerItemLink(bagID, slotID)
|
||||||
|
|
||||||
|
-- Check cache first
|
||||||
|
local cacheKey = GetTooltipCacheKey(cacheLink)
|
||||||
|
if cacheKey and tooltipCache.bindOnEquip[cacheKey] ~= nil then
|
||||||
|
tooltipCacheStats.hits = tooltipCacheStats.hits + 1
|
||||||
|
return tooltipCache.bindOnEquip[cacheKey]
|
||||||
|
end
|
||||||
|
tooltipCacheStats.misses = tooltipCacheStats.misses + 1
|
||||||
|
|
||||||
local tooltip = GetScanTooltip()
|
local tooltip = GetScanTooltip()
|
||||||
if not tooltip then return false end
|
if not tooltip then return false end
|
||||||
|
|
||||||
tooltip:ClearLines()
|
tooltip:ClearLines()
|
||||||
|
|
||||||
-- Try SetBagItem first (works for regular bags and bank when open)
|
-- Try SetBagItem first (works for regular bags and bank when open)
|
||||||
tooltip:SetBagItem(bagID, slotID)
|
tooltip:SetBagItem(bagID, slotID)
|
||||||
|
|
||||||
local numLines = tooltip:NumLines()
|
local numLines = tooltip:NumLines()
|
||||||
|
|
||||||
-- If no lines and we have itemLink, try SetHyperlink with itemString as fallback
|
-- If no lines and we have itemLink, try SetHyperlink with itemString as fallback
|
||||||
if (not numLines or numLines == 0) and itemLink then
|
if (not numLines or numLines == 0) and itemLink then
|
||||||
-- Extract itemString from link (format: item:12345:0:0:0...)
|
-- Extract itemString from link (format: item:12345:0:0:0...)
|
||||||
@@ -1038,19 +1159,25 @@ function Utils:IsBindOnEquip(bagID, slotID, itemLink)
|
|||||||
numLines = tooltip:NumLines()
|
numLines = tooltip:NumLines()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not numLines or numLines == 0 then return false end
|
if not numLines or numLines == 0 then
|
||||||
|
if cacheKey then tooltipCache.bindOnEquip[cacheKey] = false end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- Check tooltip lines for "Binds when equipped"
|
-- Check tooltip lines for "Binds when equipped"
|
||||||
for i = 1, numLines do
|
for i = 1, numLines do
|
||||||
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
|
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
|
||||||
if line then
|
if line then
|
||||||
local text = line:GetText()
|
local text = line:GetText()
|
||||||
if text and string.find(string.lower(text), "binds when equipped") then
|
if text and string.find(string.lower(text), "binds when equipped") then
|
||||||
|
if cacheKey then tooltipCache.bindOnEquip[cacheKey] = true end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if cacheKey then tooltipCache.bindOnEquip[cacheKey] = false end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1058,8 +1185,22 @@ end
|
|||||||
function Utils:IsUniqueItem(bagID, slotID, itemLink)
|
function Utils:IsUniqueItem(bagID, slotID, itemLink)
|
||||||
if not bagID or not slotID then return false end
|
if not bagID or not slotID then return false end
|
||||||
|
|
||||||
|
-- Get item link for cache key if not provided
|
||||||
|
local cacheLink = itemLink or GetContainerItemLink(bagID, slotID)
|
||||||
|
|
||||||
|
-- Check cache first
|
||||||
|
local cacheKey = GetTooltipCacheKey(cacheLink)
|
||||||
|
if cacheKey and tooltipCache.uniqueItem[cacheKey] ~= nil then
|
||||||
|
tooltipCacheStats.hits = tooltipCacheStats.hits + 1
|
||||||
|
return tooltipCache.uniqueItem[cacheKey]
|
||||||
|
end
|
||||||
|
tooltipCacheStats.misses = tooltipCacheStats.misses + 1
|
||||||
|
|
||||||
local tooltip = GetScanTooltip()
|
local tooltip = GetScanTooltip()
|
||||||
if not tooltip then return false end
|
if not tooltip then
|
||||||
|
if cacheKey then tooltipCache.uniqueItem[cacheKey] = false end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
tooltip:ClearLines()
|
tooltip:ClearLines()
|
||||||
|
|
||||||
@@ -1078,7 +1219,10 @@ function Utils:IsUniqueItem(bagID, slotID, itemLink)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not numLines or numLines == 0 then return false end
|
if not numLines or numLines == 0 then
|
||||||
|
if cacheKey then tooltipCache.uniqueItem[cacheKey] = false end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- Check tooltip lines for "Unique" (but not "Unique-Equipped")
|
-- Check tooltip lines for "Unique" (but not "Unique-Equipped")
|
||||||
for i = 1, numLines do
|
for i = 1, numLines do
|
||||||
@@ -1089,11 +1233,14 @@ function Utils:IsUniqueItem(bagID, slotID, itemLink)
|
|||||||
local textLower = string.lower(text)
|
local textLower = string.lower(text)
|
||||||
-- Match "unique" but not "unique-equipped"
|
-- Match "unique" but not "unique-equipped"
|
||||||
if textLower == "unique" or string.find(textLower, "^unique$") or string.find(textLower, "^unique%s") then
|
if textLower == "unique" or string.find(textLower, "^unique$") or string.find(textLower, "^unique%s") then
|
||||||
|
if cacheKey then tooltipCache.uniqueItem[cacheKey] = true end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if cacheKey then tooltipCache.uniqueItem[cacheKey] = false end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user