Ω-W1-T3 review fixes: skip meter invalidate when covered; push fast-path predicates into core

Fixes the critical finding — Browse/curve-popup no longer trigger 60 Hz
whole-client repaints when the meter is invisible. Pushes the fast-path and
self-containment geometry into master_meter with tests; batches the four minor
findings.
This commit is contained in:
2026-08-03 13:31:50 -04:00
parent 4320bfdb08
commit dd067a192e
9 changed files with 102 additions and 21 deletions
+27
View File
@@ -5,6 +5,8 @@
// the mono bar taking the whole field, the two stereo bars, all inside the column.
// * bar count — the SAME LaneSplit resolveLaneSplit folds, over channel mode x source
// channel count, so it can never become a second rule.
// * the editor's dirty-rect fast path — meterFastPathEligible's wholly-inside test, and
// meterBarsWithinField, the self-containment invariant the fast path rests on.
// * the dB axis — top/floor on the field's edges, an interior value, and the clamps.
// * ballistics — instantaneous rise, 20 dB/s fall, the 1.5 s hold and its release AT RATE;
// the audio thread's clip latch surviving a UI frame; the per-field single-lane fold.
@@ -287,6 +289,29 @@ static void testDrawEqualityCoversTheDrawnQuantities() {
CHECK(meterDrawEqual(a, graze));
}
// The editor's WM_PAINT fast-path test: a dirty rect wholly inside the field takes the
// meter-only redraw; anything that pokes outside it, or a field that hasn't been laid out yet,
// falls back to the full compose.
static void testFastPathEligibleOnlyForADirtyRectInsideTheField() {
const MeterRects m = meterRects(kColumn, LaneSplit::Single);
CHECK(meterFastPathEligible(m, m.field)); // the whole field
CHECK(meterFastPathEligible(
m, Rect::ltrb(m.field.x + 2, m.field.y + 2, m.field.right() - 2, m.field.bottom() - 2)));
CHECK(!meterFastPathEligible(m, Rect::ltrb(m.field.x - 1, m.field.y, m.field.right(),
m.field.bottom()))); // pokes left of the field
CHECK(!meterFastPathEligible(
m, Rect::ltrb(kColumn.x, kColumn.y, kColumn.right(), kColumn.bottom()))); // whole column
CHECK(!meterFastPathEligible(MeterRects{}, m.field)); // no cached layout at all
}
// The self-containment invariant drawMeterField's redraw-the-field-alone shortcut rests on:
// every bar meterRects() hands back stays inside the field, in both lane splits.
static void testFieldBoundsEveryBarItPaints() {
CHECK(meterBarsWithinField(meterRects(kColumn, LaneSplit::Single)));
CHECK(meterBarsWithinField(meterRects(kColumn, LaneSplit::Stereo)));
CHECK(meterBarsWithinField(MeterRects{})); // degenerate: nothing to bound, nothing drawn
}
static void testDegenerateColumnYieldsNothing() {
const MeterRects m = meterRects(Rect::ltrb(0, 0, 0, 0), LaneSplit::Stereo);
CHECK(m.field.empty() && m.barA.empty() && m.barB.empty());
@@ -300,6 +325,8 @@ int main() {
testEveryOtherTickCarriesANumeral();
testTheFloorNumeralStaysInsideTheGutter();
testAColumnTooNarrowForTheInteriorDrawsNothing();
testFastPathEligibleOnlyForADirtyRectInsideTheField();
testFieldBoundsEveryBarItPaints();
testPeakRisesAtOnceAndFallsAtTwentyDbPerSecond();
testPeakHoldSitsForItsFullWindowThenReleases();
testClipLatchesFromThePublishedFlagAndClearsOnDemand();