backends/drm: extract dmabuf feedback into its own class

This should reduce code duplication with overlay planes
This commit is contained in:
Xaver Hugl 2022-04-03 00:15:33 +02:00
parent 5df65e4be5
commit c43eb6ec42
5 changed files with 147 additions and 58 deletions

View file

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

View file

@ -0,0 +1,89 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
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 <KWaylandServer/linuxdmabufv1clientbuffer.h>
#include <KWaylandServer/surface_interface.h>
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<uint32_t, QVector<uint64_t>> &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<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(surface->buffer());
Q_ASSERT(buffer);
if (!m_attemptedFormats[buffer->format()].contains(buffer->planes().first().modifier)) {
m_attemptedFormats[buffer->format()] << buffer->planes().first().modifier;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> 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);
}
}
}
}

View file

@ -0,0 +1,45 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QMap>
#include <QPointer>
#include <QVector>
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<uint32_t, QVector<uint64_t>> &formats);
private:
QPointer<KWaylandServer::SurfaceInterface> m_surface;
QMap<uint32_t, QVector<uint64_t>> m_attemptedFormats;
bool m_attemptedThisFrame = false;
DrmGpu *const m_gpu;
EglGbmBackend *const m_eglBackend;
};
}

View file

@ -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<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(item->surface()->buffer());
const auto surface = item->surface();
const auto buffer = qobject_cast<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(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<DrmGbmBuffer>::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<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> 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<DrmBuffer> EglGbmLayer::currentBuffer() const
{
return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer;

View file

@ -9,6 +9,7 @@
#pragma once
#include "drm_layer.h"
#include "dmabuf_feedback.h"
#include "egl_gbm_layer_surface.h"
#include <QMap>
@ -28,7 +29,6 @@ namespace KWin
{
class EglGbmBackend;
class EglGbmLayerSurface;
class DrmGbmBuffer;
class EglGbmLayer : public DrmPipelineLayer
@ -48,22 +48,15 @@ public:
QSharedPointer<GLTexture> texture() const override;
private:
void sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *failedBuffer);
bool renderTestBuffer();
void destroyResources();
struct
{
QPointer<KWaylandServer::SurfaceInterface> surface;
QMap<uint32_t, QVector<uint64_t>> attemptedFormats;
bool attemptedThisFrame = false;
} m_scanoutCandidate;
QSharedPointer<DrmGbmBuffer> m_scanoutBuffer;
QSharedPointer<DrmBuffer> m_currentBuffer;
QRegion m_currentDamage;
EglGbmLayerSurface m_surface;
DmabufFeedback m_dmabufFeedback;
};
}