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.
master
Xaver Hugl 3 years ago
parent 3d0bdc56a4
commit 8d08306c48

@ -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.output->isFormatSupported(buffer->format())) {
if (output.oldScanoutCandidate && output.oldScanoutCandidate != surface) {
output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({});
output.oldScanoutCandidate = nullptr;
}
output.scanoutCandidate = surface;
const auto &sendFeedback = [&output, this]() {
if (const auto &drmOutput = qobject_cast<DrmOutput *>(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;
}
}

@ -13,6 +13,7 @@
#include <kwinglutils.h>
#include <QPointer>
#include <QSharedPointer>
struct gbm_surface;
@ -94,7 +95,9 @@ private:
QSharedPointer<DumbSwapchain> importSwapchain;
} old, current;
KWaylandServer::SurfaceInterface *surfaceInterface = nullptr;
KWaylandServer::SurfaceInterface *scanoutSurface = nullptr;
QPointer<KWaylandServer::SurfaceInterface> scanoutCandidate;
QPointer<KWaylandServer::SurfaceInterface> oldScanoutCandidate;
};
bool doesRenderFit(DrmAbstractOutput *output, const Output::RenderData &render);

@ -53,9 +53,9 @@ KWaylandServer::LinuxDmaBufV1ClientBuffer *LinuxDmaBufV1RendererInterface::impor
return nullptr;
}
void LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(const QHash<uint32_t, QSet<uint64_t>> &set)
void LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(dev_t device, const QHash<uint32_t, QSet<uint64_t>> &set)
{
waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(set);
waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(device, set);
}
}

@ -37,7 +37,7 @@ public:
quint32 flags) override;
protected:
void setSupportedFormatsAndModifiers(const QHash<uint32_t, QSet<uint64_t>> &set);
void setSupportedFormatsAndModifiers(dev_t device, const QHash<uint32_t, QSet<uint64_t>> &set);
};
}

@ -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<GLTexture> AbstractEglBackend::textureForOutput(AbstractOutput *r
return texture;
}
dev_t AbstractEglBackend::deviceId() const
{
return m_deviceId;
}
}

@ -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<QByteArray> m_clientExtensions;
const dev_t m_deviceId;
static AbstractEglBackend * s_primaryBackend;
};

@ -435,8 +435,7 @@ void EglDmabuf::setSupportedFormatsAndModifiers()
}
set.insert(format, QSet<uint64_t>());
}
LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(set);
LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(m_backend->deviceId(), set);
}
}

Loading…
Cancel
Save