From a5c4c32f1ce759874b5f53bb635b39b4d63cfe1a Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Wed, 8 May 2019 09:38:37 +0200 Subject: [PATCH] Implement wl_surface::damage_buffer Summary: Missing from our surface handling was the damage_buffer call introduced in version 4 of the wl_compositor interface. Its only difference to a normal damage call is that the damaged region is supposed to be defined by the client in buffer coordinates instead of surface coordinates. This damage must be tracked separately in KWayland and on commit with the buffer transformation united with the normal damage. Test Plan: Autotest updated. Reviewers: #kwin, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: davidedmundson, zzag, kde-frameworks-devel Tags: #frameworks Differential Revision: https://phabricator.kde.org/D15910 --- .../autotests/client/test_wayland_surface.cpp | 40 ++++++++++++++-- src/wayland/compositor_interface.cpp | 2 +- src/wayland/surface_interface.cpp | 46 +++++++++++++++++-- src/wayland/surface_interface_p.h | 4 ++ 4 files changed, 85 insertions(+), 7 deletions(-) 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; };