Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qeglfskmsgbmscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
3// Copyright (C) 2016 Pelagicore AG
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
9#include <private/qeglfsintegration_p.h>
10
11#include <QtCore/QLoggingCategory>
12
13#include <QtGui/private/qguiapplication_p.h>
14#include <QtGui/private/qtguiglobal_p.h>
15#include <QtFbSupport/private/qfbvthandler_p.h>
16
17#include <errno.h>
18
20
21Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
22
24
25static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
26{
27 Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
28 return drmFormat;
29}
30
31static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat)
32{
33 Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
34 return gbmFormat;
35}
36
38{
39 FrameBuffer *fb = static_cast<FrameBuffer *>(data);
40
41 if (fb->fb) {
42 gbm_device *device = gbm_bo_get_device(bo);
43 drmModeRmFB(gbm_device_get_fd(device), fb->fb);
44 }
45
46 delete fb;
47}
48
50{
51 {
52 FrameBuffer *fb = static_cast<FrameBuffer *>(gbm_bo_get_user_data(bo));
53 if (fb)
54 return fb;
55 }
56
57 uint32_t width = gbm_bo_get_width(bo);
58 uint32_t height = gbm_bo_get_height(bo);
59 uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
60 uint32_t strides[4] = { gbm_bo_get_stride(bo) };
61 uint32_t offsets[4] = { 0 };
62 uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
63
64 auto fb = std::make_unique<FrameBuffer>();
65 qCDebug(qLcEglfsKmsDebug, "Adding FB, size %ux%u, DRM format 0x%x, stride %u, handle %u",
66 width, height, pixelFormat, strides[0], handles[0]);
67
68 int ret = drmModeAddFB2(device()->fd(), width, height, pixelFormat,
69 handles, strides, offsets, &fb->fb, 0);
70
71 if (ret) {
72 qWarning("Failed to create KMS FB!");
73 return nullptr;
74 }
75
76 auto res = fb.get();
77 gbm_bo_set_user_data(bo, fb.release(), bufferDestroyedHandler);
78 return res;
79}
80
82 : QEglFSKmsScreen(device, output, headless)
83 , m_gbm_surface(nullptr)
84 , m_gbm_bo_current(nullptr)
85 , m_gbm_bo_next(nullptr)
86 , m_flipPending(false)
87 , m_cursor(nullptr)
88 , m_cloneSource(nullptr)
89{
90}
91
93{
94 const int remainingScreenCount = qGuiApp->screens().count();
95 qCDebug(qLcEglfsKmsDebug, "Screen dtor. Remaining screens: %d", remainingScreenCount);
96 if (!remainingScreenCount && !device()->screenConfig()->separateScreens())
97 static_cast<QEglFSKmsGbmDevice *>(device())->destroyGlobalCursor();
98}
99
101{
103 if (config->headless())
104 return nullptr;
105 if (config->hwCursor()) {
106 if (!config->separateScreens())
107 return static_cast<QEglFSKmsGbmDevice *>(device())->globalCursor();
108
109 if (m_cursor.isNull()) {
110 QEglFSKmsGbmScreen *that = const_cast<QEglFSKmsGbmScreen *>(this);
111 that->m_cursor.reset(new QEglFSKmsGbmCursor(that));
112 }
113
114 return m_cursor.data();
115 } else {
116 return QEglFSScreen::cursor();
117 }
118}
119
120gbm_surface *QEglFSKmsGbmScreen::createSurface(EGLConfig eglConfig)
121{
122 if (!m_gbm_surface) {
123 qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s", qPrintable(name()));
124
125 const auto gbmDevice = static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice();
126 // If there was no format override given in the config file,
127 // query the native (here, gbm) format from the EGL config.
128 const bool queryFromEgl = !m_output.drm_format_requested_by_user;
129 if (queryFromEgl) {
130 EGLint native_format = -1;
131 EGLBoolean success = eglGetConfigAttrib(display(), eglConfig, EGL_NATIVE_VISUAL_ID, &native_format);
132 qCDebug(qLcEglfsKmsDebug) << "Got native format" << Qt::hex << native_format << Qt::dec
133 << "from eglGetConfigAttrib() with return code" << bool(success);
134
135 if (success) {
136 m_gbm_surface = gbm_surface_create(gbmDevice,
137 rawGeometry().width(),
139 native_format,
140 gbmFlags());
141 if (m_gbm_surface)
143 }
144 }
145
146 const uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
147
148 // Fallback for older drivers, and when "format" is explicitly specified
149 // in the output config. (not guaranteed that the requested format works
150 // of course, but do what we are told to)
151 if (!m_gbm_surface) {
152 if (queryFromEgl)
153 qCDebug(qLcEglfsKmsDebug, "Could not create surface with EGL_NATIVE_VISUAL_ID, falling back to format %x", gbmFormat);
154 m_gbm_surface = gbm_surface_create(gbmDevice,
155 rawGeometry().width(),
157 gbmFormat,
158 gbmFlags());
159 }
160
161 // Fallback for some drivers, its required to request with modifiers
162 if (!m_gbm_surface) {
163 uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
164
165 m_gbm_surface = gbm_surface_create_with_modifiers(gbmDevice,
166 rawGeometry().width(),
168 gbmFormat,
169 &modifier, 1);
170 }
171 // Fail here, as it would fail with the next usage of the GBM surface, which is very unexpected
172 if (!m_gbm_surface)
173 qFatal("Could not create GBM surface!");
174 }
175 return m_gbm_surface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow() via QEglFSKmsGbmWindow::invalidateSurface()
176}
177
179{
180 m_flipPending = false; // not necessarily true but enough to keep bo_next
181 m_gbm_bo_current = nullptr;
182 m_gbm_surface = nullptr;
183
184 // Leave m_gbm_bo_next untouched. waitForFlip() should
185 // still do its work, when called. Otherwise we end up
186 // in device-is-busy errors if there is a new QWindow
187 // created afterwards. (QTBUG-122663)
188
189 // If not using atomic, will need a new drmModeSetCrtc if a new window
190 // gets created later on (and so there's a new fb).
191 if (!device()->hasAtomicSupport())
193}
194
196 const QList<QPlatformScreen *> &screensCloningThisScreen)
197{
198 // clone destinations need to know the clone source
199 const bool clonesAnother = screenThisScreenClones != nullptr;
200 if (clonesAnother && !screensCloningThisScreen.isEmpty()) {
201 qWarning("QEglFSKmsGbmScreen %s cannot be clone source and destination at the same time", qPrintable(name()));
202 return;
203 }
204 if (clonesAnother) {
205 m_cloneSource = static_cast<QEglFSKmsGbmScreen *>(screenThisScreenClones);
206 qCDebug(qLcEglfsKmsDebug, "Screen %s clones %s", qPrintable(name()), qPrintable(m_cloneSource->name()));
207 }
208
209 // clone sources need to know their additional destinations
210 for (QPlatformScreen *s : screensCloningThisScreen) {
212 d.screen = static_cast<QEglFSKmsGbmScreen *>(s);
213 m_cloneDests.append(d);
214 }
215}
216
218{
219 QKmsOutput &op(output());
220 const int fd = device()->fd();
221
223 op.mode_set = true;
225
226 bool doModeSet = true;
227 drmModeCrtcPtr currentMode = drmModeGetCrtc(fd, op.crtc_id);
228 const bool alreadySet = currentMode && currentMode->buffer_id == fb && !memcmp(&currentMode->mode, &op.modes[op.mode], sizeof(drmModeModeInfo));
229 if (currentMode)
230 drmModeFreeCrtc(currentMode);
231 if (alreadySet)
232 doModeSet = false;
233
234 if (doModeSet) {
235 qCDebug(qLcEglfsKmsDebug, "Setting mode for screen %s", qPrintable(name()));
236
237 if (device()->hasAtomicSupport()) {
238#if QT_CONFIG(drm_atomic)
239 drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
240 if (request) {
241 drmModeAtomicAddProperty(request, op.connector_id, op.crtcIdPropertyId, op.crtc_id);
242 drmModeAtomicAddProperty(request, op.crtc_id, op.modeIdPropertyId, op.mode_blob_id);
243 drmModeAtomicAddProperty(request, op.crtc_id, op.activePropertyId, 1);
244 }
245#endif
246 } else {
247 int ret = drmModeSetCrtc(fd,
248 op.crtc_id,
249 fb,
250 0, 0,
251 &op.connector_id, 1,
252 &op.modes[op.mode]);
253
254 if (ret == 0)
256 else
257 qErrnoWarning(errno, "Could not set DRM mode for screen %s", qPrintable(name()));
258 }
259 }
260 }
261}
262
264 unsigned int sequence,
265 unsigned int tv_sec,
266 unsigned int tv_usec,
267 void *user_data)
268{
269 // note that with cloning involved this callback is called also for screens that clone another one
270 Q_UNUSED(fd);
272 screen->flipFinished();
273 screen->pageFlipped(sequence, tv_sec, tv_usec);
274}
275
285
287{
289 return;
290
291 // Don't lock the mutex unless we actually need to
292 if (!m_gbm_bo_next)
293 return;
294
295 QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device());
296 if (dev->usesEventReader()) {
298 // Now, unlike on the other code path, we need to ensure the
299 // flips have completed for the screens that just scan out
300 // this one's content, because the eventReader's wait is
301 // per-output.
303 if (d.screen != this)
305 }
306 } else {
308 while (m_gbm_bo_next) {
309 drmEventContext drmEvent;
310 memset(&drmEvent, 0, sizeof(drmEvent));
311 drmEvent.version = 2;
312 drmEvent.vblank_handler = nullptr;
313 drmEvent.page_flip_handler = nonThreadedPageFlipHandler;
314 drmHandleEvent(device()->fd(), &drmEvent);
315 }
316 }
317
318#if QT_CONFIG(drm_atomic)
319 device()->threadLocalAtomicReset();
320#endif
321}
322
323#if QT_CONFIG(drm_atomic)
324static void addAtomicFlip(drmModeAtomicReq *request, const QKmsOutput &output, uint32_t fb)
325{
326 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
327 output.eglfs_plane->framebufferPropertyId, fb);
328
329 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
330 output.eglfs_plane->crtcPropertyId, output.crtc_id);
331
332 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
333 output.eglfs_plane->srcwidthPropertyId, output.size.width() << 16);
334
335 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
336 output.eglfs_plane->srcXPropertyId, 0);
337
338 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
339 output.eglfs_plane->srcYPropertyId, 0);
340
341 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
342 output.eglfs_plane->srcheightPropertyId, output.size.height() << 16);
343
344 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
345 output.eglfs_plane->crtcXPropertyId, 0);
346
347 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
348 output.eglfs_plane->crtcYPropertyId, 0);
349
350 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
351 output.eglfs_plane->crtcwidthPropertyId, output.modes[output.mode].hdisplay);
352
353 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
354 output.eglfs_plane->crtcheightPropertyId, output.modes[output.mode].vdisplay);
355}
356#endif
357
359{
360 // For headless screen just return silently. It is not necessarily an error
361 // to end up here, so show no warnings.
362 if (m_headless)
363 return;
364
365 if (m_cloneSource) {
366 qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name()));
367 return;
368 }
369
370 if (!m_gbm_surface) {
371 qWarning("Cannot sync before platform init!");
372 return;
373 }
374
375 m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface);
376 if (!m_gbm_bo_next) {
377 qWarning("Could not lock GBM surface front buffer for screen %s", qPrintable(name()));
378 return;
379 }
380
381 auto gbmRelease = qScopeGuard([this]{
382 m_flipPending = false;
383 gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
384 m_gbm_bo_next = nullptr;
385 });
386
388 if (!fb) {
389 qWarning("FrameBuffer not available. Cannot flip");
390 return;
391 }
392 ensureModeSet(fb->fb);
393
394 const QKmsOutput &thisOutput(output());
395 const int fd = device()->fd();
396 m_flipPending = true;
397
398 if (device()->hasAtomicSupport()) {
399#if QT_CONFIG(drm_atomic)
400 drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
401 if (request) {
402 addAtomicFlip(request, thisOutput, fb->fb);
403 static int zpos = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_ZPOS");
404 if (zpos) {
405 drmModeAtomicAddProperty(request, thisOutput.eglfs_plane->id,
406 thisOutput.eglfs_plane->zposPropertyId, zpos);
407 }
408 static uint blendOp = uint(qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_BLEND_OP"));
409 if (blendOp) {
410 drmModeAtomicAddProperty(request, thisOutput.eglfs_plane->id,
411 thisOutput.eglfs_plane->blendOpPropertyId, blendOp);
412 }
413 }
414#endif
415 } else {
416 int ret = drmModePageFlip(fd,
417 thisOutput.crtc_id,
418 fb->fb,
419 DRM_MODE_PAGE_FLIP_EVENT,
420 this);
421 if (ret) {
422 qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
423 return;
424 }
425 }
426
428 if (d.screen != this) {
429 d.screen->ensureModeSet(fb->fb);
430 d.cloneFlipPending = true;
431 const QKmsOutput &destOutput(d.screen->output());
432
433 if (device()->hasAtomicSupport()) {
434#if QT_CONFIG(drm_atomic)
435 drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
436 if (request)
437 addAtomicFlip(request, destOutput, fb->fb);
438
439 // ### This path is broken. On the other branch we can easily
440 // pass in d.screen as the user_data for drmModePageFlip, but
441 // using one atomic request breaks down here since we get events
442 // with the same user_data passed to drmModeAtomicCommit. Until
443 // this gets reworked (multiple requests?) screen cloning is not
444 // compatible with atomic.
445#endif
446 } else {
447 int ret = drmModePageFlip(fd,
448 destOutput.crtc_id,
449 fb->fb,
450 DRM_MODE_PAGE_FLIP_EVENT,
451 d.screen);
452 if (ret) {
453 qErrnoWarning("Could not queue DRM page flip for screen %s (clones screen %s)",
454 qPrintable(d.screen->name()),
455 qPrintable(name()));
456 d.cloneFlipPending = false;
457 }
458 }
459 }
460 }
461
462 if (device()->hasAtomicSupport()) {
463#if QT_CONFIG(drm_atomic)
464 if (!device()->threadLocalAtomicCommit(this)) {
465 return;
466 }
467#endif
468 }
469
470 gbmRelease.dismiss();
471}
472
474{
475 if (m_cloneSource) {
477 return;
478 }
479
480 m_flipPending = false;
482}
483
485{
487 if (d.screen == cloneDestScreen) {
488 d.cloneFlipPending = false;
489 break;
490 }
491 }
493}
494
496{
497 // only for 'real' outputs that own the color buffer, i.e. that are not cloning another one
498 if (m_cloneSource)
499 return;
500
501 // proceed only if flips for both this and all others that clone this have finished
502 if (m_flipPending)
503 return;
504
505 for (const CloneDestination &d : std::as_const(m_cloneDests)) {
506 if (d.cloneFlipPending)
507 return;
508 }
509
510 if (m_gbm_bo_current) {
511 gbm_surface_release_buffer(m_gbm_surface,
513 }
514
516 m_gbm_bo_next = nullptr;
517}
518
IOBluetoothDevice * device
void startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond)
QEglFSKmsEventReader * eventReader()
static QMutex m_nonThreadedFlipMutex
QEglFSKmsGbmScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless)
void cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen)
void waitForFlip() override
QEglFSKmsGbmScreen * m_cloneSource
virtual uint32_t gbmFlags()
void initCloning(QPlatformScreen *screenThisScreenClones, const QList< QPlatformScreen * > &screensCloningThisScreen)
void ensureModeSet(uint32_t fb)
QScopedPointer< QEglFSKmsGbmCursor > m_cursor
static void bufferDestroyedHandler(gbm_bo *bo, void *data)
void waitForFlipWithEventReader(QEglFSKmsGbmScreen *screen)
virtual void updateFlipStatus()
QPlatformCursor * cursor() const override
Reimplement this function in subclass to return the cursor of the screen.
gbm_surface * createSurface(EGLConfig eglConfig)
FrameBuffer * framebufferForBufferObject(gbm_bo *bo)
QList< CloneDestination > m_cloneDests
static void nonThreadedPageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
QString name() const override
QKmsOutput & output()
int currentMode() const override
Reimplement this function in subclass to return the index of the current mode from the modes list.
QEglFSKmsDevice * device() const
void setPowerState(QPlatformScreen::PowerState state) override
Sets the power state for this screen.
QRect rawGeometry() const override
QPlatformCursor * cursor() const override
Reimplement this function in subclass to return the cursor of the screen.
EGLDisplay display() const
int fd() const
QKmsScreenConfig * screenConfig() const
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:289
void lock() noexcept
Locks the mutex.
Definition qmutex.h:286
The QPlatformCursor class provides information about pointer device events (movement,...
The QPlatformScreen class provides an abstraction for visual displays.
QScreen * screen() const
T * data() const noexcept
Returns the value of the pointer referenced by this object.
bool isNull() const noexcept
Returns true if this object refers to \nullptr.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
void qErrnoWarning(const char *msg,...)
#define DRM_FORMAT_MOD_LINEAR
Definition linuxdmabuf.h:33
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
static uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat)
static uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
EGLConfig config
typedef EGLBoolean(EGLAPIENTRYP PFNEGLQUERYDEVICESEXTPROC)(EGLint max_devices
#define qGuiApp
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
GLint GLsizei GLsizei height
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei width
GLuint64 GLenum GLint fd
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
GLuint GLsizei const GLuint const GLintptr * offsets
GLsizei const GLuint const GLintptr const GLsizei * strides
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define qPrintable(string)
Definition qstring.h:1531
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
QT_BEGIN_NAMESPACE typedef uchar * output
QObject::connect nullptr
QReadWriteLock lock
[0]
QNetworkRequest request(url)
uint32_t modeIdPropertyId
uint32_t mode_blob_id
uint32_t crtc_id
QList< drmModeModeInfo > modes
uint32_t activePropertyId
uint32_t drm_format
bool drm_format_requested_by_user
uint32_t crtcIdPropertyId
uint32_t connector_id