aethersdr / aethersdr/AetherSDR

Software-rendered spectrum heat-map fill saturates the main thread (934 ms/s painting at 25 fps on Pi 5) — UI freezes under any extra load

Open
#5,192 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug GUI maintainer-review priority: medium spectrum
Dominant language
C++
Stars
221
Forks
117
Avg merge
2d 7h
Merged PRs (30d)
299

Description

Summary

On a software-rendered platform (Raspberry Pi 5, Wayland/labwc, no GPU paint path), the spectrum heat-map fill costs ~37 ms per frame at the standard 25 fps — 934 ms of every second on the main thread is spent painting (AE's own renderstats: paintsPerSec=25.6, paintMsPerSec=934). The main thread runs permanently at ~95% paint saturation; any additional main-thread load (observed trigger: the disconnected state, plausibly its reconnect overlay animation) tips it into input starvation and the UI reads as frozen — live value readouts still tick, clicks go nowhere.

Main d40863c4, native ARM64 build, real FLEX-6300 / IC-9700 bench.

Evidence

  • Main thread pegged at 100% CPU (19,384 jiffies vs <1,000 for every worker thread — DSP/PanadapterStream near idle, so this is not FFT compute).
  • gdb samples land in the paint pipeline every time: SpectrumWidget::drawSpectrum (SpectrumWidget.cpp:15403, polyline point indexing), QTextLine::layout_helper, Qt Gui raster internals — different innermost frame each sample, so paints complete and immediately reschedule (cost problem, not a stuck loop).
  • renderstats during the event: paintsPerSec = 25.58, paintMsPerSec = 933.99 → ~36.5 ms/frame.
  • RSS flat at ~315 MB throughout (run under a systemd MemoryMax=2.5G scope): not a leak.
  • Recovery observed live: reconnecting the radio removed the extra load and the UI became responsive again, with paint cost unchanged.

The hot path

drawSpectrum's heat-map fill builds a QPolygonF trapezoid per FFT column per frame, each with per-column gradient color math (SpectrumWidget.cpp ~15398 onward). At ~500+ columns × 25 fps through the software rasterizer, that alone accounts for the budget. A QTextLine::layout_helper sample suggests per-frame text relayout rides along.

Knock-on effects worth knowing

  • While saturated, an IC-9700 RS-BA1 connect attempt failed twice (never attached), while a FLEX TCP connect succeeded — consistent with the UDP handshake being sensitive to main-thread starvation. Hypothesis, not proven.
  • On a marginal power supply this sustained load participated in genuine brownouts (5 V rail measured at 4.75 V idle on the old PSU — hardware issue, fixed; noted so nobody re-conflates the two).

Suggested directions (maintainer's call)

  • Cache the heat-map fill as a column-gradient image instead of per-frame trapezoid fills, or render columns into a QImage once per FFT update rather than per paint.
  • Auto-degrade on software render: detect paint budget overrun (the renderstats plumbing already measures it) and step down fps / disable the heat-map fill with a status hint.
  • The disconnected/reconnect overlay path deserves a look for animation cost on the same thread.

Full bench provenance (sampler CSVs, gdb transcripts) available on request.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in SpectrumWidget.cpp at SpectrumWidget::drawSpectrum around lines 15398-15403, then reproduce on a software-rendered Raspberry Pi setup while capturing the existing renderstats measurements. Compare the heat-map paint cost and any disconnected/reconnect overlay cost; done means the UI remains responsive under extra load without the reported main-thread paint saturation.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, raspberry-pi
Domain
computer-graphics, desktop, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.