From: Anders da Silva Rytter Hansen Date: Thu, 02 Jul 2026 13:00:00 +0000 Subject: [PATCH] Reduce per-frame compositor work during panel floating animation PanelView::updateMask() is called once per animation frame while the panel transitions between docked and floating. It calls KWindowEffects::enableBlurBehind() and enableBackgroundContrast() which update the compositor's effect region via X11/D-Bus. Doing this for every frame causes visible stutter when other animations are running. Additionally, the animation's valueChanged handler was calling positionAndResizePanel() every frame, which calls setGeometry(), emits availableScreenRegionChanged() and calls slideWindow() every frame even though the panel's X window geometry is constant during the animation. Coalesce the blur/contrast updates with a 50 ms single-shot timer and replace the per-frame positionAndResizePanel() call with a lightweight updateMask() call. Finalize geometry and effects once when the animation finishes. This keeps the QML geometry/opacity animation smooth while drastically reducing synchronous work sent to the compositor. --- shell/panelview.cpp | 19 +++++++++++++++++++ shell/panelview.h | 5 +++++ 2 files changed, 24 insertions(+) --- a/shell/panelview.h 2026-04-08 06:33:10.000000000 -0300 +++ b/shell/panelview.h 2026-07-02 13:09:59.011179892 -0300 @@ -8,6 +8,7 @@ #include #include +#include #include #include #include // For WId @@ -306,6 +307,7 @@ void adaptToScreen(); void handleQmlStatusChange(QQmlComponent::Status status); void updateMask(); + void applyPendingMaskUpdate(); void updateEnabledBorders(); void updatePadding(); void updateFloating(); @@ -363,6 +365,9 @@ LengthMode m_lengthMode; Plasma::Theme m_theme; QTimer m_unhideTimer; + QTimer m_maskThrottleTimer; + bool m_pendingMaskUpdate = false; + bool m_forceMaskUpdate = false; Plasma::Types::BackgroundHints m_backgroundHints; KSvg::FrameSvg::EnabledBorders m_enabledBorders = KSvg::FrameSvg::AllBorders; QPointer m_lastScreen; --- a/shell/panelview.cpp 2026-04-08 06:33:10.000000000 -0300 +++ b/shell/panelview.cpp 2026-07-02 13:39:01.337688294 -0300 @@ -111,6 +111,10 @@ m_strutsTimer.setSingleShot(true); connect(&m_strutsTimer, &QTimer::timeout, this, &PanelView::updateExclusiveZone); + m_maskThrottleTimer.setSingleShot(true); + m_maskThrottleTimer.setInterval(50ms); + connect(&m_maskThrottleTimer, &QTimer::timeout, this, &PanelView::applyPendingMaskUpdate); + connect(m_corona, &Plasma::Corona::editModeChanged, this, &PanelView::updateEditModeLabel); // Register enums @@ -1304,6 +1308,17 @@ return; } + if (m_floatingnessAnimation.state() == QAbstractAnimation::Running && !m_forceMaskUpdate) { + m_pendingMaskUpdate = true; + if (!m_maskThrottleTimer.isActive()) { + m_maskThrottleTimer.start(); + } + return; + } + + m_forceMaskUpdate = false; + m_pendingMaskUpdate = false; + // Popups now align to the mask, without it they appear in the wrong position // always create it and show blur and contrast when needed QRegion mask; @@ -1364,6 +1379,14 @@ } } +void PanelView::applyPendingMaskUpdate() +{ + if (m_pendingMaskUpdate) { + m_forceMaskUpdate = true; + updateMask(); + } +} + bool PanelView::canSetStrut() const { // read the wm name, need to do this every time which means a roundtrip unfortunately @@ -1587,10 +1610,10 @@ return; } m_floatingness = get(value); - positionAndResizePanel(); + updateMask(); }); connect(&m_floatingnessAnimation, &QPropertyAnimation::finished, rootObject, [this]() { - updateMask(); + positionAndResizePanel(); }); connect(rootObject, SIGNAL(minPanelHeightChanged()), this, SLOT(updatePadding())); connect(rootObject, SIGNAL(minPanelWidthChanged()), this, SLOT(updatePadding()));