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; };