backends/drm: extract dmabuf feedback into its own class
This should reduce code duplication with overlay planes
This commit is contained in:
parent
5df65e4be5
commit
c43eb6ec42
5 changed files with 147 additions and 58 deletions
|
@ -29,6 +29,7 @@ set(DRM_SOURCES
|
||||||
virtual_egl_gbm_layer.cpp
|
virtual_egl_gbm_layer.cpp
|
||||||
drm_lease_egl_gbm_layer.cpp
|
drm_lease_egl_gbm_layer.cpp
|
||||||
egl_gbm_layer_surface.cpp
|
egl_gbm_layer_surface.cpp
|
||||||
|
dmabuf_feedback.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES})
|
add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES})
|
||||||
|
|
89
src/backends/drm/dmabuf_feedback.cpp
Normal file
89
src/backends/drm/dmabuf_feedback.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
src/backends/drm/dmabuf_feedback.h
Normal file
45
src/backends/drm/dmabuf_feedback.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ namespace KWin
|
||||||
EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline)
|
EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline)
|
||||||
: DrmPipelineLayer(pipeline)
|
: DrmPipelineLayer(pipeline)
|
||||||
, m_surface(pipeline->gpu(), eglBackend)
|
, m_surface(pipeline->gpu(), eglBackend)
|
||||||
|
, m_dmabufFeedback(pipeline->gpu(), eglBackend)
|
||||||
{
|
{
|
||||||
connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &EglGbmLayer::destroyResources);
|
connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &EglGbmLayer::destroyResources);
|
||||||
}
|
}
|
||||||
|
@ -50,14 +51,7 @@ void EglGbmLayer::destroyResources()
|
||||||
OutputLayerBeginFrameInfo EglGbmLayer::beginFrame()
|
OutputLayerBeginFrameInfo EglGbmLayer::beginFrame()
|
||||||
{
|
{
|
||||||
m_scanoutBuffer.reset();
|
m_scanoutBuffer.reset();
|
||||||
// dmabuf feedback
|
m_dmabufFeedback.renderingSurface();
|
||||||
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;
|
|
||||||
|
|
||||||
return m_surface.startRendering(m_pipeline->bufferSize(), m_pipeline->pending.sourceTransformation, m_pipeline->pending.bufferTransformation, m_pipeline->formats());
|
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()) {
|
if (!item || !item->surface()) {
|
||||||
return false;
|
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()) {
|
if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_pipeline->sourceSize()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_scanoutCandidate.surface && m_scanoutCandidate.surface != item->surface() && m_scanoutCandidate.surface->dmabufFeedbackV1()) {
|
const auto formats = m_pipeline->formats();
|
||||||
m_scanoutCandidate.surface->dmabufFeedbackV1()->setTranches({});
|
if (!formats.contains(buffer->format())) {
|
||||||
}
|
m_dmabufFeedback.scanoutFailed(surface, formats);
|
||||||
m_scanoutCandidate.surface = item->surface();
|
|
||||||
m_scanoutCandidate.attemptedThisFrame = true;
|
|
||||||
|
|
||||||
if (!m_pipeline->formats().contains(buffer->format())) {
|
|
||||||
sendDmabufFeedback(buffer);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_scanoutBuffer = QSharedPointer<DrmGbmBuffer>::create(m_pipeline->gpu(), buffer);
|
m_scanoutBuffer = QSharedPointer<DrmGbmBuffer>::create(m_pipeline->gpu(), buffer);
|
||||||
if (!m_scanoutBuffer || !m_scanoutBuffer->bufferId()) {
|
if (!m_scanoutBuffer || !m_scanoutBuffer->bufferId()) {
|
||||||
sendDmabufFeedback(buffer);
|
m_dmabufFeedback.scanoutFailed(surface, formats);
|
||||||
m_scanoutBuffer.reset();
|
m_scanoutBuffer.reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pipeline->testScanout()) {
|
if (m_pipeline->testScanout()) {
|
||||||
|
m_dmabufFeedback.scanoutSuccessful(surface);
|
||||||
m_currentBuffer = m_scanoutBuffer;
|
m_currentBuffer = m_scanoutBuffer;
|
||||||
m_currentDamage = surfaceItem->damage();
|
m_currentDamage = surfaceItem->damage();
|
||||||
surfaceItem->resetDamage();
|
surfaceItem->resetDamage();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
m_dmabufFeedback.scanoutFailed(surface, formats);
|
||||||
m_scanoutBuffer.reset();
|
m_scanoutBuffer.reset();
|
||||||
return false;
|
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
|
QSharedPointer<DrmBuffer> EglGbmLayer::currentBuffer() const
|
||||||
{
|
{
|
||||||
return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer;
|
return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "drm_layer.h"
|
#include "drm_layer.h"
|
||||||
|
|
||||||
|
#include "dmabuf_feedback.h"
|
||||||
#include "egl_gbm_layer_surface.h"
|
#include "egl_gbm_layer_surface.h"
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
@ -28,7 +29,6 @@ namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
class EglGbmBackend;
|
class EglGbmBackend;
|
||||||
class EglGbmLayerSurface;
|
|
||||||
class DrmGbmBuffer;
|
class DrmGbmBuffer;
|
||||||
|
|
||||||
class EglGbmLayer : public DrmPipelineLayer
|
class EglGbmLayer : public DrmPipelineLayer
|
||||||
|
@ -48,22 +48,15 @@ public:
|
||||||
QSharedPointer<GLTexture> texture() const override;
|
QSharedPointer<GLTexture> texture() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *failedBuffer);
|
|
||||||
bool renderTestBuffer();
|
bool renderTestBuffer();
|
||||||
void destroyResources();
|
void destroyResources();
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
QPointer<KWaylandServer::SurfaceInterface> surface;
|
|
||||||
QMap<uint32_t, QVector<uint64_t>> attemptedFormats;
|
|
||||||
bool attemptedThisFrame = false;
|
|
||||||
} m_scanoutCandidate;
|
|
||||||
|
|
||||||
QSharedPointer<DrmGbmBuffer> m_scanoutBuffer;
|
QSharedPointer<DrmGbmBuffer> m_scanoutBuffer;
|
||||||
QSharedPointer<DrmBuffer> m_currentBuffer;
|
QSharedPointer<DrmBuffer> m_currentBuffer;
|
||||||
QRegion m_currentDamage;
|
QRegion m_currentDamage;
|
||||||
|
|
||||||
EglGbmLayerSurface m_surface;
|
EglGbmLayerSurface m_surface;
|
||||||
|
DmabufFeedback m_dmabufFeedback;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue