diff --git a/src/wayland/autotests/client/test_wayland_surface.cpp b/src/wayland/autotests/client/test_wayland_surface.cpp index f23dd86445..d669ec7765 100644 --- a/src/wayland/autotests/client/test_wayland_surface.cpp +++ b/src/wayland/autotests/client/test_wayland_surface.cpp @@ -264,6 +264,7 @@ void TestWaylandSurface::testDamage() QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); + s->setScale(2); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); @@ -292,14 +293,14 @@ void TestWaylandSurface::testDamage() s->damage(QRect(0, 0, 10, 10)); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); - QCOMPARE(serverSurface->damage(), QRegion(0, 0, 10, 10)); - QCOMPARE(damageSpy.first().first().value(), QRegion(0, 0, 10, 10)); + QCOMPARE(serverSurface->damage(), QRegion(0, 0, 5, 5)); // scale is 2 + QCOMPARE(damageSpy.first().first().value(), QRegion(0, 0, 5, 5)); QVERIFY(serverSurface->isMapped()); QCOMPARE(committedSpy.count(), 2); // damage multiple times QRegion testRegion(5, 8, 3, 6); - testRegion = testRegion.united(QRect(10, 20, 30, 15)); + testRegion = testRegion.united(QRect(10, 11, 6, 1)); img = QImage(QSize(40, 35), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); b = m_shm->createBuffer(img); @@ -312,6 +313,39 @@ void TestWaylandSurface::testDamage() QCOMPARE(damageSpy.first().first().value(), testRegion); QVERIFY(serverSurface->isMapped()); QCOMPARE(committedSpy.count(), 3); + + // damage buffer + const QRegion testRegion2(30, 40, 22, 4); + const QRegion cmpRegion2(15, 20, 11, 2); // divided by scale factor + img = QImage(QSize(80, 70), QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::black); + b = m_shm->createBuffer(img); + s->attachBuffer(b); + s->damageBuffer(testRegion2); + damageSpy.clear(); + s->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(damageSpy.wait()); + QCOMPARE(serverSurface->damage(), cmpRegion2); + QCOMPARE(damageSpy.first().first().value(), cmpRegion2); + QVERIFY(serverSurface->isMapped()); + + // combined regular damage and damaged buffer + const QRegion testRegion3 = testRegion.united(cmpRegion2); + img = QImage(QSize(80, 70), QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::black); + b = m_shm->createBuffer(img); + s->attachBuffer(b); + s->damage(testRegion); + s->damageBuffer(testRegion2); + damageSpy.clear(); + s->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(damageSpy.wait()); + QVERIFY(serverSurface->damage() != testRegion); + QVERIFY(serverSurface->damage() != testRegion2); + QVERIFY(serverSurface->damage() != cmpRegion2); + QCOMPARE(serverSurface->damage(), testRegion3); + QCOMPARE(damageSpy.first().first().value(), testRegion3); + QVERIFY(serverSurface->isMapped()); } void TestWaylandSurface::testFrameCallback() diff --git a/src/wayland/compositor_interface.cpp b/src/wayland/compositor_interface.cpp index 877f5015b9..82ffc355b2 100644 --- a/src/wayland/compositor_interface.cpp +++ b/src/wayland/compositor_interface.cpp @@ -51,7 +51,7 @@ private: static const quint32 s_version; }; -const quint32 CompositorInterface::Private::s_version = 3; +const quint32 CompositorInterface::Private::s_version = 4; CompositorInterface::Private::Private(CompositorInterface *q, Display *d) : Global::Private(d, &wl_compositor_interface, s_version) diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp index 1abe106dde..bc1f2294a8 100644 --- a/src/wayland/surface_interface.cpp +++ b/src/wayland/surface_interface.cpp @@ -274,7 +274,8 @@ const struct wl_surface_interface SurfaceInterface::Private::s_interface = { inputRegionCallback, commitCallback, bufferTransformCallback, - bufferScaleCallback + bufferScaleCallback, + damageBufferCallback }; #endif @@ -373,6 +374,7 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em if (bufferChanged) { target->buffer = buffer; target->damage = source->damage; + target->bufferDamage = source->bufferDamage; target->bufferIsSet = source->bufferIsSet; } if (childrenChanged) { @@ -439,10 +441,32 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em emit q->transformChanged(target->transform); } if (bufferChanged && emitChanged) { - if (target->buffer && !target->damage.isEmpty()) { + if (target->buffer && (!target->damage.isEmpty() || !target->bufferDamage.isEmpty())) { const QRegion windowRegion = QRegion(0, 0, q->size().width(), q->size().height()); if (!windowRegion.isEmpty()) { - target->damage = windowRegion.intersected(target->damage); + QRegion bufferDamage; + if (!target->bufferDamage.isEmpty()) { + typedef OutputInterface::Transform Tr; + const Tr tr = target->transform; + const qint32 sc = target->scale; + if (tr == Tr::Rotated90 || tr == Tr::Rotated270 || + tr == Tr::Flipped90 || tr == Tr::Flipped270) { + // calculate transformed + scaled buffer damage + for (const auto &rect : target->bufferDamage) { + const auto add = QRegion(rect.x() / sc, rect.y() / sc, rect.height() / sc, rect.width() / sc); + bufferDamage = bufferDamage.united(add); + } + } else if (sc != 1) { + // calculate scaled buffer damage + for (const auto &rect : target->bufferDamage) { + const auto add = QRegion(rect.x() / sc, rect.y() / sc, rect.width() / sc, rect.height() / sc); + bufferDamage = bufferDamage.united(add); + } + } else { + bufferDamage = target->bufferDamage; + } + } + target->damage = windowRegion.intersected(target->damage.united(bufferDamage)); if (emitChanged) { subSurfaceIsMapped = true; trackedDamage = trackedDamage.united(target->damage); @@ -529,6 +553,15 @@ void SurfaceInterface::Private::damage(const QRect &rect) pending.damage = pending.damage.united(rect); } +void SurfaceInterface::Private::damageBuffer(const QRect &rect) +{ + if (!pending.bufferIsSet || (pending.bufferIsSet && !pending.buffer)) { + // TODO: should we send an error? + return; + } + pending.bufferDamage = pending.bufferDamage.united(rect); +} + void SurfaceInterface::Private::setScale(qint32 scale) { pending.scale = scale; @@ -562,6 +595,7 @@ void SurfaceInterface::Private::attachBuffer(wl_resource *buffer, const QPoint & // got a null buffer, deletes content in next frame pending.buffer = nullptr; pending.damage = QRegion(); + pending.bufferDamage = QRegion(); return; } Q_Q(SurfaceInterface); @@ -602,6 +636,12 @@ void SurfaceInterface::Private::damageCallback(wl_client *client, wl_resource *r cast(resource)->damage(QRect(x, y, width, height)); } +void SurfaceInterface::Private::damageBufferCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(client) + cast(resource)->damageBuffer(QRect(x, y, width, height)); +} + void SurfaceInterface::Private::frameCallback(wl_client *client, wl_resource *resource, uint32_t callback) { auto s = cast(resource); diff --git a/src/wayland/surface_interface_p.h b/src/wayland/surface_interface_p.h index de5906e38d..c6103ba0db 100644 --- a/src/wayland/surface_interface_p.h +++ b/src/wayland/surface_interface_p.h @@ -39,6 +39,7 @@ class SurfaceInterface::Private : public Resource::Private public: struct State { QRegion damage = QRegion(); + QRegion bufferDamage = QRegion(); QRegion opaque = QRegion(); QRegion input = QRegion(); bool inputIsSet = false; @@ -114,6 +115,7 @@ private: } void swapStates(State *source, State *target, bool emitChanged); void damage(const QRect &rect); + void damageBuffer(const QRect &rect); void setScale(qint32 scale); void setTransform(OutputInterface::Transform transform); void addFrameCallback(uint32_t callback); @@ -133,6 +135,8 @@ private: static void bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform); // since version 3 static void bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale); + // since version 4 + static void damageBufferCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static const struct wl_surface_interface s_interface; };