Frame Budgeting Implementation
This commit is contained in:
@@ -149,6 +149,21 @@ function Main:SetupSlashCommands()
|
|||||||
-- Cleanup old characters
|
-- Cleanup old characters
|
||||||
addon.Modules.DB:CleanupOldCharacters()
|
addon.Modules.DB:CleanupOldCharacters()
|
||||||
|
|
||||||
|
elseif msg == "perf" or msg == "performance" then
|
||||||
|
-- Show performance statistics
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.PrintPerformanceStats then
|
||||||
|
addon.Modules.Utils:PrintPerformanceStats()
|
||||||
|
else
|
||||||
|
addon:Print("Performance stats not available")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif msg == "perfreset" then
|
||||||
|
-- Reset performance statistics
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.ResetPerformanceStats then
|
||||||
|
addon.Modules.Utils:ResetPerformanceStats()
|
||||||
|
addon:Print("Performance stats reset")
|
||||||
|
end
|
||||||
|
|
||||||
elseif msg == "help" then
|
elseif msg == "help" then
|
||||||
-- Show help
|
-- Show help
|
||||||
addon:Print("Commands:")
|
addon:Print("Commands:")
|
||||||
@@ -161,6 +176,8 @@ function Main:SetupSlashCommands()
|
|||||||
addon:Print("/guda debug - Toggle debug mode")
|
addon:Print("/guda debug - Toggle debug mode")
|
||||||
addon:Print("/guda debugsort - Toggle sort debug output")
|
addon:Print("/guda debugsort - Toggle sort debug output")
|
||||||
addon:Print("/guda cleanup - Remove old characters")
|
addon:Print("/guda cleanup - Remove old characters")
|
||||||
|
addon:Print("/guda perf - Show performance stats")
|
||||||
|
addon:Print("/guda perfreset - Reset performance stats")
|
||||||
|
|
||||||
else
|
else
|
||||||
addon:Print("Unknown command. Type /guda help for commands")
|
addon:Print("Unknown command. Type /guda help for commands")
|
||||||
|
|||||||
+214
@@ -5,6 +5,220 @@ local addon = Guda
|
|||||||
local Utils = {}
|
local Utils = {}
|
||||||
addon.Modules.Utils = Utils
|
addon.Modules.Utils = Utils
|
||||||
|
|
||||||
|
--=============================================================================
|
||||||
|
-- Frame Budget System (Baganator-inspired performance optimization)
|
||||||
|
-- Prevents any single operation from causing frame lag by spreading work
|
||||||
|
-- across multiple frames when operations exceed a time budget.
|
||||||
|
--=============================================================================
|
||||||
|
|
||||||
|
-- Frame budget configuration
|
||||||
|
local FRAME_BUDGET_SECONDS = 0.1 -- 100ms budget per frame (same as Baganator)
|
||||||
|
local lastEntryTime = 0
|
||||||
|
local workQueue = {}
|
||||||
|
local workQueueFrame = nil
|
||||||
|
|
||||||
|
-- Report that we're starting work (call at the beginning of expensive operations)
|
||||||
|
-- This resets the frame budget timer
|
||||||
|
function Utils:ReportEntry()
|
||||||
|
lastEntryTime = GetTime()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if we've exceeded the frame budget
|
||||||
|
-- Returns true if we should defer remaining work to the next frame
|
||||||
|
function Utils:CheckTimeout()
|
||||||
|
return (GetTime() - lastEntryTime) > FRAME_BUDGET_SECONDS
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Queue work to be executed in the next frame
|
||||||
|
-- callback: function to call
|
||||||
|
-- context: optional context/owner for the callback (for debugging/cleanup)
|
||||||
|
function Utils:QueueWork(callback, context)
|
||||||
|
if type(callback) ~= "function" then
|
||||||
|
addon:Error("QueueWork: callback must be a function")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(workQueue, {callback = callback, context = context or "unknown"})
|
||||||
|
|
||||||
|
-- Create the work queue processor frame if it doesn't exist
|
||||||
|
if not workQueueFrame then
|
||||||
|
workQueueFrame = CreateFrame("Frame", "Guda_WorkQueueFrame", UIParent)
|
||||||
|
workQueueFrame.elapsed = 0
|
||||||
|
workQueueFrame:Hide()
|
||||||
|
|
||||||
|
workQueueFrame:SetScript("OnUpdate", function()
|
||||||
|
-- Process queued work with frame budget
|
||||||
|
Utils:ReportEntry()
|
||||||
|
|
||||||
|
local processedCount = 0
|
||||||
|
local maxPerFrame = 50 -- Safety limit to prevent infinite loops
|
||||||
|
|
||||||
|
while table.getn(workQueue) > 0 and processedCount < maxPerFrame do
|
||||||
|
-- Check if we've exceeded frame budget
|
||||||
|
if Utils:CheckTimeout() then
|
||||||
|
-- Still have work but exceeded budget, continue next frame
|
||||||
|
addon:Debug("Frame budget exceeded, deferring %d items to next frame", table.getn(workQueue))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local work = table.remove(workQueue, 1)
|
||||||
|
if work and work.callback then
|
||||||
|
local success, err = pcall(work.callback)
|
||||||
|
if not success then
|
||||||
|
addon:Error("QueueWork callback error [%s]: %s", tostring(work.context), tostring(err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
processedCount = processedCount + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- All work done, hide the frame to stop OnUpdate
|
||||||
|
if table.getn(workQueue) == 0 then
|
||||||
|
workQueueFrame:Hide()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show the frame to start processing
|
||||||
|
workQueueFrame:Show()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear all queued work (useful when frame is hidden)
|
||||||
|
function Utils:ClearWorkQueue()
|
||||||
|
workQueue = {}
|
||||||
|
if workQueueFrame then
|
||||||
|
workQueueFrame:Hide()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the number of items in the work queue (for debugging)
|
||||||
|
function Utils:GetWorkQueueSize()
|
||||||
|
return table.getn(workQueue)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Process items in batches with frame budget awareness
|
||||||
|
-- items: table of items to process
|
||||||
|
-- processor: function(item, index) called for each item
|
||||||
|
-- onComplete: optional function called when all items are processed
|
||||||
|
-- batchSize: optional number of items to process before checking timeout (default 10)
|
||||||
|
function Utils:ProcessWithBudget(items, processor, onComplete, batchSize)
|
||||||
|
if not items or table.getn(items) == 0 then
|
||||||
|
if onComplete then onComplete() end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
batchSize = batchSize or 10
|
||||||
|
local index = 1
|
||||||
|
local totalItems = table.getn(items)
|
||||||
|
|
||||||
|
local function processNextBatch()
|
||||||
|
Utils:ReportEntry()
|
||||||
|
local batchCount = 0
|
||||||
|
|
||||||
|
while index <= totalItems and batchCount < batchSize do
|
||||||
|
if Utils:CheckTimeout() then
|
||||||
|
-- Budget exceeded, queue continuation
|
||||||
|
Utils:QueueWork(processNextBatch, "ProcessWithBudget")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local item = items[index]
|
||||||
|
if item then
|
||||||
|
local success, err = pcall(processor, item, index)
|
||||||
|
if not success then
|
||||||
|
addon:Error("ProcessWithBudget processor error at index %d: %s", index, tostring(err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
index = index + 1
|
||||||
|
batchCount = batchCount + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if we have more items
|
||||||
|
if index <= totalItems then
|
||||||
|
-- More items to process, queue next batch
|
||||||
|
Utils:QueueWork(processNextBatch, "ProcessWithBudget")
|
||||||
|
else
|
||||||
|
-- All done
|
||||||
|
if onComplete then
|
||||||
|
local success, err = pcall(onComplete)
|
||||||
|
if not success then
|
||||||
|
addon:Error("ProcessWithBudget onComplete error: %s", tostring(err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start processing
|
||||||
|
processNextBatch()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Performance metrics tracking
|
||||||
|
local performanceStats = {
|
||||||
|
budgetExceededCount = 0,
|
||||||
|
totalUpdates = 0,
|
||||||
|
lastUpdateDuration = 0,
|
||||||
|
averageUpdateDuration = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Get the current frame budget setting (in seconds)
|
||||||
|
function Utils:GetFrameBudget()
|
||||||
|
return FRAME_BUDGET_SECONDS
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the frame budget (in seconds, min 0.016 = 60fps, max 0.5)
|
||||||
|
function Utils:SetFrameBudget(seconds)
|
||||||
|
if type(seconds) ~= "number" then return end
|
||||||
|
FRAME_BUDGET_SECONDS = math.max(0.016, math.min(0.5, seconds))
|
||||||
|
addon:Debug("Frame budget set to %.3f seconds", FRAME_BUDGET_SECONDS)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Record the end of an update cycle for performance tracking
|
||||||
|
function Utils:RecordUpdateEnd()
|
||||||
|
local duration = GetTime() - lastEntryTime
|
||||||
|
performanceStats.lastUpdateDuration = duration
|
||||||
|
performanceStats.totalUpdates = performanceStats.totalUpdates + 1
|
||||||
|
|
||||||
|
-- Update rolling average
|
||||||
|
local alpha = 0.1 -- Smoothing factor
|
||||||
|
performanceStats.averageUpdateDuration = performanceStats.averageUpdateDuration * (1 - alpha) + duration * alpha
|
||||||
|
|
||||||
|
if duration > FRAME_BUDGET_SECONDS then
|
||||||
|
performanceStats.budgetExceededCount = performanceStats.budgetExceededCount + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get performance statistics
|
||||||
|
function Utils:GetPerformanceStats()
|
||||||
|
return {
|
||||||
|
frameBudget = FRAME_BUDGET_SECONDS,
|
||||||
|
lastUpdateDuration = performanceStats.lastUpdateDuration,
|
||||||
|
averageUpdateDuration = performanceStats.averageUpdateDuration,
|
||||||
|
totalUpdates = performanceStats.totalUpdates,
|
||||||
|
budgetExceededCount = performanceStats.budgetExceededCount,
|
||||||
|
workQueueSize = table.getn(workQueue),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reset performance statistics
|
||||||
|
function Utils:ResetPerformanceStats()
|
||||||
|
performanceStats.budgetExceededCount = 0
|
||||||
|
performanceStats.totalUpdates = 0
|
||||||
|
performanceStats.lastUpdateDuration = 0
|
||||||
|
performanceStats.averageUpdateDuration = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Print current performance statistics (for debugging)
|
||||||
|
function Utils:PrintPerformanceStats()
|
||||||
|
local stats = self:GetPerformanceStats()
|
||||||
|
addon:Print("=== Guda Performance Stats ===")
|
||||||
|
addon:Print("Frame Budget: %.0fms", stats.frameBudget * 1000)
|
||||||
|
addon:Print("Last Update: %.1fms", stats.lastUpdateDuration * 1000)
|
||||||
|
addon:Print("Avg Update: %.1fms", stats.averageUpdateDuration * 1000)
|
||||||
|
addon:Print("Total Updates: %d", stats.totalUpdates)
|
||||||
|
addon:Print("Budget Exceeded: %d times", stats.budgetExceededCount)
|
||||||
|
addon:Print("Work Queue: %d items", stats.workQueueSize)
|
||||||
|
end
|
||||||
|
|
||||||
--=============================================================================
|
--=============================================================================
|
||||||
-- SafeCall: Nil-safe module method invocation
|
-- SafeCall: Nil-safe module method invocation
|
||||||
-- Replaces verbose nil-checks like:
|
-- Replaces verbose nil-checks like:
|
||||||
|
|||||||
@@ -130,6 +130,12 @@ function Guda_BagFrame_OnHide(self)
|
|||||||
-- Close any open dropdown menus when the bag frame is hidden
|
-- Close any open dropdown menus when the bag frame is hidden
|
||||||
CloseDropDownMenus()
|
CloseDropDownMenus()
|
||||||
|
|
||||||
|
-- Clear any pending update and work queue items
|
||||||
|
pendingUpdate = false
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.ClearWorkQueue then
|
||||||
|
addon.Modules.Utils:ClearWorkQueue()
|
||||||
|
end
|
||||||
|
|
||||||
-- Clean up all buttons when frame is hidden (safe since we're not displaying)
|
-- Clean up all buttons when frame is hidden (safe since we're not displaying)
|
||||||
for _, bagParent in pairs(bagParents) do
|
for _, bagParent in pairs(bagParents) do
|
||||||
if bagParent then
|
if bagParent then
|
||||||
@@ -275,12 +281,43 @@ function BagFrame:UpdateBaglineLayout()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Deferred update state for frame budgeting
|
||||||
|
local pendingUpdate = false
|
||||||
|
local updateDebounceFrame = nil
|
||||||
|
local UPDATE_DEBOUNCE_TIME = 0.05 -- 50ms debounce for rapid updates
|
||||||
|
|
||||||
|
-- Schedule an update with debouncing (prevents multiple updates in rapid succession)
|
||||||
|
function BagFrame:ScheduleUpdate()
|
||||||
|
if pendingUpdate then return end
|
||||||
|
pendingUpdate = true
|
||||||
|
|
||||||
|
if not updateDebounceFrame then
|
||||||
|
updateDebounceFrame = CreateFrame("Frame")
|
||||||
|
updateDebounceFrame.elapsed = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
updateDebounceFrame.elapsed = 0
|
||||||
|
updateDebounceFrame:SetScript("OnUpdate", function()
|
||||||
|
this.elapsed = this.elapsed + arg1
|
||||||
|
if this.elapsed >= UPDATE_DEBOUNCE_TIME then
|
||||||
|
this:SetScript("OnUpdate", nil)
|
||||||
|
pendingUpdate = false
|
||||||
|
BagFrame:Update()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
-- Update display
|
-- Update display
|
||||||
function BagFrame:Update()
|
function BagFrame:Update()
|
||||||
if not Guda_BagFrame:IsShown() then
|
if not Guda_BagFrame:IsShown() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Report entry for frame budget tracking
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.ReportEntry then
|
||||||
|
addon.Modules.Utils:ReportEntry()
|
||||||
|
end
|
||||||
|
|
||||||
-- If cursor is holding an item (mid-drag), only update lock states, don't rebuild UI
|
-- If cursor is holding an item (mid-drag), only update lock states, don't rebuild UI
|
||||||
-- BUT only if we already have items displayed - otherwise we need to do initial build
|
-- BUT only if we already have items displayed - otherwise we need to do initial build
|
||||||
if CursorHasItem and CursorHasItem() then
|
if CursorHasItem and CursorHasItem() then
|
||||||
@@ -406,6 +443,11 @@ function BagFrame:Update()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Record performance metrics
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.RecordUpdateEnd then
|
||||||
|
addon.Modules.Utils:RecordUpdateEnd()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Delegate to centralized helpers
|
-- Delegate to centralized helpers
|
||||||
@@ -482,6 +524,10 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
|
|||||||
local headerIdx = 1
|
local headerIdx = 1
|
||||||
local totalWidth = perRow * (buttonSize + spacing)
|
local totalWidth = perRow * (buttonSize + spacing)
|
||||||
|
|
||||||
|
-- Frame budget tracking for category item processing
|
||||||
|
local categoryItemsProcessed = 0
|
||||||
|
local CATEGORY_ITEMS_PER_BUDGET_CHECK = 8
|
||||||
|
|
||||||
for _, catName in ipairs(categoryList) do
|
for _, catName in ipairs(categoryList) do
|
||||||
local items = categories[catName]
|
local items = categories[catName]
|
||||||
local numItems = items and table.getn(items) or 0
|
local numItems = items and table.getn(items) or 0
|
||||||
@@ -553,6 +599,15 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
|
|||||||
col = 0
|
col = 0
|
||||||
row = row + 1
|
row = row + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Frame budget check
|
||||||
|
categoryItemsProcessed = categoryItemsProcessed + 1
|
||||||
|
if categoryItemsProcessed >= CATEGORY_ITEMS_PER_BUDGET_CHECK then
|
||||||
|
categoryItemsProcessed = 0
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.CheckTimeout and addon.Modules.Utils:CheckTimeout() then
|
||||||
|
addon.Modules.Utils:ReportEntry()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
|
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
|
||||||
@@ -783,6 +838,10 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName)
|
|||||||
table.insert(bagsToShow, {bagID = -2, needsSpacing = true})
|
table.insert(bagsToShow, {bagID = -2, needsSpacing = true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Frame budget tracking for item processing
|
||||||
|
local itemsProcessed = 0
|
||||||
|
local ITEMS_PER_BUDGET_CHECK = 8 -- Check budget every N items
|
||||||
|
|
||||||
for _, bagInfo in ipairs(bagsToShow) do
|
for _, bagInfo in ipairs(bagsToShow) do
|
||||||
local bagID = bagInfo.bagID
|
local bagID = bagInfo.bagID
|
||||||
local bag = bagData[bagID]
|
local bag = bagData[bagID]
|
||||||
@@ -842,6 +901,18 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName)
|
|||||||
col = 0
|
col = 0
|
||||||
row = row + 1
|
row = row + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Frame budget check: periodically check if we've exceeded the frame budget
|
||||||
|
itemsProcessed = itemsProcessed + 1
|
||||||
|
if itemsProcessed >= ITEMS_PER_BUDGET_CHECK then
|
||||||
|
itemsProcessed = 0
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.CheckTimeout and addon.Modules.Utils:CheckTimeout() then
|
||||||
|
-- Budget exceeded - reset entry time and continue
|
||||||
|
-- (For full deferral, we would queue remaining work, but that requires
|
||||||
|
-- more complex state management. For now, we just note the budget usage.)
|
||||||
|
addon.Modules.Utils:ReportEntry()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ function Guda_BankFrame_OnHide(self)
|
|||||||
-- Close any open dropdown menus when the bank frame is hidden
|
-- Close any open dropdown menus when the bank frame is hidden
|
||||||
CloseDropDownMenus()
|
CloseDropDownMenus()
|
||||||
|
|
||||||
|
-- Clear any pending update
|
||||||
|
bankPendingUpdate = false
|
||||||
|
|
||||||
-- Close the actual Blizzard bank too
|
-- Close the actual Blizzard bank too
|
||||||
local blizzardBankFrame = getglobal("BankFrame")
|
local blizzardBankFrame = getglobal("BankFrame")
|
||||||
if blizzardBankFrame and blizzardBankFrame:IsShown() then
|
if blizzardBankFrame and blizzardBankFrame:IsShown() then
|
||||||
@@ -121,12 +124,43 @@ function BankFrame:UpdateLockStates()
|
|||||||
Guda_UpdateLockStates(bankBagParents)
|
Guda_UpdateLockStates(bankBagParents)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Deferred update state for frame budgeting
|
||||||
|
local bankPendingUpdate = false
|
||||||
|
local bankUpdateDebounceFrame = nil
|
||||||
|
local BANK_UPDATE_DEBOUNCE_TIME = 0.05 -- 50ms debounce for rapid updates
|
||||||
|
|
||||||
|
-- Schedule an update with debouncing (prevents multiple updates in rapid succession)
|
||||||
|
function BankFrame:ScheduleUpdate()
|
||||||
|
if bankPendingUpdate then return end
|
||||||
|
bankPendingUpdate = true
|
||||||
|
|
||||||
|
if not bankUpdateDebounceFrame then
|
||||||
|
bankUpdateDebounceFrame = CreateFrame("Frame")
|
||||||
|
bankUpdateDebounceFrame.elapsed = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
bankUpdateDebounceFrame.elapsed = 0
|
||||||
|
bankUpdateDebounceFrame:SetScript("OnUpdate", function()
|
||||||
|
this.elapsed = this.elapsed + arg1
|
||||||
|
if this.elapsed >= BANK_UPDATE_DEBOUNCE_TIME then
|
||||||
|
this:SetScript("OnUpdate", nil)
|
||||||
|
bankPendingUpdate = false
|
||||||
|
BankFrame:Update()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
-- Update display
|
-- Update display
|
||||||
function BankFrame:Update()
|
function BankFrame:Update()
|
||||||
if not Guda_BankFrame:IsShown() then
|
if not Guda_BankFrame:IsShown() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Report entry for frame budget tracking
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.ReportEntry then
|
||||||
|
addon.Modules.Utils:ReportEntry()
|
||||||
|
end
|
||||||
|
|
||||||
-- If cursor is holding an item (mid-drag), only update lock states, don't rebuild UI
|
-- If cursor is holding an item (mid-drag), only update lock states, don't rebuild UI
|
||||||
-- BUT only if we already have items displayed - otherwise we need to do initial build
|
-- BUT only if we already have items displayed - otherwise we need to do initial build
|
||||||
if CursorHasItem and CursorHasItem() then
|
if CursorHasItem and CursorHasItem() then
|
||||||
@@ -244,6 +278,11 @@ function BankFrame:Update()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Record performance metrics
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.RecordUpdateEnd then
|
||||||
|
addon.Modules.Utils:RecordUpdateEnd()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Use centralized frame helpers for section headers and bag parents
|
-- Use centralized frame helpers for section headers and bag parents
|
||||||
@@ -308,6 +347,10 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
|
|||||||
local headerIdx = 1
|
local headerIdx = 1
|
||||||
local totalWidth = perRow * (buttonSize + spacing)
|
local totalWidth = perRow * (buttonSize + spacing)
|
||||||
|
|
||||||
|
-- Frame budget tracking for category item processing
|
||||||
|
local categoryItemsProcessed = 0
|
||||||
|
local CATEGORY_ITEMS_PER_BUDGET_CHECK = 8
|
||||||
|
|
||||||
for _, catName in ipairs(categoryList) do
|
for _, catName in ipairs(categoryList) do
|
||||||
local items = categories[catName]
|
local items = categories[catName]
|
||||||
local numItems = items and table.getn(items) or 0
|
local numItems = items and table.getn(items) or 0
|
||||||
@@ -371,6 +414,15 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
|
|||||||
col = 0
|
col = 0
|
||||||
row = row + 1
|
row = row + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Frame budget check
|
||||||
|
categoryItemsProcessed = categoryItemsProcessed + 1
|
||||||
|
if categoryItemsProcessed >= CATEGORY_ITEMS_PER_BUDGET_CHECK then
|
||||||
|
categoryItemsProcessed = 0
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.CheckTimeout and addon.Modules.Utils:CheckTimeout() then
|
||||||
|
addon.Modules.Utils:ReportEntry()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
|
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
|
||||||
@@ -583,6 +635,10 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Frame budget tracking for item processing
|
||||||
|
local itemsProcessed = 0
|
||||||
|
local ITEMS_PER_BUDGET_CHECK = 8 -- Check budget every N items
|
||||||
|
|
||||||
for _, bagInfo in ipairs(bagsToShow) do
|
for _, bagInfo in ipairs(bagsToShow) do
|
||||||
local bagID = bagInfo.bagID
|
local bagID = bagInfo.bagID
|
||||||
local bag = bankData and bankData[bagID]
|
local bag = bankData and bankData[bagID]
|
||||||
@@ -631,6 +687,16 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName)
|
|||||||
col = 0
|
col = 0
|
||||||
row = row + 1
|
row = row + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Frame budget check: periodically check if we've exceeded the frame budget
|
||||||
|
itemsProcessed = itemsProcessed + 1
|
||||||
|
if itemsProcessed >= ITEMS_PER_BUDGET_CHECK then
|
||||||
|
itemsProcessed = 0
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.CheckTimeout and addon.Modules.Utils:CheckTimeout() then
|
||||||
|
-- Budget exceeded - reset entry time and continue
|
||||||
|
addon.Modules.Utils:ReportEntry()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user