Fix macOS GPU rendering: CPU optimization + build fix#719
Merged
Conversation
…tate detection Fixes ported from our GPU branch onto upstream's v0.8.0 codebase: 1. WA_NativeWindow + setApi(Metal) — required for QRhi initialization on macOS 2. Retina DPR overlay — render overlay QImages at device pixel resolution with setDevicePixelRatio() for sharp text on Retina displays 3. Overlay upload caching — skip 5.8MB texture upload when overlay content unchanged (only upload after m_overlayStaticDirty triggers repaint) 4. State change detection — monitor centerMhz, bandwidthMhz, refLevel, dynamicRange, spectrumFrac in renderGpuFrame to catch zoom/scroll that bypass markOverlayDirty() 5. Qt6GuiPrivate enabled on macOS (needed for rhi/qrhi.h) Known tradeoff: WA_NativeWindow on macOS adds ~75% CPU overhead from native window compositing (paintSiblingsRecursive). Without it, QRhi doesn't initialize (grey screen). This is a Qt/macOS limitation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…erlays Three optimizations that reduce macOS CPU from ~100% to ~35% idle: 1. paintEvent GPU guard: When QRhi is initialized, skip the full QPainter rendering path and delegate to SPECTRUM_BASE_CLASS::paintEvent() which triggers the GPU render() pipeline. The QPainter path was running the complete software rendering (grid, spectrum, waterfall, overlays) every frame even though render() already handles everything via Metal. 2. VFO cascade prevention: VfoWidget::updatePosition() was calling move() and raise() on 4-5 child widgets every frame from renderGpuFrame(). The raise() calls force z-order changes that trigger Qt's paintSiblingsRecursive, causing double rendering per frame. Fix: add position-change guard (skip if pos unchanged) and remove per-frame raise() calls (buttons are raised once on creation/activation). 3. Overlay dirty fix: setWfBlackLevel() was calling markOverlayDirty() 25x/sec from the auto-black calculation, forcing full QPainter overlay re-rendering every frame. Black level only affects waterfall, not the overlay (grid/scales/markers). Fix: use update() instead, and add change guards to setWfBlackLevel, setWfColorGain, setFrequencyRange, setDbmRange, and setSliceOverlay to skip when values unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The upstream CMakeLists.txt skips finding Qt6GuiPrivate on macOS to avoid Homebrew/aqtinstall conflicts. But Qt6GuiPrivate is required for QRhi headers (rhi/qrhi.h) used by the GPU spectrum rendering. With Homebrew-only builds (aqtinstall dropped in aethersdr#711), this conflict no longer applies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Jun 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Qt6GuiPrivateon macOS — required for QRhi headers (rhi/qrhi.h) since aqtinstall was dropped in macOS: drop aqtinstall, use Homebrew for everything #711m_overlayNeedsUploadflag), and render-time state change detectionrender()VfoWidget::updatePosition()calledraise()on 4 buttons every frame fromrenderGpuFrame(), forcingpaintSiblingsRecursiveand double GPU rendering per frame. Fixed with position-change guards and removing per-frameraise()setWfBlackLevel()calledmarkOverlayDirty()25x/sec from auto-black calculation, forcing full QPainter overlay re-rendering every frame. Waterfall params don't affect the overlay. Added change guards tosetWfBlackLevel,setWfColorGain,setFrequencyRange,setDbmRange, andsetSliceOverlayProfiling results (macOS, Apple Silicon, Metal backend)
Remaining ~35% is primarily unavoidable Qt
WA_NativeWindowbacking store compositing overhead (QCALayerBackingStore::flushSubWindowsoftware blit).Test plan
QRhi initialized, backend: Metalin log🤖 Generated with Claude Code