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.
This commit is contained in:
parent
b98ffaf785
commit
9e898c0e68
11 changed files with 138 additions and 18 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
~SceneDelegate() override;
|
||||
|
||||
Output *output() const;
|
||||
qreal scale() const;
|
||||
QRect viewport() const;
|
||||
|
||||
QRegion repaints() const override;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<SurfacePixmap> m_pixmap;
|
||||
std::unique_ptr<SurfacePixmap> m_previousPixmap;
|
||||
QMatrix4x4 m_surfaceToBufferMatrix;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -40,6 +40,7 @@ private Q_SLOTS:
|
|||
void handleSurfaceToBufferMatrixChanged();
|
||||
void handleSurfaceCommitted();
|
||||
void handleSurfaceSizeChanged();
|
||||
void handleBufferSizeChanged();
|
||||
|
||||
void handleChildSubSurfaceRemoved(KWaylandServer::SubSurfaceInterface *child);
|
||||
void handleChildSubSurfacesChanged();
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue