Skip to content

feat(gui): WAVE showcase visualizations — 3D Ridge, Tunnel, Horizon (stacked on #3955)#3991

Merged
ten9876 merged 3 commits into
aethersdr:mainfrom
jensenpat:feat/wave-viz-showcase
Jul 4, 2026
Merged

feat(gui): WAVE showcase visualizations — 3D Ridge, Tunnel, Horizon (stacked on #3955)#3991
ten9876 merged 3 commits into
aethersdr:mainfrom
jensenpat:feat/wave-viz-showcase

Conversation

@jensenpat

@jensenpat jensenpat commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #3955 — the first 5 commits are the WAVE perf PR (incremental reduction + QRhi GPU render). Review only the last commit here; this PR should merge after #3955 (or be rebased onto main once it lands).

Summary

Three GPU-only, audio-reactive showcase visualizations for the WAVE scope — crossing the line from utility to fun, deliberately. One new fragment shader (wavedemo.frag), one extra pipeline over the existing fullscreen quad, zero new dependencies. Demoscene rules: fully analytic scenes, the palette comes entirely from the active theme (a theme switch recolors the whole show), and every scene keeps animating in an ambient attract mode when no audio is flowing — the screen is never dead.

Mode Vibe Audio hooks
3D Ridge Joy Division × 3DSS: hidden-line perspective heightfield of the envelope's last ~3 s envelope history (256×96 R32F ring texture, one ~1 KB row per 33 ms)
Tunnel Amiga demo tunnel live waveform columns texture the walls; mid band launches rings down the bore; high band warms the spokes; RMS breathes the core; low band sways the vanishing point
Horizon Synthwave outrun grid the live waveform IS the mountain skyline; a scanline sun swells with RMS; grid speed rides the low band; peaks flash the horizon

Contained & cheap

  • New ViewMode values + WAVE drawer combo entries (GPU builds only — CPU builds fall back to Graph and never offer them; a persisted demo mode degrades gracefully).
  • Fixed 256-column reduction + the existing 12-band Goertzel (already cached on the model generation) drive everything; per-frame uploads are ≤4 KB columns + ~150 B uniforms + a 1 KB history row.
  • Measured 213–220 µs/frame at 60 fps via get wavestats — the showcase modes cost the same as the utility modes.
  • Classic modes regression-checked after the render-loop restructure (Scope: 136 µs/frame), and the -DAETHER_GPU_SPECTRUM=OFF configuration builds and runs clean (the perf(gui): WAVE scope — incremental reduction + QRhi GPU render, wavestats/scrollTo bridge verbs (Phases 1+2) #3955 Linux lesson, applied preemptively this time).

Screenshots (live RX on the FLEX-8400M)

3D Ridge Tunnel
3D Ridge Tunnel

Horizon:

Horizon

Proof

Bridge grabs of all three scenes live on the FLEX-8400M (RX band noise as input — voice input makes Ridge/Horizon dramatically more mountainous), plus a Scope-mode regression grab. Perf numbers from get wavestats per mode.

💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat

jensenpat added a commit to jensenpat/AetherSDR that referenced this pull request Jul 3, 2026
Bridge grabs of the three wavedemo.frag scenes (live FLEX-8400M RX) and
the Scope-mode regression check, referenced from PR aethersdr#3991.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jensenpat jensenpat marked this pull request as ready for review July 3, 2026 06:24
@jensenpat jensenpat requested review from a team as code owners July 3, 2026 06:24
jensenpat and others added 2 commits July 4, 2026 07:36
Three GPU-only, audio-reactive scenes for the WAVE scope (wavedemo.frag,
one extra pipeline over the same fullscreen quad). Demoscene rules:
fully analytic, palette entirely from the active theme (accent /
bright / success / warning / danger over the theme background — a theme
switch recolors the whole show), and every scene keeps moving in an
ambient attract mode when no audio flows.

- 3D Ridge: hidden-line perspective heightfield of the envelope's last
  ~3 s (Joy Division × 3DSS) — dark mountain bodies, theme-bright
  crests, star field. Fed by a 256×96 R32F history ring texture that
  costs one 1 KB row upload per ~33 ms.
- Tunnel: Amiga demo tunnel. The live waveform columns ARE the wall
  texture, mid-band energy launches bright rings down the bore, high
  band warms the spokes, RMS breathes the core; low band sways the
  vanishing point.
- Horizon: synthwave grid. The live waveform IS the mountain skyline,
  a scanline sun swells with RMS, grid speed rides the low band, peaks
  flash the horizon line.

Plumbing: three new ViewMode values + combo entries (GPU builds only —
CPU builds fall back to Graph and never offer them), fixed 256-column
reduction + 12-band Goertzel energies per frame, ~150 B uniform block.
Measured 213–220 µs/frame at 60 fps — showcase costs the same as the
utility modes. Classic modes regression-checked; -DAETHER_GPU_SPECTRUM=OFF
configuration builds and runs clean (the aethersdr#3955 Linux lesson).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bridge grabs of the three wavedemo.frag scenes (live FLEX-8400M RX) and
the Scope-mode regression check, referenced from PR aethersdr#3991.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ten9876 ten9876 force-pushed the feat/wave-viz-showcase branch from 3ff876f to 4863629 Compare July 4, 2026 14:40
@ten9876 ten9876 self-assigned this Jul 4, 2026

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review: request changes

Rebased onto current main (dropped #3955's now-merged commits; resolved the one renderGpuFrame keep-both conflict), both AETHER_GPU_SPECTRUM on/off configs build clean. The showcase itself is nicely crafted — the std140 uniform contract, history ring-index math, and enum/CPU-build safety all verified clean. But the review (multi-agent + adversarial verify) found a shared-path regression, a robustness issue that gates all GPU rendering, and a functional gap. Each inline comment carries a fix (apply-able suggestion, or a sketch where it spans files).

Blocking:

  1. texSize truncation regresses the shared non-demo path — 1px texture/image mismatch on fractional-DPR displays affects Graph/Bars/overlay (already-merged rendering), not just demo.
  2. A demo-only shader failure blanks the entire GPU widget + leaks per frame — the optional demo pipeline shouldn't be a hard prerequisite in the readiness guard.

Should fix in this pass:
3. Demo scenes freeze at idle — no animation timer, so the attract-mode/timeSec motion the shader is built around never runs without live audio.
4. Ridge reads an uninitialized history texture (~3 s of garbage on first entry / after device-loss) — seed it to zero.
5. NaN reaches the UBOstd::clamp doesn't sanitize; guard rms/peak/energy.

Notes (fast-follow): shader visual robustness (atan(0,0), horizon moiré, timeSec precision, negative-star), the ~96-tap Ridge loop cost, and a dead zoom uniform + demo-scene footer.

The core design (dedicated demo pipeline, procedural theme-driven scenes) is sound. I can push fixes for 1–5 if you'd like.

Comment thread src/gui/WaveformWidget.cpp Outdated
Comment thread src/gui/WaveformWidget.cpp Outdated
Comment thread src/gui/WaveformWidget.cpp Outdated
Comment thread src/gui/WaveformWidget.cpp
Comment thread src/gui/WaveformWidget.cpp
Comment thread src/gui/WaveformWidget.cpp Outdated
Comment thread resources/shaders/wavedemo.frag
Comment thread resources/shaders/wavedemo.frag
Comment thread src/gui/WaveformWidget.cpp
…solation, idle animation, hist seed, NaN guard (aethersdr#3991)

Review follow-ups for the showcase visualizations.

- texSize: size the grid/overlay textures with `size()*dpr` (qRound), matching
  m_gridCache/m_overlayImage, instead of truncating — a 1px mismatch on
  fractional DPR size-mismatched uploadTexture on the SHARED (non-demo) grid +
  overlay path (Graph/Bars). Fixes both the init and the render-path sizing.
- Demo pipeline isolation: drop m_demoPipeline from the initialize() readiness
  guard and gate the demo branch on it via `demo`. A missing/failed
  wavedemo.frag no longer blanks the whole GPU widget (Graph/Bars still render)
  and no longer re-inits every frame (unbounded QRhi leak); demo modes fall
  back to Graph.
- Idle animation: the demo scenes animate continuously (attract mode) but
  repaints are data-driven, so they froze without live audio. Drive ~60 fps
  repaints via m_demoAnimTimer only while a demo mode is active.
- History ring: zero-seed m_histTex once (m_histCleared) so Ridge doesn't
  sample undefined GPU memory (garbage/NaN ridgelines) for ~3 s on first entry
  and after every device-loss re-init; reset the flag + m_histHead in
  releaseResources().
- NaN: std::clamp doesn't sanitize NaN — guard rms/peak/energy before the UBO
  upload so a degenerate reduction can't blacken the frame.

Both AETHER_GPU_SPECTRUM on/off configs build clean.

Fixes Are Demonstrated (Principle XI).

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

ten9876 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Pushed fixes for the blocking + confirmed findings (commit `ba187aca`). Both `AETHER_GPU_SPECTRUM` on/off configs build clean locally.

  1. texSize (shared-path regression) — grid/overlay textures now sized size()*dpr (qRound), matching m_gridCache/m_overlayImage; no more 1px upload mismatch on fractional DPR. Fixed at both the init and render sizing sites.
  2. Demo pipeline isolation — dropped m_demoPipeline from the initialize() readiness guard and gated the demo branch on it (demo = isDemoMode(...) && m_demoPipeline); a missing/failed wavedemo.frag now falls back to Graph instead of blanking the whole GPU widget or re-init-leaking every frame.
  3. Idle animation — added m_demoAnimTimer (~60 fps) that runs only while a demo mode is active, so the attract-mode/timeSec motion animates without live audio.
  4. History ring — zero-seed m_histTex once (m_histCleared, reset in releaseResources()) so Ridge doesn't sample undefined GPU memory.
  5. NaN guard — sanitize rms/peak/energy before the UBO upload.

Deferred as fast-follows (advisory, non-blocking): the shader visual-robustness polish (atan(0,0), horizon moiré, timeSec precision, negative-star), the ~96-tap Ridge loop cost, and the dead zoom uniform / demo-scene footer. Happy to take those in a follow-up or leave to your polish pass.

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving the showcase after the rebase + review-follow-up fixes (texSize shared-path fix, demo-pipeline isolation, idle animation timer, history-ring seed, NaN guard). Both AETHER_GPU_SPECTRUM configs build clean locally; all review threads resolved. Set to auto-merge on CI.

@ten9876 ten9876 enabled auto-merge (squash) July 4, 2026 15:06
@ten9876 ten9876 merged commit f3086a3 into aethersdr:main Jul 4, 2026
5 checks passed
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.

2 participants