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()