backends/drm: improve dmabuf feedback

Instead of only allowing the current format, send the default tranches modified
to only contain formats and modifiers suitable for scanout.
In order to not fail when we can't do direct scanout with a given format
(because that may require a modeset, which we don't allow), keep a blacklist
of attempted formats and modifiers for the current client.
This commit is contained in:
Xaver Hugl 2021-12-30 19:36:54 +01:00
parent 0ba2c35e1a
commit 93d5127014
6 changed files with 75 additions and 39 deletions

View file

@ -445,13 +445,18 @@ DrmOutput *DrmPipeline::output() const
return m_output;
}
static const QMap<uint32_t, QVector<uint64_t>> legacyFormats = {
{DRM_FORMAT_XRGB8888, {}},
{DRM_FORMAT_ARGB8888, {}}
};
bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const
{
if (pending.crtc) {
if (pending.crtc->primaryPlane()) {
return pending.crtc->primaryPlane()->formats().contains(drmFormat);
} else {
return drmFormat == DRM_FORMAT_XRGB8888 || drmFormat == DRM_FORMAT_ARGB8888;
return legacyFormats.keys().contains(drmFormat);
}
} else {
return false;
@ -467,6 +472,19 @@ QVector<uint64_t> DrmPipeline::supportedModifiers(uint32_t drmFormat) const
}
}
QMap<uint32_t, QVector<uint64_t>> DrmPipeline::supportedFormats() const
{
if (pending.crtc) {
if (pending.crtc->primaryPlane()) {
return pending.crtc->primaryPlane()->formats();
} else {
return legacyFormats;
}
} else {
return {};
}
}
bool DrmPipeline::needsModeset() const
{
return pending.crtc != m_current.crtc

View file

@ -81,6 +81,7 @@ public:
bool isFormatSupported(uint32_t drmFormat) const;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const;
QMap<uint32_t, QVector<uint64_t>> supportedFormats() const;
void setOutput(DrmOutput *output);
DrmOutput *output() const;

View file

@ -529,9 +529,9 @@ QRegion EglGbmBackend::beginFrame(AbstractOutput *drmOutput)
qCDebug(KWIN_DRM) << "Direct scanout stopped on output" << output.output->name();
}
output.scanoutSurface = nullptr;
if (output.scanoutCandidate) {
output.oldScanoutCandidate = output.scanoutCandidate;
output.scanoutCandidate = nullptr;
if (output.scanoutCandidate.surface) {
output.oldScanoutCandidate = output.scanoutCandidate.surface;
output.scanoutCandidate = {};
} else if (output.oldScanoutCandidate && output.oldScanoutCandidate->dmabufFeedbackV1()) {
output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({});
}
@ -642,7 +642,8 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem)
return false;
}
Output &output = m_outputs[drmOutput];
if (buffer->size() != output.output->modeSize()) {
const auto planes = buffer->planes();
if (buffer->size() != output.output->modeSize() || planes.isEmpty()) {
return false;
}
if (output.oldScanoutCandidate && output.oldScanoutCandidate != surface) {
@ -651,35 +652,45 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem)
}
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(output.current.format.drmFormat);
const auto &supportedModifiers = primaryBackend()->dmabuf()->supportedFormats()[output.current.format.drmFormat];
for (const auto &mod : mods) {
if (supportedModifiers.contains(mod)) {
tranche.formatTable[output.current.format.drmFormat] << mod;
if (output.scanoutCandidate.surface != surface) {
output.scanoutCandidate.attemptedFormats = {};
}
output.scanoutCandidate.surface = surface;
const auto &sendFeedback = [&output, &buffer, &planes, this]() {
if (!output.scanoutCandidate.attemptedFormats[buffer->format()].contains(planes.first().modifier)) {
output.scanoutCandidate.attemptedFormats[buffer->format()] << planes.first().modifier;
}
if (const auto &drmOutput = qobject_cast<DrmOutput *>(output.output); drmOutput && output.scanoutCandidate.surface->dmabufFeedbackV1()) {
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> scanoutTranches;
const auto &drmFormats = drmOutput->pipeline()->supportedFormats();
const auto tranches = primaryBackend()->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) && !output.scanoutCandidate.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;
}
}
if (tranche.formatTable.isEmpty()) {
output.scanoutCandidate->dmabufFeedbackV1()->setTranches({});
} else {
output.scanoutCandidate->dmabufFeedbackV1()->setTranches({tranche});
}
output.scanoutCandidate.surface->dmabufFeedbackV1()->setTranches(scanoutTranches);
}
};
if (!buffer->planes().count() || !output.output->isFormatSupported(buffer->format())) {
if (!output.output->isFormatSupported(buffer->format())) {
sendFeedback();
return false;
}
gbm_bo *importedBuffer;
const auto planes = buffer->planes();
if (planes.first().modifier != DRM_FORMAT_MOD_INVALID
|| planes.first().offset > 0
|| planes.count() > 1) {
@ -740,7 +751,10 @@ bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem)
output.scanoutSurface = surface;
return true;
} else {
sendFeedback();
// TODO clean the modeset and direct scanout code paths up
if (!m_gpu->needsModeset()) {
sendFeedback();
}
return false;
}
}

View file

@ -108,7 +108,10 @@ private:
} old, current;
KWaylandServer::SurfaceInterface *scanoutSurface = nullptr;
QPointer<KWaylandServer::SurfaceInterface> scanoutCandidate;
struct {
QPointer<KWaylandServer::SurfaceInterface> surface;
QMap<uint32_t, QVector<uint64_t>> attemptedFormats;
} scanoutCandidate;
QPointer<KWaylandServer::SurfaceInterface> oldScanoutCandidate;
};

View file

@ -440,6 +440,7 @@ void EglDmabuf::setSupportedFormatsAndModifiers()
filterFormatsWithMultiplePlanes(formats);
QHash<uint32_t, QSet<uint64_t>> supportedFormats;
for (auto format : qAsConst(formats)) {
if (eglQueryDmaBufModifiersEXT != nullptr) {
EGLint count = 0;
@ -451,42 +452,41 @@ void EglDmabuf::setSupportedFormatsAndModifiers()
for (const uint64_t &mod : qAsConst(modifiers)) {
modifiersSet.insert(mod);
}
m_supportedFormats.insert(format, modifiersSet);
supportedFormats.insert(format, modifiersSet);
continue;
}
}
}
m_supportedFormats.insert(format, QSet<uint64_t>());
supportedFormats.insert(format, QSet<uint64_t>());
}
auto filterFormats = [this](int bpc) {
auto filterFormats = [&supportedFormats](int bpc) {
QHash<uint32_t, QSet<uint64_t>> set;
for (auto it = m_supportedFormats.constBegin(); it != m_supportedFormats.constEnd(); it++) {
for (auto it = supportedFormats.constBegin(); it != supportedFormats.constEnd(); it++) {
if (bpcForFormat(it.key()) == bpc) {
set.insert(it.key(), it.value());
}
}
return set;
};
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> tranches;
if (m_backend->prefer10bpc()) {
tranches.append({
m_tranches.append({
.device = m_backend->deviceId(),
.flags = {},
.formatTable = filterFormats(10),
});
}
tranches.append({
m_tranches.append({
.device = m_backend->deviceId(),
.flags = {},
.formatTable = filterFormats(8),
});
tranches.append({
m_tranches.append({
.device = m_backend->deviceId(),
.flags = {},
.formatTable = filterFormats(-1),
});
LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(tranches);
LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(m_tranches);
}
}

View file

@ -67,8 +67,8 @@ public:
const QSize &size,
quint32 flags) override;
QHash<uint32_t, QSet<uint64_t>> supportedFormats() const {
return m_supportedFormats;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> tranches() const {
return m_tranches;
}
private:
@ -84,7 +84,7 @@ private:
void setSupportedFormatsAndModifiers();
AbstractEglBackend *m_backend;
QHash<uint32_t, QSet<uint64_t>> m_supportedFormats;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> m_tranches;
friend class EglDmabufBuffer;
};