diff --git a/autotests/client/test_wayland_surface.cpp b/autotests/client/test_wayland_surface.cpp --- a/autotests/client/test_wayland_surface.cpp +++ b/autotests/client/test_wayland_surface.cpp @@ -264,6 +264,7 @@ 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 @@ 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 @@ 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/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -120,7 +120,7 @@ }; static const QMap s_interfaces = { {Registry::Interface::Compositor, { - 3, + 4, QByteArrayLiteral("wl_compositor"), &wl_compositor_interface, &Registry::compositorAnnounced, diff --git a/src/client/surface.h b/src/client/surface.h --- a/src/client/surface.h +++ b/src/client/surface.h @@ -149,12 +149,26 @@ void commit(CommitFlag flag = CommitFlag::FrameCallback); /** * Mark @p rect as damaged for the next frame. + * @see damageBuffer **/ void damage(const QRect &rect); /** * Mark @p region as damaged for the next frame. + * @see damageBuffer **/ void damage(const QRegion ®ion); + /** + * Mark @p rect in buffer coordinates as damaged for the next frame. + * @see damage + * @since 5.59 + **/ + void damageBuffer(const QRect &rect); + /** + * Mark @p region in buffer coordinates as damaged for the next frame. + * @see damage + * @since 5.59 + **/ + void damageBuffer(const QRegion ®ion); /** * Attaches the @p buffer to this Surface for the next frame. * @param buffer The buffer to attach to this Surface diff --git a/src/client/surface.cpp b/src/client/surface.cpp --- a/src/client/surface.cpp +++ b/src/client/surface.cpp @@ -239,6 +239,19 @@ wl_surface_damage(d->surface, rect.x(), rect.y(), rect.width(), rect.height()); } +void Surface::damageBuffer(const QRegion ®ion) +{ + for (const QRect &r : region) { + damageBuffer(r); + } +} + +void Surface::damageBuffer(const QRect &rect) +{ + Q_ASSERT(isValid()); + wl_surface_damage_buffer(d->surface, rect.x(), rect.y(), rect.width(), rect.height()); +} + void Surface::attachBuffer(wl_buffer *buffer, const QPoint &offset) { Q_ASSERT(isValid()); diff --git a/src/server/compositor_interface.cpp b/src/server/compositor_interface.cpp --- a/src/server/compositor_interface.cpp +++ b/src/server/compositor_interface.cpp @@ -51,7 +51,7 @@ 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/server/surface_interface.cpp b/src/server/surface_interface.cpp --- a/src/server/surface_interface.cpp +++ b/src/server/surface_interface.cpp @@ -274,7 +274,8 @@ inputRegionCallback, commitCallback, bufferTransformCallback, - bufferScaleCallback + bufferScaleCallback, + damageBufferCallback }; #endif @@ -373,6 +374,7 @@ if (bufferChanged) { target->buffer = buffer; target->damage = source->damage; + target->bufferDamage = source->bufferDamage; target->bufferIsSet = source->bufferIsSet; } if (childrenChanged) { @@ -439,10 +441,32 @@ 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 @@ 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 @@ // 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 @@ 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/server/surface_interface_p.h b/src/server/surface_interface_p.h --- a/src/server/surface_interface_p.h +++ b/src/server/surface_interface_p.h @@ -39,6 +39,7 @@ public: struct State { QRegion damage = QRegion(); + QRegion bufferDamage = QRegion(); QRegion opaque = QRegion(); QRegion input = QRegion(); bool inputIsSet = false; @@ -114,6 +115,7 @@ } 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 @@ 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; };