[server] Fix remote access buffer handling when output not bound
Summary: If a client has not bound a certain output do not directly return but try to deliver the buffer to other clients. If none of them has requested it, send bufferReleased signal immediately to let compositor know that the buffer should be cleaned up. Test Plan: Manually. Autotest planned. Reviewers: #kwin, #frameworks, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: jgrulich, davidedmundson, kde-frameworks-devel Tags: #frameworks Differential Revision: https://phabricator.kde.org/D15422
This commit is contained in:
parent
44f0d3ced0
commit
88fde0b4ce
2 changed files with 222 additions and 99 deletions
|
@ -1,5 +1,6 @@
|
|||
/********************************************************************
|
||||
Copyright 2016 Oleg Chernovskiy <kanedias@xaker.ru>
|
||||
Copyright 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -48,22 +49,108 @@ private Q_SLOTS:
|
|||
|
||||
void testSendReleaseSingle();
|
||||
void testSendReleaseMultiple();
|
||||
void testSendReleaseCrossScreen();
|
||||
void testSendClientGone();
|
||||
void testSendReceiveClientGone();
|
||||
|
||||
private:
|
||||
Display *m_display = nullptr;
|
||||
OutputInterface *m_outputInterface = nullptr;
|
||||
OutputInterface *m_outputInterface[2] = {nullptr};
|
||||
RemoteAccessManagerInterface *m_remoteAccessInterface = nullptr;
|
||||
ConnectionThread *m_connection = nullptr;
|
||||
QThread *m_thread = nullptr;
|
||||
EventQueue *m_queue = nullptr;
|
||||
Registry *m_registry = nullptr;
|
||||
Output *m_output = nullptr;
|
||||
};
|
||||
|
||||
class MockupClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MockupClient(QObject *parent = nullptr);
|
||||
~MockupClient();
|
||||
|
||||
void bindOutput(int index);
|
||||
|
||||
ConnectionThread *connection = nullptr;
|
||||
QThread *thread = nullptr;
|
||||
EventQueue *queue = nullptr;
|
||||
Registry *registry = nullptr;
|
||||
RemoteAccessManager *remoteAccess = nullptr;
|
||||
Output *outputs[2] = {nullptr};
|
||||
};
|
||||
|
||||
static const QString s_socketName = QStringLiteral("kwayland-test-remote-access-0");
|
||||
|
||||
MockupClient::MockupClient(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
// setup connection
|
||||
connection = new KWayland::Client::ConnectionThread;
|
||||
QSignalSpy connectedSpy(connection, &ConnectionThread::connected);
|
||||
QVERIFY(connectedSpy.isValid());
|
||||
connection->setSocketName(s_socketName);
|
||||
|
||||
thread = new QThread(this);
|
||||
connection->moveToThread(thread);
|
||||
thread->start();
|
||||
|
||||
connection->initConnection();
|
||||
QVERIFY(connectedSpy.wait());
|
||||
|
||||
queue = new EventQueue(this);
|
||||
queue->setup(connection);
|
||||
|
||||
registry = new Registry(this);
|
||||
QSignalSpy interfacesAnnouncedSpy(registry, &Registry::interfacesAnnounced);
|
||||
QVERIFY(interfacesAnnouncedSpy.isValid());
|
||||
registry->setEventQueue(queue);
|
||||
registry->create(connection);
|
||||
QVERIFY(registry->isValid());
|
||||
registry->setup();
|
||||
QVERIFY(interfacesAnnouncedSpy.wait());
|
||||
|
||||
remoteAccess = registry->createRemoteAccessManager(
|
||||
registry->interface(Registry::Interface::RemoteAccessManager).name,
|
||||
registry->interface(Registry::Interface::RemoteAccessManager).version,
|
||||
this);
|
||||
QVERIFY(remoteAccess->isValid());
|
||||
connection->flush();
|
||||
}
|
||||
|
||||
MockupClient::~MockupClient()
|
||||
{
|
||||
#define CLEANUP(variable) \
|
||||
if (variable) { \
|
||||
delete variable; \
|
||||
variable = nullptr; \
|
||||
}
|
||||
CLEANUP(outputs[0])
|
||||
CLEANUP(outputs[1])
|
||||
CLEANUP(remoteAccess)
|
||||
CLEANUP(queue)
|
||||
CLEANUP(registry)
|
||||
if (thread) {
|
||||
if (connection) {
|
||||
connection->flush();
|
||||
connection->deleteLater();
|
||||
connection = nullptr;
|
||||
}
|
||||
|
||||
thread->quit();
|
||||
thread->wait();
|
||||
delete thread;
|
||||
thread = nullptr;
|
||||
}
|
||||
#undef CLEANUP
|
||||
}
|
||||
|
||||
void MockupClient::bindOutput(int index)
|
||||
{
|
||||
// client-bound output
|
||||
outputs[index] = registry->createOutput(registry->interfaces(Registry::Interface::Output)[index].name,
|
||||
registry->interfaces(Registry::Interface::Output)[index].version,
|
||||
this);
|
||||
QVERIFY(outputs[index]->isValid());
|
||||
connection->flush();
|
||||
}
|
||||
|
||||
void RemoteAccessTest::init()
|
||||
{
|
||||
qRegisterMetaType<const BufferHandle *>();
|
||||
|
@ -76,42 +163,18 @@ void RemoteAccessTest::init()
|
|||
m_display->start();
|
||||
QVERIFY(m_display->isRunning());
|
||||
m_display->createShm();
|
||||
m_outputInterface = m_display->createOutput();
|
||||
m_outputInterface->create();
|
||||
|
||||
auto initOutputIface = [this](int i) {
|
||||
m_outputInterface[i] = m_display->createOutput();
|
||||
m_outputInterface[i]->create();
|
||||
};
|
||||
initOutputIface(0);
|
||||
initOutputIface(1);
|
||||
|
||||
m_remoteAccessInterface = m_display->createRemoteAccessManager();
|
||||
m_remoteAccessInterface->create();
|
||||
QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased);
|
||||
QVERIFY(bufferReleasedSpy.isValid());
|
||||
|
||||
// setup connection
|
||||
m_connection = new KWayland::Client::ConnectionThread;
|
||||
QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
|
||||
QVERIFY(connectedSpy.isValid());
|
||||
m_connection->setSocketName(s_socketName);
|
||||
|
||||
m_thread = new QThread(this);
|
||||
m_connection->moveToThread(m_thread);
|
||||
m_thread->start();
|
||||
|
||||
m_connection->initConnection();
|
||||
QVERIFY(connectedSpy.wait());
|
||||
|
||||
m_queue = new EventQueue(this);
|
||||
m_queue->setup(m_connection);
|
||||
|
||||
m_registry = new Registry(this);
|
||||
QSignalSpy interfacesAnnouncedSpy(m_registry, &Registry::interfacesAnnounced);
|
||||
QVERIFY(interfacesAnnouncedSpy.isValid());
|
||||
m_registry->setEventQueue(m_queue);
|
||||
m_registry->create(m_connection);
|
||||
QVERIFY(m_registry->isValid());
|
||||
m_registry->setup();
|
||||
QVERIFY(interfacesAnnouncedSpy.wait());
|
||||
|
||||
// client-bound output
|
||||
m_output = m_registry->createOutput(m_registry->interface(Registry::Interface::Output).name,
|
||||
m_registry->interface(Registry::Interface::Output).version,
|
||||
this);
|
||||
}
|
||||
|
||||
void RemoteAccessTest::cleanup()
|
||||
|
@ -121,22 +184,8 @@ void RemoteAccessTest::cleanup()
|
|||
delete variable; \
|
||||
variable = nullptr; \
|
||||
}
|
||||
CLEANUP(m_output)
|
||||
CLEANUP(m_queue)
|
||||
CLEANUP(m_registry)
|
||||
if (m_thread) {
|
||||
if (m_connection) {
|
||||
m_connection->flush();
|
||||
m_connection->deleteLater();
|
||||
m_connection = nullptr;
|
||||
}
|
||||
|
||||
m_thread->quit();
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
m_thread = nullptr;
|
||||
}
|
||||
|
||||
CLEANUP(m_outputInterface[0])
|
||||
CLEANUP(m_outputInterface[1])
|
||||
CLEANUP(m_remoteAccessInterface)
|
||||
CLEANUP(m_display)
|
||||
#undef CLEANUP
|
||||
|
@ -148,16 +197,13 @@ void RemoteAccessTest::testSendReleaseSingle()
|
|||
|
||||
// setup
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
auto client = m_registry->createRemoteAccessManager(
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).name,
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).version,
|
||||
this);
|
||||
QVERIFY(client->isValid());
|
||||
m_connection->flush();
|
||||
auto *client = new MockupClient(this);
|
||||
client->bindOutput(0);
|
||||
|
||||
m_display->dispatchEvents();
|
||||
|
||||
QVERIFY(m_remoteAccessInterface->isBound()); // we have one client now
|
||||
QSignalSpy bufferReadySpy(client, &RemoteAccessManager::bufferReady);
|
||||
QSignalSpy bufferReadySpy(client->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy.isValid());
|
||||
|
||||
BufferHandle *buf = new BufferHandle();
|
||||
|
@ -168,7 +214,7 @@ void RemoteAccessTest::testSendReleaseSingle()
|
|||
buf->setSize(50, 50);
|
||||
buf->setFormat(100500);
|
||||
buf->setStride(7800);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface, buf);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf);
|
||||
|
||||
// receive buffer
|
||||
QVERIFY(bufferReadySpy.wait());
|
||||
|
@ -193,7 +239,6 @@ void RemoteAccessTest::testSendReleaseSingle()
|
|||
// cleanup
|
||||
delete buf;
|
||||
delete client;
|
||||
m_connection->flush();
|
||||
m_display->dispatchEvents();
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
}
|
||||
|
@ -204,23 +249,16 @@ void RemoteAccessTest::testSendReleaseMultiple()
|
|||
|
||||
// setup
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
auto client1 = m_registry->createRemoteAccessManager(
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).name,
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).version,
|
||||
this);
|
||||
QVERIFY(client1->isValid());
|
||||
auto client2 = m_registry->createRemoteAccessManager(
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).name,
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).version,
|
||||
this);
|
||||
QVERIFY(client2->isValid());
|
||||
m_connection->flush();
|
||||
auto *client1 = new MockupClient(this);
|
||||
auto *client2 = new MockupClient(this);
|
||||
client1->bindOutput(0);
|
||||
client2->bindOutput(0);
|
||||
m_display->dispatchEvents();
|
||||
|
||||
QVERIFY(m_remoteAccessInterface->isBound()); // now we have 2 clients
|
||||
QSignalSpy bufferReadySpy1(client1, &RemoteAccessManager::bufferReady);
|
||||
|
||||
QSignalSpy bufferReadySpy1(client1->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy1.isValid());
|
||||
QSignalSpy bufferReadySpy2(client2, &RemoteAccessManager::bufferReady);
|
||||
QSignalSpy bufferReadySpy2(client2->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy2.isValid());
|
||||
|
||||
BufferHandle *buf = new BufferHandle();
|
||||
|
@ -231,10 +269,13 @@ void RemoteAccessTest::testSendReleaseMultiple()
|
|||
buf->setSize(50, 50);
|
||||
buf->setFormat(100500);
|
||||
buf->setStride(7800);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface, buf);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf);
|
||||
|
||||
// wait for event loop
|
||||
QVERIFY(bufferReadySpy1.wait());
|
||||
if (bufferReadySpy2.size() == 0) {
|
||||
QVERIFY(bufferReadySpy2.wait());
|
||||
}
|
||||
|
||||
// receive buffer at client 1
|
||||
QCOMPARE(bufferReadySpy1.size(), 1);
|
||||
|
@ -251,6 +292,9 @@ void RemoteAccessTest::testSendReleaseMultiple()
|
|||
// wait for event loop
|
||||
QVERIFY(paramsObtainedSpy1.wait());
|
||||
QCOMPARE(paramsObtainedSpy1.size(), 1);
|
||||
if (paramsObtainedSpy2.size() == 0) {
|
||||
QVERIFY(paramsObtainedSpy2.wait());
|
||||
}
|
||||
QCOMPARE(paramsObtainedSpy2.size(), 1);
|
||||
|
||||
// release
|
||||
|
@ -266,7 +310,92 @@ void RemoteAccessTest::testSendReleaseMultiple()
|
|||
delete buf;
|
||||
delete client1;
|
||||
delete client2;
|
||||
m_connection->flush();
|
||||
m_display->dispatchEvents();
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
}
|
||||
|
||||
void RemoteAccessTest::testSendReleaseCrossScreen()
|
||||
{
|
||||
// this test verifies that multiple buffers for multiple screens are sent to
|
||||
// multiple clients and returned back
|
||||
|
||||
// setup
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
auto *client1 = new MockupClient(this);
|
||||
auto *client2 = new MockupClient(this);
|
||||
client1->bindOutput(1);
|
||||
client2->bindOutput(0);
|
||||
m_display->dispatchEvents();
|
||||
QVERIFY(m_remoteAccessInterface->isBound()); // now we have 2 clients
|
||||
|
||||
QSignalSpy bufferReadySpy1(client1->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy1.isValid());
|
||||
QSignalSpy bufferReadySpy2(client2->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy2.isValid());
|
||||
|
||||
BufferHandle *buf1 = new BufferHandle();
|
||||
QTemporaryFile *tmpFile1 = new QTemporaryFile(this);
|
||||
tmpFile1->open();
|
||||
|
||||
BufferHandle *buf2 = new BufferHandle();
|
||||
QTemporaryFile *tmpFile2 = new QTemporaryFile(this);
|
||||
tmpFile2->open();
|
||||
|
||||
buf1->setFd(tmpFile1->handle());
|
||||
buf1->setSize(50, 50);
|
||||
buf1->setFormat(100500);
|
||||
buf1->setStride(7800);
|
||||
|
||||
buf2->setFd(tmpFile2->handle());
|
||||
buf2->setSize(100, 100);
|
||||
buf2->setFormat(100500);
|
||||
buf2->setStride(7800);
|
||||
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf1);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface[1], buf2);
|
||||
|
||||
// wait for event loop
|
||||
QVERIFY(bufferReadySpy1.wait());
|
||||
if (bufferReadySpy2.size() == 0) {
|
||||
QVERIFY(bufferReadySpy2.wait());
|
||||
}
|
||||
|
||||
// receive buffer at client 1
|
||||
QCOMPARE(bufferReadySpy1.size(), 1);
|
||||
auto rbuf1 = bufferReadySpy1.takeFirst()[1].value<const RemoteBuffer *>();
|
||||
QSignalSpy paramsObtainedSpy1(rbuf1, &RemoteBuffer::parametersObtained);
|
||||
QVERIFY(paramsObtainedSpy1.isValid());
|
||||
|
||||
// receive buffer at client 2
|
||||
QCOMPARE(bufferReadySpy2.size(), 1);
|
||||
auto rbuf2 = bufferReadySpy2.takeFirst()[1].value<const RemoteBuffer *>();
|
||||
QSignalSpy paramsObtainedSpy2(rbuf2, &RemoteBuffer::parametersObtained);
|
||||
QVERIFY(paramsObtainedSpy2.isValid());
|
||||
|
||||
// wait for event loop
|
||||
QVERIFY(paramsObtainedSpy1.wait());
|
||||
if (paramsObtainedSpy2.size() == 0) {
|
||||
QVERIFY(paramsObtainedSpy2.wait());
|
||||
}
|
||||
QCOMPARE(paramsObtainedSpy1.size(), 1);
|
||||
QCOMPARE(paramsObtainedSpy2.size(), 1);
|
||||
|
||||
// release
|
||||
QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased);
|
||||
QVERIFY(bufferReleasedSpy.isValid());
|
||||
delete rbuf1;
|
||||
QVERIFY(bufferReleasedSpy.wait());
|
||||
|
||||
delete rbuf2;
|
||||
QVERIFY(bufferReleasedSpy.wait());
|
||||
|
||||
QCOMPARE(bufferReleasedSpy.size(), 2);
|
||||
|
||||
// cleanup
|
||||
delete buf1;
|
||||
delete buf2;
|
||||
delete client1;
|
||||
delete client2;
|
||||
m_display->dispatchEvents();
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
}
|
||||
|
@ -275,16 +404,12 @@ void RemoteAccessTest::testSendClientGone()
|
|||
{
|
||||
// this test verifies that when buffer is sent and client is gone, server will release buffer correctly
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
auto client = m_registry->createRemoteAccessManager(
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).name,
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).version,
|
||||
this);
|
||||
QVERIFY(client->isValid());
|
||||
m_connection->flush();
|
||||
auto *client = new MockupClient(this);
|
||||
client->bindOutput(0);
|
||||
m_display->dispatchEvents();
|
||||
|
||||
QVERIFY(m_remoteAccessInterface->isBound()); // we have one client now
|
||||
QSignalSpy bufferReadySpy(client, &RemoteAccessManager::bufferReady);
|
||||
QSignalSpy bufferReadySpy(client->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy.isValid());
|
||||
|
||||
BufferHandle *buf = new BufferHandle();
|
||||
|
@ -295,7 +420,7 @@ void RemoteAccessTest::testSendClientGone()
|
|||
buf->setSize(50, 50);
|
||||
buf->setFormat(100500);
|
||||
buf->setStride(7800);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface, buf);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf);
|
||||
|
||||
// release forcefully
|
||||
QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased);
|
||||
|
@ -305,7 +430,6 @@ void RemoteAccessTest::testSendClientGone()
|
|||
|
||||
// cleanup
|
||||
delete buf;
|
||||
m_connection->flush();
|
||||
m_display->dispatchEvents();
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
}
|
||||
|
@ -315,16 +439,12 @@ void RemoteAccessTest::testSendReceiveClientGone()
|
|||
// this test verifies that when buffer is sent, received and client is gone,
|
||||
// both client and server will release buffer correctly
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
auto client = m_registry->createRemoteAccessManager(
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).name,
|
||||
m_registry->interface(Registry::Interface::RemoteAccessManager).version,
|
||||
this);
|
||||
QVERIFY(client->isValid());
|
||||
m_connection->flush();
|
||||
auto *client = new MockupClient(this);
|
||||
client->bindOutput(0);
|
||||
m_display->dispatchEvents();
|
||||
|
||||
QVERIFY(m_remoteAccessInterface->isBound()); // we have one client now
|
||||
QSignalSpy bufferReadySpy(client, &RemoteAccessManager::bufferReady);
|
||||
QSignalSpy bufferReadySpy(client->remoteAccess, &RemoteAccessManager::bufferReady);
|
||||
QVERIFY(bufferReadySpy.isValid());
|
||||
|
||||
BufferHandle *buf = new BufferHandle();
|
||||
|
@ -335,7 +455,7 @@ void RemoteAccessTest::testSendReceiveClientGone()
|
|||
buf->setSize(50, 50);
|
||||
buf->setFormat(100500);
|
||||
buf->setStride(7800);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface, buf);
|
||||
m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf);
|
||||
|
||||
// receive buffer
|
||||
QVERIFY(bufferReadySpy.wait());
|
||||
|
@ -359,11 +479,9 @@ void RemoteAccessTest::testSendReceiveClientGone()
|
|||
|
||||
// cleanup
|
||||
delete buf;
|
||||
m_connection->flush();
|
||||
m_display->dispatchEvents();
|
||||
QVERIFY(!m_remoteAccessInterface->isBound());
|
||||
}
|
||||
|
||||
|
||||
QTEST_GUILESS_MAIN(RemoteAccessTest)
|
||||
#include "test_remote_access.moc"
|
||||
|
|
|
@ -204,13 +204,18 @@ void RemoteAccessManagerInterface::Private::sendBufferReady(const OutputInterfac
|
|||
|
||||
// clients don't necessarily bind outputs
|
||||
if (boundScreens.isEmpty()) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// no reason for client to bind wl_output multiple times, send only to first one
|
||||
org_kde_kwin_remote_access_manager_send_buffer_ready(res, buf->fd(), boundScreens[0]);
|
||||
holder.counter++;
|
||||
}
|
||||
if (holder.counter == 0) {
|
||||
// buffer was not requested by any client
|
||||
emit q->bufferReleased(buf);
|
||||
return;
|
||||
}
|
||||
// store buffer locally, clients will ask it later
|
||||
sentBuffers[buf->fd()] = holder;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue