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