platforms/drm: explicit modifiers

IN_FORMATS contains information about which buffer formats can be
scanned out with a given drm plane. Using these plane-specific
format+modifier combinations as well as explicit modifiers in
general can yield bandwidth and performance improvements,
especially in multi-gpu systems.
This commit is contained in:
Xaver Hugl 2021-07-31 15:32:50 +02:00
parent 19cc22102c
commit 26dff99f78
15 changed files with 176 additions and 37 deletions

View file

@ -34,6 +34,8 @@ public:
virtual bool isDpmsEnabled() const = 0;
virtual GbmBuffer *currentBuffer() const = 0;
virtual QSize sourceSize() const = 0;
virtual bool isFormatSupported(uint32_t drmFormat) const = 0;
virtual QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const = 0;
DrmGpu *gpu() const;
RenderLoop *renderLoop() const override;

View file

@ -68,10 +68,8 @@ DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t device
m_presentationClock = CLOCK_REALTIME;
}
if (!qEnvironmentVariableIsSet("KWIN_DRM_NO_MODIFIERS")) {
m_addFB2ModifiersSupported = drmGetCap(fd, DRM_CAP_ADDFB2_MODIFIERS, &capability) == 0 && capability == 1;
qCDebug(KWIN_DRM) << "drmModeAddFB2WithModifiers is" << (m_addFB2ModifiersSupported ? "supported" : "not supported") << "on GPU" << m_devNode;
}
m_addFB2ModifiersSupported = drmGetCap(fd, DRM_CAP_ADDFB2_MODIFIERS, &capability) == 0 && capability == 1;
qCDebug(KWIN_DRM) << "drmModeAddFB2WithModifiers is" << (m_addFB2ModifiersSupported ? "supported" : "not supported") << "on GPU" << m_devNode;
// find out if this GPU is using the NVidia proprietary driver
DrmScopedPointer<drmVersion> version(drmGetVersion(fd));
@ -500,4 +498,18 @@ void DrmGpu::removeVirtualOutput(DrmVirtualOutput *output)
}
}
bool DrmGpu::isFormatSupported(uint32_t gbmFormat) const
{
if (!m_atomicModeSetting) {
return gbmFormat == GBM_BO_FORMAT_XRGB8888 || gbmFormat == GBM_BO_FORMAT_ARGB8888;
} else {
for (const auto &plane : qAsConst(m_planes)) {
if (plane->type() == DrmPlane::TypeIndex::Primary && !plane->formats().contains(gbmFormat)) {
return false;
}
}
return true;
}
}
}

View file

@ -98,6 +98,7 @@ public:
void waitIdle();
DrmBackend *platform() const;
const QVector<DrmPipeline*> pipelines() const;
bool isFormatSupported(uint32_t gbmFormat) const;
DrmVirtualOutput *createVirtualOutput();
void removeVirtualOutput(DrmVirtualOutput *output);

View file

@ -11,6 +11,11 @@
#include "drm_gpu.h"
#include "drm_pointer.h"
#include "logging.h"
#include "config-kwin.h"
#if HAVE_GBM
#include <gbm.h>
#endif
namespace KWin
{
@ -38,6 +43,7 @@ DrmPlane::DrmPlane(DrmGpu *gpu, uint32_t planeId)
QByteArrayLiteral("rotate-270"),
QByteArrayLiteral("reflect-x"),
QByteArrayLiteral("reflect-y")}),
PropertyDefinition(QByteArrayLiteral("IN_FORMATS")),
}, DRM_MODE_OBJECT_PLANE)
{
}
@ -54,12 +60,6 @@ bool DrmPlane::init()
m_possibleCrtcs = p->possible_crtcs;
int count_formats = p->count_formats;
m_formats.resize(count_formats);
for (int i = 0; i < count_formats; i++) {
m_formats[i] = p->formats[i];
}
bool success = initProps();
if (success) {
m_supportedTransformations = Transformations();
@ -74,6 +74,42 @@ bool DrmPlane::init()
checkSupport(3, Transformation::Rotate270);
checkSupport(4, Transformation::ReflectX);
checkSupport(5, Transformation::ReflectY);
// read formats from blob if available and if modifiers are supported, and from the plane object if not
if (auto formatProp = getProp(PropertyIndex::In_Formats); formatProp && qEnvironmentVariableIsSet("KWIN_DRM_NO_MODIFIERS") && gpu()->addFB2ModifiersSupported()) {
auto blob = static_cast<drm_format_modifier_blob*>(formatProp->currentBlob()->data);
auto modifiers = reinterpret_cast<drm_format_modifier*>(reinterpret_cast<uint8_t*>(blob) + blob->modifiers_offset);
uint32_t *formatarr = reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(blob) + blob->formats_offset);
for (uint32_t f = 0; f < blob->count_formats; f++) {
auto format = formatarr[f];
QVector<uint64_t> mods;
for (uint32_t m = 0; m < blob->count_modifiers; m++) {
auto modifier = &modifiers[m];
// The modifier advertisement blob is partitioned into groups of 64 formats
if (m < modifier->offset || m > modifier->offset + 63) {
continue;
}
if (!(modifier->formats & (1 << (f - modifier->offset)))) {
continue;
}
mods << modifier->modifier;
}
m_supportedFormats.insert(format, mods);
}
} else {
for (uint32_t i = 0; i < p->count_formats; i++) {
m_supportedFormats.insert(p->formats[i], {});
}
}
#if HAVE_GBM
if (m_supportedFormats.isEmpty()) {
qCWarning(KWIN_DRM) << "Driver doesn't advertise any formats for this plane. Falling back to XRGB8888 and ARGB8888 without modifiers";
m_supportedFormats.insert(GBM_BO_FORMAT_XRGB8888, {});
m_supportedFormats.insert(GBM_BO_FORMAT_ARGB8888, {});
}
#endif
}
return success;
}

View file

@ -11,8 +11,8 @@
#include "drm_object.h"
#include <qobjectdefs.h>
#include <xf86drmMode.h>
#include <QSharedPointer>
#include <QMap>
namespace KWin
{
@ -38,6 +38,7 @@ public:
FbId,
CrtcId,
Rotation,
In_Formats,
Count
};
Q_ENUM(PropertyIndex)
@ -67,8 +68,8 @@ public:
bool isCrtcSupported(int pipeIndex) const {
return (m_possibleCrtcs & (1 << pipeIndex));
}
QVector<uint32_t> formats() const {
return m_formats;
QMap<uint32_t, QVector<uint64_t>> formats() const {
return m_supportedFormats;
}
QSharedPointer<DrmBuffer> current() const {
@ -99,12 +100,8 @@ private:
QSharedPointer<DrmBuffer> m_current;
QSharedPointer<DrmBuffer> m_next;
// TODO: See weston drm_output_check_plane_format for future use of these member variables
QVector<uint32_t> m_formats; // Possible formats, which can be presented on this plane
// TODO: when using overlay planes in the future: restrict possible screens / crtcs of planes
QMap<uint32_t, QVector<uint64_t>> m_supportedFormats;
uint32_t m_possibleCrtcs;
Transformations m_supportedTransformations = Transformation::Rotate0;
};

View file

@ -403,4 +403,14 @@ QSize DrmOutput::sourceSize() const
return m_pipeline->sourceSize();
}
bool DrmOutput::isFormatSupported(uint32_t drmFormat) const
{
return m_pipeline->isFormatSupported(drmFormat);
}
QVector<uint64_t> DrmOutput::supportedModifiers(uint32_t drmFormat) const
{
return m_pipeline->supportedModifiers(drmFormat);
}
}

View file

@ -54,6 +54,8 @@ public:
DrmPipeline *pipeline() const;
GbmBuffer *currentBuffer() const override;
QSize sourceSize() const override;
bool isFormatSupported(uint32_t drmFormat) const override;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
private:
friend class DrmGpu;

View file

@ -546,6 +546,16 @@ bool DrmPipeline::isConnected() const
}
}
bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const
{
return m_primaryPlane->formats().contains(drmFormat);
}
QVector<uint64_t> DrmPipeline::supportedModifiers(uint32_t drmFormat) const
{
return m_primaryPlane->formats()[drmFormat];
}
static void printProps(DrmObject *object)
{
auto list = object->properties();

View file

@ -79,6 +79,9 @@ public:
QSize sourceSize() const;
void updateProperties();
bool isFormatSupported(uint32_t drmFormat) const;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const;
private:
bool atomicCommit();
bool atomicTest(const QVector<DrmPipeline*> &pipelines);

View file

@ -105,4 +105,17 @@ GbmBuffer *DrmVirtualOutput::currentBuffer() const
#endif
}
bool DrmVirtualOutput::isFormatSupported(uint32_t drmFormat) const
{
Q_UNUSED(drmFormat);
return true;
}
QVector<uint64_t> DrmVirtualOutput::supportedModifiers(uint32_t drmFormat) const
{
Q_UNUSED(drmFormat);
// empty list -> implicit modifiers are used / modifier is freely chosen by gbm
return {};
}
}

View file

@ -32,6 +32,9 @@ public:
QSize sourceSize() const override;
bool isDpmsEnabled() const override;
bool isFormatSupported(uint32_t drmFormat) const override;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
int gammaRampSize() const override {
return 200;
}
@ -40,7 +43,6 @@ public:
return true;
}
private:
void vblank(std::chrono::nanoseconds timestamp);
void updateMode(int modeIndex) override;

View file

@ -139,18 +139,32 @@ bool EglGbmBackend::initRenderingContext()
}
return true;
}
bool EglGbmBackend::resetOutput(Output &output, DrmAbstractOutput *drmOutput)
{
output.output = drmOutput;
const QSize size = drmOutput->sourceSize();
int flags = GBM_BO_USE_RENDERING;
if (drmOutput->gpu() == m_gpu) {
flags |= GBM_BO_USE_SCANOUT;
QVector<uint64_t> modifiers = drmOutput->supportedModifiers(m_gbmFormat);
QSharedPointer<GbmSurface> gbmSurface;
if (modifiers.isEmpty()) {
int flags = GBM_BO_USE_RENDERING;
if (drmOutput->gpu() == m_gpu) {
flags |= GBM_BO_USE_SCANOUT;
} else {
flags |= GBM_BO_USE_LINEAR;
}
gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, m_gbmFormat, flags);
} else {
flags |= GBM_BO_USE_LINEAR;
gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, m_gbmFormat, modifiers);
if (!gbmSurface->isValid()) {
// the egl / gbm implementation may reject the modifier list from another gpu
// as a fallback use linear, to at least make CPU copy more efficient
modifiers = {DRM_FORMAT_MOD_LINEAR};
gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, m_gbmFormat, modifiers);
}
}
auto gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, GBM_FORMAT_XRGB8888, flags);
if (!gbmSurface) {
if (!gbmSurface->isValid()) {
qCCritical(KWIN_DRM) << "Creating GBM surface failed:" << strerror(errno);
return false;
}
@ -386,34 +400,51 @@ bool EglGbmBackend::initBufferConfigs()
qCDebug(KWIN_DRM) << "EGL buffer configs count:" << count;
uint32_t fallbackFormat;
EGLConfig fallbackConfig = nullptr;
// Loop through all configs, choosing the first one that has suitable format.
for (EGLint i = 0; i < count; i++) {
EGLint gbmFormat;
// Query some configuration parameters, to show in debug log.
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat);
// Query number of bits for color channel
EGLint blueSize, redSize, greenSize, alphaSize;
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &redSize);
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &greenSize);
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blueSize);
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &alphaSize);
if (KWIN_DRM().isDebugEnabled()) {
// GBM formats are declared as FOURCC code (integer from ASCII chars, so use this fact).
char gbmFormatStr[sizeof(EGLint) + 1] = {0};
memcpy(gbmFormatStr, &gbmFormat, sizeof(EGLint));
// Query number of bits for color channel.
EGLint blueSize, redSize, greenSize, alphaSize;
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &redSize);
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &greenSize);
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blueSize);
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &alphaSize);
qCDebug(KWIN_DRM) << " EGL config #" << i << " has GBM FOURCC format:" << gbmFormatStr
<< "; color sizes (RGBA order):"
<< redSize << greenSize << blueSize << alphaSize;
}
if ((gbmFormat == GBM_FORMAT_XRGB8888) || (gbmFormat == GBM_FORMAT_ARGB8888)) {
if (!m_gpu->isFormatSupported(gbmFormat)) {
continue;
}
// prefer XRGB8888 as it's most likely to be supported by secondary GPUs as well
if (gbmFormat == GBM_BO_FORMAT_XRGB8888) {
m_gbmFormat = gbmFormat;
setConfig(configs[i]);
return true;
} else if (!fallbackConfig && blueSize >= 8 && redSize >= 8 && greenSize >= 8) {
fallbackFormat = gbmFormat;
fallbackConfig = configs[i];
}
}
if (fallbackConfig) {
m_gbmFormat = fallbackFormat;
setConfig(fallbackConfig);
return true;
}
qCCritical(KWIN_DRM) << "Choosing EGL config did not return a suitable config. There were"
<< count << "configs.";
return false;
@ -591,15 +622,18 @@ bool EglGbmBackend::scanout(int screenId, SurfaceItem *surfaceItem)
if (buffer->size() != output.output->modeSize()) {
return false;
}
if (!buffer->planes().count() ||
!gbm_device_is_format_supported(m_gpu->gbmDevice(), buffer->format(), GBM_BO_USE_SCANOUT)) {
if (!buffer->planes().count()) {
return false;
}
if (!output.output->isFormatSupported(buffer->format())) {
return false;
}
gbm_bo *importedBuffer;
if (buffer->planes()[0].modifier != DRM_FORMAT_MOD_INVALID
|| buffer->planes()[0].offset > 0
|| buffer->planes().size() > 1) {
if (!m_gpu->addFB2ModifiersSupported()) {
if (!m_gpu->addFB2ModifiersSupported() || !output.output->supportedModifiers(buffer->format()).contains(buffer->planes()[0].modifier)) {
return false;
}
gbm_import_fd_modifier_data data = {};

View file

@ -114,6 +114,7 @@ private:
QVector<Output> m_outputs;
QVector<Output> m_secondaryGpuOutputs;
uint32_t m_gbmFormat;
friend class EglGbmTexture;
};

View file

@ -28,7 +28,22 @@ GbmSurface::GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, uint32_t
qCCritical(KWIN_DRM) << "Could not create gbm surface!" << strerror(errno);
return;
}
m_eglSurface = eglCreatePlatformWindowSurfaceEXT(m_gpu->eglDisplay(), m_gpu->eglBackend()->config(), (void *)(m_surface), nullptr);
m_eglSurface = eglCreatePlatformWindowSurfaceEXT(m_gpu->eglDisplay(), m_gpu->eglBackend()->config(), m_surface, nullptr);
if (m_eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
}
}
GbmSurface::GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, QVector<uint64_t> modifiers)
: m_surface(gbm_surface_create_with_modifiers(gpu->gbmDevice(), size.width(), size.height(), format, modifiers.isEmpty() ? nullptr : modifiers.constData(), modifiers.count()))
, m_gpu(gpu)
, m_size(size)
{
if (!m_surface) {
qCCritical(KWIN_DRM) << "Could not create gbm surface!" << strerror(errno);
return;
}
m_eglSurface = eglCreatePlatformWindowSurfaceEXT(m_gpu->eglDisplay(), m_gpu->eglBackend()->config(), m_surface, nullptr);
if (m_eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
}

View file

@ -25,6 +25,7 @@ class GbmSurface
{
public:
explicit GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, uint32_t flags);
explicit GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, QVector<uint64_t> modifiers);
~GbmSurface();
QSharedPointer<DrmGbmBuffer> swapBuffersForDrm();