From 8d08306c482d76777650ee43ab3f173991e3d11c Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Wed, 24 Nov 2021 09:16:47 +0100 Subject: [PATCH] backends/drm: support dmabuf-feedback dmabuf-feedback allows the compositor to give the clients better feedback on what formats and modifiers they should use, and for which device they should allocate. This way they can reallocate for scanout whenever the compositor tells them to, which makes direct scanout work for a lot more devices and applications. --- src/backends/drm/egl_gbm_backend.cpp | 49 +++++++++++++++---- src/backends/drm/egl_gbm_backend.h | 5 +- src/linux_dmabuf.cpp | 4 +- src/linux_dmabuf.h | 2 +- .../scenes/opengl/abstract_egl_backend.cpp | 7 ++- .../scenes/opengl/abstract_egl_backend.h | 5 +- .../scenes/opengl/egl_dmabuf.cpp | 3 +- 7 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/backends/drm/egl_gbm_backend.cpp b/src/backends/drm/egl_gbm_backend.cpp index 5c09a7328e..affd5398b0 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -44,7 +44,8 @@ namespace KWin { EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend, DrmGpu *gpu) - : m_backend(drmBackend) + : AbstractEglBackend(gpu->deviceId()) + , m_backend(drmBackend) , m_gpu(gpu) { m_gpu->setEglBackend(this); @@ -502,10 +503,16 @@ QRegion EglGbmBackend::beginFrame(AbstractOutput *drmOutput) { Q_ASSERT(m_outputs.contains(drmOutput)); Output &output = m_outputs[drmOutput]; - if (output.surfaceInterface) { + if (output.scanoutSurface) { qCDebug(KWIN_DRM) << "Direct scanout stopped on output" << output.output->name(); } - output.surfaceInterface = nullptr; + output.scanoutSurface = nullptr; + if (output.scanoutCandidate) { + output.oldScanoutCandidate = output.scanoutCandidate; + output.scanoutCandidate = nullptr; + } else if (output.oldScanoutCandidate && output.oldScanoutCandidate->dmabufFeedbackV1()) { + output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({}); + } if (isPrimary()) { return prepareRenderingForOutput(output); } else { @@ -616,10 +623,31 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) if (buffer->size() != output.output->modeSize()) { return false; } - if (!buffer->planes().count()) { - return false; + if (output.oldScanoutCandidate && output.oldScanoutCandidate != surface) { + output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({}); + output.oldScanoutCandidate = nullptr; } - if (!output.output->isFormatSupported(buffer->format())) { + 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(m_gbmFormat); + for (const auto &mod : mods) { + tranche.formatTable[m_gbmFormat] << mod; + } + if (tranche.formatTable.isEmpty()) { + output.scanoutCandidate->dmabufFeedbackV1()->setTranches({}); + } else { + output.scanoutCandidate->dmabufFeedbackV1()->setTranches({tranche}); + } + } + }; + if (!buffer->planes().count() || !output.output->isFormatSupported(buffer->format())) { + sendFeedback(); return false; } @@ -629,6 +657,7 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) || planes.first().offset > 0 || planes.count() > 1) { if (!m_gpu->addFB2ModifiersSupported() || !output.output->supportedModifiers(buffer->format()).contains(planes.first().modifier)) { + sendFeedback(); return false; } gbm_import_fd_modifier_data data = {}; @@ -654,6 +683,7 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT); } if (!importedBuffer) { + sendFeedback(); if (errno != EINVAL) { qCWarning(KWIN_DRM) << "Importing buffer for direct scanout failed:" << strerror(errno); } @@ -661,7 +691,7 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) } // damage tracking for screen casting QRegion damage; - if (output.surfaceInterface == surface && buffer->size() == output.output->modeSize()) { + if (output.scanoutSurface == surface && buffer->size() == output.output->modeSize()) { QRegion trackedDamage = surfaceItem->damage(); surfaceItem->resetDamage(); for (const auto &rect : trackedDamage) { @@ -677,13 +707,14 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem) makeCurrent(); if (output.output->present(bo, damage)) { output.current.damageJournal.clear(); - if (output.surfaceInterface != surface) { + if (output.scanoutSurface != surface) { auto path = surface->client()->executablePath(); qCDebug(KWIN_DRM).nospace() << "Direct scanout starting on output " << output.output->name() << " for application \"" << path << "\""; } - output.surfaceInterface = surface; + output.scanoutSurface = surface; return true; } else { + sendFeedback(); return false; } } diff --git a/src/backends/drm/egl_gbm_backend.h b/src/backends/drm/egl_gbm_backend.h index d55d798b0d..5329eeee7d 100644 --- a/src/backends/drm/egl_gbm_backend.h +++ b/src/backends/drm/egl_gbm_backend.h @@ -13,6 +13,7 @@ #include +#include #include struct gbm_surface; @@ -94,7 +95,9 @@ private: QSharedPointer importSwapchain; } old, current; - KWaylandServer::SurfaceInterface *surfaceInterface = nullptr; + KWaylandServer::SurfaceInterface *scanoutSurface = nullptr; + QPointer scanoutCandidate; + QPointer oldScanoutCandidate; }; bool doesRenderFit(DrmAbstractOutput *output, const Output::RenderData &render); diff --git a/src/linux_dmabuf.cpp b/src/linux_dmabuf.cpp index 0549e82c0a..9cb3a56e25 100644 --- a/src/linux_dmabuf.cpp +++ b/src/linux_dmabuf.cpp @@ -53,9 +53,9 @@ KWaylandServer::LinuxDmaBufV1ClientBuffer *LinuxDmaBufV1RendererInterface::impor return nullptr; } -void LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(const QHash> &set) +void LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(dev_t device, const QHash> &set) { - waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(set); + waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(device, set); } } diff --git a/src/linux_dmabuf.h b/src/linux_dmabuf.h index 53a144669e..b46561644b 100644 --- a/src/linux_dmabuf.h +++ b/src/linux_dmabuf.h @@ -37,7 +37,7 @@ public: quint32 flags) override; protected: - void setSupportedFormatsAndModifiers(const QHash> &set); + void setSupportedFormatsAndModifiers(dev_t device, const QHash> &set); }; } diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp index ea6de89297..ad651079d4 100644 --- a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -38,7 +38,8 @@ static bool isOpenGLES_helper() AbstractEglBackend *AbstractEglBackend::s_primaryBackend = nullptr; -AbstractEglBackend::AbstractEglBackend() +AbstractEglBackend::AbstractEglBackend(dev_t deviceId) + : m_deviceId(deviceId) { if (s_primaryBackend == nullptr) { setPrimaryBackend(this); @@ -387,4 +388,8 @@ QSharedPointer AbstractEglBackend::textureForOutput(AbstractOutput *r return texture; } +dev_t AbstractEglBackend::deviceId() const +{ + return m_deviceId; +} } diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.h b/src/platformsupport/scenes/opengl/abstract_egl_backend.h index 9347cda2f9..920ae319f5 100644 --- a/src/platformsupport/scenes/opengl/abstract_egl_backend.h +++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.h @@ -70,8 +70,10 @@ public: return this == s_primaryBackend; } + dev_t deviceId() const; + protected: - AbstractEglBackend(); + AbstractEglBackend(dev_t deviceId = 0); void setEglDisplay(const EGLDisplay &display); void setSurface(const EGLSurface &surface); void setConfig(const EGLConfig &config); @@ -101,6 +103,7 @@ private: // note: m_dmaBuf is nullptr if this is not the primary backend EglDmabuf *m_dmaBuf = nullptr; QList m_clientExtensions; + const dev_t m_deviceId; static AbstractEglBackend * s_primaryBackend; }; diff --git a/src/platformsupport/scenes/opengl/egl_dmabuf.cpp b/src/platformsupport/scenes/opengl/egl_dmabuf.cpp index dde2decf43..4e0c68ace0 100644 --- a/src/platformsupport/scenes/opengl/egl_dmabuf.cpp +++ b/src/platformsupport/scenes/opengl/egl_dmabuf.cpp @@ -435,8 +435,7 @@ void EglDmabuf::setSupportedFormatsAndModifiers() } set.insert(format, QSet()); } - - LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(set); + LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(m_backend->deviceId(), set); } }