From a286843b181ba8404f80aec437817a700eee5d41 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:26:20 -0500 Subject: [PATCH] nameplates: gate the per-plate update before classifying it The central loop calls the per-plate update for every visible plate ~100 times a second, and the plate's own throttle sat at the bottom of the function. Above it ran the whole classification: a GetAlpha call, a castbar IsShown, a cast lookup, and one or two libthrottle:Get resolutions -- all to work out which throttle applied, on plates throttled to 10fps that were going to return anyway. Forty plates makes that four thousand times a second. Add a cheap gate first, against the floor across all four throttle categories. Nothing that would have updated can be turned away by it -- a plate past its real throttle is necessarily past the minimum -- and the category-specific gate still runs after the classification, unchanged. Event flags are read before both and bypass both, as they did before. libthrottle:Get is not a lookup: it walks the saved variables, a defaults fallback and a preset table, and can build a "_custom" key. Resolve the four categories in CacheConfig instead, where config changes already land, and read them from cfg on the hot path. That covers the two calls in the castbar branch as well. The castbar hide moves below the cheap gate, which is safe rather than merely tolerable: a plate showing a castbar sets isCastingNonTarget, which selects the castbar throttle -- one of the four the floor is derived from -- so it always clears the first gate and reaches the hide at its normal rate. --- modules/nameplates.lua | 54 +++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/modules/nameplates.lua b/modules/nameplates.lua index 078ee466..450dda63 100644 --- a/modules/nameplates.lua +++ b/modules/nameplates.lua @@ -159,6 +159,24 @@ pfUI:RegisterModule("nameplates", function () cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0 cfg.debufftext = tonumber(C.nameplates.debufftext) or 1 + -- Throttle delays, resolved once instead of per plate per tick. + -- libthrottle:Get walks the saved-variable table, a defaults fallback and a + -- preset table, and can build a "_custom" key -- the per-plate + -- OnUpdate was calling it one or two times for every visible plate, a + -- hundred times a second, just to decide it had nothing to do. + -- + -- cfg.throttle_min is the floor across all four. No plate can ever be due + -- sooner than that, so the update can bail on it before working out which + -- category it actually belongs to. + cfg.throttle_target = pfUI.throttle:Get("nameplates_target") + cfg.throttle_mass = pfUI.throttle:Get("nameplates_mass") + cfg.throttle_normal = pfUI.throttle:Get("nameplates") + cfg.throttle_castbar = pfUI.throttle:Get("nameplates_castbar") + cfg.throttle_min = cfg.throttle_target + if cfg.throttle_mass < cfg.throttle_min then cfg.throttle_min = cfg.throttle_mass end + if cfg.throttle_normal < cfg.throttle_min then cfg.throttle_min = cfg.throttle_normal end + if cfg.throttle_castbar < cfg.throttle_min then cfg.throttle_min = cfg.throttle_castbar end + -- Rebuild offtanks lookup table offtanks = {} for k, v in pairs({strsplit("#", C.nameplates.combatofftanks)}) do @@ -1409,6 +1427,16 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE") -- cachedGuid is maintained by NAME_PLATE_UNIT_ADDED / _REMOVED events. + -- Cheap gate first. The central loop calls this for every visible plate ~100 + -- times a second, and classifying the plate below costs two C calls, a cast + -- lookup and a throttle resolution -- all of it wasted on a plate that is + -- throttled to 10fps. cfg.throttle_min is the floor across every category, + -- so nothing that would have updated can be turned away here; the real + -- category-specific throttle is still applied after the classification. + -- Event flags bypass both gates, as before. + local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate + if not hasEventUpdate and (nameplate.lasttick or 0) + cfg.throttle_min > now then return end + -- PERF: Intelligent throttling based on target/castbar status and plate count -- Use GUID comparison as primary target detection: instant, immune to alpha transitions, -- and immediately correct on de-target (unlike istarget which updates one tick later) @@ -1435,25 +1463,24 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE") end end + -- Resolved in CacheConfig, so these are table reads rather than a walk + -- through the saved variables and preset tables. local throttle if target then - throttle = pfUI.throttle:Get("nameplates_target") + throttle = cfg.throttle_target elseif visiblePlateCount > 20 then - throttle = pfUI.throttle:Get("nameplates_mass") + throttle = cfg.throttle_mass else - throttle = pfUI.throttle:Get("nameplates") + throttle = cfg.throttle_normal end -- Non-target plates with active castbar use the castbar throttle - if isCastingNonTarget then - local cbThrottle = pfUI.throttle:Get("nameplates_castbar") - if cbThrottle < throttle then throttle = cbThrottle end + if isCastingNonTarget and cfg.throttle_castbar < throttle then + throttle = cfg.throttle_castbar end - -- Check for pending event updates (these bypass throttle for immediate response) - local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate - - -- Event updates bypass throttle + -- The category-specific gate. hasEventUpdate was read above, before the + -- classification, and still bypasses the throttle. if not hasEventUpdate and (nameplate.lasttick or 0) + throttle > now then return end nameplate.lasttick = now @@ -1655,10 +1682,9 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE") -- engine framerate, decoupled from central loop). Only update non-target castbars here. local isTargetPlate = target or nameplate.istarget or (nameplate.health and nameplate.health.zoomed) if cfg.showcastbar and not cfg.targetcastbar and not isTargetPlate then - local cbThrottle = pfUI.throttle:Get("nameplates_castbar") - if visiblePlateCount > 20 then - local massThrottle = pfUI.throttle:Get("nameplates_mass") - if massThrottle > cbThrottle then cbThrottle = massThrottle end + local cbThrottle = cfg.throttle_castbar + if visiblePlateCount > 20 and cfg.throttle_mass > cbThrottle then + cbThrottle = cfg.throttle_mass end if (nameplate.castbar_tick or 0) + cbThrottle <= now then nameplate.castbar_tick = now