Qt 6.x
The Qt SDK
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 gbm_bo_set_user_data(bo, fb.get(), bufferDestroyedHandler);
77 return fb.release();
78}
79
81 : QEglFSKmsScreen(device, output, headless)
82 , m_gbm_surface(nullptr)
83 , m_gbm_bo_current(nullptr)
84 , m_gbm_bo_next(nullptr)
85 , m_flipPending(false)
86 , m_cursor(nullptr)
87 , m_cloneSource(nullptr)
88{
89}
90
92{
93 const int remainingScreenCount = qGuiApp->screens().count();
94 qCDebug(qLcEglfsKmsDebug, "Screen dtor. Remaining screens: %d", remainingScreenCount);
95 if (!remainingScreenCount && !device()->screenConfig()->separateScreens())
96 static_cast<QEglFSKmsGbmDevice *>(device())->destroyGlobalCursor();
97}
98
100{
102 if (config->headless())
103 return nullptr;
104 if (config->hwCursor()) {
105 if (!config->separateScreens())
106 return static_cast<QEglFSKmsGbmDevice *>(device())->globalCursor();
107
108 if (m_cursor.isNull()) {
109 QEglFSKmsGbmScreen *that = const_cast<QEglFSKmsGbmScreen *>(this);
110 that->m_cursor.reset(new QEglFSKmsGbmCursor(that));
111 }
112
113 return m_cursor.data();
114 } else {
115 return QEglFSScreen::cursor();
116 }
117}
118
119gbm_surface *QEglFSKmsGbmScreen::createSurface(EGLConfig eglConfig)
120{
121 if (!m_gbm_surface) {
122 qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s", qPrintable(name()));
123
124 const auto gbmDevice = static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice();
125 // If there was no format override given in the config file,
126 // query the native (here, gbm) format from the EGL config.
127 const bool queryFromEgl = !m_output.drm_format_requested_by_user;
128 if (queryFromEgl) {
129 EGLint native_format = -1;
130 EGLBoolean success = eglGetConfigAttrib(display(), eglConfig, EGL_NATIVE_VISUAL_ID, &native_format);
131 qCDebug(qLcEglfsKmsDebug) << "Got native format" << Qt::hex << native_format << Qt::dec
132 << "from eglGetConfigAttrib() with return code" << bool(success);
133
134 if (success) {
135 m_gbm_surface = gbm_surface_create(gbmDevice,
136 rawGeometry().width(),
138 native_format,
139 gbmFlags());
140 if (m_gbm_surface)
142 }
143 }
144
145 // Fallback for older drivers, and when "format" is explicitly specified
146 // in the output config. (not guaranteed that the requested format works
147 // of course, but do what we are told to)
148 if (!m_gbm_surface) {
149 uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
150 if (queryFromEgl)
151 qCDebug(qLcEglfsKmsDebug, "Could not create surface with EGL_NATIVE_VISUAL_ID, falling back to format %x", gbmFormat);
152 m_gbm_surface = gbm_surface_create(gbmDevice,
153 rawGeometry().width(),
155 gbmFormat,
156 gbmFlags());
157 }
158 }
159 return m_gbm_surface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow() via QEglFSKmsGbmWindow::invalidateSurface()
160}
161
163{
164 m_flipPending = false;
165 m_gbm_bo_current = nullptr;
166 m_gbm_bo_next = nullptr;
167 m_gbm_surface = nullptr;
168}
169
171 const QList<QPlatformScreen *> &screensCloningThisScreen)
172{
173 // clone destinations need to know the clone source
174 const bool clonesAnother = screenThisScreenClones != nullptr;
175 if (clonesAnother && !screensCloningThisScreen.isEmpty()) {
176 qWarning("QEglFSKmsGbmScreen %s cannot be clone source and destination at the same time", qPrintable(name()));
177 return;
178 }
179 if (clonesAnother) {
180 m_cloneSource = static_cast<QEglFSKmsGbmScreen *>(screenThisScreenClones);
181 qCDebug(qLcEglfsKmsDebug, "Screen %s clones %s", qPrintable(name()), qPrintable(m_cloneSource->name()));
182 }
183
184 // clone sources need to know their additional destinations
185 for (QPlatformScreen *s : screensCloningThisScreen) {
187 d.screen = static_cast<QEglFSKmsGbmScreen *>(s);
188 m_cloneDests.append(d);
189 }
190}
191
193{
194 QKmsOutput &op(output());
195 const int fd = device()->fd();
196
197 if (!op.mode_set) {
198 op.mode_set = true;
199
200 bool doModeSet = true;
201 drmModeCrtcPtr currentMode = drmModeGetCrtc(fd, op.crtc_id);
202 const bool alreadySet = currentMode && currentMode->buffer_id == fb && !memcmp(&currentMode->mode, &op.modes[op.mode], sizeof(drmModeModeInfo));
203 if (currentMode)
204 drmModeFreeCrtc(currentMode);
205 if (alreadySet)
206 doModeSet = false;
207
208 if (doModeSet) {
209 qCDebug(qLcEglfsKmsDebug, "Setting mode for screen %s", qPrintable(name()));
210
211 if (device()->hasAtomicSupport()) {
212#if QT_CONFIG(drm_atomic)
213 drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
214 if (request) {
215 drmModeAtomicAddProperty(request, op.connector_id, op.crtcIdPropertyId, op.crtc_id);
216 drmModeAtomicAddProperty(request, op.crtc_id, op.modeIdPropertyId, op.mode_blob_id);
217 drmModeAtomicAddProperty(request, op.crtc_id, op.activePropertyId, 1);
218 }
219#endif
220 } else {
221 int ret = drmModeSetCrtc(fd,
222 op.crtc_id,
223 fb,
224 0, 0,
225 &op.connector_id, 1,
226 &op.modes[op.mode]);
227
228 if (ret == 0)
230 else
231 qErrnoWarning(errno, "Could not set DRM mode for screen %s", qPrintable(name()));
232 }
233 }
234 }
235}
236
238 unsigned int sequence,
239 unsigned int tv_sec,
240 unsigned int tv_usec,
241 void *user_data)
242{
243 // note that with cloning involved this callback is called also for screens that clone another one
244 Q_UNUSED(fd);
246 screen->flipFinished();
247 screen->pageFlipped(sequence, tv_sec, tv_usec);
248}
249
251{
253 QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device());
257 screen->flipFinished();
258}
259
261{
263 return;
264
265 // Don't lock the mutex unless we actually need to
266 if (!m_gbm_bo_next)
267 return;
268
269 QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device());
270 if (dev->usesEventReader()) {
272 // Now, unlike on the other code path, we need to ensure the
273 // flips have completed for the screens that just scan out
274 // this one's content, because the eventReader's wait is
275 // per-output.
277 if (d.screen != this)
279 }
280 } else {
282 while (m_gbm_bo_next) {
283 drmEventContext drmEvent;
284 memset(&drmEvent, 0, sizeof(drmEvent));
285 drmEvent.version = 2;
286 drmEvent.vblank_handler = nullptr;
287 drmEvent.page_flip_handler = nonThreadedPageFlipHandler;
288 drmHandleEvent(device()->fd(), &drmEvent);
289 }
290 }
291
292#if QT_CONFIG(drm_atomic)
293 device()->threadLocalAtomicReset();
294#endif
295}
296
297#if QT_CONFIG(drm_atomic)
298static void addAtomicFlip(drmModeAtomicReq *request, const QKmsOutput &output, uint32_t fb)
299{
300 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
301 output.eglfs_plane->framebufferPropertyId, fb);
302
303 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
304 output.eglfs_plane->crtcPropertyId, output.crtc_id);
305
306 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
307 output.eglfs_plane->srcwidthPropertyId, output.size.width() << 16);
308
309 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
310 output.eglfs_plane->srcXPropertyId, 0);
311
312 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
313 output.eglfs_plane->srcYPropertyId, 0);
314
315 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
316 output.eglfs_plane->srcheightPropertyId, output.size.height() << 16);
317
318 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
319 output.eglfs_plane->crtcXPropertyId, 0);
320
321 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
322 output.eglfs_plane->crtcYPropertyId, 0);
323
324 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
325 output.eglfs_plane->crtcwidthPropertyId, output.modes[output.mode].hdisplay);
326
327 drmModeAtomicAddProperty(request, output.eglfs_plane->id,
328 output.eglfs_plane->crtcheightPropertyId, output.modes[output.mode].vdisplay);
329}
330#endif
331
333{
334 // For headless screen just return silently. It is not necessarily an error
335 // to end up here, so show no warnings.
336 if (m_headless)
337 return;
338
339 if (m_cloneSource) {
340 qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name()));
341 return;
342 }
343
344 if (!m_gbm_surface) {
345 qWarning("Cannot sync before platform init!");
346 return;
347 }
348
349 m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface);
350 if (!m_gbm_bo_next) {
351 qWarning("Could not lock GBM surface front buffer for screen %s", qPrintable(name()));
352 return;
353 }
354
355 auto gbmRelease = qScopeGuard([this]{
356 m_flipPending = false;
357 gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
358 m_gbm_bo_next = nullptr;
359 });
360
362 if (!fb) {
363 qWarning("FrameBuffer not available. Cannot flip");
364 return;
365 }
366 ensureModeSet(fb->fb);
367
368 const QKmsOutput &thisOutput(output());
369 const int fd = device()->fd();
370 m_flipPending = true;
371
372 if (device()->hasAtomicSupport()) {
373#if QT_CONFIG(drm_atomic)
374 drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
375 if (request) {
376 addAtomicFlip(request, thisOutput, fb->fb);
377 static int zpos = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_ZPOS");
378 if (zpos) {
379 drmModeAtomicAddProperty(request, thisOutput.eglfs_plane->id,
380 thisOutput.eglfs_plane->zposPropertyId, zpos);
381 }
382 static uint blendOp = uint(qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_BLEND_OP"));
383 if (blendOp) {
384 drmModeAtomicAddProperty(request, thisOutput.eglfs_plane->id,
385 thisOutput.eglfs_plane->blendOpPropertyId, blendOp);
386 }
387 }
388#endif
389 } else {
390 int ret = drmModePageFlip(fd,
391 thisOutput.crtc_id,
392 fb->fb,
393 DRM_MODE_PAGE_FLIP_EVENT,
394 this);
395 if (ret) {
396 qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
397 return;
398 }
399 }
400
402 if (d.screen != this) {
403 d.screen->ensureModeSet(fb->fb);
404 d.cloneFlipPending = true;
405 const QKmsOutput &destOutput(d.screen->output());
406
407 if (device()->hasAtomicSupport()) {
408#if QT_CONFIG(drm_atomic)
409 drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
410 if (request)
411 addAtomicFlip(request, destOutput, fb->fb);
412
413 // ### This path is broken. On the other branch we can easily
414 // pass in d.screen as the user_data for drmModePageFlip, but
415 // using one atomic request breaks down here since we get events
416 // with the same user_data passed to drmModeAtomicCommit. Until
417 // this gets reworked (multiple requests?) screen cloning is not
418 // compatible with atomic.
419#endif
420 } else {
421 int ret = drmModePageFlip(fd,
422 destOutput.crtc_id,
423 fb->fb,
424 DRM_MODE_PAGE_FLIP_EVENT,
425 d.screen);
426 if (ret) {
427 qErrnoWarning("Could not queue DRM page flip for screen %s (clones screen %s)",
428 qPrintable(d.screen->name()),
429 qPrintable(name()));
430 d.cloneFlipPending = false;
431 }
432 }
433 }
434 }
435
436 if (device()->hasAtomicSupport()) {
437#if QT_CONFIG(drm_atomic)
438 if (!device()->threadLocalAtomicCommit(this)) {
439 return;
440 }
441#endif
442 }
443
444 gbmRelease.dismiss();
445}
446
448{
449 if (m_cloneSource) {
451 return;
452 }
453
454 m_flipPending = false;
456}
457
459{
461 if (d.screen == cloneDestScreen) {
462 d.cloneFlipPending = false;
463 break;
464 }
465 }
467}
468
470{
471 // only for 'real' outputs that own the color buffer, i.e. that are not cloning another one
472 if (m_cloneSource)
473 return;
474
475 // proceed only if flips for both this and all others that clone this have finished
476 if (m_flipPending)
477 return;
478
479 for (const CloneDestination &d : std::as_const(m_cloneDests)) {
480 if (d.cloneFlipPending)
481 return;
482 }
483
484 if (m_gbm_bo_current) {
485 gbm_surface_release_buffer(m_gbm_surface,
487 }
488
490 m_gbm_bo_next = nullptr;
491}
492
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
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:293
void lock() noexcept
Locks the mutex.
Definition qmutex.h:290
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,...)
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:162
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
GLint GLsizei GLsizei height
GLint GLsizei width
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint64 GLenum GLint fd
GLuint GLsizei const GLuint const GLintptr * offsets
GLdouble s
[6]
Definition qopenglext.h:235
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:1391
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:29
QT_BEGIN_NAMESPACE typedef uchar * output
QObject::connect nullptr
QReadWriteLock lock
[0]
QNetworkRequest request(url)
uint32_t modeIdPropertyId
struct QKmsPlane * eglfs_plane
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
uint32_t zposPropertyId
uint32_t id
uint32_t blendOpPropertyId