Ω-W1-T3 remediation: meter stays live and washed under the curve popup, not frozen
This commit is contained in:
@@ -285,10 +285,14 @@ against a performance budget — they are there because `VoiceEngine::applyLiveT
|
||||
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. **When the
|
||||
meter is covered or absent** (Browse, the curve popup, the empty state) `invalidateMeter`
|
||||
skips invalidating anything at all rather than falling back to a whole-client repaint — the
|
||||
ballistics still advance on `onMeterTimer`'s own clock, but nothing visible changed, so a
|
||||
60 FPS whole-client repaint under a modal sheet would be pure cost for zero pixels shown.
|
||||
meter is covered or absent** (Browse, the empty state) `invalidateMeter` skips invalidating
|
||||
anything at all rather than falling back to a whole-client repaint — the ballistics still
|
||||
advance on `onMeterTimer`'s own clock, but nothing visible changed, so a 60 FPS whole-client
|
||||
repaint under a modal sheet would be pure cost for zero pixels shown. **The curve popup does
|
||||
NOT cover the meter** — its centered sheet clamps to 520x380 (`curve_popup.h`) against the
|
||||
meter's fixed right-anchored slot, at any resizable window size — so the fast path stays live
|
||||
under it; `paintMeterField` reapplies the popup's own 0.50-alpha wash to the field alone so a
|
||||
meter-only frame doesn't flash through it at full brightness.
|
||||
- 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
|
||||
|
||||
@@ -80,10 +80,12 @@ void ReaSamplerEditor::paint(HDC hdc, const RECT& dirty) {
|
||||
Font::Label, Role::BgBase);
|
||||
}
|
||||
|
||||
// A sheet layered over the face covers the meter, so the fast path must not paint
|
||||
// through it even if something else invalidates a rect that happens to match the
|
||||
// stale field bounds — invalidateMeter itself already skips while covered (see there).
|
||||
if (view_ == View::kBrowse || curvePopup_ != CurveTarget::kNone) meterRects_ = {};
|
||||
// Browse is a full-window modal that covers the meter, so the fast path must not paint
|
||||
// through it even if something else invalidates a rect that happens to match the stale
|
||||
// field bounds — invalidateMeter itself already skips while covered (see there). The
|
||||
// curve popup's sheet never reaches the meter column (see invalidateMeter), so its
|
||||
// meterRects_ stays valid and the fast path stays live under the popup's wash.
|
||||
if (view_ == View::kBrowse) meterRects_ = {};
|
||||
}
|
||||
|
||||
BitBlt(hdc, dx, dy, dr - dx, db - dy, bmp->getDC(), dx, dy, SRCCOPY);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -62,6 +63,7 @@ void drawMeterField(LICE_IBitmap* bmp, const MeterRects& m, const MasterMeterUi&
|
||||
// A column narrower than the interior needs yields all-empty rects, which under rect.h's
|
||||
// contract means suppressed — not a zero-height field to fill, tick twelve times and cap.
|
||||
if (m.field.empty()) return;
|
||||
assert(meterBarsWithinField(m)); // the self-containment invariant this draw rests on
|
||||
fillSurface(bmp, toKitBox(m.field), Role::BgCell, InteractionState::Rest);
|
||||
|
||||
// Scale: a rule every 6 dB, numeralled every 12 with 0 dB heavier — the reference the
|
||||
@@ -111,6 +113,14 @@ void drawMeterField(LICE_IBitmap* bmp, const MeterRects& m, const MasterMeterUi&
|
||||
|
||||
void ReaSamplerEditor::paintMeterField(LICE_IBitmap* bmp) {
|
||||
drawMeterField(bmp, meterRects_, masterMeter_);
|
||||
// A full paint under the curve popup washes the whole client at 0.50 alpha AFTER the deck
|
||||
// draws (paintCurvePopup); a meter-only fast-path frame draws fresh opaque bars into that
|
||||
// same back buffer, so it must reapply the same wash to the field alone or the meter would
|
||||
// flash through at full brightness against the dimmed rest of the face.
|
||||
if (curvePopup_ != CurveTarget::kNone && !meterRects_.field.empty()) {
|
||||
const Rect& f = meterRects_.field;
|
||||
LICE_FillRect(bmp, f.x, f.y, f.width, f.height, toLice(roleColor(Role::BgBase)), 0.50f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
|
||||
@@ -77,13 +77,16 @@ void ReaSamplerEditor::invalidate() {
|
||||
|
||||
void ReaSamplerEditor::invalidateMeter() {
|
||||
if (!childHwnd_) return;
|
||||
// The meter is covered (Browse, the curve popup) or has nothing to draw (empty state) —
|
||||
// ballistics still advance in onMeterTimer, but nothing on screen changed, so invalidating
|
||||
// anything here would only buy a whole-client repaint of chrome/waveform/deck the meter
|
||||
// never touches. Distinct from "bounds not resolved yet" below: this is a fact about what
|
||||
// the face is showing, not about whether meterRects_ happens to be populated.
|
||||
const bool meterOnScreen =
|
||||
view_ == View::kSample && curvePopup_ == CurveTarget::kNone && !selectedId_.empty();
|
||||
// The meter is covered (Browse) or has nothing to draw (empty state) — ballistics still
|
||||
// advance in onMeterTimer, but nothing on screen changed, so invalidating anything here
|
||||
// would only buy a whole-client repaint of chrome/waveform/deck the meter never touches.
|
||||
// Distinct from "bounds not resolved yet" below: this is a fact about what the face is
|
||||
// showing, not about whether meterRects_ happens to be populated. The curve popup does NOT
|
||||
// cover the meter — its centered sheet tops out at 520x380 (curve_popup.h) against the
|
||||
// meter's fixed right-anchored slot, at any resizable size — so the meter stays on screen
|
||||
// (dimmed by the popup's wash, which paintMeterField reapplies to the field alone) and keeps
|
||||
// its own rate rather than freezing under the sheet.
|
||||
const bool meterOnScreen = view_ == View::kSample && !selectedId_.empty();
|
||||
if (!meterOnScreen) return;
|
||||
|
||||
const Rect& f = meterRects_.field;
|
||||
|
||||
@@ -335,10 +335,9 @@ private:
|
||||
// fold, so a drawn envelope disables them through the same predicate.
|
||||
bool loopControlsLive() const;
|
||||
|
||||
// Which marks the band DRAWS, and which of those accept a grab. They differ in exactly one
|
||||
// place — Trigger, where the loop marks stay drawn (hiding a set loop on a mode flip would
|
||||
// destroy information the user put there) but refuse every gesture, because that refusal
|
||||
// comes from the engine and no drag can talk it out of it.
|
||||
// Which marks the band DRAWS, and which of those accept a grab. Trigger has no loop at all,
|
||||
// so the pair and crossfade are ABSENT rather than drawn inert (an offered-but-refused
|
||||
// gesture reads as broken) — drawn iff grabbable, so the two cannot currently diverge.
|
||||
instrument::ui::WaveMarks waveMarksFor(const SetupMarkers& m) const;
|
||||
instrument::ui::WaveMarks grabbableMarks(const SetupMarkers& m) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user