From 886773eef2cef3f50e580f6210e32650f26cd9a2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 3 Aug 2017 13:01:38 +0200 Subject: eglfs_kms: Output cloning support { "device": "/dev/dri/card0", "outputs": [ { "name": "HDMI1", "mode": "1920x1080" }, { "name": "DP1", "mode": "1920x1080", "clones": "HDMI1" } ] } Here, assuming the QScreen for DP1 is unused and the resolution is the same, DP1 will simply mirror whatever is on HDMI1. The plane-based mouse cursor is not currently supported. Same goes for any form of scaling since this simply scans out the same framebuffer on all target CRTCs. Task-number: QTBUG-62262 Change-Id: I391be204264284a1bff752ebc2a1dbe5c8592013 Reviewed-by: Andy Nichols --- .../eglfs_kms/qeglfskmsgbmdevice.cpp | 11 ++ .../eglfs_kms/qeglfskmsgbmdevice.h | 3 + .../eglfs_kms/qeglfskmsgbmscreen.cpp | 133 +++++++++++++++++---- .../eglfs_kms/qeglfskmsgbmscreen.h | 18 ++- 4 files changed, 141 insertions(+), 24 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp index e218d580a2..47752510b6 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp @@ -161,4 +161,15 @@ QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output) return screen; } +void QEglFSKmsGbmDevice::registerScreenCloning(QPlatformScreen *screen, + QPlatformScreen *screenThisScreenClones, + const QVector &screensCloningThisScreen) +{ + if (!screenThisScreenClones && screensCloningThisScreen.isEmpty()) + return; + + QEglFSKmsGbmScreen *gbmScreen = static_cast(screen); + gbmScreen->initCloning(screenThisScreenClones, screensCloningThisScreen); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h index 08ca28d48e..93be197792 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h @@ -68,6 +68,9 @@ public: void handleDrmEvent(); QPlatformScreen *createScreen(const QKmsOutput &output) override; + void registerScreenCloning(QPlatformScreen *screen, + QPlatformScreen *screenThisScreenClones, + const QVector &screensCloningThisScreen) override; private: Q_DISABLE_COPY(QEglFSKmsGbmDevice) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp index 55cfd90537..d3573a4512 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp @@ -114,7 +114,9 @@ QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &out , m_gbm_surface(Q_NULLPTR) , m_gbm_bo_current(Q_NULLPTR) , m_gbm_bo_next(Q_NULLPTR) + , m_flipPending(false) , m_cursor(Q_NULLPTR) + , m_cloneSource(Q_NULLPTR) { } @@ -163,32 +165,28 @@ void QEglFSKmsGbmScreen::resetSurface() m_gbm_surface = nullptr; } -void QEglFSKmsGbmScreen::waitForFlip() -{ - // Don't lock the mutex unless we actually need to - if (!m_gbm_bo_next) - return; - - QMutexLocker lock(&m_waitForFlipMutex); - while (m_gbm_bo_next) - static_cast(device())->handleDrmEvent(); -} - -void QEglFSKmsGbmScreen::flip() +void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones, + const QVector &screensCloningThisScreen) { - if (!m_gbm_surface) { - qWarning("Cannot sync before platform init!"); + // clone destinations need to know the clone source + const bool clonesAnother = screenThisScreenClones != nullptr; + if (clonesAnother && !screensCloningThisScreen.isEmpty()) { + qWarning("QEglFSKmsGbmScreen %s cannot be clone source and destination at the same time", qPrintable(name())); return; } - - m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); - if (!m_gbm_bo_next) { - qWarning("Could not lock GBM surface front buffer!"); - return; + if (clonesAnother) + m_cloneSource = static_cast(screenThisScreenClones); + + // clone sources need to know their additional destinations + for (QPlatformScreen *s : screensCloningThisScreen) { + CloneDestination d; + d.screen = static_cast(s); + m_cloneDests.append(d); } +} - FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); - +void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb) +{ QKmsOutput &op(output()); const int fd = device()->fd(); const uint32_t w = op.modes[op.mode].hdisplay; @@ -214,7 +212,7 @@ void QEglFSKmsGbmScreen::flip() qCDebug(qLcEglfsKmsDebug, "Setting mode for screen %s", qPrintable(name())); int ret = drmModeSetCrtc(fd, op.crtc_id, - fb->fb, + fb, 0, 0, &op.connector_id, 1, &op.modes[op.mode]); @@ -238,7 +236,48 @@ void QEglFSKmsGbmScreen::flip() qErrnoWarning(errno, "drmModeSetPlane failed"); } } +} + +void QEglFSKmsGbmScreen::waitForFlip() +{ + if (m_cloneSource) { + qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name())); + return; + } + + // Don't lock the mutex unless we actually need to + if (!m_gbm_bo_next) + return; + + QMutexLocker lock(&m_waitForFlipMutex); + while (m_gbm_bo_next) + static_cast(device())->handleDrmEvent(); +} +void QEglFSKmsGbmScreen::flip() +{ + if (m_cloneSource) { + qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name())); + return; + } + + if (!m_gbm_surface) { + qWarning("Cannot sync before platform init!"); + return; + } + + m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); + if (!m_gbm_bo_next) { + qWarning("Could not lock GBM surface front buffer!"); + return; + } + + FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); + ensureModeSet(fb->fb); + + QKmsOutput &op(output()); + const int fd = device()->fd(); + m_flipPending = true; int ret = drmModePageFlip(fd, op.crtc_id, fb->fb, @@ -246,13 +285,63 @@ void QEglFSKmsGbmScreen::flip() this); if (ret) { qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name())); + m_flipPending = false; gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); m_gbm_bo_next = Q_NULLPTR; + return; + } + + for (CloneDestination &d : m_cloneDests) { + if (d.screen != this) { + d.screen->ensureModeSet(fb->fb); + d.cloneFlipPending = true; + int ret = drmModePageFlip(fd, + d.screen->output().crtc_id, + fb->fb, + DRM_MODE_PAGE_FLIP_EVENT, + d.screen); + if (ret) { + qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name())); + d.cloneFlipPending = false; + } + } } } void QEglFSKmsGbmScreen::flipFinished() { + if (m_cloneSource) { + m_cloneSource->cloneDestFlipFinished(this); + return; + } + + m_flipPending = false; + updateFlipStatus(); +} + +void QEglFSKmsGbmScreen::cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen) +{ + for (CloneDestination &d : m_cloneDests) { + if (d.screen == cloneDestScreen) { + d.cloneFlipPending = false; + break; + } + } + updateFlipStatus(); +} + +void QEglFSKmsGbmScreen::updateFlipStatus() +{ + Q_ASSERT(!m_cloneSource); + + if (m_flipPending) + return; + + for (const CloneDestination &d : m_cloneDests) { + if (d.cloneFlipPending) + return; + } + if (m_gbm_bo_current) gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_current); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h index 3680afa7ec..f6ee68d1c4 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h @@ -62,25 +62,39 @@ public: gbm_surface *createSurface(); void resetSurface(); + void initCloning(QPlatformScreen *screenThisScreenClones, + const QVector &screensCloningThisScreen); + void waitForFlip() override; void flip() override; void flipFinished() override; private: + void ensureModeSet(uint32_t fb); + void cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen); + void updateFlipStatus(); + gbm_surface *m_gbm_surface; gbm_bo *m_gbm_bo_current; gbm_bo *m_gbm_bo_next; + bool m_flipPending; QScopedPointer m_cursor; struct FrameBuffer { - FrameBuffer() : fb(0) {} - uint32_t fb; + uint32_t fb = 0; }; static void bufferDestroyedHandler(gbm_bo *bo, void *data); FrameBuffer *framebufferForBufferObject(gbm_bo *bo); + QEglFSKmsGbmScreen *m_cloneSource; + struct CloneDestination { + QEglFSKmsGbmScreen *screen = nullptr; + bool cloneFlipPending = false; + }; + QVector m_cloneDests; + static QMutex m_waitForFlipMutex; }; -- cgit v1.2.3