From 9e898c0e68879e9bc2a4bd0f756fa9b981cf1ca5 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 4 Jul 2023 09:19:08 +0300 Subject: [PATCH] scene: Expand surface damage if the surface is scaled If the surface item's contents is scaled, i.e. its scale factor doesn't match the output's scale, GL_LINEAR will be applied to smooth the contents. The unfortunate thing is that it's possible some of the changed pixels will bleed to the neighbor ones. In order to handle that scenario better, this change makes the SurfaceItem expand the damage if there's scale factor mismatch. bufferSourceBox and bufferTransform properties were introduced to detect if the surface contents is going to be scaled. bufferSourceBox covers both crop transform from wp_viewport and scale factor from wl_surface. bufferTransform is same as wl_surface's buffer transform property. --- src/core/output.cpp | 32 +++++++-------- src/scene/item.cpp | 17 ++++++++ src/scene/item.h | 2 + src/scene/scene.cpp | 5 +++ src/scene/scene.h | 1 + src/scene/surfaceitem.cpp | 65 +++++++++++++++++++++++++++++- src/scene/surfaceitem.h | 12 ++++++ src/scene/surfaceitem_internal.cpp | 4 ++ src/scene/surfaceitem_wayland.cpp | 13 +++++- src/scene/surfaceitem_wayland.h | 1 + src/scene/surfaceitem_x11.cpp | 4 ++ 11 files changed, 138 insertions(+), 18 deletions(-) diff --git a/src/core/output.cpp b/src/core/output.cpp index 30f47223ce..88bc085ce2 100644 --- a/src/core/output.cpp +++ b/src/core/output.cpp @@ -99,36 +99,36 @@ QRectF applyOutputTransform(const QRectF &rect, const QSizeF &bounds, Output::Tr switch (transform) { case Output::Transform::Normal: - dest.setX(rect.x()); - dest.setY(rect.y()); + dest.moveLeft(rect.x()); + dest.moveTop(rect.y()); break; case Output::Transform::Rotated90: - dest.setX(bounds.height() - (rect.y() + rect.height())); - dest.setY(rect.x()); + dest.moveLeft(bounds.height() - (rect.y() + rect.height())); + dest.moveTop(rect.x()); break; case Output::Transform::Rotated180: - dest.setX(bounds.width() - (rect.x() + rect.width())); - dest.setY(bounds.height() - (rect.y() + rect.height())); + dest.moveLeft(bounds.width() - (rect.x() + rect.width())); + dest.moveTop(bounds.height() - (rect.y() + rect.height())); break; case Output::Transform::Rotated270: - dest.setX(rect.y()); - dest.setY(bounds.width() - (rect.x() + rect.width())); + dest.moveLeft(rect.y()); + dest.moveTop(bounds.width() - (rect.x() + rect.width())); break; case Output::Transform::Flipped: - dest.setX(bounds.width() - (rect.x() + rect.width())); - dest.setY(rect.y()); + dest.moveLeft(bounds.width() - (rect.x() + rect.width())); + dest.moveTop(rect.y()); break; case Output::Transform::Flipped90: - dest.setX(rect.y()); - dest.setY(rect.x()); + dest.moveLeft(rect.y()); + dest.moveTop(rect.x()); break; case Output::Transform::Flipped180: - dest.setX(rect.x()); - dest.setY(bounds.height() - (rect.y() + rect.height())); + dest.moveLeft(rect.x()); + dest.moveTop(bounds.height() - (rect.y() + rect.height())); break; case Output::Transform::Flipped270: - dest.setX(bounds.height() - (rect.y() + rect.height())); - dest.setY(bounds.width() - (rect.x() + rect.width())); + dest.moveLeft(bounds.height() - (rect.y() + rect.height())); + dest.moveTop(bounds.width() - (rect.x() + rect.width())); break; } diff --git a/src/scene/item.cpp b/src/scene/item.cpp index 88f7ceee17..66622701fb 100644 --- a/src/scene/item.cpp +++ b/src/scene/item.cpp @@ -295,6 +295,13 @@ void Item::scheduleRepaint(const QRegion ®ion) } } +void Item::scheduleRepaint(SceneDelegate *delegate, const QRegion ®ion) +{ + if (isVisible()) { + scheduleRepaintInternal(delegate, region); + } +} + void Item::scheduleRepaintInternal(const QRegion ®ion) { const QRegion globalRegion = mapToGlobal(region); @@ -308,6 +315,16 @@ void Item::scheduleRepaintInternal(const QRegion ®ion) } } +void Item::scheduleRepaintInternal(SceneDelegate *delegate, const QRegion ®ion) +{ + const QRegion globalRegion = mapToGlobal(region); + const QRegion dirtyRegion = globalRegion & delegate->viewport(); + if (!dirtyRegion.isEmpty()) { + m_repaints[delegate] += dirtyRegion; + delegate->layer()->loop()->scheduleRepaint(this); + } +} + void Item::scheduleFrame() { if (!isVisible()) { diff --git a/src/scene/item.h b/src/scene/item.h index 740af90105..692df67f5d 100644 --- a/src/scene/item.h +++ b/src/scene/item.h @@ -104,6 +104,7 @@ public: void scheduleRepaint(const QRectF ®ion); void scheduleRepaint(const QRegion ®ion); + void scheduleRepaint(SceneDelegate *delegate, const QRegion ®ion); void scheduleFrame(); QRegion repaints(SceneDelegate *delegate) const; void resetRepaints(SceneDelegate *delegate); @@ -137,6 +138,7 @@ private: void removeChild(Item *item); void updateBoundingRect(); void scheduleRepaintInternal(const QRegion ®ion); + void scheduleRepaintInternal(SceneDelegate *delegate, const QRegion ®ion); void markSortedChildItemsDirty(); bool computeEffectiveVisibility() const; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index d187300bb8..622d32938e 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -54,6 +54,11 @@ Output *SceneDelegate::output() const return m_output; } +qreal SceneDelegate::scale() const +{ + return m_output ? m_output->scale() : 1.0; +} + QRect SceneDelegate::viewport() const { return m_output ? m_output->geometry() : m_scene->geometry(); diff --git a/src/scene/scene.h b/src/scene/scene.h index a41df2a6e7..a9ecf7f8a4 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -26,6 +26,7 @@ public: ~SceneDelegate() override; Output *output() const; + qreal scale() const; QRect viewport() const; QRegion repaints() const override; diff --git a/src/scene/surfaceitem.cpp b/src/scene/surfaceitem.cpp index 0341710cce..a43fabf314 100644 --- a/src/scene/surfaceitem.cpp +++ b/src/scene/surfaceitem.cpp @@ -5,6 +5,7 @@ */ #include "scene/surfaceitem.h" +#include "scene/scene.h" namespace KWin { @@ -25,6 +26,36 @@ void SurfaceItem::setSurfaceToBufferMatrix(const QMatrix4x4 &matrix) m_bufferToSurfaceMatrix = matrix.inverted(); } +QRectF SurfaceItem::bufferSourceBox() const +{ + return m_bufferSourceBox; +} + +void SurfaceItem::setBufferSourceBox(const QRectF &box) +{ + m_bufferSourceBox = box; +} + +Output::Transform SurfaceItem::bufferTransform() const +{ + return m_bufferTransform; +} + +void SurfaceItem::setBufferTransform(Output::Transform transform) +{ + m_bufferTransform = transform; +} + +QSize SurfaceItem::bufferSize() const +{ + return m_bufferSize; +} + +void SurfaceItem::setBufferSize(const QSize &size) +{ + m_bufferSize = size; +} + QRegion SurfaceItem::mapFromBuffer(const QRegion ®ion) const { QRegion result; @@ -34,10 +65,42 @@ QRegion SurfaceItem::mapFromBuffer(const QRegion ®ion) const return result; } +static QRegion expandRegion(const QRegion ®ion, const QMargins &padding) +{ + if (region.isEmpty()) { + return QRegion(); + } + + QRegion ret; + for (const QRect &rect : region) { + ret += rect.marginsAdded(padding); + } + + return ret; +} + void SurfaceItem::addDamage(const QRegion ®ion) { m_damage += region; - scheduleRepaint(mapFromBuffer(region)); + + const QRectF sourceBox = applyOutputTransform(m_bufferSourceBox, m_bufferSize, m_bufferTransform); + const qreal xScale = sourceBox.width() / size().width(); + const qreal yScale = sourceBox.height() / size().height(); + const QRegion logicalDamage = mapFromBuffer(region); + + const auto delegates = scene()->delegates(); + for (SceneDelegate *delegate : delegates) { + QRegion delegateDamage = logicalDamage; + const qreal delegateScale = delegate->scale(); + if (xScale != delegateScale || yScale != delegateScale) { + // Simplified version of ceil(ceil(0.5 * output_scale / surface_scale) / output_scale) + const int xPadding = std::ceil(0.5 / xScale); + const int yPadding = std::ceil(0.5 / yScale); + delegateDamage = expandRegion(delegateDamage, QMargins(xPadding, yPadding, xPadding, yPadding)); + } + scheduleRepaint(delegate, delegateDamage); + } + Q_EMIT damaged(); } diff --git a/src/scene/surfaceitem.h b/src/scene/surfaceitem.h index 8bfc6c4459..227579fd67 100644 --- a/src/scene/surfaceitem.h +++ b/src/scene/surfaceitem.h @@ -26,6 +26,15 @@ public: QMatrix4x4 surfaceToBufferMatrix() const; void setSurfaceToBufferMatrix(const QMatrix4x4 &matrix); + QRectF bufferSourceBox() const; + void setBufferSourceBox(const QRectF &box); + + Output::Transform bufferTransform() const; + void setBufferTransform(Output::Transform transform); + + QSize bufferSize() const; + void setBufferSize(const QSize &size); + QRegion mapFromBuffer(const QRegion ®ion) const; void addDamage(const QRegion ®ion); @@ -55,6 +64,9 @@ protected: WindowQuadList buildQuads() const override; QRegion m_damage; + Output::Transform m_bufferTransform; + QRectF m_bufferSourceBox; + QSize m_bufferSize; std::unique_ptr m_pixmap; std::unique_ptr m_previousPixmap; QMatrix4x4 m_surfaceToBufferMatrix; diff --git a/src/scene/surfaceitem_internal.cpp b/src/scene/surfaceitem_internal.cpp index 8d592fe39a..8ad764a4ad 100644 --- a/src/scene/surfaceitem_internal.cpp +++ b/src/scene/surfaceitem_internal.cpp @@ -22,6 +22,8 @@ SurfaceItemInternal::SurfaceItemInternal(InternalWindow *window, Scene *scene, I this, &SurfaceItemInternal::handleBufferGeometryChanged); setSize(window->bufferGeometry().size()); + setBufferSourceBox(QRectF(QPointF(0, 0), window->bufferGeometry().size())); + setBufferSize((window->bufferGeometry().size() * window->bufferScale()).toSize()); // The device pixel ratio of the internal window is static. QMatrix4x4 surfaceToBufferMatrix; @@ -50,6 +52,8 @@ void SurfaceItemInternal::handleBufferGeometryChanged(const QRectF &old) discardPixmap(); } setSize(m_window->bufferGeometry().size()); + setBufferSourceBox(QRectF(QPointF(0, 0), m_window->bufferGeometry().size())); + setBufferSize((m_window->bufferGeometry().size() * m_window->bufferScale()).toSize()); } SurfacePixmapInternal::SurfacePixmapInternal(SurfaceItemInternal *item, QObject *parent) diff --git a/src/scene/surfaceitem_wayland.cpp b/src/scene/surfaceitem_wayland.cpp index 18c0dda5ec..b2fc0c2714 100644 --- a/src/scene/surfaceitem_wayland.cpp +++ b/src/scene/surfaceitem_wayland.cpp @@ -26,7 +26,7 @@ SurfaceItemWayland::SurfaceItemWayland(KWaylandServer::SurfaceInterface *surface connect(surface, &KWaylandServer::SurfaceInterface::sizeChanged, this, &SurfaceItemWayland::handleSurfaceSizeChanged); connect(surface, &KWaylandServer::SurfaceInterface::bufferSizeChanged, - this, &SurfaceItemWayland::discardPixmap); + this, &SurfaceItemWayland::handleBufferSizeChanged); connect(surface, &KWaylandServer::SurfaceInterface::childSubSurfacesChanged, this, &SurfaceItemWayland::handleChildSubSurfacesChanged); @@ -51,6 +51,9 @@ SurfaceItemWayland::SurfaceItemWayland(KWaylandServer::SurfaceInterface *surface handleChildSubSurfacesChanged(); setSize(surface->size()); + setBufferTransform(surface->bufferTransform()); + setBufferSourceBox(surface->bufferSourceBox()); + setBufferSize(surface->bufferSize()); setSurfaceToBufferMatrix(surface->surfaceToBufferMatrix()); } @@ -74,6 +77,8 @@ KWaylandServer::SurfaceInterface *SurfaceItemWayland::surface() const void SurfaceItemWayland::handleSurfaceToBufferMatrixChanged() { + setBufferTransform(m_surface->bufferTransform()); + setBufferSourceBox(m_surface->bufferSourceBox()); setSurfaceToBufferMatrix(m_surface->surfaceToBufferMatrix()); discardQuads(); discardPixmap(); @@ -84,6 +89,12 @@ void SurfaceItemWayland::handleSurfaceSizeChanged() setSize(m_surface->size()); } +void SurfaceItemWayland::handleBufferSizeChanged() +{ + setBufferSize(m_surface->bufferSize()); + discardPixmap(); +} + void SurfaceItemWayland::handleSurfaceCommitted() { if (m_surface->hasFrameCallbacks()) { diff --git a/src/scene/surfaceitem_wayland.h b/src/scene/surfaceitem_wayland.h index 7be91f4280..a8eeb61d29 100644 --- a/src/scene/surfaceitem_wayland.h +++ b/src/scene/surfaceitem_wayland.h @@ -40,6 +40,7 @@ private Q_SLOTS: void handleSurfaceToBufferMatrixChanged(); void handleSurfaceCommitted(); void handleSurfaceSizeChanged(); + void handleBufferSizeChanged(); void handleChildSubSurfaceRemoved(KWaylandServer::SubSurfaceInterface *child); void handleChildSubSurfacesChanged(); diff --git a/src/scene/surfaceitem_x11.cpp b/src/scene/surfaceitem_x11.cpp index a0617aecd6..8c433a9187 100644 --- a/src/scene/surfaceitem_x11.cpp +++ b/src/scene/surfaceitem_x11.cpp @@ -35,6 +35,8 @@ SurfaceItemX11::SurfaceItemX11(X11Window *window, Scene *scene, Item *parent) } setSize(window->bufferGeometry().size()); + setBufferSourceBox(QRectF(QPointF(0, 0), window->bufferGeometry().size())); + setBufferSize(window->bufferGeometry().size().toSize()); } SurfaceItemX11::~SurfaceItemX11() @@ -144,6 +146,8 @@ void SurfaceItemX11::handleBufferGeometryChanged(const QRectF &old) discardPixmap(); } setSize(m_window->bufferGeometry().size()); + setBufferSourceBox(QRectF(QPointF(0, 0), m_window->bufferGeometry().size())); + setBufferSize(m_window->bufferGeometry().size().toSize()); } void SurfaceItemX11::handleShapeChanged()