eglfs: kms: Make screen cloning functional by default

It is not necessary to disable the thread-based drm event reading
(QT_QPA_EGLFS_KMS_NO_EVENT_READER_THREAD) anymore when using screen
cloning.

Amends 820775166132b073a941f2389fba81db49619688 and
14bb413309092adc53e8451daff5690c4698c07d

Note that this does not work when atomic commits are enabled. (i.e.
running with QT_QPA_EGLFS_KMS_ATOMIC=1 and attempting to use screens
that clone will not function as expected, regardless of which event
reading method is used - that needs a rework of how atomic requests
are handled, and is not something we are going to invest into given
that atomic is not even used by default)

Fixes: QTBUG-91882
Change-Id: Iba83688c7790d7e721db3704d422034b654a8d8a
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
(cherry picked from commit 91d1ec3589b84e7f5d6611edd7e8f82cbfd38afb)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Laszlo Agocs 2022-10-26 10:47:54 +02:00 committed by Qt Cherry-pick Bot
parent 97c95f606f
commit dddd08e1e7
2 changed files with 93 additions and 41 deletions

View File

@ -173,8 +173,10 @@ void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones,
qWarning("QEglFSKmsGbmScreen %s cannot be clone source and destination at the same time", qPrintable(name()));
return;
}
if (clonesAnother)
if (clonesAnother) {
m_cloneSource = static_cast<QEglFSKmsGbmScreen *>(screenThisScreenClones);
qCDebug(qLcEglfsKmsDebug, "Screen %s clones %s", qPrintable(name()), qPrintable(m_cloneSource->name()));
}
// clone sources need to know their additional destinations
for (QPlatformScreen *s : screensCloningThisScreen) {
@ -235,12 +237,23 @@ void QEglFSKmsGbmScreen::nonThreadedPageFlipHandler(int fd,
unsigned int tv_usec,
void *user_data)
{
// note that with cloning involved this callback is called also for screens that clone another one
Q_UNUSED(fd);
QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(user_data);
screen->flipFinished();
screen->pageFlipped(sequence, tv_sec, tv_usec);
}
void QEglFSKmsGbmScreen::waitForFlipWithEventReader(QEglFSKmsGbmScreen *screen)
{
m_flipMutex.lock();
QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device());
dev->eventReader()->startWaitFlip(screen, &m_flipMutex, &m_flipCond);
m_flipCond.wait(&m_flipMutex);
m_flipMutex.unlock();
screen->flipFinished();
}
void QEglFSKmsGbmScreen::waitForFlip()
{
if (m_headless || m_cloneSource)
@ -252,11 +265,15 @@ void QEglFSKmsGbmScreen::waitForFlip()
QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device());
if (dev->usesEventReader()) {
m_flipMutex.lock();
dev->eventReader()->startWaitFlip(this, &m_flipMutex, &m_flipCond);
m_flipCond.wait(&m_flipMutex);
m_flipMutex.unlock();
flipFinished();
waitForFlipWithEventReader(this);
// Now, unlike on the other code path, we need to ensure the
// flips have completed for the screens that just scan out
// this one's content, because the eventReader's wait is
// per-output.
for (CloneDestination &d : m_cloneDests) {
if (d.screen != this)
waitForFlipWithEventReader(d.screen);
}
} else {
QMutexLocker lock(&m_nonThreadedFlipMutex);
while (m_gbm_bo_next) {
@ -274,6 +291,41 @@ void QEglFSKmsGbmScreen::waitForFlip()
#endif
}
#if QT_CONFIG(drm_atomic)
static void addAtomicFlip(drmModeAtomicReq *request, const QKmsOutput &output, uint32_t fb)
{
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->framebufferPropertyId, fb);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->crtcPropertyId, output.crtc_id);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->srcwidthPropertyId, output.size.width() << 16);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->srcXPropertyId, 0);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->srcYPropertyId, 0);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->srcheightPropertyId, output.size.height() << 16);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->crtcXPropertyId, 0);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->crtcYPropertyId, 0);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->crtcwidthPropertyId, output.modes[output.mode].hdisplay);
drmModeAtomicAddProperty(request, output.eglfs_plane->id,
output.eglfs_plane->crtcheightPropertyId, output.modes[output.mode].vdisplay);
}
#endif
void QEglFSKmsGbmScreen::flip()
{
// For headless screen just return silently. It is not necessarily an error
@ -293,14 +345,14 @@ void QEglFSKmsGbmScreen::flip()
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!");
qWarning("Could not lock GBM surface front buffer for screen %s", qPrintable(name()));
return;
}
FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next);
ensureModeSet(fb->fb);
QKmsOutput &op(output());
const QKmsOutput &thisOutput(output());
const int fd = device()->fd();
m_flipPending = true;
@ -308,35 +360,25 @@ void QEglFSKmsGbmScreen::flip()
#if QT_CONFIG(drm_atomic)
drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
if (request) {
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->framebufferPropertyId, fb->fb);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcPropertyId, op.crtc_id);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcwidthPropertyId,
op.size.width() << 16);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcXPropertyId, 0);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcYPropertyId, 0);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcheightPropertyId,
op.size.height() << 16);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcXPropertyId, 0);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcYPropertyId, 0);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcwidthPropertyId,
m_output.modes[m_output.mode].hdisplay);
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcheightPropertyId,
m_output.modes[m_output.mode].vdisplay);
addAtomicFlip(request, thisOutput, fb->fb);
static int zpos = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_ZPOS");
if (zpos)
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->zposPropertyId, zpos);
if (zpos) {
drmModeAtomicAddProperty(request, thisOutput.eglfs_plane->id,
thisOutput.eglfs_plane->zposPropertyId, zpos);
}
static uint blendOp = uint(qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_BLEND_OP"));
if (blendOp)
drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->blendOpPropertyId, blendOp);
if (blendOp) {
drmModeAtomicAddProperty(request, thisOutput.eglfs_plane->id,
thisOutput.eglfs_plane->blendOpPropertyId, blendOp);
}
}
#endif
} else {
int ret = drmModePageFlip(fd,
op.crtc_id,
fb->fb,
DRM_MODE_PAGE_FLIP_EVENT,
this);
thisOutput.crtc_id,
fb->fb,
DRM_MODE_PAGE_FLIP_EVENT,
this);
if (ret) {
qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
m_flipPending = false;
@ -350,17 +392,20 @@ void QEglFSKmsGbmScreen::flip()
if (d.screen != this) {
d.screen->ensureModeSet(fb->fb);
d.cloneFlipPending = true;
QKmsOutput &destOutput(d.screen->output());
const QKmsOutput &destOutput(d.screen->output());
if (device()->hasAtomicSupport()) {
#if QT_CONFIG(drm_atomic)
drmModeAtomicReq *request = device()->threadLocalAtomicRequest();
if (request) {
drmModeAtomicAddProperty(request, destOutput.eglfs_plane->id,
destOutput.eglfs_plane->framebufferPropertyId, fb->fb);
drmModeAtomicAddProperty(request, destOutput.eglfs_plane->id,
destOutput.eglfs_plane->crtcPropertyId, destOutput.crtc_id);
}
if (request)
addAtomicFlip(request, destOutput, fb->fb);
// ### This path is broken. On the other branch we can easily
// pass in d.screen as the user_data for drmModePageFlip, but
// using one atomic request breaks down here since we get events
// with the same user_data passed to drmModeAtomicCommit. Until
// this gets reworked (multiple requests?) screen cloning is not
// compatible with atomic.
#endif
} else {
int ret = drmModePageFlip(fd,
@ -369,7 +414,9 @@ void QEglFSKmsGbmScreen::flip()
DRM_MODE_PAGE_FLIP_EVENT,
d.screen);
if (ret) {
qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name()));
qErrnoWarning("Could not queue DRM page flip for screen %s (clones screen %s)",
qPrintable(d.screen->name()),
qPrintable(name()));
d.cloneFlipPending = false;
}
}
@ -405,8 +452,11 @@ void QEglFSKmsGbmScreen::cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScre
void QEglFSKmsGbmScreen::updateFlipStatus()
{
Q_ASSERT(!m_cloneSource);
// only for 'real' outputs that own the color buffer, i.e. that are not cloning another one
if (m_cloneSource)
return;
// proceed only if flips for both this and all others that clone this have finished
if (m_flipPending)
return;
@ -415,9 +465,10 @@ void QEglFSKmsGbmScreen::updateFlipStatus()
return;
}
if (m_gbm_bo_current)
if (m_gbm_bo_current) {
gbm_surface_release_buffer(m_gbm_surface,
m_gbm_bo_current);
}
m_gbm_bo_current = m_gbm_bo_next;
m_gbm_bo_next = nullptr;

View File

@ -52,6 +52,7 @@ protected:
void flipFinished();
void ensureModeSet(uint32_t fb);
void cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen);
void waitForFlipWithEventReader(QEglFSKmsGbmScreen *screen);
static void nonThreadedPageFlipHandler(int fd,
unsigned int sequence,
unsigned int tv_sec,