diff --git a/src/backends/drm/CMakeLists.txt b/src/backends/drm/CMakeLists.txt index c010f727d9..95fa76db7a 100644 --- a/src/backends/drm/CMakeLists.txt +++ b/src/backends/drm/CMakeLists.txt @@ -29,6 +29,7 @@ set(DRM_SOURCES virtual_egl_gbm_layer.cpp drm_lease_egl_gbm_layer.cpp egl_gbm_layer_surface.cpp + dmabuf_feedback.cpp ) add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) diff --git a/src/backends/drm/dmabuf_feedback.cpp b/src/backends/drm/dmabuf_feedback.cpp new file mode 100644 index 0000000000..9a72327ade --- /dev/null +++ b/src/backends/drm/dmabuf_feedback.cpp @@ -0,0 +1,89 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2022 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#include "dmabuf_feedback.h" + +#include "drm_gpu.h" +#include "egl_dmabuf.h" +#include "egl_gbm_backend.h" + +#include +#include + +namespace KWin +{ + +DmabufFeedback::DmabufFeedback(DrmGpu *gpu, EglGbmBackend *eglBackend) + : m_gpu(gpu) + , m_eglBackend(eglBackend) +{ +} + +void DmabufFeedback::renderingSurface() +{ + if (m_surface && !m_attemptedThisFrame) { + if (const auto &feedback = m_surface->dmabufFeedbackV1()) { + feedback->setTranches({}); + } + m_surface = nullptr; + } + m_attemptedThisFrame = false; +} + +void DmabufFeedback::scanoutSuccessful(KWaylandServer::SurfaceInterface *surface) +{ + if (surface != m_surface) { + if (m_surface && m_surface->dmabufFeedbackV1()) { + m_surface->dmabufFeedbackV1()->setTranches({}); + } + m_surface = surface; + m_attemptedFormats = {}; + } +} + +void DmabufFeedback::scanoutFailed(KWaylandServer::SurfaceInterface *surface, const QMap> &formats) +{ + m_attemptedThisFrame = true; + if (surface != m_surface) { + m_attemptedFormats = {}; + if (m_surface && m_surface->dmabufFeedbackV1()) { + m_surface->dmabufFeedbackV1()->setTranches({}); + } + m_surface = surface; + } + if (const auto &feedback = m_surface->dmabufFeedbackV1()) { + const auto buffer = qobject_cast(surface->buffer()); + Q_ASSERT(buffer); + if (!m_attemptedFormats[buffer->format()].contains(buffer->planes().first().modifier)) { + m_attemptedFormats[buffer->format()] << buffer->planes().first().modifier; + QVector scanoutTranches; + const auto tranches = m_eglBackend->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 = formats[format]; + for (const auto &mod : trancheModifiers) { + if (drmModifiers.contains(mod) && !m_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; + } + } + feedback->setTranches(scanoutTranches); + } + } +} + +} diff --git a/src/backends/drm/dmabuf_feedback.h b/src/backends/drm/dmabuf_feedback.h new file mode 100644 index 0000000000..a4590e5e76 --- /dev/null +++ b/src/backends/drm/dmabuf_feedback.h @@ -0,0 +1,45 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2022 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once + +#include +#include +#include + +namespace KWaylandServer +{ +class SurfaceInterface; +class LinuxDmaBufV1ClientBuffer; +} + +namespace KWin +{ + +class EglGbmBackend; +class DrmGpu; + +class DmabufFeedback +{ +public: + DmabufFeedback(DrmGpu *gpu, EglGbmBackend *eglBackend); + + void renderingSurface(); + void scanoutSuccessful(KWaylandServer::SurfaceInterface *surface); + void scanoutFailed(KWaylandServer::SurfaceInterface *surface, const QMap> &formats); + +private: + QPointer m_surface; + QMap> m_attemptedFormats; + bool m_attemptedThisFrame = false; + + DrmGpu *const m_gpu; + EglGbmBackend *const m_eglBackend; +}; + +} diff --git a/src/backends/drm/egl_gbm_layer.cpp b/src/backends/drm/egl_gbm_layer.cpp index c9d7b96a70..3bb623bafa 100644 --- a/src/backends/drm/egl_gbm_layer.cpp +++ b/src/backends/drm/egl_gbm_layer.cpp @@ -33,6 +33,7 @@ namespace KWin EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline) : DrmPipelineLayer(pipeline) , m_surface(pipeline->gpu(), eglBackend) + , m_dmabufFeedback(pipeline->gpu(), eglBackend) { connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &EglGbmLayer::destroyResources); } @@ -50,14 +51,7 @@ void EglGbmLayer::destroyResources() OutputLayerBeginFrameInfo EglGbmLayer::beginFrame() { m_scanoutBuffer.reset(); - // dmabuf feedback - if (!m_scanoutCandidate.attemptedThisFrame && m_scanoutCandidate.surface) { - if (const auto feedback = m_scanoutCandidate.surface->dmabufFeedbackV1()) { - feedback->setTranches({}); - } - m_scanoutCandidate.surface = nullptr; - } - m_scanoutCandidate.attemptedThisFrame = false; + m_dmabufFeedback.renderingSurface(); return m_surface.startRendering(m_pipeline->bufferSize(), m_pipeline->pending.sourceTransformation, m_pipeline->pending.bufferTransformation, m_pipeline->formats()); } @@ -119,70 +113,37 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) if (!item || !item->surface()) { return false; } - const auto buffer = qobject_cast(item->surface()->buffer()); + const auto surface = item->surface(); + const auto buffer = qobject_cast(surface->buffer()); if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_pipeline->sourceSize()) { return false; } - if (m_scanoutCandidate.surface && m_scanoutCandidate.surface != item->surface() && m_scanoutCandidate.surface->dmabufFeedbackV1()) { - m_scanoutCandidate.surface->dmabufFeedbackV1()->setTranches({}); - } - m_scanoutCandidate.surface = item->surface(); - m_scanoutCandidate.attemptedThisFrame = true; - - if (!m_pipeline->formats().contains(buffer->format())) { - sendDmabufFeedback(buffer); + const auto formats = m_pipeline->formats(); + if (!formats.contains(buffer->format())) { + m_dmabufFeedback.scanoutFailed(surface, formats); return false; } m_scanoutBuffer = QSharedPointer::create(m_pipeline->gpu(), buffer); if (!m_scanoutBuffer || !m_scanoutBuffer->bufferId()) { - sendDmabufFeedback(buffer); + m_dmabufFeedback.scanoutFailed(surface, formats); m_scanoutBuffer.reset(); return false; } if (m_pipeline->testScanout()) { + m_dmabufFeedback.scanoutSuccessful(surface); m_currentBuffer = m_scanoutBuffer; m_currentDamage = surfaceItem->damage(); surfaceItem->resetDamage(); return true; } else { + m_dmabufFeedback.scanoutFailed(surface, formats); m_scanoutBuffer.reset(); return false; } } -void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *failedBuffer) -{ - if (!m_scanoutCandidate.attemptedFormats[failedBuffer->format()].contains(failedBuffer->planes().first().modifier)) { - m_scanoutCandidate.attemptedFormats[failedBuffer->format()] << failedBuffer->planes().first().modifier; - } - if (m_scanoutCandidate.surface->dmabufFeedbackV1()) { - QVector scanoutTranches; - const auto drmFormats = m_pipeline->formats(); - const auto tranches = m_surface.eglBackend()->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) && !m_scanoutCandidate.attemptedFormats[format].contains(mod)) { - scanoutTranche.formatTable[format] << mod; - } - } - } - if (!scanoutTranche.formatTable.isEmpty()) { - scanoutTranche.device = m_pipeline->gpu()->deviceId(); - scanoutTranche.flags = KWaylandServer::LinuxDmaBufV1Feedback::TrancheFlag::Scanout; - scanoutTranches << scanoutTranche; - } - } - m_scanoutCandidate.surface->dmabufFeedbackV1()->setTranches(scanoutTranches); - } -} - QSharedPointer EglGbmLayer::currentBuffer() const { return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer; diff --git a/src/backends/drm/egl_gbm_layer.h b/src/backends/drm/egl_gbm_layer.h index 5e62440196..4c6b9e56a7 100644 --- a/src/backends/drm/egl_gbm_layer.h +++ b/src/backends/drm/egl_gbm_layer.h @@ -9,6 +9,7 @@ #pragma once #include "drm_layer.h" +#include "dmabuf_feedback.h" #include "egl_gbm_layer_surface.h" #include @@ -28,7 +29,6 @@ namespace KWin { class EglGbmBackend; -class EglGbmLayerSurface; class DrmGbmBuffer; class EglGbmLayer : public DrmPipelineLayer @@ -48,22 +48,15 @@ public: QSharedPointer texture() const override; private: - void sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *failedBuffer); bool renderTestBuffer(); void destroyResources(); - struct - { - QPointer surface; - QMap> attemptedFormats; - bool attemptedThisFrame = false; - } m_scanoutCandidate; - QSharedPointer m_scanoutBuffer; QSharedPointer m_currentBuffer; QRegion m_currentDamage; EglGbmLayerSurface m_surface; + DmabufFeedback m_dmabufFeedback; }; }