Skip to content

fix(spectrum): default slice flags to live widgets, GPU sprites opt-in (#3777)#3803

Closed
ten9876 wants to merge 1 commit into
mainfrom
revert/gpu-flags-default-live
Closed

fix(spectrum): default slice flags to live widgets, GPU sprites opt-in (#3777)#3803
ten9876 wants to merge 1 commit into
mainfrom
revert/gpu-flags-default-live

Conversation

@ten9876

@ten9876 ten9876 commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Strategy A of the GPU-flag reversion plan attached to #3777. Flips the slice-flag compositing default from GPU sprites to legacy live widgets, so idle flag text is crisp again and the live↔sprite input regressions go away — without deleting anything.

One line, at SpectrumWidget.cpp:613:

// before: m_gpuFlagMode = !qEnvironmentVariableIsSet("AETHER_NO_GPU_FLAGS");  // GPU default
m_gpuFlagMode = qEnvironmentVariableIsSet("AETHER_GPU_FLAGS");                 // live default, GPU opt-in

Every sprite code path is already gated on m_gpuFlagMode, and the live-widget path is the documented A/B baseline — so this is a genuine behavioral revert with zero machinery removed. The GPU-sprite path stays fully intact behind AETHER_GPU_FLAGS=1 for measurement. (The old AETHER_NO_GPU_FLAGS toggle is now redundant — live is the default — and pre-existing users of it still get live flags.)

What this fixes

The trade-off (stated plainly)

Live widgets re-introduce the #3617 main-thread composite cost that #3695 removed — 0.82 vs 0.45 cores, +0.37 ≈ +45% main-thread (#3695's own measurement). This is separate from #3797 and likely stacks on it on multi-panadapter setups. Per the plan, the 4-panadapter re-profile is the decision gate for whether Strategy B (full excision of the sprite machinery) follows.

Why a flip, not an excision (yet)

So the −45% optimization isn't deleted on the strength of a cosmetic + interaction complaint until the cost is measured in the very area (#3797) that's currently most painful. Reversible in one line if the measurement argues against it.

Test plan

  • Builds clean (cmake --build build --target AetherSDR)
  • Visual: idle VFO flag text is crisp on Windows (no hover-pop)
  • Diversity-mode RX-antenna selection works
  • 4-panadapter main-thread CPU re-profile (decision gate for Strategy B) — and confirm dragging a flag doesn't reintroduce audio crackle/the re-raster crash feat(spectrum): GPU-composite slice flags instead of raster siblings #3695 noted
  • AETHER_GPU_FLAGS=1 still brings back GPU sprites (A/B hatch intact)

Refs #3777, #3617. Plan: see the #3777 comment.

🤖 Generated with Claude Code

#3777)

Strategy A of the #3617-lineage reversion plan (attached to #3777). Flips the
flag-compositing default from GPU sprites to legacy live widgets, addressing the
idle-flag text blur (#3777) and the Diversity-mode RX-antenna input regression —
both inherent to the off-screen-sprite + live-on-hover swap.

This is a one-line gate flip, not an excision: every sprite code path is already
gated on m_gpuFlagMode, and the legacy live-widget path is the documented A/B
baseline. The GPU-sprite machinery is left fully intact behind an opt-IN env var
(AETHER_GPU_FLAGS=1) so the trade can still be measured before any code removal
(Strategy B).

Trade-off, stated plainly: live widgets re-introduce the #3617 main-thread
composite cost #3695 removed (~0.82 vs 0.45 cores, +0.37 ≈ +45% main-thread).
That is separate from #3797 and likely stacks on it on multi-panadapter setups —
the required 4-panadapter re-profile is the gate for whether Strategy B (full
removal) follows. Idle flag text is now crisp (native-surface ClearType) and the
live↔sprite input regressions are gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the change against the file — this is a clean, well-scoped default flip. Thanks @ten9876.

Verification of the "zero machinery removed" claim — holds up:

  • m_gpuFlagMode = qEnvironmentVariableIsSet("AETHER_GPU_FLAGS") is the only behavioral line; the GPU-sprite path stays fully intact behind the opt-in.
  • The 12.5 Hz re-raster timer doesn't just no-op when GPU is off — it never starts: updateFlagRefreshTimer() (SpectrumWidget.cpp:1231) folds m_gpuFlagMode into its present predicate, so the live default incurs no idle timer cost. Good — that's the right gate.
  • updateLiveFlag() (:1094), grabFlagSprites() (:1248), and the GPU draw paths (:8098, :8220, :8341) all early-return on !m_gpuFlagMode, so the live path is the same untouched legacy-widget behavior the code comments already describe as the software-path baseline.
  • Dropping the AETHER_NO_GPU_FLAGS read is correct: anyone who previously set it to force live flags still gets live flags (now by default), so no one's behavior silently inverts. The comment documents this.

Only affects AETHER_GPU_SPECTRUM builds; without that macro flags are already live widgets, so it's a no-op there. All six CI checks pass.

Non-blocking notes (your call / maintainer's, not review objections):

  • The PR is honest that this re-introduces the #3617/#3695 main-thread composite cost (+~0.37 core) and likely stacks on #3797. The two genuinely load-bearing test-plan items — the 4-panadapter re-profile and confirming a flag drag doesn't reintroduce the #3695 re-raster crash / audio crackle — are still unchecked. Those are the actual decision gate for whether this lands as-is vs. Strategy B, so worth resolving before merge even though the code itself is sound.
  • Minor: with the default now live, the comment block is the only remaining documentation of the GPU opt-in path — which is good, but AETHER_GPU_FLAGS isn't surfaced anywhere user-facing. Fine for an A/B measurement toggle; just flagging it's comment-only.

Code LGTM as a reversible behavioral revert.


🤖 aethersdr-agent · cost: $4.9290 · model: claude-opus-4-8

NF0T pushed a commit to NF0T/AetherSDR that referenced this pull request Jun 26, 2026
…ve (aethersdr#3777) (aethersdr#3806)

## Summary

**Strategy B** of the GPU-flag reversion plan (attached to aethersdr#3777) — the
**full excision** that aethersdr#3803 (the one-line default-flip) was the
reversible precursor to. VFO slice flags are now **unconditionally live
child widgets**; the off-screen-sprite + live-on-hover machinery is
removed entirely, fixing the aethersdr#3777 idle-flag blur and the Diversity-mode
RX-antenna regression at the source.

**Net −582 lines** across `SpectrumWidget.{cpp,h}` and
`VfoWidget.{cpp,h}`.

## Relationship to aethersdr#3803
These are **alternatives — merge one, not both.** aethersdr#3803 flips the
default but keeps the sprite path behind `AETHER_GPU_FLAGS=1`
(reversible, for measurement). This PR deletes the machinery (no env
opt-in remains). Recommended sequence: validate live-flag behavior + the
4-panadapter re-profile via aethersdr#3803 first; if the group commits to it,
merge **this** as the clean removal and close aethersdr#3803.

## Removed (all sprite-path)
- SpectrumWidget: `m_gpuFlagMode` + the env gate, `m_flagRefreshTimer` +
lambda, `grabFlagSprites`/`releaseFlagSprite`,
`setLiveFlag`/`updateLiveFlag`,
`updateFlagRefreshTimer`/`attachFlagTimerWindowWatcher` +
`m_flagTimerFilteredWindow`, `FlagSprite` +
`m_flagSprites`/`m_flagDrawOrder`/`m_flagQuadVbo`/`m_flagSampler`/`kMaxFlagSprites`,
the `renderGpuFrame` sprite draw-order/texture/quad blocks, and the QRhi
flag-resource init/teardown.
- VfoWidget: `setButtonsOccluded` +
`m_occludeRestore`/`m_buttonsOccluded`.

## Kept intact (verified)
- **`repositionVfoFlags()`** — the *shared* positioner that calls
`updatePosition()` on every flag; only its trailing GPU-mode hide-block
was removed. Still called each frame from `renderGpuFrame`, so live
flags position correctly. This was the key correctness invariant.
- The whole **GPU panadapter** path (`AETHER_GPU_SPECTRUM`:
FFT/waterfall/overlay) — untouched.
- The **SmartMTR** render-mode-agnostic fixes from aethersdr#3771/aethersdr#3773/aethersdr#3775.
- `event()`/`eventFilter()` non-flag logic (`setMouseTracking`, Mac QRhi
teardown in `prepareForTopLevelChange`) preserved.

## Trade-off (unchanged from aethersdr#3803)
Re-introduces the aethersdr#3617 main-thread composite cost aethersdr#3695 removed
(**~0.82 vs 0.45 cores, +0.37 ≈ +45% main-thread**). Separate from and
likely additive to aethersdr#3797.

## Test plan
- [x] Builds clean (`cmake --build build --target AetherSDR`); zero
dangling references to removed symbols
- [ ] Idle VFO-flag text crisp; no hover-pop (Windows)
- [ ] Diversity-mode RX-antenna selection works
- [ ] Flags position/move correctly with 1, 2, and N panadapters;
split-partner side-locking intact (repositionVfoFlags path)
- [ ] Dragging a flag: no audio crackle / re-raster crash (aethersdr#3695's
original concern)
- [ ] 4-panadapter main-thread CPU re-profile (the decision gate)
- [ ] a11y linter clean; SmartMTR meter renders correctly

Refs aethersdr#3777, aethersdr#3617. Supersedes aethersdr#3803. Plan: see the aethersdr#3777 comment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant