summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2017-08-04 13:31:20 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-09-05 10:30:50 +0000
commitcf24d203850cd87e31bc123407e7ae126be9961c (patch)
tree52bbf250df26c320b706f2cfff6fc028cfef2ea3 /src
parentf2289bbcbbbddfd248c5e0858e148d7cc7336f8a (diff)
eglfs_kms: Add headless mode for DRM render nodes
Attempting to switch /dev/dri/cardX to /dev/dri/renderDY is futile on its own now since many output-related drm operations fail and we eventually crash. Add a new headless mode that skips the real screen stuff and registers a fairly dummy, headless screen, does not bother with the mouse cursor, and disallows rendering to the screen via a QWindow (while keeping the actual rendering still fully functional). Such applications will not need any special privileges and will run even if there is a DRM master (X11, Wayland compositor) active. For example the configuration can look like this: { "device": "/dev/dri/renderD128", "headless": "1024x768" } After this applications have two choices to perform offscreen rendering: 1. Use an ordinary window (and its default framebuffer, meaning the gbm_surface), e.g. a QOpenGLWindow subclass MyOpenGLWindow w; w.show(); // will not actually show on screen w.grabFramebuffer().save("output.png"); Note that there is no vsync-based throttling. Also note that windows are still sized to match the screen size, hence the need for specifying a size in the headless property. 2. Or the typical offscreen approach with an extra FBO QOffscreenSurface s; s.setFormat(ctx.format()); s.create(); ctx.makeCurrent(&s0; QOpenGLFramebufferObject fbo(1024, 768); fbo.bind(); ctx.functions()->glClearColor(1, 0, 0, 1); ctx.functions()->glClear(GL_COLOR_BUFFER_BIT); fbo.toImage().save("output.png"); ctx.doneCurrent(); Task-number: QTBUG-62262 Change-Id: Ic1dbfa2b27b223bd5ef8ba36b665f0f61abf4f06 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/platformsupport/kmsconvenience/qkmsdevice.cpp39
-rw-r--r--src/platformsupport/kmsconvenience/qkmsdevice_p.h31
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp7
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp15
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp11
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h4
8 files changed, 83 insertions, 27 deletions
diff --git a/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/src/platformsupport/kmsconvenience/qkmsdevice.cpp
index 401a2aec81..753fdb07af 100644
--- a/src/platformsupport/kmsconvenience/qkmsdevice.cpp
+++ b/src/platformsupport/kmsconvenience/qkmsdevice.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2016 Pelagicore AG
** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** Contact: https://www.qt.io/licensing/
@@ -179,7 +179,7 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources,
drmModeModeInfo configurationModeline;
auto userConfig = m_screenConfig->outputSettings();
- auto userConnectorConfig = userConfig.value(QString::fromUtf8(connectorName));
+ QVariantMap userConnectorConfig = userConfig.value(QString::fromUtf8(connectorName));
// default to the preferred mode unless overridden in the config
const QByteArray mode = userConnectorConfig.value(QStringLiteral("mode"), QStringLiteral("preferred"))
.toByteArray().toLower();
@@ -489,9 +489,22 @@ static bool orderedScreenLessThan(const OrderedScreen &a, const OrderedScreen &b
void QKmsDevice::createScreens()
{
+ // Headless mode using a render node: cannot do any output related DRM
+ // stuff. Skip it all and register a dummy screen.
+ if (m_screenConfig->headless()) {
+ QPlatformScreen *screen = createHeadlessScreen();
+ if (screen) {
+ qCDebug(qLcKmsDebug, "Headless mode enabled");
+ registerScreen(screen, true, QPoint(0, 0), QList<QPlatformScreen *>());
+ return;
+ } else {
+ qWarning("QKmsDevice: Requested headless mode without support in the backend. Request is ignored.");
+ }
+ }
+
drmModeResPtr resources = drmModeGetResources(m_dri_fd);
if (!resources) {
- qWarning("drmModeGetResources failed");
+ qErrnoWarning(errno, "drmModeGetResources failed");
return;
}
@@ -597,6 +610,12 @@ void QKmsDevice::createScreens()
}
}
+QPlatformScreen *QKmsDevice::createHeadlessScreen()
+{
+ // headless mode not supported by default
+ return nullptr;
+}
+
// not all subclasses support screen cloning
void QKmsDevice::registerScreenCloning(QPlatformScreen *screen,
QPlatformScreen *screenThisScreenClones,
@@ -628,7 +647,8 @@ QKmsScreenConfig *QKmsDevice::screenConfig() const
}
QKmsScreenConfig::QKmsScreenConfig()
- : m_hwCursor(true)
+ : m_headless(false)
+ , m_hwCursor(true)
, m_separateScreens(false)
, m_pbuffers(false)
, m_virtualDesktopLayout(VirtualDesktopLayoutHorizontal)
@@ -663,6 +683,16 @@ void QKmsScreenConfig::loadConfig()
const QJsonObject object = doc.object();
+ const QString headlessStr = object.value(QLatin1String("headless")).toString();
+ const QByteArray headless = headlessStr.toUtf8();
+ QSize headlessSize;
+ if (sscanf(headless.constData(), "%dx%d", &headlessSize.rwidth(), &headlessSize.rheight()) == 2) {
+ m_headless = true;
+ m_headlessSize = headlessSize;
+ } else {
+ m_headless = false;
+ }
+
m_hwCursor = object.value(QLatin1String("hwcursor")).toBool(m_hwCursor);
m_pbuffers = object.value(QLatin1String("pbuffers")).toBool(m_pbuffers);
m_devicePath = object.value(QLatin1String("device")).toString();
@@ -694,6 +724,7 @@ void QKmsScreenConfig::loadConfig()
}
qCDebug(qLcKmsDebug) << "Requested configuration (some settings may be ignored):\n"
+ << "\theadless:" << m_headless << "\n"
<< "\thwcursor:" << m_hwCursor << "\n"
<< "\tpbuffers:" << m_pbuffers << "\n"
<< "\tseparateScreens:" << m_separateScreens << "\n"
diff --git a/src/platformsupport/kmsconvenience/qkmsdevice_p.h b/src/platformsupport/kmsconvenience/qkmsdevice_p.h
index 390cc52e4d..bbc03b8e2a 100644
--- a/src/platformsupport/kmsconvenience/qkmsdevice_p.h
+++ b/src/platformsupport/kmsconvenience/qkmsdevice_p.h
@@ -77,6 +77,8 @@ public:
QString devicePath() const { return m_devicePath; }
+ bool headless() const { return m_headless; }
+ QSize headlessSize() const { return m_headlessSize; }
bool hwCursor() const { return m_hwCursor; }
bool separateScreens() const { return m_separateScreens; }
bool supportsPBuffers() const { return m_pbuffers; }
@@ -88,6 +90,8 @@ private:
void loadConfig();
QString m_devicePath;
+ bool m_headless;
+ QSize m_headlessSize;
bool m_hwCursor;
bool m_separateScreens;
bool m_pbuffers;
@@ -98,21 +102,21 @@ private:
struct QKmsOutput
{
QString name;
- uint32_t connector_id;
- uint32_t crtc_id;
+ uint32_t connector_id = 0;
+ uint32_t crtc_id = 0;
QSizeF physical_size;
- int preferred_mode; // index of preferred mode in list below
- int mode; // index of selected mode in list below
- bool mode_set;
- drmModeCrtcPtr saved_crtc;
+ int preferred_mode = -1; // index of preferred mode in list below
+ int mode = -1; // index of selected mode in list below
+ bool mode_set = false;
+ drmModeCrtcPtr saved_crtc = nullptr;
QList<drmModeModeInfo> modes;
- int subpixel;
- drmModePropertyPtr dpms_prop;
- drmModePropertyBlobPtr edid_blob;
- bool wants_plane;
- uint32_t plane_id;
- bool plane_set;
- uint32_t drm_format;
+ int subpixel = DRM_MODE_SUBPIXEL_UNKNOWN;
+ drmModePropertyPtr dpms_prop = nullptr;
+ drmModePropertyBlobPtr edid_blob = nullptr;
+ bool wants_plane = false;
+ uint32_t plane_id = 0;
+ bool plane_set = false;
+ uint32_t drm_format = DRM_FORMAT_XRGB8888;
QString clone_source;
void restoreMode(QKmsDevice *device);
@@ -147,6 +151,7 @@ public:
protected:
virtual QPlatformScreen *createScreen(const QKmsOutput &output) = 0;
+ virtual QPlatformScreen *createHeadlessScreen();
virtual void registerScreenCloning(QPlatformScreen *screen,
QPlatformScreen *screenThisScreenClones,
const QVector<QPlatformScreen *> &screensCloningThisScreen);
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
index 47752510b6..79c9687dc6 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
@@ -151,7 +151,7 @@ void QEglFSKmsGbmDevice::handleDrmEvent()
QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output)
{
- QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output);
+ QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output, false);
if (!m_globalCursor && screenConfig()->hwCursor()) {
qCDebug(qLcEglfsKmsDebug, "Creating new global GBM mouse cursor");
@@ -161,6 +161,11 @@ QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output)
return screen;
}
+QPlatformScreen *QEglFSKmsGbmDevice::createHeadlessScreen()
+{
+ return new QEglFSKmsGbmScreen(this, QKmsOutput(), true);
+}
+
void QEglFSKmsGbmDevice::registerScreenCloning(QPlatformScreen *screen,
QPlatformScreen *screenThisScreenClones,
const QVector<QPlatformScreen *> &screensCloningThisScreen)
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
index 93be197792..ad5a4d6e6a 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
@@ -68,6 +68,7 @@ public:
void handleDrmEvent();
QPlatformScreen *createScreen(const QKmsOutput &output) override;
+ QPlatformScreen *createHeadlessScreen() override;
void registerScreenCloning(QPlatformScreen *screen,
QPlatformScreen *screenThisScreenClones,
const QVector<QPlatformScreen *> &screensCloningThisScreen) override;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
index d3573a4512..ac6f8944ed 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
@@ -109,8 +109,8 @@ QEglFSKmsGbmScreen::FrameBuffer *QEglFSKmsGbmScreen::framebufferForBufferObject(
return fb.take();
}
-QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output)
- : QEglFSKmsScreen(device, output)
+QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless)
+ : QEglFSKmsScreen(device, output, headless)
, m_gbm_surface(Q_NULLPTR)
, m_gbm_bo_current(Q_NULLPTR)
, m_gbm_bo_next(Q_NULLPTR)
@@ -131,6 +131,8 @@ QEglFSKmsGbmScreen::~QEglFSKmsGbmScreen()
QPlatformCursor *QEglFSKmsGbmScreen::cursor() const
{
QKmsScreenConfig *config = device()->screenConfig();
+ if (config->headless())
+ return nullptr;
if (config->hwCursor()) {
if (!config->separateScreens())
return static_cast<QEglFSKmsGbmDevice *>(device())->globalCursor();
@@ -240,10 +242,8 @@ void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb)
void QEglFSKmsGbmScreen::waitForFlip()
{
- if (m_cloneSource) {
- qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name()));
+ if (m_headless || m_cloneSource)
return;
- }
// Don't lock the mutex unless we actually need to
if (!m_gbm_bo_next)
@@ -256,6 +256,11 @@ void QEglFSKmsGbmScreen::waitForFlip()
void QEglFSKmsGbmScreen::flip()
{
+ // For headless screen just return silently. It is not necessarily an error
+ // to end up here, so show no warnings.
+ if (m_headless)
+ return;
+
if (m_cloneSource) {
qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name()));
return;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
index f6ee68d1c4..4541e500d5 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
@@ -54,7 +54,7 @@ class QEglFSKmsGbmCursor;
class QEglFSKmsGbmScreen : public QEglFSKmsScreen
{
public:
- QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output);
+ QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless);
~QEglFSKmsGbmScreen();
QPlatformCursor *cursor() const override;
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 533695ea0f..cc70ab5a3c 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
@@ -68,12 +68,13 @@ private:
QEglFSKmsScreen *m_screen;
};
-QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output)
+QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless)
: QEglFSScreen(static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->display())
, m_device(device)
, m_output(output)
, m_powerState(PowerStateOn)
, m_interruptHandler(new QEglFSKmsInterruptHandler(this))
+ , m_headless(headless)
{
m_siblings << this; // gets overridden later
@@ -109,6 +110,9 @@ void QEglFSKmsScreen::setVirtualPosition(const QPoint &pos)
// geometry() calls rawGeometry() and may apply additional transforms.
QRect QEglFSKmsScreen::rawGeometry() const
{
+ if (m_headless)
+ return QRect(QPoint(0, 0), m_device->screenConfig()->headlessSize());
+
const int mode = m_output.mode;
return QRect(m_pos.x(), m_pos.y(),
m_output.modes[mode].hdisplay,
@@ -177,7 +181,7 @@ Qt::ScreenOrientation QEglFSKmsScreen::orientation() const
QString QEglFSKmsScreen::name() const
{
- return m_output.name;
+ return !m_headless ? m_output.name : QStringLiteral("qt_Headless");
}
QString QEglFSKmsScreen::manufacturer() const
@@ -214,6 +218,9 @@ void QEglFSKmsScreen::restoreMode()
qreal QEglFSKmsScreen::refreshRate() const
{
+ if (m_headless)
+ return 60;
+
quint32 refresh = m_output.modes[m_output.mode].vrefresh;
return refresh > 0 ? refresh : 60;
}
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 a2e3402bf2..aacea90eae 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
@@ -56,7 +56,7 @@ class QEglFSKmsInterruptHandler;
class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen
{
public:
- QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output);
+ QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless = false);
~QEglFSKmsScreen();
void setVirtualPosition(const QPoint &pos);
@@ -113,6 +113,8 @@ protected:
PowerState m_powerState;
QEglFSKmsInterruptHandler *m_interruptHandler;
+
+ bool m_headless;
};
QT_END_NAMESPACE