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:
parent
5a9df05460
commit
a5c4c32f1c
4 changed files with 85 additions and 7 deletions
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue