| 9363 | #else // !AETHER_GPU_SPECTRUM |
| 9364 | |
| 9365 | void SpectrumWidget::paintEvent(QPaintEvent* ev) |
| 9366 | { |
| 9367 | if (width() <= 0 || height() <= freqScaleH() + DIVIDER_H + 2) return; |
| 9368 | |
| 9369 | #ifdef AETHER_GPU_SPECTRUM |
| 9370 | // GPU mode: render() handles everything via QRhi. Skip the full |
| 9371 | // QPainter path to avoid redundant rendering + compositing overhead. |
| 9372 | // This is the single biggest CPU optimization on macOS (100% → 20%). |
| 9373 | if (m_rhiInitialized) { |
| 9374 | SPECTRUM_BASE_CLASS::paintEvent(ev); |
| 9375 | return; |
| 9376 | } |
| 9377 | #endif |
| 9378 | |
| 9379 | // panstats: software-path paint cost (GPU builds only reach here before |
| 9380 | // QRhi init or after GPU teardown). |
| 9381 | m_panStats.paintEvents++; |
| 9382 | struct PaintCost { |
| 9383 | quint64& acc; |
| 9384 | QElapsedTimer t; |
| 9385 | explicit PaintCost(quint64& a) : acc(a) { t.start(); } |
| 9386 | ~PaintCost() { acc += static_cast<quint64>(t.nsecsElapsed() / 1000); } |
| 9387 | } panStatsPaintCost(m_panStats.paintUs); |
| 9388 | Q_UNUSED(ev); |
| 9389 | |
| 9390 | QElapsedTimer frameTimer; |
| 9391 | frameTimer.start(); |
| 9392 | |
| 9393 | QPainter p(this); |
| 9394 | p.setRenderHint(QPainter::Antialiasing, false); |
| 9395 | |
| 9396 | const int chromeH = freqScaleH() + DIVIDER_H; |
| 9397 | const int contentH = height() - chromeH; |
| 9398 | const int specH = static_cast<int>(contentH * m_spectrumFrac); |
| 9399 | const int wfH = contentH - specH; |
| 9400 | |
| 9401 | const int divY = specH; |
| 9402 | const int scaleY = specH + DIVIDER_H; |
| 9403 | const int wfY = scaleY + freqScaleH(); |
| 9404 | |
| 9405 | const QRect specRect (0, 0, width(), specH); |
| 9406 | const QRect divRect (0, divY, width(), DIVIDER_H); |
| 9407 | const QRect scaleRect(0, scaleY, width(), freqScaleH()); |
| 9408 | const QRect wfRect (0, wfY, width(), wfH); |
| 9409 | |
| 9410 | const bool is3D = (m_spectrumRenderMode == SpectrumRenderMode::Mode3D); |
| 9411 | |
| 9412 | // Spectrum region: the 3DSS surface, or the classic bg + grid + FFT trace. |
| 9413 | // 3DSS replaces ONLY the spectrum trace — the divider, freq scale, waterfall, |
| 9414 | // overlays, and scales below run identically in both modes. |
| 9415 | if (is3D) { |
| 9416 | // Cap the software surface (like the GPU path) so a HiDPI/maximized |
| 9417 | // window doesn't rebuild a multi-megapixel QImage every frame; the |
| 9418 | // surface is intrinsically low-res, so stretch it on draw. |
| 9419 | const QImage& surf = |
| 9420 | buildDssImage(specRect.size().boundedTo(QSize(kDssMaxW, kDssMaxH)), 0); |
| 9421 | if (!surf.isNull()) { |
| 9422 | p.drawImage(specRect, surf); |
nothing calls this directly
no test coverage detected