From 54b1699d1fa722a9944b91bed020f805ae48faab Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Thu, 23 Jul 2026 11:02:35 +0200 Subject: [PATCH] viewer: coalesce overlay rebuilds from navigation events updateOverlay() tears down and recreates every overlay item (spots, predictions, hkl labels, rings, ROIs). It was called directly from every scroll, wheel, pan and resize event. A single pan step emits valueChanged on both scrollbars (each -> onScroll -> updateOverlay) plus a direct call, so one visual change triggered ~3 full overlay rebuilds. Route the navigation hot paths through scheduleOverlayUpdate(), which sets a dirty flag and defers the rebuild to the next event-loop pass via QTimer::singleShot(0). Bursts within one handler collapse to a single rebuild. The synchronous one-offs (image load, ROI edit, viewport-lock apply, setZoom) keep calling updateOverlay() directly. Co-Authored-By: Claude Opus 4.8 (1M context) --- viewer/image_viewer/JFJochImage.cpp | 26 +++++++++++++++++++++----- viewer/image_viewer/JFJochImage.h | 5 +++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/viewer/image_viewer/JFJochImage.cpp b/viewer/image_viewer/JFJochImage.cpp index ff5752e0..c266660b 100644 --- a/viewer/image_viewer/JFJochImage.cpp +++ b/viewer/image_viewer/JFJochImage.cpp @@ -43,7 +43,7 @@ JFJochImage::JFJochImage(QWidget *parent) : QGraphicsView(parent) { } void JFJochImage::onScroll(int value) { - updateOverlay(); + scheduleOverlayUpdate(); } void JFJochImage::changeBackground(float val) { @@ -122,7 +122,7 @@ void JFJochImage::wheelEvent(QWheelEvent *event) { QPointF delta = targetScenePos - updatedViewportCenter; translate(delta.x(), delta.y()); // Shift the view - updateOverlay(); + scheduleOverlayUpdate(); emitViewportChanged(); } } @@ -140,7 +140,7 @@ void JFJochImage::resizeEvent(QResizeEvent *event) { if (!initial_fit_done_) fitToViewShorterSideOnce(); - updateOverlay(); + scheduleOverlayUpdate(); } QPointF JFJochImage::RoundPoint(const QPointF &input) { @@ -221,7 +221,7 @@ void JFJochImage::mouseMoveEvent(QMouseEvent *event) { horizontalScrollBar()->setValue(horizontalScrollBar()->value() - viewDelta.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() - viewDelta.y()); - updateOverlay(); + scheduleOverlayUpdate(); emitViewportChanged(); break; } @@ -272,7 +272,7 @@ void JFJochImage::mouseMoveEvent(QMouseEvent *event) { // Update hover state so overlay can draw arrows/handles accordingly if (h != hover_handle_) { hover_handle_ = h; - updateOverlay(); + scheduleOverlayUpdate(); } // Set an informative cursor switch (h) { @@ -837,6 +837,22 @@ void JFJochImage::resetScenePointers() { overlay_items_.clear(); } +void JFJochImage::scheduleOverlayUpdate() { + // Coalesce a burst of navigation events into one overlay rebuild. A single + // pan step, for instance, emits valueChanged on both scrollbars plus a + // direct call - 3 rebuilds for one visual change. Rebuilding tears down and + // recreates every spot/prediction/ROI item, so collapsing the duplicates to + // one rebuild on the next event-loop pass sharply cuts CPU and, over remote + // X forwarding, rasterised-pixel traffic. + if (overlay_update_scheduled_) + return; + overlay_update_scheduled_ = true; + QTimer::singleShot(0, this, [this]() { + overlay_update_scheduled_ = false; + updateOverlay(); + }); +} + void JFJochImage::updateOverlay() { if (!scene() || W * H <= 0) return; diff --git a/viewer/image_viewer/JFJochImage.h b/viewer/image_viewer/JFJochImage.h index a6955915..894ccd8b 100644 --- a/viewer/image_viewer/JFJochImage.h +++ b/viewer/image_viewer/JFJochImage.h @@ -17,6 +17,10 @@ class JFJochImage : public QGraphicsView { bool m_adjustForegroundWithWheel = false; + // Coalescing guard for scheduleOverlayUpdate(): collapses a burst of + // navigation events into a single overlay rebuild on the next event loop. + bool overlay_update_scheduled_ = false; + // Viewport-lock support: guard prevents the emit<->apply ping-pong between // two linked views, and the helper broadcasts the current transform+center. bool m_applyingViewport = false; @@ -104,6 +108,7 @@ protected: ResizeHandle hitTestROIHandle(const QPointF& scenePos, qreal tol = 3.0) const; void updateOverlay(); + void scheduleOverlayUpdate(); void GeneratePixmap(); void Redraw(); void CalcROI();