Files
virtual-controller/remote-client-android/BISECT_LOG.md
T
Paul Lipscomb 60c8255c2d Android perf: fix fader stutter (appendLog), drawText feedback widget, bisect docs
Bisect session with per-suspect kill switches (see BISECT_LOG.md):
- appendLog was the confirmed stutter culprit — ran per-message (~110/sec)
  on the main thread even with the log panel hidden; A/B verified. Disabled
  via DEBUG_DISABLE_APPEND_LOG; shippable visible-only fix still TODO.
- Per-message Log.d also disabled (freebie, no felt difference).
- Glow, gradient fill, network flash, feedback setText all exonerated and
  restored; switches left in place at false.
- FocusFeedbackWidgetView rewritten: TextView -> bare View + canvas.drawText.
  60hz updates are now field-assign + invalidate, no per-update text Layout.
- ContextMenuViewLogic: explicit color-picker branch for the new view type.
- Timecode chunkiness root cause was REAPER audio buffer size (source-side
  burst cadence), documented in PERF_SESSION_2026-07-31.md; stutter bug
  report stamped RESOLVED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:46:07 -05:00

98 lines
5.5 KiB
Markdown

# Focus Fader Stutter — Bisect Log
Companion to FEEDBACK_STUTTER_BUG.md. One kill-switch per suspect; flip one,
rebuild, drag a Focus Fader while 60hz feedback streams, record the result here,
then either keep it off or restore it and move to the next.
## Ranked suspect list
| # | Suspect | Where | Switch |
|---|---------|-------|--------|
| 1 | appendLog per message (even when panel hidden) | ArrangeFunctionsWebSocket.appendLog | `DEBUG_DISABLE_APPEND_LOG` |
| 2 | logScrollView.post(fullScroll) per message | inside appendLog | covered by #1 |
| 3 | Runnable flood (~3 posts/msg, 300+/sec) | appendLog + dispatch handlers | mostly covered by #1 |
| 4 | flashCard per message at 60hz | ArrangeFunctionsWebSocket.flashCard → ArrangeCardView.flashActivity | `DEBUG_DISABLE_NETWORK_FLASH` |
| 5 | setText at 60hz on feedback TextView | FocusFeedbackWidgetView.setTextFromNetwork | `DEBUG_DISABLE_FEEDBACK_SETTEXT` |
| 6 | LAYER_TYPE_SOFTWARE + blur glow on fader | FocusFaderWidgetView ctor (also FaderWidgetView) | `DEBUG_DISABLE_GLOW` (one per class) |
| 7 | new LinearGradient every onDraw | FocusFaderWidgetView.onDraw (also FaderWidgetView) | `DEBUG_DISABLE_GRADIENT_FILL` (one per class) |
| 8 | Log.d full-JSON concat per message | ArrangeFunctionsWebSocket.onMessage | `DEBUG_DISABLE_RECEIVE_LOGCAT` |
| 9 | Echo stream (sent values come back, run appendLog+flashCard mid-drag) | handleWidgetUpdateF | partially covered by #1 |
| 10 | vibrator.vibrate at value edges | FocusFaderWidgetView.checkEdgeHaptic | not yet added |
## Verdict (2026-07-31)
**appendLog was the stutter.** A/B confirmed: re-enabling it brings the stutter
back immediately. Receive-logcat kept off as a freebie (no felt difference).
Everything else exonerated and restored to original behavior — including the
glow/software-layer rendering, which Paul tested and cleared.
Final switch states: `DEBUG_DISABLE_APPEND_LOG = true`,
`DEBUG_DISABLE_RECEIVE_LOGCAT = true`, all others `false` (original behavior).
**Timecode fluidity root cause (found by Paul, same day):** REAPER's audio
buffer size. REAPER emits OSC feedback per audio block, so a large buffer
makes updates arrive in chunky bursts — uneven cadence no client-side
rendering can smooth. Lowering the buffer restored fluid feedback. The
drawText rewrite of FocusFeedbackWidgetView (done just before this discovery)
stays: it's the cheapest render path and collapses same-frame updates to one
draw regardless of source pacing.
## Test runs
### Run 1 — DEBUG_DISABLE_APPEND_LOG = true (suspects 1+2, most of 3)
- Date: 2026-07-31
- Switches off: appendLog (all in/out log lines + auto-scroll posts)
- Side effect while off: debug log panel shows nothing new when opened
- Result: **CONFIRMED CULPRIT — "way smoother"** with appendLog disabled.
- A/B re-test same day: re-enabled appendLog, stutter came back immediately;
disabled again. Not a placebo — appendLog is definitively implicated.
Left OFF for now; permanent fix TBD.
### Run 2 — DEBUG_DISABLE_RECEIVE_LOGCAT = true (suspect 8)
- Date: 2026-07-31
- Switches off: appendLog (kept off from Run 1) + per-message Log.d in onMessage
- Side effect while off: incoming messages no longer visible in logcat
- Result: **No perceptible difference.** Kept off anyway (free optimization,
nobody reads logcat during performance).
### Run 3 — DEBUG_DISABLE_GLOW = true (suspect 6)
- Date: 2026-07-31
- Switches off: appendLog + receive-logcat (from Runs 1-2) + shadow-layer glow
and software rendering in FocusFaderWidgetView AND FaderWidgetView
- Side effect while off: faders lose their glow — plain value line + dot
- Result: **Exonerated.** Still smooth with glow off, but no felt difference
attributable to it — Paul: "the glow wasn't an issue." Turned back ON
(original software-layer glow restored) at end of bisect.
### Run 4 — DEBUG_DISABLE_NETWORK_FLASH = true (suspect 4)
- Date: 2026-07-31
- Switches off: appendLog + receive-logcat + glow (Runs 1-3) + network-triggered
activity-ring flashes (local touch flashes still work)
- Side effect while off: cards don't blink green on desktop-driven updates
- Result: **SKIPPED — suspect was already inert.** The tablet's "Activity:
ON/OFF" button (default OFF) was off during all stutter tests, and
flashActivity() early-returns on that flag before any timer work. Kill
switch left in place (harmless, skips a registry lookup) but this suspect
is exonerated for the observed stutter.
### Run 5 — DEBUG_DISABLE_FEEDBACK_SETTEXT = true (suspect 5, plus its share of 3)
- Date: 2026-07-31
- Switches off: appendLog + receive-logcat + glow (Runs 1-3) + the ENTIRE
widget_feedback UI path: the runOnUiThread post, registry lookup, and
setText all skipped (placed before the post, so this also removes 60/sec
of the runnable flood)
- Side effect while off: feedback readouts (timecode etc.) freeze at last text
- Result: **Nothing earth-shaking — no clear felt difference.** Switch turned
back OFF (readouts are needed regardless). 60hz setText is tolerable now
that appendLog is gone; candidate for once-per-frame conflation later, not
a primary culprit.
### Run 6 — DEBUG_DISABLE_GRADIENT_FILL = true (suspect 7)
- Date: 2026-07-31
- Switches off: appendLog + receive-logcat + glow (kept from earlier runs);
gradient fill replaced with flat translucent fill in both fader classes
- Side effect while off: fader fill is flat color, no fade-to-transparent
- Result: **No felt difference — gradient turned back ON** (look kept).
Create-once gradient (setLocalMatrix) stays on the permanent-fix list as
hygiene, not a culprit.