summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2017-08-03 13:01:38 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-09-05 10:30:40 +0000
commit886773eef2cef3f50e580f6210e32650f26cd9a2 (patch)
tree53b67ef1ba34392346f6272d090c7741361ff703
parent372f5e1faeece85691bd0844b3871b5717d958c2 (diff)
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 <andy.nichols@qt.io>
-rw-r--r--src/platformsupport/kmsconvenience/qkmsdevice.cpp108
-rw-r--r--src/platformsupport/kmsconvenience/qkmsdevice_p.h14
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp11
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h3
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp133
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h18
6 files changed, 225 insertions, 62 deletions
diff --git a/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/src/platformsupport/kmsconvenience/qkmsdevice.cpp
index d8eb2f11de..8d8be4a919 100644
--- a/src/platformsupport/kmsconvenience/qkmsdevice.cpp
+++ b/src/platformsupport/kmsconvenience/qkmsdevice.cpp
@@ -162,8 +162,9 @@ static bool parseModeline(const QByteArray &text, drmModeModeInfoPtr mode)
QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources,
drmModeConnectorPtr connector,
- VirtualDesktopInfo *vinfo)
+ ScreenInfo *vinfo)
{
+ Q_ASSERT(vinfo);
const QByteArray connectorName = nameForConnector(connector);
const int crtc = crtcForConnector(resources, connector);
@@ -200,18 +201,17 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources,
qWarning("Invalid mode \"%s\" for output %s", mode.constData(), connectorName.constData());
configuration = OutputConfigPreferred;
}
- if (vinfo) {
- *vinfo = VirtualDesktopInfo();
- vinfo->virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt();
- if (userConnectorConfig.contains(QStringLiteral("virtualPos"))) {
- const QByteArray vpos = userConnectorConfig.value(QStringLiteral("virtualPos")).toByteArray();
- const QByteArrayList vposComp = vpos.split(',');
- if (vposComp.count() == 2)
- vinfo->virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt());
- }
- if (userConnectorConfig.value(QStringLiteral("primary")).toBool())
- vinfo->isPrimary = true;
+
+ *vinfo = ScreenInfo();
+ vinfo->virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt();
+ if (userConnectorConfig.contains(QStringLiteral("virtualPos"))) {
+ const QByteArray vpos = userConnectorConfig.value(QStringLiteral("virtualPos")).toByteArray();
+ const QByteArrayList vposComp = vpos.split(',');
+ if (vposComp.count() == 2)
+ vinfo->virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt());
}
+ if (userConnectorConfig.value(QStringLiteral("primary")).toBool())
+ vinfo->isPrimary = true;
const uint32_t crtc_id = resources->crtcs[crtc];
@@ -359,24 +359,28 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources,
drmFormat = DRM_FORMAT_XRGB8888;
}
- QKmsOutput output = {
- QString::fromUtf8(connectorName),
- connector->connector_id,
- crtc_id,
- physSize,
- preferred >= 0 ? preferred : selected_mode,
- selected_mode,
- false, // mode_set
- drmModeGetCrtc(m_dri_fd, crtc_id), // saved_crtc
- modes,
- connector->subpixel,
- connectorProperty(connector, QByteArrayLiteral("DPMS")),
- connectorPropertyBlob(connector, QByteArrayLiteral("EDID")),
- false, // wants_plane
- 0, // plane_id
- false, // plane_set
- drmFormat
- };
+ const QString cloneSource = userConnectorConfig.value(QStringLiteral("clones")).toString();
+ if (!cloneSource.isEmpty())
+ qCDebug(qLcKmsDebug) << "Output" << connectorName << " clones output " << cloneSource;
+
+ QKmsOutput output;
+ output.name = QString::fromUtf8(connectorName);
+ output.connector_id = connector->connector_id;
+ output.crtc_id = crtc_id;
+ output.physical_size = physSize;
+ output.preferred_mode = preferred >= 0 ? preferred : selected_mode;
+ output.mode = selected_mode;
+ output.mode_set = false;
+ output.saved_crtc = drmModeGetCrtc(m_dri_fd, crtc_id);
+ output.modes = modes;
+ output.subpixel = connector->subpixel;
+ output.dpms_prop = connectorProperty(connector, QByteArrayLiteral("DPMS"));
+ output.edid_blob = connectorPropertyBlob(connector, QByteArrayLiteral("EDID"));
+ output.wants_plane = false;
+ output.plane_id = 0;
+ output.plane_set = false;
+ output.drm_format = drmFormat;
+ output.clone_source = cloneSource;
bool ok;
int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_PLANE_INDEX", &ok);
@@ -401,6 +405,8 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources,
m_crtc_allocator |= (1 << output.crtc_id);
m_connector_allocator |= (1 << output.connector_id);
+ vinfo->output = output;
+
return createScreen(output);
}
@@ -461,10 +467,10 @@ QKmsDevice::~QKmsDevice()
struct OrderedScreen
{
OrderedScreen() : screen(nullptr) { }
- OrderedScreen(QPlatformScreen *screen, const QKmsDevice::VirtualDesktopInfo &vinfo)
+ OrderedScreen(QPlatformScreen *screen, const QKmsDevice::ScreenInfo &vinfo)
: screen(screen), vinfo(vinfo) { }
QPlatformScreen *screen;
- QKmsDevice::VirtualDesktopInfo vinfo;
+ QKmsDevice::ScreenInfo vinfo;
};
QDebug operator<<(QDebug dbg, const OrderedScreen &s)
@@ -511,7 +517,7 @@ void QKmsDevice::createScreens()
if (!connector)
continue;
- VirtualDesktopInfo vinfo;
+ ScreenInfo vinfo;
QPlatformScreen *screen = createScreenForConnector(resources, connector, &vinfo);
if (screen)
screens.append(OrderedScreen(screen, vinfo));
@@ -526,6 +532,32 @@ void QKmsDevice::createScreens()
std::stable_sort(screens.begin(), screens.end(), orderedScreenLessThan);
qCDebug(qLcKmsDebug) << "Sorted screen list:" << screens;
+ // The final list of screens is available, so do the second phase setup.
+ // Hook up clone sources and targets.
+ for (const OrderedScreen &orderedScreen : screens) {
+ QVector<QPlatformScreen *> screensCloningThisScreen;
+ for (const OrderedScreen &s : screens) {
+ if (s.vinfo.output.clone_source == orderedScreen.vinfo.output.name)
+ screensCloningThisScreen.append(s.screen);
+ }
+ QPlatformScreen *screenThisScreenClones = nullptr;
+ if (!orderedScreen.vinfo.output.clone_source.isEmpty()) {
+ for (const OrderedScreen &s : screens) {
+ if (s.vinfo.output.name == orderedScreen.vinfo.output.clone_source) {
+ screenThisScreenClones = s.screen;
+ break;
+ }
+ }
+ }
+ if (screenThisScreenClones)
+ qCDebug(qLcKmsDebug) << orderedScreen.screen->name() << "clones" << screenThisScreenClones;
+ if (!screensCloningThisScreen.isEmpty())
+ qCDebug(qLcKmsDebug) << orderedScreen.screen->name() << "is cloned by" << screensCloningThisScreen;
+
+ registerScreenCloning(orderedScreen.screen, screenThisScreenClones, screensCloningThisScreen);
+ }
+
+ // Figure out the virtual desktop and register the screens to QPA/QGuiApplication.
QPoint pos(0, 0);
QList<QPlatformScreen *> siblings;
QVector<QPoint> virtualPositions;
@@ -567,6 +599,16 @@ void QKmsDevice::createScreens()
}
}
+// not all subclasses support screen cloning
+void QKmsDevice::registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ Q_UNUSED(screen);
+ Q_UNUSED(screenThisScreenClones);
+ Q_UNUSED(screensCloningThisScreen);
+}
+
int QKmsDevice::fd() const
{
return m_dri_fd;
diff --git a/src/platformsupport/kmsconvenience/qkmsdevice_p.h b/src/platformsupport/kmsconvenience/qkmsdevice_p.h
index c33a958f4c..14e430351c 100644
--- a/src/platformsupport/kmsconvenience/qkmsdevice_p.h
+++ b/src/platformsupport/kmsconvenience/qkmsdevice_p.h
@@ -113,6 +113,7 @@ struct QKmsOutput
uint32_t plane_id;
bool plane_set;
uint32_t drm_format;
+ QString clone_source;
void restoreMode(QKmsDevice *device);
void cleanup(QKmsDevice *device);
@@ -123,11 +124,11 @@ struct QKmsOutput
class QKmsDevice
{
public:
- struct VirtualDesktopInfo {
- VirtualDesktopInfo() : virtualIndex(0), isPrimary(false) { }
- int virtualIndex;
+ struct ScreenInfo {
+ int virtualIndex = 0;
QPoint virtualPos;
- bool isPrimary;
+ bool isPrimary = false;
+ QKmsOutput output;
};
QKmsDevice(QKmsScreenConfig *screenConfig, const QString &path = QString());
@@ -146,6 +147,9 @@ public:
protected:
virtual QPlatformScreen *createScreen(const QKmsOutput &output) = 0;
+ virtual void registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen);
virtual void registerScreen(QPlatformScreen *screen,
bool isPrimary,
const QPoint &virtualPos,
@@ -155,7 +159,7 @@ protected:
int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector);
QPlatformScreen *createScreenForConnector(drmModeResPtr resources,
drmModeConnectorPtr connector,
- VirtualDesktopInfo *vinfo);
+ ScreenInfo *vinfo);
drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name);
drmModePropertyBlobPtr connectorPropertyBlob(drmModeConnectorPtr connector, const QByteArray &name);
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<QPlatformScreen *> &screensCloningThisScreen)
+{
+ if (!screenThisScreenClones && screensCloningThisScreen.isEmpty())
+ return;
+
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(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<QPlatformScreen *> &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<QEglFSKmsGbmDevice *>(device())->handleDrmEvent();
-}
-
-void QEglFSKmsGbmScreen::flip()
+void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &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<QEglFSKmsGbmScreen *>(screenThisScreenClones);
+
+ // clone sources need to know their additional destinations
+ for (QPlatformScreen *s : screensCloningThisScreen) {
+ CloneDestination d;
+ d.screen = static_cast<QEglFSKmsGbmScreen *>(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<QEglFSKmsGbmDevice *>(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<QPlatformScreen *> &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<QEglFSKmsGbmCursor> 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<CloneDestination> m_cloneDests;
+
static QMutex m_waitForFlipMutex;
};