Ω-W1-T3: run the master meter on its own 60 FPS timer, and make a meter frame cost one rect

This commit is contained in:
2026-08-03 12:39:36 -04:00
parent 0eb2c67875
commit 4320bfdb08
7 changed files with 248 additions and 88 deletions
+22 -12
View File
@@ -260,21 +260,31 @@ against a performance budget — they are there because `VoiceEngine::applyLiveT
detector's 4-sample group delay INSIDE that budget, not on top), bounded by one 500 ms tick.
Narrowing it further means a second deferral mechanism (a posted window message) rather than
the tick — deliberately not built.
- **The MASTER meter's ballistics ride the sync tick, and that tick is 500 ms.** They run
BEFORE the tick's in-flight-drag guard on purpose — a drag suppresses the reload poll, but
the bus keeps sounding. Elapsed time is measured (`GetTickCount64`), never assumed from the
timer's period, and the tick repaints only when `meterDrawEqual` says the picture changed.
**The published block state is therefore ACCUMULATED, not sampled**: at 48 kHz / 512 frames
~47 blocks elapse per tick, so the processor folds a per-channel max and a min limiter gain
across them and `masterBusMeter()` clears the accumulators as it reads. A plain overwriting
store displayed one block in ~47 and lost the rest — the specified "a peak displays on the
first UI frame after it occurs" is what the fold restores. `masterBusMeter()` is CONSUMING,
so exactly one caller may hold it; the embed strip reads its own non-consuming
- **The MASTER meter has its OWN 16 ms timer, beside the 500 ms sync poll.** It runs outside
that poll's in-flight-drag guard on purpose — a drag suppresses the reload poll, but the bus
keeps sounding. Elapsed time is measured (`steady_clock`, whose resolution the rate needs —
`GetTickCount64`'s ~15.6 ms quantized a frame's delta to 0 or 16), never assumed from the
timer's period, a zero delta skips the advance, and the tick repaints only when
`meterDrawEqual` says the picture changed. **The published block state is ACCUMULATED, not
sampled**: the processor folds a per-channel max and a min limiter gain across every block
since the last read and `masterBusMeter()` clears the accumulators as it reads. A plain
overwriting store displayed one block per window and lost the rest — the specified "a peak
displays on the first UI frame after it occurs" is what the fold restores. `masterBusMeter()`
is CONSUMING, so exactly one caller may hold it — which is why the meter block MOVED to the
meter tick rather than being copied there; the embed strip reads its own non-consuming
`embedActivityLevel()`. **The tick's FIRST read is discarded**, because that caller is the
only consumer: with no editor open the accumulators hold everything since the instance was
created, and advancing off them would open the meter at the session's loudest peak. The clip
latch is not discarded with them — it is a latch the user clears. A meter-rate timer remains a
separate change and is not in.
latch is not discarded with them — it is a latch the user clears.
- **A meter frame repaints the bar FIELD, not the client area**, which is what makes the rate
affordable: `paint` honours `ps.rcPaint` against the field rect `paintDeck` cached, composes
into a RETAINED back buffer (so the rest of the face is still there from the last full
compose) and blits the dirty region alone. The fast path is chosen GEOMETRICALLY, never by a
flag — Windows unions a meter invalidate with any other pending one into a single rcPaint, so
a flag would paint a meter frame over a face that had really changed. What stays outside the
field stays on the full path: the static numeral gutter (AA text re-blended onto itself every
frame thickens) and the GR lamp (drawn straight onto the deck group's gradient, which a
sub-rect fill cannot reproduce), so a lamp transition takes a whole-client repaint.
- The bake's availability probe runs on the SAME tick that paints the button, so the
control can never be enabled on one tick and refuse on the next. The bake Hold control's
applicability (`resolveBakeHoldNeeded`) rides the same tick for the same reason, and