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:
parent
19cc22102c
commit
26dff99f78
15 changed files with 176 additions and 37 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -114,6 +114,7 @@ private:
|
|||
|
||||
QVector<Output> m_outputs;
|
||||
QVector<Output> m_secondaryGpuOutputs;
|
||||
uint32_t m_gbmFormat;
|
||||
|
||||
friend class EglGbmTexture;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue