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) {
|
switch (transform) {
|
||||||
case Output::Transform::Normal:
|
case Output::Transform::Normal:
|
||||||
dest.setX(rect.x());
|
dest.moveLeft(rect.x());
|
||||||
dest.setY(rect.y());
|
dest.moveTop(rect.y());
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Rotated90:
|
case Output::Transform::Rotated90:
|
||||||
dest.setX(bounds.height() - (rect.y() + rect.height()));
|
dest.moveLeft(bounds.height() - (rect.y() + rect.height()));
|
||||||
dest.setY(rect.x());
|
dest.moveTop(rect.x());
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Rotated180:
|
case Output::Transform::Rotated180:
|
||||||
dest.setX(bounds.width() - (rect.x() + rect.width()));
|
dest.moveLeft(bounds.width() - (rect.x() + rect.width()));
|
||||||
dest.setY(bounds.height() - (rect.y() + rect.height()));
|
dest.moveTop(bounds.height() - (rect.y() + rect.height()));
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Rotated270:
|
case Output::Transform::Rotated270:
|
||||||
dest.setX(rect.y());
|
dest.moveLeft(rect.y());
|
||||||
dest.setY(bounds.width() - (rect.x() + rect.width()));
|
dest.moveTop(bounds.width() - (rect.x() + rect.width()));
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Flipped:
|
case Output::Transform::Flipped:
|
||||||
dest.setX(bounds.width() - (rect.x() + rect.width()));
|
dest.moveLeft(bounds.width() - (rect.x() + rect.width()));
|
||||||
dest.setY(rect.y());
|
dest.moveTop(rect.y());
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Flipped90:
|
case Output::Transform::Flipped90:
|
||||||
dest.setX(rect.y());
|
dest.moveLeft(rect.y());
|
||||||
dest.setY(rect.x());
|
dest.moveTop(rect.x());
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Flipped180:
|
case Output::Transform::Flipped180:
|
||||||
dest.setX(rect.x());
|
dest.moveLeft(rect.x());
|
||||||
dest.setY(bounds.height() - (rect.y() + rect.height()));
|
dest.moveTop(bounds.height() - (rect.y() + rect.height()));
|
||||||
break;
|
break;
|
||||||
case Output::Transform::Flipped270:
|
case Output::Transform::Flipped270:
|
||||||
dest.setX(bounds.height() - (rect.y() + rect.height()));
|
dest.moveLeft(bounds.height() - (rect.y() + rect.height()));
|
||||||
dest.setY(bounds.width() - (rect.x() + rect.width()));
|
dest.moveTop(bounds.width() - (rect.x() + rect.width()));
|
||||||
break;
|
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)
|
void Item::scheduleRepaintInternal(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
const QRegion globalRegion = mapToGlobal(region);
|
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()
|
void Item::scheduleFrame()
|
||||||
{
|
{
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
|
|
|
@ -104,6 +104,7 @@ public:
|
||||||
|
|
||||||
void scheduleRepaint(const QRectF ®ion);
|
void scheduleRepaint(const QRectF ®ion);
|
||||||
void scheduleRepaint(const QRegion ®ion);
|
void scheduleRepaint(const QRegion ®ion);
|
||||||
|
void scheduleRepaint(SceneDelegate *delegate, const QRegion ®ion);
|
||||||
void scheduleFrame();
|
void scheduleFrame();
|
||||||
QRegion repaints(SceneDelegate *delegate) const;
|
QRegion repaints(SceneDelegate *delegate) const;
|
||||||
void resetRepaints(SceneDelegate *delegate);
|
void resetRepaints(SceneDelegate *delegate);
|
||||||
|
@ -137,6 +138,7 @@ private:
|
||||||
void removeChild(Item *item);
|
void removeChild(Item *item);
|
||||||
void updateBoundingRect();
|
void updateBoundingRect();
|
||||||
void scheduleRepaintInternal(const QRegion ®ion);
|
void scheduleRepaintInternal(const QRegion ®ion);
|
||||||
|
void scheduleRepaintInternal(SceneDelegate *delegate, const QRegion ®ion);
|
||||||
void markSortedChildItemsDirty();
|
void markSortedChildItemsDirty();
|
||||||
|
|
||||||
bool computeEffectiveVisibility() const;
|
bool computeEffectiveVisibility() const;
|
||||||
|
|
|
@ -54,6 +54,11 @@ Output *SceneDelegate::output() const
|
||||||
return m_output;
|
return m_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal SceneDelegate::scale() const
|
||||||
|
{
|
||||||
|
return m_output ? m_output->scale() : 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
QRect SceneDelegate::viewport() const
|
QRect SceneDelegate::viewport() const
|
||||||
{
|
{
|
||||||
return m_output ? m_output->geometry() : m_scene->geometry();
|
return m_output ? m_output->geometry() : m_scene->geometry();
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
~SceneDelegate() override;
|
~SceneDelegate() override;
|
||||||
|
|
||||||
Output *output() const;
|
Output *output() const;
|
||||||
|
qreal scale() const;
|
||||||
QRect viewport() const;
|
QRect viewport() const;
|
||||||
|
|
||||||
QRegion repaints() const override;
|
QRegion repaints() const override;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scene/surfaceitem.h"
|
#include "scene/surfaceitem.h"
|
||||||
|
#include "scene/scene.h"
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,36 @@ void SurfaceItem::setSurfaceToBufferMatrix(const QMatrix4x4 &matrix)
|
||||||
m_bufferToSurfaceMatrix = matrix.inverted();
|
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 SurfaceItem::mapFromBuffer(const QRegion ®ion) const
|
||||||
{
|
{
|
||||||
QRegion result;
|
QRegion result;
|
||||||
|
@ -34,10 +65,42 @@ QRegion SurfaceItem::mapFromBuffer(const QRegion ®ion) const
|
||||||
return result;
|
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)
|
void SurfaceItem::addDamage(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
m_damage += region;
|
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();
|
Q_EMIT damaged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,15 @@ public:
|
||||||
QMatrix4x4 surfaceToBufferMatrix() const;
|
QMatrix4x4 surfaceToBufferMatrix() const;
|
||||||
void setSurfaceToBufferMatrix(const QMatrix4x4 &matrix);
|
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;
|
QRegion mapFromBuffer(const QRegion ®ion) const;
|
||||||
|
|
||||||
void addDamage(const QRegion ®ion);
|
void addDamage(const QRegion ®ion);
|
||||||
|
@ -55,6 +64,9 @@ protected:
|
||||||
WindowQuadList buildQuads() const override;
|
WindowQuadList buildQuads() const override;
|
||||||
|
|
||||||
QRegion m_damage;
|
QRegion m_damage;
|
||||||
|
Output::Transform m_bufferTransform;
|
||||||
|
QRectF m_bufferSourceBox;
|
||||||
|
QSize m_bufferSize;
|
||||||
std::unique_ptr<SurfacePixmap> m_pixmap;
|
std::unique_ptr<SurfacePixmap> m_pixmap;
|
||||||
std::unique_ptr<SurfacePixmap> m_previousPixmap;
|
std::unique_ptr<SurfacePixmap> m_previousPixmap;
|
||||||
QMatrix4x4 m_surfaceToBufferMatrix;
|
QMatrix4x4 m_surfaceToBufferMatrix;
|
||||||
|
|
|
@ -22,6 +22,8 @@ SurfaceItemInternal::SurfaceItemInternal(InternalWindow *window, Scene *scene, I
|
||||||
this, &SurfaceItemInternal::handleBufferGeometryChanged);
|
this, &SurfaceItemInternal::handleBufferGeometryChanged);
|
||||||
|
|
||||||
setSize(window->bufferGeometry().size());
|
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.
|
// The device pixel ratio of the internal window is static.
|
||||||
QMatrix4x4 surfaceToBufferMatrix;
|
QMatrix4x4 surfaceToBufferMatrix;
|
||||||
|
@ -50,6 +52,8 @@ void SurfaceItemInternal::handleBufferGeometryChanged(const QRectF &old)
|
||||||
discardPixmap();
|
discardPixmap();
|
||||||
}
|
}
|
||||||
setSize(m_window->bufferGeometry().size());
|
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)
|
SurfacePixmapInternal::SurfacePixmapInternal(SurfaceItemInternal *item, QObject *parent)
|
||||||
|
|
|
@ -26,7 +26,7 @@ SurfaceItemWayland::SurfaceItemWayland(KWaylandServer::SurfaceInterface *surface
|
||||||
connect(surface, &KWaylandServer::SurfaceInterface::sizeChanged,
|
connect(surface, &KWaylandServer::SurfaceInterface::sizeChanged,
|
||||||
this, &SurfaceItemWayland::handleSurfaceSizeChanged);
|
this, &SurfaceItemWayland::handleSurfaceSizeChanged);
|
||||||
connect(surface, &KWaylandServer::SurfaceInterface::bufferSizeChanged,
|
connect(surface, &KWaylandServer::SurfaceInterface::bufferSizeChanged,
|
||||||
this, &SurfaceItemWayland::discardPixmap);
|
this, &SurfaceItemWayland::handleBufferSizeChanged);
|
||||||
|
|
||||||
connect(surface, &KWaylandServer::SurfaceInterface::childSubSurfacesChanged,
|
connect(surface, &KWaylandServer::SurfaceInterface::childSubSurfacesChanged,
|
||||||
this, &SurfaceItemWayland::handleChildSubSurfacesChanged);
|
this, &SurfaceItemWayland::handleChildSubSurfacesChanged);
|
||||||
|
@ -51,6 +51,9 @@ SurfaceItemWayland::SurfaceItemWayland(KWaylandServer::SurfaceInterface *surface
|
||||||
|
|
||||||
handleChildSubSurfacesChanged();
|
handleChildSubSurfacesChanged();
|
||||||
setSize(surface->size());
|
setSize(surface->size());
|
||||||
|
setBufferTransform(surface->bufferTransform());
|
||||||
|
setBufferSourceBox(surface->bufferSourceBox());
|
||||||
|
setBufferSize(surface->bufferSize());
|
||||||
setSurfaceToBufferMatrix(surface->surfaceToBufferMatrix());
|
setSurfaceToBufferMatrix(surface->surfaceToBufferMatrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +77,8 @@ KWaylandServer::SurfaceInterface *SurfaceItemWayland::surface() const
|
||||||
|
|
||||||
void SurfaceItemWayland::handleSurfaceToBufferMatrixChanged()
|
void SurfaceItemWayland::handleSurfaceToBufferMatrixChanged()
|
||||||
{
|
{
|
||||||
|
setBufferTransform(m_surface->bufferTransform());
|
||||||
|
setBufferSourceBox(m_surface->bufferSourceBox());
|
||||||
setSurfaceToBufferMatrix(m_surface->surfaceToBufferMatrix());
|
setSurfaceToBufferMatrix(m_surface->surfaceToBufferMatrix());
|
||||||
discardQuads();
|
discardQuads();
|
||||||
discardPixmap();
|
discardPixmap();
|
||||||
|
@ -84,6 +89,12 @@ void SurfaceItemWayland::handleSurfaceSizeChanged()
|
||||||
setSize(m_surface->size());
|
setSize(m_surface->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SurfaceItemWayland::handleBufferSizeChanged()
|
||||||
|
{
|
||||||
|
setBufferSize(m_surface->bufferSize());
|
||||||
|
discardPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
void SurfaceItemWayland::handleSurfaceCommitted()
|
void SurfaceItemWayland::handleSurfaceCommitted()
|
||||||
{
|
{
|
||||||
if (m_surface->hasFrameCallbacks()) {
|
if (m_surface->hasFrameCallbacks()) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ private Q_SLOTS:
|
||||||
void handleSurfaceToBufferMatrixChanged();
|
void handleSurfaceToBufferMatrixChanged();
|
||||||
void handleSurfaceCommitted();
|
void handleSurfaceCommitted();
|
||||||
void handleSurfaceSizeChanged();
|
void handleSurfaceSizeChanged();
|
||||||
|
void handleBufferSizeChanged();
|
||||||
|
|
||||||
void handleChildSubSurfaceRemoved(KWaylandServer::SubSurfaceInterface *child);
|
void handleChildSubSurfaceRemoved(KWaylandServer::SubSurfaceInterface *child);
|
||||||
void handleChildSubSurfacesChanged();
|
void handleChildSubSurfacesChanged();
|
||||||
|
|
|
@ -35,6 +35,8 @@ SurfaceItemX11::SurfaceItemX11(X11Window *window, Scene *scene, Item *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(window->bufferGeometry().size());
|
setSize(window->bufferGeometry().size());
|
||||||
|
setBufferSourceBox(QRectF(QPointF(0, 0), window->bufferGeometry().size()));
|
||||||
|
setBufferSize(window->bufferGeometry().size().toSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceItemX11::~SurfaceItemX11()
|
SurfaceItemX11::~SurfaceItemX11()
|
||||||
|
@ -144,6 +146,8 @@ void SurfaceItemX11::handleBufferGeometryChanged(const QRectF &old)
|
||||||
discardPixmap();
|
discardPixmap();
|
||||||
}
|
}
|
||||||
setSize(m_window->bufferGeometry().size());
|
setSize(m_window->bufferGeometry().size());
|
||||||
|
setBufferSourceBox(QRectF(QPointF(0, 0), m_window->bufferGeometry().size()));
|
||||||
|
setBufferSize(m_window->bufferGeometry().size().toSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceItemX11::handleShapeChanged()
|
void SurfaceItemX11::handleShapeChanged()
|
||||||
|
|
Loading…
Reference in a new issue