From 93d51270145f59c738612b05093031ac8c08066f Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Thu, 30 Dec 2021 19:36:54 +0100 Subject: [PATCH] backends/drm: improve dmabuf feedback Instead of only allowing the current format, send the default tranches modified to only contain formats and modifiers suitable for scanout. In order to not fail when we can't do direct scanout with a given format (because that may require a modeset, which we don't allow), keep a blacklist of attempted formats and modifiers for the current client. --- src/backends/drm/drm_pipeline.cpp | 20 +++++- src/backends/drm/drm_pipeline.h | 1 + src/backends/drm/egl_gbm_backend.cpp | 64 +++++++++++-------- src/backends/drm/egl_gbm_backend.h | 5 +- .../scenes/opengl/egl_dmabuf.cpp | 18 +++--- .../scenes/opengl/egl_dmabuf.h | 6 +- 6 files changed, 75 insertions(+), 39 deletions(-) diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index fb23f81d48..71281d9a73 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -445,13 +445,18 @@ DrmOutput *DrmPipeline::output() const return m_output; } +static const QMap> legacyFormats = { + {DRM_FORMAT_XRGB8888, {}}, + {DRM_FORMAT_ARGB8888, {}} +}; + bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const { if (pending.crtc) { if (pending.crtc->primaryPlane()) { return pending.crtc->primaryPlane()->formats().contains(drmFormat); } else { - return drmFormat == DRM_FORMAT_XRGB8888 || drmFormat == DRM_FORMAT_ARGB8888; + return legacyFormats.keys().contains(drmFormat); } } else { return false; @@ -467,6 +472,19 @@ QVector DrmPipeline::supportedModifiers(uint32_t drmFormat) const } } +QMap> DrmPipeline::supportedFormats() const +{ + if (pending.crtc) { + if (pending.crtc->primaryPlane()) { + return pending.crtc->primaryPlane()->formats(); + } else { + return legacyFormats; + } + } else { + return {}; + } +} + bool DrmPipeline::needsModeset() const { return pending.crtc != m_current.crtc diff --git a/src/backends/drm/drm_pipeline.h b/src/backends/drm/drm_pipeline.h index 7a636fe363..98fe19da5b 100644 --- a/src/backends/drm/drm_pipeline.h +++ b/src/backends/drm/drm_pipeline.h @@ -81,6 +81,7 @@ public: bool isFormatSupported(uint32_t drmFormat) const; QVector supportedModifiers(uint32_t drmFormat) const; + QMap> supportedFormats() const; void setOutput(DrmOutput *output); DrmOutput *output() const; diff --git a/src/backends/drm/egl_gbm_backend.cpp b/src/backends/drm/egl_gbm_backend.cpp index d80da88a66..868c042947 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -529,9 +529,9 @@ QRegion EglGbmBackend::beginFrame(AbstractOutput *drmOutput) qCDebug(KWIN_DRM) << "Direct scanout stopped on output" << output.output->name(); } output.scanoutSurface = nullptr; - if (output.scanoutCandidate) { - output.oldScanoutCandidate = output.scanoutCandidate; - output.scanoutCandidate = nullptr; + if (output.scanoutCandidate.surface) { + output.oldScanoutCandidate = output.scanoutCandidate.surface; + output.scanoutCandidate = {}; } else if (output.oldScanoutCandidate && output.oldScanoutCandidate->dmabufFeedbackV1()) { output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({}); } @@ -642,7 +642,8 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) return false; } Output &output = m_outputs[drmOutput]; - if (buffer->size() != output.output->modeSize()) { + const auto planes = buffer->planes(); + if (buffer->size() != output.output->modeSize() || planes.isEmpty()) { return false; } if (output.oldScanoutCandidate && output.oldScanoutCandidate != surface) { @@ -651,35 +652,45 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) } output.oldScanoutCandidate = nullptr; } - output.scanoutCandidate = surface; - const auto &sendFeedback = [&output, this]() { - if (const auto &drmOutput = qobject_cast(output.output); drmOutput && output.scanoutCandidate->dmabufFeedbackV1()) { - KWaylandServer::LinuxDmaBufV1Feedback::Tranche tranche; - tranche.device = m_gpu->deviceId(); - tranche.flags = KWaylandServer::LinuxDmaBufV1Feedback::TrancheFlag::Scanout; - // atm no format changes are sent as those might require a modeset - // and thus require more elaborate feedback - const auto &mods = drmOutput->pipeline()->supportedModifiers(output.current.format.drmFormat); - const auto &supportedModifiers = primaryBackend()->dmabuf()->supportedFormats()[output.current.format.drmFormat]; - for (const auto &mod : mods) { - if (supportedModifiers.contains(mod)) { - tranche.formatTable[output.current.format.drmFormat] << mod; + if (output.scanoutCandidate.surface != surface) { + output.scanoutCandidate.attemptedFormats = {}; + } + output.scanoutCandidate.surface = surface; + const auto &sendFeedback = [&output, &buffer, &planes, this]() { + if (!output.scanoutCandidate.attemptedFormats[buffer->format()].contains(planes.first().modifier)) { + output.scanoutCandidate.attemptedFormats[buffer->format()] << planes.first().modifier; + } + if (const auto &drmOutput = qobject_cast(output.output); drmOutput && output.scanoutCandidate.surface->dmabufFeedbackV1()) { + QVector scanoutTranches; + const auto &drmFormats = drmOutput->pipeline()->supportedFormats(); + const auto tranches = primaryBackend()->dmabuf()->tranches(); + for (const auto &tranche : tranches) { + KWaylandServer::LinuxDmaBufV1Feedback::Tranche scanoutTranche; + for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) { + const uint32_t format = it.key(); + const auto trancheModifiers = it.value(); + const auto drmModifiers = drmFormats[format]; + for (const auto &mod : trancheModifiers) { + if (drmModifiers.contains(mod) && !output.scanoutCandidate.attemptedFormats[format].contains(mod)) { + scanoutTranche.formatTable[format] << mod; + } + } + } + if (!scanoutTranche.formatTable.isEmpty()) { + scanoutTranche.device = m_gpu->deviceId(); + scanoutTranche.flags = KWaylandServer::LinuxDmaBufV1Feedback::TrancheFlag::Scanout; + scanoutTranches << scanoutTranche; } } - if (tranche.formatTable.isEmpty()) { - output.scanoutCandidate->dmabufFeedbackV1()->setTranches({}); - } else { - output.scanoutCandidate->dmabufFeedbackV1()->setTranches({tranche}); - } + output.scanoutCandidate.surface->dmabufFeedbackV1()->setTranches(scanoutTranches); } }; - if (!buffer->planes().count() || !output.output->isFormatSupported(buffer->format())) { + if (!output.output->isFormatSupported(buffer->format())) { sendFeedback(); return false; } gbm_bo *importedBuffer; - const auto planes = buffer->planes(); if (planes.first().modifier != DRM_FORMAT_MOD_INVALID || planes.first().offset > 0 || planes.count() > 1) { @@ -740,7 +751,10 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) output.scanoutSurface = surface; return true; } else { - sendFeedback(); + // TODO clean the modeset and direct scanout code paths up + if (!m_gpu->needsModeset()) { + sendFeedback(); + } return false; } } diff --git a/src/backends/drm/egl_gbm_backend.h b/src/backends/drm/egl_gbm_backend.h index fff23fcdce..9eea733731 100644 --- a/src/backends/drm/egl_gbm_backend.h +++ b/src/backends/drm/egl_gbm_backend.h @@ -108,7 +108,10 @@ private: } old, current; KWaylandServer::SurfaceInterface *scanoutSurface = nullptr; - QPointer scanoutCandidate; + struct { + QPointer surface; + QMap> attemptedFormats; + } scanoutCandidate; QPointer oldScanoutCandidate; }; diff --git a/src/platformsupport/scenes/opengl/egl_dmabuf.cpp b/src/platformsupport/scenes/opengl/egl_dmabuf.cpp index 236c3ef663..d17bdaaac6 100644 --- a/src/platformsupport/scenes/opengl/egl_dmabuf.cpp +++ b/src/platformsupport/scenes/opengl/egl_dmabuf.cpp @@ -440,6 +440,7 @@ void EglDmabuf::setSupportedFormatsAndModifiers() filterFormatsWithMultiplePlanes(formats); + QHash> supportedFormats; for (auto format : qAsConst(formats)) { if (eglQueryDmaBufModifiersEXT != nullptr) { EGLint count = 0; @@ -451,42 +452,41 @@ void EglDmabuf::setSupportedFormatsAndModifiers() for (const uint64_t &mod : qAsConst(modifiers)) { modifiersSet.insert(mod); } - m_supportedFormats.insert(format, modifiersSet); + supportedFormats.insert(format, modifiersSet); continue; } } } - m_supportedFormats.insert(format, QSet()); + supportedFormats.insert(format, QSet()); } - auto filterFormats = [this](int bpc) { + auto filterFormats = [&supportedFormats](int bpc) { QHash> set; - for (auto it = m_supportedFormats.constBegin(); it != m_supportedFormats.constEnd(); it++) { + for (auto it = supportedFormats.constBegin(); it != supportedFormats.constEnd(); it++) { if (bpcForFormat(it.key()) == bpc) { set.insert(it.key(), it.value()); } } return set; }; - QVector tranches; if (m_backend->prefer10bpc()) { - tranches.append({ + m_tranches.append({ .device = m_backend->deviceId(), .flags = {}, .formatTable = filterFormats(10), }); } - tranches.append({ + m_tranches.append({ .device = m_backend->deviceId(), .flags = {}, .formatTable = filterFormats(8), }); - tranches.append({ + m_tranches.append({ .device = m_backend->deviceId(), .flags = {}, .formatTable = filterFormats(-1), }); - LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(tranches); + LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(m_tranches); } } diff --git a/src/platformsupport/scenes/opengl/egl_dmabuf.h b/src/platformsupport/scenes/opengl/egl_dmabuf.h index b2b5733e2c..25e27e3c85 100644 --- a/src/platformsupport/scenes/opengl/egl_dmabuf.h +++ b/src/platformsupport/scenes/opengl/egl_dmabuf.h @@ -67,8 +67,8 @@ public: const QSize &size, quint32 flags) override; - QHash> supportedFormats() const { - return m_supportedFormats; + QVector tranches() const { + return m_tranches; } private: @@ -84,7 +84,7 @@ private: void setSupportedFormatsAndModifiers(); AbstractEglBackend *m_backend; - QHash> m_supportedFormats; + QVector m_tranches; friend class EglDmabufBuffer; };