Skip to content

fix(gui): use R16F unconditionally for FFT column texture — fixes blurry 2D trace on old D3D11 GPUs (#3967). Principle II.#3968

Closed
aethersdr-agent[bot] wants to merge 1 commit into
mainfrom
aetherclaude/issue-3967
Closed

fix(gui): use R16F unconditionally for FFT column texture — fixes blurry 2D trace on old D3D11 GPUs (#3967). Principle II.#3968
aethersdr-agent[bot] wants to merge 1 commit into
mainfrom
aetherclaude/issue-3967

Conversation

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Summary

Fixes #3967

What was changed

fix(gui): use R16F unconditionally for FFT column texture — fixes blurry 2D trace on old D3D11 GPUs (#3967). Principle II.

Files modified

  • src/gui/SpectrumWidget.cpp
 src/gui/SpectrumWidget.cpp | 41 ++++++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 19 deletions(-)

Generated by AetherClaude (automated agent for AetherSDR)


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

…rry 2D trace on old D3D11 GPUs (#3967). Principle II.

Regression from #3958 (per-pixel GPU FFT trace). initSpectrumPipeline
preferred R32F whenever isTextureFormatSupported(R32F) returned true, but
that predicate only reports create/sample support — NOT linear
filterability. On older Windows GPUs (Intel iGPUs, D3D11 feature level
<= 10.0) R32_FLOAT is sample-supported but not linearly filterable, so the
Linear column sampler silently degrades to point sampling. panscope.frag
then slope-corrects stroke width from dFdx(yTrace)/fwidth() of the sampled
trace; point-sampled columns step between texels, dFdx spikes on every
step, and the perpendicular-distance stroke fattens into the blurred band
the reporter sees.

Fix: use R16F unconditionally — the only float format QRhi guarantees is
linear-filterable on every backend. The stored value is a normalized 0..1
amplitude, for which half-float has ample precision, so this removes the
unreliable hardware-parity fork with no shader change and no visible
quality loss on hardware where R32F was previously filterable. Also drops
the now-dead R32F upload branch in renderGpuFrame.

Honors Principle II (radio is authoritative on live state): the panadapter
must faithfully mirror the FFT the radio reports across all client
hardware, not a GPU-degraded approximation of it.

Blast radius: risk_score=0.272, 19 high-risk affected (top:
MainWindow::MainWindow, MainWindow::wirePanadapter, MainWindow::buildMenuBar).
All are file-level SpectrumWidget callers that construct/wire the widget and
are agnostic to the FFT column texture's internal pixel format; the change is
confined to the GPU render path and reaches none of them behaviorally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aethersdr-agent aethersdr-agent Bot requested a review from a team as a code owner July 2, 2026 17:19
@aethersdr-agent aethersdr-agent Bot enabled auto-merge (squash) July 2, 2026 17:19
@jensenpat jensenpat disabled auto-merge July 2, 2026 17:41
@jensenpat

Copy link
Copy Markdown
Collaborator

Closing this without merge — the root-cause analysis here doesn't hold up against git history.

The blurry 2D FFT trace reported in #3967 is not a D3D11 texture-format problem, so switching the column texture to R16F unconditionally wouldn't fix it (and would only change precision, not sharpness). The softened trace reproduces on all platforms/GPUs and traces back to rendering changes merged 2026-06-26:

So the "blur" is a deliberate-but-unconditional cosmetic smoothing regression, not a hardware fallback. The right fix is to expose the spatial smoothing as a user-facing Display option (off/light/heavy) next to "FFT Line", separable from #3836's legitimate zoomed-flatline fix (the y_pixels guard in PanadapterStream.cpp, which must stay).

See #3967 for the full root-cause writeup.

@jensenpat jensenpat closed this Jul 2, 2026
NF0T pushed a commit that referenced this pull request Jul 3, 2026
…u-gated smoothing — Principle XI. (#3975)

## Summary

Fixes #3967. Also fixes #3932. Root-caused with per-commit builds
(pre-#3836, post-#3836, 26.7.1-main, this fix) captured minutes apart
against the same live AM broadcast band on a FLEX-8400M:

![4-way
comparison](https://raw.githubusercontent.com/jensenpat/AetherSDR/pr-assets/pan-trace-regression-4way.png)

## The regression chain (two releases, two causes)

**26.6.5 — #3836's unconditional spatial smoothing ("out of focus",
#3932).** `buildFftDisplayTrace`'s 5-tap binomial smooth at a fixed 0.75
blend exists to melt the radio's RBW stair-steps when zoomed in. Applied
to every frame at every span, it rounds narrow carriers (visible height
loss on AM carriers, panel 2) and defocuses the noise floor. **Fix:**
gate the blend on the measured plateau fraction (adjacent equal-pixel
bins). Zoomed-in plateau runs (frac ≳0.65) keep the full blend — #3836's
stair-step fix is preserved; busy wide spans (frac ≲0.35) get zero — the
pre-#3836 crispness returns; the ramp between prevents a visible mode
flip while zooming.

**26.7.1 — #3958's per-pixel stroke ("spiky above and below the line",
the rest of #3967).** The stroke distance was approximated as vertical
distance scaled by a `dFdx`-derived slope — i.e. distance to the
segment's **infinite line**. On steep segments that line passes near the
entire pixel column, so the stroke drew full-height needles above and
below the trace (panel 3), and the fixed ±0.75 px smoothstep band read
as a soft fat stroke at 1× DPI. **Fix:** true endpoint-clamped distance
to the three adjacent polyline segments (4 column samples — matches the
coverage the old geometry quads had), plus falloff parity with the old
`spectrum.frag`: solid to `halfWidth−1`, 1 px fade, 1 px feather annulus
at α 0.22.

Also folds in #3968's R16F-unconditional column format as hardening
(float32 linear filtering is optional on GLES and
`isTextureFormatSupported()` can't detect filterability). Note it could
**not** have caused #3967: the reporter's own About screenshot shows
`D3D11; Intel(R) HD Graphics 520` — feature level 12.1 hardware where
R32F linear filtering is mandatory. With this folded in, #3968 can be
closed.

Not implicated (left untouched, documented for the record): #3836's
`decodeFFT` over-range renormalization only fires on radio/client
`y_pixels` mismatch (transient), and the DPR-scaled
`x_pixels`/`y_pixels` requests are orthogonal to trace sharpness.

## Why our platforms missed it

The #3958 parity checks ran on 2× Retina, where the extra AA softness is
half the logical size and the spike needles read as band activity in
static grabs. The 4-way same-band comparison above is exactly the test
that should have been run: panel 4 (this fix) restores panel 1
(pre-#3836).

## Verification

- 4-way live comparison above (same band, same radio pan, same display
settings; three grabs per build).
- `get panstats 0`: frame prep unchanged at ~121 µs/frame, 58
presents/s, `fftBuildMsPerSec` 0.28 (plateau gate skips the smoothing
copy on wide spans).
- 3D stacked-trace mode, lean mode, line-width 0, heat/solid fill:
unaffected paths re-checked by inspection; the software (non-QRhi-build)
QPainter renderer shares `buildFftDisplayTrace` and picks up the plateau
gate automatically.

## Also on the #3967 thread (separate follow-ups, not in this PR)

- "3D option no longer exists": the 2D/3D combo is the **last row of the
Display panel**, below Background — on short screens (768p laptops) it
clips off the bottom of the panadapter. Needs a scrollable/compacted
Display panel; the option itself ships fine in 26.7.1.
- The 60 fps FFT slider ceiling from #3958 is unrelated to this report
but worth an explicit release-notes mention.

💻 Generated with Claude Code (claude-fable-5 7/2/26) with architecture
by @jensenpat

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

Co-authored-by: Claude Fable 5 <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.

Waterfall FFT lines blurry when not in 3D Mode

1 participant