diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2019-11-08 13:15:53 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2019-11-14 16:25:49 +0100 |
commit | 14bb413309092adc53e8451daff5690c4698c07d (patch) | |
tree | 8a80182fc46339a3fc39b70f580035ea46f6b1e8 /src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support | |
parent | 813e4460de0316f176d03f19f497e52ddc3c859e (diff) |
eglfs: kms: Read page flip events on a dedicated thread
Task-number: QTBUG-74953
Change-Id: I9a630c9245d8b0afe40ade9199cf4f1d358275da
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Diffstat (limited to 'src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support')
6 files changed, 333 insertions, 6 deletions
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro index 40806b6a9b..e51903ed96 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro @@ -14,9 +14,11 @@ CONFIG += egl SOURCES += $$PWD/qeglfskmsintegration.cpp \ $$PWD/qeglfskmsdevice.cpp \ - $$PWD/qeglfskmsscreen.cpp + $$PWD/qeglfskmsscreen.cpp \ + $$PWD/qeglfskmseventreader.cpp HEADERS += $$PWD/qeglfskmsintegration.h \ $$PWD/qeglfskmsdevice.h \ $$PWD/qeglfskmsscreen.h \ - $$PWD/qeglfskmshelpers.h + $$PWD/qeglfskmshelpers.h \ + $$PWD/qeglfskmseventreader.h diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h index fc83a620d9..34908aa60f 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h @@ -42,6 +42,7 @@ #define QEGLFSKMSDEVICE_H #include "private/qeglfsglobal_p.h" +#include "qeglfskmseventreader.h" #include <QtKmsSupport/private/qkmsdevice_p.h> QT_BEGIN_NAMESPACE @@ -55,6 +56,11 @@ public: bool isPrimary, const QPoint &virtualPos, const QList<QPlatformScreen *> &virtualSiblings) override; + + QEglFSKmsEventReader *eventReader() { return &m_eventReader; } + +protected: + QEglFSKmsEventReader m_eventReader; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp new file mode 100644 index 0000000000..645a0ae2e9 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmseventreader.h" +#include "qeglfskmsdevice.h" +#include <QSocketNotifier> +#include <QCoreApplication> +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) + +static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + Q_UNUSED(fd); + Q_UNUSED(sequence); + Q_UNUSED(tv_sec); + Q_UNUSED(tv_usec); + + QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread()); + t->eventHost()->handlePageFlipCompleted(user_data); +} + +class RegisterWaitFlipEvent : public QEvent +{ +public: + static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 1); + RegisterWaitFlipEvent(void *key, QMutex *mutex, QWaitCondition *cond) + : QEvent(TYPE), key(key), mutex(mutex), cond(cond) + { } + void *key; + QMutex *mutex; + QWaitCondition *cond; +}; + +bool QEglFSKmsEventHost::event(QEvent *event) +{ + if (event->type() == RegisterWaitFlipEvent::TYPE) { + RegisterWaitFlipEvent *e = static_cast<RegisterWaitFlipEvent *>(event); + PendingFlipWait *p = &pendingFlipWaits[0]; + PendingFlipWait *end = p + MAX_FLIPS; + while (p < end) { + if (!p->key) { + p->key = e->key; + p->mutex = e->mutex; + p->cond = e->cond; + updateStatus(); + return true; + } + ++p; + } + qWarning("Cannot queue page flip wait (more than %d screens?)", MAX_FLIPS); + e->mutex->lock(); + e->cond->wakeOne(); + e->mutex->unlock(); + return true; + } + return QObject::event(event); +} + +void QEglFSKmsEventHost::updateStatus() +{ + void **begin = &completedFlips[0]; + void **end = begin + MAX_FLIPS; + + for (int i = 0; i < MAX_FLIPS; ++i) { + PendingFlipWait *w = pendingFlipWaits + i; + if (!w->key) + continue; + + void **p = begin; + while (p < end) { + if (*p == w->key) { + *p = nullptr; + w->key = nullptr; + w->mutex->lock(); + w->cond->wakeOne(); + w->mutex->unlock(); + return; + } + ++p; + } + } +} + +void QEglFSKmsEventHost::handlePageFlipCompleted(void *key) +{ + void **begin = &completedFlips[0]; + void **end = begin + MAX_FLIPS; + void **p = begin; + while (p < end) { + if (*p == key) { + updateStatus(); + return; + } + ++p; + } + p = begin; + while (p < end) { + if (!*p) { + *p = key; + updateStatus(); + return; + } + ++p; + } + qWarning("Cannot store page flip status (more than %d screens?)", MAX_FLIPS); +} + +void QEglFSKmsEventReaderThread::run() +{ + qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop"); + + QSocketNotifier notifier(m_fd, QSocketNotifier::Read); + QObject::connect(¬ifier, &QSocketNotifier::activated, ¬ifier, [this] { + drmEventContext drmEvent; + memset(&drmEvent, 0, sizeof(drmEvent)); + drmEvent.version = 2; + drmEvent.vblank_handler = nullptr; + drmEvent.page_flip_handler = pageFlipHandler; + drmHandleEvent(m_fd, &drmEvent); + }); + + exec(); + + m_ev.moveToThread(thread()); // move back to the thread where m_ev was created + + qCDebug(qLcEglfsKmsDebug, "Event reader thread: event loop stopped"); +} + +QEglFSKmsEventReader::~QEglFSKmsEventReader() +{ + destroy(); +} + +void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) +{ + destroy(); + + if (!device) + return; + + m_device = device; + + qCDebug(qLcEglfsKmsDebug, "Initalizing event reader for device %p fd %d", + m_device, m_device->fd()); + + m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); + m_thread->start(); + + // Change thread affinity for the event host, so that postEvent() + // goes through the event reader thread's event loop for that object. + m_thread->eventHost()->moveToThread(m_thread); +} + +void QEglFSKmsEventReader::destroy() +{ + if (!m_device) + return; + + qCDebug(qLcEglfsKmsDebug, "Stopping event reader for device %p", m_device); + + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + + m_device = nullptr; +} + +void QEglFSKmsEventReader::startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond) +{ + if (m_thread) { + QCoreApplication::postEvent(m_thread->eventHost(), + new RegisterWaitFlipEvent(key, mutex, cond)); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.h new file mode 100644 index 0000000000..4aa285b0fe --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKKMSEVENTREADER_H +#define QEGLFSKKMSEVENTREADER_H + +#include "private/qeglfsglobal_p.h" +#include <QObject> +#include <QThread> +#include <QMutex> +#include <QWaitCondition> + +QT_BEGIN_NAMESPACE + +class QEglFSKmsDevice; + +struct QEglFSKmsEventHost : public QObject +{ + struct PendingFlipWait { + void *key; + QMutex *mutex; + QWaitCondition *cond; + }; + + static const int MAX_FLIPS = 32; + void *completedFlips[MAX_FLIPS] = {}; + QEglFSKmsEventHost::PendingFlipWait pendingFlipWaits[MAX_FLIPS] = {}; + + bool event(QEvent *event) override; + void updateStatus(); + void handlePageFlipCompleted(void *key); +}; + +class QEglFSKmsEventReaderThread : public QThread +{ +public: + QEglFSKmsEventReaderThread(int fd) : m_fd(fd) { } + void run() override; + QEglFSKmsEventHost *eventHost() { return &m_ev; } + +private: + int m_fd; + QEglFSKmsEventHost m_ev; +}; + +class Q_EGLFS_EXPORT QEglFSKmsEventReader +{ +public: + ~QEglFSKmsEventReader(); + + void create(QEglFSKmsDevice *device); + void destroy(); + + void startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond); + +private: + QEglFSKmsDevice *m_device = nullptr; + QEglFSKmsEventReaderThread *m_thread = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QEGLFSKKMSEVENTREADER_H diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp index b097f67e93..959f17eba3 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qeglfskmsscreen.h" +#include "qeglfskmsdevice.h" #include "qeglfsintegration_p.h" #include <QtCore/QLoggingCategory> @@ -68,7 +69,7 @@ private: QEglFSKmsScreen *m_screen; }; -QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless) +QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless) : QEglFSScreen(static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->display()) , m_device(device) , m_output(output) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h index 93d7e4c1eb..a5c8f5b4e8 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h @@ -51,12 +51,13 @@ QT_BEGIN_NAMESPACE +class QEglFSKmsDevice; class QEglFSKmsInterruptHandler; class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen { public: - QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless = false); + QEglFSKmsScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless = false); ~QEglFSKmsScreen(); void setVirtualPosition(const QPoint &pos); @@ -87,7 +88,7 @@ public: int currentMode() const override; int preferredMode() const override; - QKmsDevice *device() const { return m_device; } + QEglFSKmsDevice *device() const { return m_device; } virtual void waitForFlip(); @@ -103,7 +104,7 @@ public: void setCursorOutOfRange(bool b) { m_cursorOutOfRange = b; } protected: - QKmsDevice *m_device; + QEglFSKmsDevice *m_device; QKmsOutput m_output; QEdidParser m_edid; |