Note on sequencing
Transparency up front: the reference implementation (PR #3656) already exists — it was built during an exploratory session to validate feasibility on real hybrid hardware. Per GOVERNANCE.md this is out of order (RFC should precede the PR). This RFC formalizes the design for maintainer sign-off, and #3656 is explicitly held — it will not merge until this RFC is approved. Filing it now rather than pretending the order was clean.
Problem
On multi-GPU systems — hybrid laptops especially (Intel/AMD iGPU + NVIDIA/AMD dGPU) — AetherSDR's GPU-rendered spectrum/waterfall (QRhiWidget) runs on whichever adapter the OS/compositor picks by default, which is almost always the integrated GPU. There is no in-app way to put the flagship panadapter render path on the discrete GPU.
Today the only related mechanism is the static NvOptimusEnablement = 1 / AmdPowerXpressRequestHighPerformance = 1 export in main.cpp (Windows-only, all-or-nothing, not user-selectable). Linux has nothing. A user who wants the 4090 driving their 4K/8K panadapter has to set PRIME/DRI_PRIME env vars by hand and launch from a terminal — not a flagship experience.
Proposal
A GPU selector in the panadapter Display menu, shown only when more than one GPU is present (so single-GPU users never see it):
- Enumerates available GPUs (Linux: DRM sysfs vendor/
boot_vga; Windows: DXGI EnumAdapters1), plus an "Auto (system default)" entry.
- The render adapter can't be hot-swapped under a live GL/D3D context, so the choice is persisted and applied on the next launch (with a "Restart to apply" note). Persistence is one nested-JSON
Graphics key (Principle V).
GpuSelector::applyAtStartup() runs in main() before QApplication and sets the platform-appropriate lever; it honors an explicit user env override and no-ops on Auto / a missing GPU.
Cross-platform impact
| Platform |
Render path |
Lever set at startup |
| Linux / Wayland (EGL) |
Qt EGL |
NVIDIA → __NV_PRIME_RENDER_OFFLOAD; AMD/Intel → DRI_PRIME=pci-<addr> |
| Linux / X11 (GLX) |
Qt GLX |
NVIDIA → __NV_PRIME_RENDER_OFFLOAD + __GLX_VENDOR_LIBRARY_NAME=nvidia; AMD/Intel → DRI_PRIME=pci-<addr> |
| Windows |
Qt D3D11/12 RHI |
QT_D3D_ADAPTER_INDEX (DXGI adapter index) |
| macOS |
Metal |
Selector hidden (Macs are effectively single-GPU / OS-managed); no-op |
Notable interactions to validate:
- Windows: the existing
NvOptimusEnablement export (driver-level dGPU hint) and QT_D3D_ADAPTER_INDEX (RHI adapter) operate at different layers; "Integrated" must actually land on the iGPU — needs hardware confirmation.
- Linux/Wayland NVIDIA offload requires a healthy driver (a fallen-over dGPU breaks all paths) — out of scope for the app.
Alternatives considered
- Keep the Windows-only
NvOptimusEnablement export, do nothing on Linux — the status quo: not user-selectable, Windows-only, all-or-nothing. Rejected (the actual gap is Linux + selectability).
.desktop PrefersNonDefaultGPU=true (Linux) — only affects menu launches (not ./AetherSDR from a terminal), GNOME/KDE-specific, and still all-or-nothing. Useful complement, not a substitute.
- Live (in-session) GPU switching — not feasible: the RHI context is bound to an adapter at creation; switching means recreating the context. Persist-and-restart is the honest model.
- Leave it to users' env vars — works, but it's exactly the non-flagship friction this removes.
Implementation scope (reference: PR #3656)
src/core/GpuSelector.{h,cpp} (enumeration + persistence + startup apply), a Display-menu dropdown in SpectrumOverlayMenu, the main.cpp call before QApplication, CMake (dxgi link on Windows). 6 files, ~370 lines.
Validation status (honest):
- ✅ NVIDIA offload verified on an Intel iGPU + NVIDIA RTX 4090 laptop under native Wayland (
renderD128, nvidia-smi shows AetherSDR as a graphics process).
- ⚠️ Linux AMD/Intel
DRI_PRIME path follows Mesa's documented PCI-tag semantics but is not soaked (no AMD-dGPU box here).
- ⚠️ Windows
QT_D3D_ADAPTER_INDEX is CI-built but needs a Windows hardware tester (incl. the NvOptimusEnablement-vs-Integrated interaction above).
Requesting maintainer approval (and any scope adjustments — e.g. whether to gate non-soaked platform paths behind validation) before merging #3656.
Note on sequencing
Transparency up front: the reference implementation (PR #3656) already exists — it was built during an exploratory session to validate feasibility on real hybrid hardware. Per GOVERNANCE.md this is out of order (RFC should precede the PR). This RFC formalizes the design for maintainer sign-off, and #3656 is explicitly held — it will not merge until this RFC is approved. Filing it now rather than pretending the order was clean.
Problem
On multi-GPU systems — hybrid laptops especially (Intel/AMD iGPU + NVIDIA/AMD dGPU) — AetherSDR's GPU-rendered spectrum/waterfall (
QRhiWidget) runs on whichever adapter the OS/compositor picks by default, which is almost always the integrated GPU. There is no in-app way to put the flagship panadapter render path on the discrete GPU.Today the only related mechanism is the static
NvOptimusEnablement = 1/AmdPowerXpressRequestHighPerformance = 1export inmain.cpp(Windows-only, all-or-nothing, not user-selectable). Linux has nothing. A user who wants the 4090 driving their 4K/8K panadapter has to set PRIME/DRI_PRIME env vars by hand and launch from a terminal — not a flagship experience.Proposal
A GPU selector in the panadapter Display menu, shown only when more than one GPU is present (so single-GPU users never see it):
boot_vga; Windows: DXGIEnumAdapters1), plus an "Auto (system default)" entry.Graphicskey (Principle V).GpuSelector::applyAtStartup()runs inmain()beforeQApplicationand sets the platform-appropriate lever; it honors an explicit user env override and no-ops on Auto / a missing GPU.Cross-platform impact
__NV_PRIME_RENDER_OFFLOAD; AMD/Intel →DRI_PRIME=pci-<addr>__NV_PRIME_RENDER_OFFLOAD+__GLX_VENDOR_LIBRARY_NAME=nvidia; AMD/Intel →DRI_PRIME=pci-<addr>QT_D3D_ADAPTER_INDEX(DXGI adapter index)Notable interactions to validate:
NvOptimusEnablementexport (driver-level dGPU hint) andQT_D3D_ADAPTER_INDEX(RHI adapter) operate at different layers; "Integrated" must actually land on the iGPU — needs hardware confirmation.Alternatives considered
NvOptimusEnablementexport, do nothing on Linux — the status quo: not user-selectable, Windows-only, all-or-nothing. Rejected (the actual gap is Linux + selectability)..desktopPrefersNonDefaultGPU=true(Linux) — only affects menu launches (not./AetherSDRfrom a terminal), GNOME/KDE-specific, and still all-or-nothing. Useful complement, not a substitute.Implementation scope (reference: PR #3656)
src/core/GpuSelector.{h,cpp}(enumeration + persistence + startup apply), a Display-menu dropdown inSpectrumOverlayMenu, themain.cppcall beforeQApplication, CMake (dxgilink on Windows). 6 files, ~370 lines.Validation status (honest):
renderD128,nvidia-smishows AetherSDR as a graphics process).DRI_PRIMEpath follows Mesa's documented PCI-tag semantics but is not soaked (no AMD-dGPU box here).QT_D3D_ADAPTER_INDEXis CI-built but needs a Windows hardware tester (incl. theNvOptimusEnablement-vs-Integrated interaction above).Requesting maintainer approval (and any scope adjustments — e.g. whether to gate non-soaked platform paths behind validation) before merging #3656.