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 | |
6 | #include "qeglfskmsgbmscreen_p.h" |
7 | #include "qeglfskmsgbmdevice_p.h" |
8 | #include "qeglfskmsgbmcursor_p.h" |
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 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) |
22 | |
23 | QMutex QEglFSKmsGbmScreen::m_nonThreadedFlipMutex; |
24 | |
25 | static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat) |
26 | { |
27 | Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888); |
28 | return drmFormat; |
29 | } |
30 | |
31 | static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat) |
32 | { |
33 | Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888); |
34 | return gbmFormat; |
35 | } |
36 | |
37 | void QEglFSKmsGbmScreen::bufferDestroyedHandler(gbm_bo *bo, void *data) |
38 | { |
39 | FrameBuffer *fb = static_cast<FrameBuffer *>(data); |
40 | |
41 | if (fb->fb) { |
42 | gbm_device *device = gbm_bo_get_device(bo); |
43 | drmModeRmFB(fd: gbm_device_get_fd(gbm: device), bufferId: fb->fb); |
44 | } |
45 | |
46 | delete fb; |
47 | } |
48 | |
49 | QEglFSKmsGbmScreen::FrameBuffer *QEglFSKmsGbmScreen::framebufferForBufferObject(gbm_bo *bo) |
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(gbmFormat: 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(fd: device()->fd(), width, height, pixel_format: pixelFormat, |
69 | bo_handles: handles, pitches: strides, offsets, buf_id: &fb->fb, flags: 0); |
70 | |
71 | if (ret) { |
72 | qWarning(msg: "Failed to create KMS FB!"); |
73 | return nullptr; |
74 | } |
75 | |
76 | auto res = fb.get(); |
77 | gbm_bo_set_user_data(bo, data: fb.release(), destroy_user_data: bufferDestroyedHandler); |
78 | return res; |
79 | } |
80 | |
81 | QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless) |
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 | |
92 | QEglFSKmsGbmScreen::~QEglFSKmsGbmScreen() |
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 | |
100 | QPlatformCursor *QEglFSKmsGbmScreen::cursor() const |
101 | { |
102 | QKmsScreenConfig *config = device()->screenConfig(); |
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(other: new QEglFSKmsGbmCursor(that)); |
112 | } |
113 | |
114 | return m_cursor.data(); |
115 | } else { |
116 | return QEglFSScreen::cursor(); |
117 | } |
118 | } |
119 | |
120 | gbm_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(dpy: display(), config: eglConfig, EGL_NATIVE_VISUAL_ID, value: &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(gbm: gbmDevice, |
137 | width: rawGeometry().width(), |
138 | height: rawGeometry().height(), |
139 | format: native_format, |
140 | flags: gbmFlags()); |
141 | if (m_gbm_surface) |
142 | m_output.drm_format = gbmFormatToDrmFormat(gbmFormat: native_format); |
143 | } |
144 | } |
145 | |
146 | const uint32_t gbmFormat = drmFormatToGbmFormat(drmFormat: 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(gbm: gbmDevice, |
155 | width: rawGeometry().width(), |
156 | height: rawGeometry().height(), |
157 | format: gbmFormat, |
158 | flags: 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(gbm: gbmDevice, |
166 | width: rawGeometry().width(), |
167 | height: rawGeometry().height(), |
168 | format: gbmFormat, |
169 | modifiers: &modifier, count: 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(msg: "Could not create GBM surface!"); |
174 | } |
175 | return m_gbm_surface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow() via QEglFSKmsGbmWindow::invalidateSurface() |
176 | } |
177 | |
178 | void QEglFSKmsGbmScreen::resetSurface() |
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()) |
192 | needsNewModeSetForNextFb = true; |
193 | } |
194 | |
195 | void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones, |
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(msg: "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) { |
211 | CloneDestination d; |
212 | d.screen = static_cast<QEglFSKmsGbmScreen *>(s); |
213 | m_cloneDests.append(t: d); |
214 | } |
215 | } |
216 | |
217 | void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb) |
218 | { |
219 | QKmsOutput &op(output()); |
220 | const int fd = device()->fd(); |
221 | |
222 | if (!op.mode_set || needsNewModeSetForNextFb) { |
223 | op.mode_set = true; |
224 | needsNewModeSetForNextFb = false; |
225 | |
226 | bool doModeSet = true; |
227 | drmModeCrtcPtr currentMode = drmModeGetCrtc(fd, crtcId: op.crtc_id); |
228 | const bool alreadySet = currentMode && currentMode->buffer_id == fb && !memcmp(s1: ¤tMode->mode, s2: &op.modes[op.mode], n: sizeof(drmModeModeInfo)); |
229 | if (currentMode) |
230 | drmModeFreeCrtc(ptr: 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(req: request, object_id: op.connector_id, property_id: op.crtcIdPropertyId, value: op.crtc_id); |
242 | drmModeAtomicAddProperty(req: request, object_id: op.crtc_id, property_id: op.modeIdPropertyId, value: op.mode_blob_id); |
243 | drmModeAtomicAddProperty(req: request, object_id: op.crtc_id, property_id: op.activePropertyId, value: 1); |
244 | } |
245 | #endif |
246 | } else { |
247 | int ret = drmModeSetCrtc(fd, |
248 | crtcId: op.crtc_id, |
249 | bufferId: fb, |
250 | x: 0, y: 0, |
251 | connectors: &op.connector_id, count: 1, |
252 | mode: &op.modes[op.mode]); |
253 | |
254 | if (ret == 0) |
255 | setPowerState(PowerStateOn); |
256 | else |
257 | qErrnoWarning(errno, msg: "Could not set DRM mode for screen %s", qPrintable(name())); |
258 | } |
259 | } |
260 | } |
261 | } |
262 | |
263 | void QEglFSKmsGbmScreen::nonThreadedPageFlipHandler(int fd, |
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); |
271 | QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(user_data); |
272 | screen->flipFinished(); |
273 | screen->pageFlipped(sequence, tv_sec, tv_usec); |
274 | } |
275 | |
276 | void QEglFSKmsGbmScreen::waitForFlipWithEventReader(QEglFSKmsGbmScreen *screen) |
277 | { |
278 | m_flipMutex.lock(); |
279 | QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device()); |
280 | dev->eventReader()->startWaitFlip(key: screen, mutex: &m_flipMutex, cond: &m_flipCond); |
281 | m_flipCond.wait(lockedMutex: &m_flipMutex); |
282 | m_flipMutex.unlock(); |
283 | screen->flipFinished(); |
284 | } |
285 | |
286 | void QEglFSKmsGbmScreen::waitForFlip() |
287 | { |
288 | if (m_headless || m_cloneSource) |
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()) { |
297 | waitForFlipWithEventReader(screen: this); |
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. |
302 | for (CloneDestination &d : m_cloneDests) { |
303 | if (d.screen != this) |
304 | waitForFlipWithEventReader(screen: d.screen); |
305 | } |
306 | } else { |
307 | QMutexLocker lock(&m_nonThreadedFlipMutex); |
308 | while (m_gbm_bo_next) { |
309 | drmEventContext drmEvent; |
310 | memset(s: &drmEvent, c: 0, n: sizeof(drmEvent)); |
311 | drmEvent.version = 2; |
312 | drmEvent.vblank_handler = nullptr; |
313 | drmEvent.page_flip_handler = nonThreadedPageFlipHandler; |
314 | drmHandleEvent(fd: device()->fd(), evctx: &drmEvent); |
315 | } |
316 | } |
317 | |
318 | #if QT_CONFIG(drm_atomic) |
319 | device()->threadLocalAtomicReset(); |
320 | #endif |
321 | } |
322 | |
323 | #if QT_CONFIG(drm_atomic) |
324 | static void addAtomicFlip(drmModeAtomicReq *request, const QKmsOutput &output, uint32_t fb) |
325 | { |
326 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
327 | property_id: output.eglfs_plane->framebufferPropertyId, value: fb); |
328 | |
329 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
330 | property_id: output.eglfs_plane->crtcPropertyId, value: output.crtc_id); |
331 | |
332 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
333 | property_id: output.eglfs_plane->srcwidthPropertyId, value: output.size.width() << 16); |
334 | |
335 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
336 | property_id: output.eglfs_plane->srcXPropertyId, value: 0); |
337 | |
338 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
339 | property_id: output.eglfs_plane->srcYPropertyId, value: 0); |
340 | |
341 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
342 | property_id: output.eglfs_plane->srcheightPropertyId, value: output.size.height() << 16); |
343 | |
344 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
345 | property_id: output.eglfs_plane->crtcXPropertyId, value: 0); |
346 | |
347 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
348 | property_id: output.eglfs_plane->crtcYPropertyId, value: 0); |
349 | |
350 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
351 | property_id: output.eglfs_plane->crtcwidthPropertyId, value: output.modes[output.mode].hdisplay); |
352 | |
353 | drmModeAtomicAddProperty(req: request, object_id: output.eglfs_plane->id, |
354 | property_id: output.eglfs_plane->crtcheightPropertyId, value: output.modes[output.mode].vdisplay); |
355 | } |
356 | #endif |
357 | |
358 | void QEglFSKmsGbmScreen::flip() |
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(msg: "Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name())); |
367 | return; |
368 | } |
369 | |
370 | if (!m_gbm_surface) { |
371 | qWarning(msg: "Cannot sync before platform init!"); |
372 | return; |
373 | } |
374 | |
375 | m_gbm_bo_next = gbm_surface_lock_front_buffer(surface: m_gbm_surface); |
376 | if (!m_gbm_bo_next) { |
377 | qWarning(msg: "Could not lock GBM surface front buffer for screen %s", qPrintable(name())); |
378 | return; |
379 | } |
380 | |
381 | auto gbmRelease = qScopeGuard(f: [this]{ |
382 | m_flipPending = false; |
383 | gbm_surface_release_buffer(surface: m_gbm_surface, bo: m_gbm_bo_next); |
384 | m_gbm_bo_next = nullptr; |
385 | }); |
386 | |
387 | FrameBuffer *fb = framebufferForBufferObject(bo: m_gbm_bo_next); |
388 | if (!fb) { |
389 | qWarning(msg: "FrameBuffer not available. Cannot flip"); |
390 | return; |
391 | } |
392 | ensureModeSet(fb: 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, output: thisOutput, fb: fb->fb); |
403 | static int zpos = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_KMS_ZPOS"); |
404 | if (zpos) { |
405 | drmModeAtomicAddProperty(req: request, object_id: thisOutput.eglfs_plane->id, |
406 | property_id: thisOutput.eglfs_plane->zposPropertyId, value: zpos); |
407 | } |
408 | static uint blendOp = uint(qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_KMS_BLEND_OP")); |
409 | if (blendOp) { |
410 | drmModeAtomicAddProperty(req: request, object_id: thisOutput.eglfs_plane->id, |
411 | property_id: thisOutput.eglfs_plane->blendOpPropertyId, value: blendOp); |
412 | } |
413 | } |
414 | #endif |
415 | } else { |
416 | int ret = drmModePageFlip(fd, |
417 | crtc_id: thisOutput.crtc_id, |
418 | fb_id: fb->fb, |
419 | DRM_MODE_PAGE_FLIP_EVENT, |
420 | user_data: this); |
421 | if (ret) { |
422 | qErrnoWarning(msg: "Could not queue DRM page flip on screen %s", qPrintable(name())); |
423 | return; |
424 | } |
425 | } |
426 | |
427 | for (CloneDestination &d : m_cloneDests) { |
428 | if (d.screen != this) { |
429 | d.screen->ensureModeSet(fb: 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, output: destOutput, fb: 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 | crtc_id: destOutput.crtc_id, |
449 | fb_id: fb->fb, |
450 | DRM_MODE_PAGE_FLIP_EVENT, |
451 | user_data: d.screen); |
452 | if (ret) { |
453 | qErrnoWarning(msg: "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(user_data: this)) { |
465 | return; |
466 | } |
467 | #endif |
468 | } |
469 | |
470 | gbmRelease.dismiss(); |
471 | } |
472 | |
473 | void QEglFSKmsGbmScreen::flipFinished() |
474 | { |
475 | if (m_cloneSource) { |
476 | m_cloneSource->cloneDestFlipFinished(cloneDestScreen: this); |
477 | return; |
478 | } |
479 | |
480 | m_flipPending = false; |
481 | updateFlipStatus(); |
482 | } |
483 | |
484 | void QEglFSKmsGbmScreen::cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen) |
485 | { |
486 | for (CloneDestination &d : m_cloneDests) { |
487 | if (d.screen == cloneDestScreen) { |
488 | d.cloneFlipPending = false; |
489 | break; |
490 | } |
491 | } |
492 | updateFlipStatus(); |
493 | } |
494 | |
495 | void QEglFSKmsGbmScreen::updateFlipStatus() |
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(t&: m_cloneDests)) { |
506 | if (d.cloneFlipPending) |
507 | return; |
508 | } |
509 | |
510 | if (m_gbm_bo_current) { |
511 | gbm_surface_release_buffer(surface: m_gbm_surface, |
512 | bo: m_gbm_bo_current); |
513 | } |
514 | |
515 | m_gbm_bo_current = m_gbm_bo_next; |
516 | m_gbm_bo_next = nullptr; |
517 | } |
518 | |
519 | QT_END_NAMESPACE |
520 |
Definitions
- m_nonThreadedFlipMutex
- drmFormatToGbmFormat
- gbmFormatToDrmFormat
- bufferDestroyedHandler
- framebufferForBufferObject
- QEglFSKmsGbmScreen
- ~QEglFSKmsGbmScreen
- cursor
- createSurface
- resetSurface
- initCloning
- ensureModeSet
- nonThreadedPageFlipHandler
- waitForFlipWithEventReader
- waitForFlip
- addAtomicFlip
- flip
- flipFinished
- cloneDestFlipFinished
Learn to use CMake with our Intro Training
Find out more