Skip to content

fix(actionbars): the cast kick must re-open the rate gates it re-arms the wave for - #1171

Merged
EllesmereGaming merged 1 commit into
EllesmereGaming:mainfrom
dfrisone:ab-castkick-gate-reopen
Aug 3, 2026
Merged

fix(actionbars): the cast kick must re-open the rate gates it re-arms the wave for#1171
EllesmereGaming merged 1 commit into
EllesmereGaming:mainfrom
dfrisone:ab-castkick-gate-reopen

Conversation

@dfrisone

@dfrisone dfrisone commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Symptom

Cooldown swipes started visibly late on 8.7.4, and worse while spamming a keybind. Reported with video, and the delay was put at "0.5s or more".

Cause

The cooldown walk runs in two cadence tiers: fast is the curated Essential rotation kit, and slow is every other spell plus items and macros, riding a 0.5s gate.

A cast is meant to bypass both gates. UNIT_SPELLCAST_SUCCEEDED zeroes _cdWalkNext and _cdSlowNext, then schedules _cdCastKick as the guaranteed post-cascade pass. But a cooldown event arriving in the cast's own frame consumes that opening: it runs a pass on state read inside the cooldown API's transient-disagreement window, and in doing so re-arms the storm cap to +0.15s and the slow tier to +0.5s.

The kick already re-armed _cdCastWave to survive exactly that theft. It did not re-arm the gates. So the corrective pass the kick exists to perform was capped out of the walk entirely, and the slow tier kept whatever the transient push painted until its gate expired, or until the ~1/sec heartbeat when no event landed on the gate.

Fix

Two lines in _cdCastKick, reopening both gates alongside the wave it already re-arms, for the same documented reason.

It is self-limiting: the kick runs once per cast behind its existing pending guard, and when the wave was not stolen both values are already 0, so the common path is unchanged.

Measurement

Found and verified with a repaint-latency harness that times the gap between the engine reporting a button as on cooldown and the button actually being painted. Three captures on the reporter's machine, roughly 8-11k frames each:

before (run 1) before (run 2) after
mean 304ms 231ms 85ms
max 616ms 578ms 208ms
500ms-1s 74 50 0
200-500ms 60 65 16

The entire half-second-plus tail is gone, which is the slow-tier gate disappearing.

What named the cause was the shape of the distribution rather than the mean: both pre-fix captures showed an identical 51 sub-frame paints, a fixed subset always prompt while the rest were always late, which is a tier split and not a throttle. The worst offender in every capture was a utility spell, i.e. slow tier.

Notes

… the wave for

Cooldown swipes started visibly late on 8.7.4, and worse while spamming a
keybind.

UNIT_SPELLCAST_SUCCEEDED zeroes both rate gates so the cast's own repaint
cannot be throttled, and schedules the kick as the guaranteed post-cascade
pass. But a cooldown event arriving in the cast's OWN frame consumes that
opening: it runs a pass off state read inside the cooldown API's transient
window, then re-arms the storm cap to +0.15s and the slow tier to +0.5s.
The kick already re-armed the cast wave to survive exactly that theft, but
left the gates re-armed, so the corrective pass it exists to perform was
capped out of the walk, and the slow tier -- every utility spell, item and
macro, which is most of the bar -- kept whatever the transient push painted
until its 0.5s gate expired, or until the ~1/sec heartbeat if no event
landed on the gate.

Measured with a repaint-latency harness across two captures before and one
after, roughly 8-11k frames each: swipe start a mean of 304ms and 231ms late
over ~240 samples each, max 616ms, falling to a mean of 85ms with nothing
past 208ms and the entire 500ms-plus band empty. What named the cause was
the shape rather than the mean: both pre-fix captures showed an identical 51
sub-frame paints, a fixed subset always prompt while the rest were always
late, which is a tier split and not a throttle. The worst offender in every
capture was a utility spell, that is, slow tier.

Reopening is self-limiting: the kick runs once per cast behind its pending
guard, and when the wave was not stolen both values are already 0, so the
common path is unchanged.
@dfrisone
dfrisone force-pushed the ab-castkick-gate-reopen branch from bcf68a5 to 0536a09 Compare August 3, 2026 13:28
@EllesmereGaming
EllesmereGaming merged commit e07a018 into EllesmereGaming:main Aug 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants