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
This commit is contained in:
Roman Gilg 2019-05-08 09:38:37 +02:00
parent 5a9df05460
commit a5c4c32f1c
4 changed files with 85 additions and 7 deletions

View file

@ -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<KWayland::Server::SurfaceInterface*>();
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>(), QRegion(0, 0, 10, 10));
QCOMPARE(serverSurface->damage(), QRegion(0, 0, 5, 5)); // scale is 2
QCOMPARE(damageSpy.first().first().value<QRegion>(), 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<QRegion>(), 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<QRegion>(), 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<QRegion>(), testRegion3);
QVERIFY(serverSurface->isMapped());
}
void TestWaylandSurface::testFrameCallback()

View file

@ -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)

View file

@ -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<Private>(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<Private>(resource)->damageBuffer(QRect(x, y, width, height));
}
void SurfaceInterface::Private::frameCallback(wl_client *client, wl_resource *resource, uint32_t callback)
{
auto s = cast<Private>(resource);

View file

@ -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;
};