Commit d7ec5199 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

backends/drm: Import gbm bos using dmabuf

It appears that importing gbm_bo's using eglCreateImageKHR() doesn't
work on nvidia.

Another issue is that Platform::createDmaBufTexture() uses
gbm_bo_create(). The nvidia driver doesn't like that, it's preferred to
use gbm_bo_create_with_modifiers().

In order to address those issues, this change refactors how gbm_bo
objects are imported and how gbm_bo's are allocated for dmabuf textures,
so the same code can be reused in EglGbmBackend::textureForOutput()
and Platform::createDmaBufTexture().
parent db2af550
Pipeline #175736 passed with stage
in 12 minutes and 18 seconds
......@@ -24,7 +24,6 @@ set(DRM_SOURCES
egl_gbm_layer.cpp
drm_buffer_gbm.cpp
gbm_surface.cpp
gbm_dmabuf.cpp
placeholderinputeventfilter.cpp
virtual_egl_gbm_layer.cpp
drm_lease_egl_gbm_layer.cpp
......
......@@ -13,6 +13,7 @@
#include "backends/libinput/libinputbackend.h"
#include "composite.h"
#include "cursor.h"
#include "drm_fourcc.h"
#include "drm_gpu.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
......@@ -623,11 +624,37 @@ void DrmBackend::removeVirtualOutput(Output *output)
primaryGpu()->removeVirtualOutput(virtualOutput);
}
DmaBufTexture *DrmBackend::createDmaBufTexture(const QSize &size)
QSharedPointer<DmaBufTexture> DrmBackend::createDmaBufTexture(const QSize &size)
{
if (const auto eglBackend = dynamic_cast<EglGbmBackend *>(m_renderBackend); eglBackend && primaryGpu()->gbmDevice()) {
eglBackend->makeCurrent();
return GbmDmaBuf::createBuffer(size, primaryGpu()->gbmDevice());
const int format = GBM_FORMAT_ARGB8888;
const uint64_t modifiers[] = {DRM_FORMAT_MOD_LINEAR};
gbm_bo *bo = gbm_bo_create_with_modifiers(primaryGpu()->gbmDevice(),
size.width(),
size.height(),
format,
modifiers, 1);
// If modifiers are not supported fallback to gbm_bo_create().
if (!bo && errno == ENOSYS) {
bo = gbm_bo_create(primaryGpu()->gbmDevice(),
size.width(),
size.height(),
format,
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
}
if (!bo) {
return nullptr;
}
// The bo will be kept around until the last fd is closed.
const DmaBufAttributes attributes = dmaBufAttributesForBo(bo);
gbm_bo_destroy(bo);
return QSharedPointer<DmaBufTexture>::create(eglBackend->importDmaBufAsTexture(attributes), attributes);
} else {
return nullptr;
}
......
......@@ -44,7 +44,7 @@ public:
InputBackend *createInputBackend() override;
QPainterBackend *createQPainterBackend() override;
OpenGLBackend *createOpenGLBackend() override;
DmaBufTexture *createDmaBufTexture(const QSize &size) override;
QSharedPointer<DmaBufTexture> createDmaBufTexture(const QSize &size) override;
Session *session() const override;
bool initialize() override;
......
......@@ -12,7 +12,6 @@
#include "config-kwin.h"
#include "drm_gpu.h"
#include "kwineglimagetexture.h"
#include "kwineglutils_p.h"
#include "logging.h"
#include "wayland/clientbuffer.h"
......@@ -129,17 +128,6 @@ bool GbmBuffer::map(uint32_t flags)
return m_data;
}
QSharedPointer<GLTexture> GbmBuffer::createTexture(EGLDisplay eglDisplay) const
{
EGLImageKHR image = eglCreateImageKHR(eglDisplay, nullptr, EGL_NATIVE_PIXMAP_KHR, m_bo, nullptr);
if (image != EGL_NO_IMAGE_KHR) {
return QSharedPointer<EGLImageTexture>::create(eglDisplay, image, GL_RGBA8, QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)));
} else {
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
return nullptr;
}
}
void GbmBuffer::createFds()
{
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
......
......@@ -22,6 +22,7 @@
#include "egl_dmabuf.h"
#include "egl_gbm_cursor_layer.h"
#include "egl_gbm_layer.h"
#include "gbm_dmabuf.h"
#include "gbm_surface.h"
#include "kwineglutils_p.h"
#include "linux_dmabuf.h"
......@@ -267,4 +268,99 @@ bool operator==(const GbmFormat &lhs, const GbmFormat &rhs)
return lhs.drmFormat == rhs.drmFormat;
}
EGLImageKHR EglGbmBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf)
{
QVector<EGLint> attribs;
attribs.reserve(6 + dmabuf.planeCount * 10 + 1);
attribs
<< EGL_WIDTH << dmabuf.width
<< EGL_HEIGHT << dmabuf.height
<< EGL_LINUX_DRM_FOURCC_EXT << dmabuf.format;
attribs
<< EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.fd[0]
<< EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.offset[0]
<< EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.pitch[0];
if (dmabuf.modifier[0] != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier[0] & 0xffffffff)
<< EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier[0] >> 32);
}
if (dmabuf.planeCount > 1) {
attribs
<< EGL_DMA_BUF_PLANE1_FD_EXT << dmabuf.fd[1]
<< EGL_DMA_BUF_PLANE1_OFFSET_EXT << dmabuf.offset[1]
<< EGL_DMA_BUF_PLANE1_PITCH_EXT << dmabuf.pitch[1];
if (dmabuf.modifier[1] != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabuf.modifier[1] & 0xffffffff)
<< EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(dmabuf.modifier[1] >> 32);
}
}
if (dmabuf.planeCount > 2) {
attribs
<< EGL_DMA_BUF_PLANE2_FD_EXT << dmabuf.fd[2]
<< EGL_DMA_BUF_PLANE2_OFFSET_EXT << dmabuf.offset[2]
<< EGL_DMA_BUF_PLANE2_PITCH_EXT << dmabuf.pitch[2];
if (dmabuf.modifier[2] != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabuf.modifier[2] & 0xffffffff)
<< EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT << EGLint(dmabuf.modifier[2] >> 32);
}
}
if (dmabuf.planeCount > 3) {
attribs
<< EGL_DMA_BUF_PLANE3_FD_EXT << dmabuf.fd[3]
<< EGL_DMA_BUF_PLANE3_OFFSET_EXT << dmabuf.offset[3]
<< EGL_DMA_BUF_PLANE3_PITCH_EXT << dmabuf.pitch[3];
if (dmabuf.modifier[3] != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabuf.modifier[3] & 0xffffffff)
<< EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT << EGLint(dmabuf.modifier[3] >> 32);
}
}
attribs << EGL_NONE;
return eglCreateImageKHR(eglDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());
}
EGLImageKHR EglGbmBackend::importDmaBufAsImage(gbm_bo *bo)
{
const DmaBufAttributes dmabuf = dmaBufAttributesForBo(bo);
EGLImage image = importDmaBufAsImage(dmabuf);
for (int i = 0; i < dmabuf.planeCount; ++i) {
close(dmabuf.fd[i]);
}
return image;
}
QSharedPointer<GLTexture> EglGbmBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes)
{
EGLImageKHR image = importDmaBufAsImage(attributes);
if (image != EGL_NO_IMAGE_KHR) {
return QSharedPointer<EGLImageTexture>::create(eglDisplay(), image, GL_RGBA8, QSize(attributes.width, attributes.height));
} else {
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
return nullptr;
}
}
QSharedPointer<GLTexture> EglGbmBackend::importDmaBufAsTexture(gbm_bo *bo)
{
EGLImageKHR image = importDmaBufAsImage(bo);
if (image != EGL_NO_IMAGE_KHR) {
return QSharedPointer<EGLImageTexture>::create(eglDisplay(), image, GL_RGBA8, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)));
} else {
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
return nullptr;
}
}
} // namespace KWin
......@@ -27,6 +27,8 @@ class SurfaceInterface;
namespace KWin
{
struct DmaBufAttributes;
class Output;
class DrmAbstractOutput;
class DrmBuffer;
......@@ -79,6 +81,11 @@ public:
std::optional<GbmFormat> gbmFormatForDrmFormat(uint32_t format) const;
DrmGpu *gpu() const;
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes);
EGLImageKHR importDmaBufAsImage(gbm_bo *bo);
QSharedPointer<GLTexture> importDmaBufAsTexture(const DmaBufAttributes &attributes);
QSharedPointer<GLTexture> importDmaBufAsTexture(gbm_bo *bo);
private:
bool initializeEgl();
bool initBufferConfigs();
......
......@@ -79,7 +79,7 @@ bool EglGbmLayer::checkTestBuffer()
QSharedPointer<GLTexture> EglGbmLayer::texture() const
{
if (m_scanoutBuffer) {
return static_cast<GbmBuffer *>(m_scanoutBuffer->buffer())->createTexture(m_surface.eglBackend()->eglDisplay());
return m_surface.eglBackend()->importDmaBufAsTexture(static_cast<GbmBuffer *>(m_scanoutBuffer->buffer())->bo());
} else {
return m_surface.texture();
}
......
......@@ -336,7 +336,7 @@ QSharedPointer<GLTexture> EglGbmLayerSurface::texture() const
qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!";
return nullptr;
}
return m_currentBuffer->createTexture(m_eglBackend->eglDisplay());
return m_eglBackend->importDmaBufAsTexture(m_currentBuffer->bo());
}
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::renderTestBuffer(const QSize &bufferSize, const QMap<uint32_t, QVector<uint64_t>> &formats, uint32_t additionalFlags)
......
/*
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gbm_dmabuf.h"
#include "drm_fourcc.h"
#include "kwineglimagetexture.h"
#include "main.h"
#include "platform.h"
#include <unistd.h>
namespace KWin
{
GbmDmaBuf::GbmDmaBuf(GLTexture *texture, gbm_bo *bo, int fd)
: DmaBufTexture(texture)
, m_bo(bo)
, m_fd(fd)
{
}
GbmDmaBuf::~GbmDmaBuf()
{
m_texture.reset(nullptr);
close(m_fd);
gbm_bo_destroy(m_bo);
}
int GbmDmaBuf::fd() const
{
return m_fd;
}
quint32 GbmDmaBuf::stride() const
{
return gbm_bo_get_stride(m_bo);
}
GbmDmaBuf *GbmDmaBuf::createBuffer(const QSize &size, gbm_device *device)
{
if (!device) {
return nullptr;
}
auto bo = gbm_bo_create(device, size.width(), size.height(), GBM_FORMAT_ARGB8888, GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
if (!bo) {
return nullptr;
}
const int fd = gbm_bo_get_fd(bo);
if (fd < 0) {
gbm_bo_destroy(bo);
return nullptr;
}
EGLint importAttributes[] = {
EGL_WIDTH, EGLint(gbm_bo_get_width(bo)),
EGL_HEIGHT, EGLint(gbm_bo_get_height(bo)),
EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
EGL_DMA_BUF_PLANE0_FD_EXT, fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(gbm_bo_get_offset(bo, 0)),
EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(gbm_bo_get_stride(bo)),
EGL_NONE};
EGLDisplay display = kwinApp()->platform()->sceneEglDisplay();
if (display == EGL_NO_DISPLAY) {
return nullptr;
}
EGLImageKHR destinationImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, importAttributes);
if (destinationImage == EGL_NO_IMAGE_KHR) {
close(fd);
gbm_bo_destroy(bo);
return nullptr;
}
return new GbmDmaBuf(new KWin::EGLImageTexture(display, destinationImage, GL_RGBA8, size), bo, fd);
}
}
......@@ -7,27 +7,28 @@
#pragma once
#include "dmabuftexture.h"
#include <QSize>
#include <epoxy/egl.h>
#include <gbm.h>
namespace KWin
{
class GbmDmaBuf : public DmaBufTexture
inline DmaBufAttributes dmaBufAttributesForBo(gbm_bo *bo)
{
public:
~GbmDmaBuf();
int fd() const override;
quint32 stride() const override;
static GbmDmaBuf *createBuffer(const QSize &size, gbm_device *device);
private:
GbmDmaBuf(GLTexture *texture, gbm_bo *bo, int fd);
struct gbm_bo *const m_bo;
const int m_fd;
};
DmaBufAttributes attributes;
attributes.planeCount = gbm_bo_get_plane_count(bo);
attributes.width = gbm_bo_get_width(bo);
attributes.height = gbm_bo_get_height(bo);
attributes.format = gbm_bo_get_format(bo);
for (int i = 0; i < attributes.planeCount; ++i) {
attributes.fd[i] = gbm_bo_get_fd_for_plane(bo, i);
attributes.offset[i] = gbm_bo_get_offset(bo, i);
attributes.pitch[i] = gbm_bo_get_stride_for_plane(bo, i);
attributes.modifier[i] = gbm_bo_get_modifier(bo);
}
return attributes;
}
} // namespace KWin
......@@ -17,7 +17,6 @@
#include "egl_dmabuf.h"
#include "egl_gbm_backend.h"
#include "gbm_surface.h"
#include "kwineglimagetexture.h"
#include "kwineglutils_p.h"
#include "logging.h"
#include "shadowbuffer.h"
......@@ -139,7 +138,7 @@ QSharedPointer<GLTexture> VirtualEglGbmLayer::texture() const
qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!";
return nullptr;
}
return m_currentBuffer->createTexture(m_eglBackend->eglDisplay());
return m_eglBackend->importDmaBufAsTexture(m_currentBuffer->bo());
}
bool VirtualEglGbmLayer::scanout(SurfaceItem *surfaceItem)
......
......@@ -6,7 +6,7 @@ set(WAYLAND_BACKEND_SOURCES
)
if (HAVE_WAYLAND_EGL)
set(WAYLAND_BACKEND_SOURCES egl_wayland_backend.cpp ${WAYLAND_BACKEND_SOURCES} ../drm/gbm_dmabuf.cpp)
set(WAYLAND_BACKEND_SOURCES egl_wayland_backend.cpp ${WAYLAND_BACKEND_SOURCES})
endif()
add_library(KWinWaylandWaylandBackend MODULE ${WAYLAND_BACKEND_SOURCES})
......
......@@ -10,7 +10,6 @@
#include "wayland_backend.h"
#if HAVE_WAYLAND_EGL
#include "../drm/gbm_dmabuf.h"
#include "egl_wayland_backend.h"
#include <gbm.h>
#endif
......@@ -991,15 +990,6 @@ void WaylandBackend::addConfiguredOutput(WaylandOutput *output)
}
}
DmaBufTexture *WaylandBackend::createDmaBufTexture(const QSize &size)
{
#if HAVE_WAYLAND_EGL
return GbmDmaBuf::createBuffer(size, m_gbmDevice);
#else
return nullptr;
#endif
}
void WaylandBackend::createDpmsFilter()
{
if (m_dpmsFilter) {
......
......@@ -260,7 +260,6 @@ public:
InputBackend *createInputBackend() override;
OpenGLBackend *createOpenGLBackend() override;
QPainterBackend *createQPainterBackend() override;
DmaBufTexture *createDmaBufTexture(const QSize &size) override;
void flush();
......
......@@ -6,19 +6,31 @@
#include "dmabuftexture.h"
#include "kwineglimagetexture.h"
#include "kwinglutils.h"
#include <unistd.h>
namespace KWin
{
DmaBufTexture::DmaBufTexture(KWin::GLTexture *texture)
DmaBufTexture::DmaBufTexture(QSharedPointer<GLTexture> texture, const DmaBufAttributes &attributes)
: m_texture(texture)
, m_framebuffer(new KWin::GLFramebuffer(texture))
, m_framebuffer(new GLFramebuffer(texture.data()))
, m_attributes(attributes)
{
}
DmaBufTexture::~DmaBufTexture()
{
for (int i = 0; i < m_attributes.planeCount; ++i) {
::close(m_attributes.fd[i]);
}
}
DmaBufTexture::~DmaBufTexture() = default;
DmaBufAttributes DmaBufTexture::attributes() const
{
return m_attributes;
}
KWin::GLTexture *DmaBufTexture::texture() const
{
......
......@@ -6,27 +6,42 @@
#pragma once
#include "kwin_export.h"
#include <QScopedPointer>
#include <QSharedPointer>
namespace KWin
{
class GLFramebuffer;
class GLTexture;
struct DmaBufAttributes
{
int planeCount;
int width;
int height;
int format;
int fd[4];
int offset[4];
int pitch[4];
uint64_t modifier[4];
};
class KWIN_EXPORT DmaBufTexture
{
public:
explicit DmaBufTexture(KWin::GLTexture *texture);
explicit DmaBufTexture(QSharedPointer<GLTexture> texture, const DmaBufAttributes &attributes);
virtual ~DmaBufTexture();
virtual quint32 stride() const = 0;
virtual int fd() const = 0;
KWin::GLTexture *texture() const;
KWin::GLFramebuffer *framebuffer() const;
DmaBufAttributes attributes() const;
GLTexture *texture() const;
GLFramebuffer *framebuffer() const;
protected:
QScopedPointer<KWin::GLTexture> m_texture;
QScopedPointer<KWin::GLFramebuffer> m_framebuffer;
QSharedPointer<GLTexture> m_texture;
QScopedPointer<GLFramebuffer> m_framebuffer;
DmaBufAttributes m_attributes;
};
}
......@@ -80,6 +80,12 @@ QPainterBackend *Platform::createQPainterBackend()
return nullptr;
}
QSharedPointer<DmaBufTexture> Platform::createDmaBufTexture(const QSize &size)
{
Q_UNUSED(size)
return {};
}
Edge *Platform::createScreenEdge(ScreenEdges *edges)
{
return new Edge(edges);
......
......@@ -66,11 +66,7 @@ public:
virtual InputBackend *createInputBackend();
virtual OpenGLBackend *createOpenGLBackend();
virtual QPainterBackend *createQPainterBackend();
virtual DmaBufTexture *createDmaBufTexture(const QSize &size)
{
Q_UNUSED(size);
return nullptr;
}
virtual QSharedPointer<DmaBufTexture> createDmaBufTexture(const QSize &size);
/**
* Allows the platform to create a platform specific screen edge.
......
......@@ -142,14 +142,16 @@ void ScreenCastStream::onStreamAddBuffer(void *data, pw_buffer *buffer)
spa_data->flags = SPA_DATA_FLAG_READWRITE;
if (spa_data[0].type != SPA_ID_INVALID && spa_data[0].type & (1 << SPA_DATA_DmaBuf)) {
dmabuf.reset(kwinApp()->platform()->createDmaBufTexture(stream->m_resolution));
dmabuf = kwinApp()->platform()->createDmaBufTexture(stream->m_resolution);
}
if (dmabuf) {
const DmaBufAttributes dmabufAttribs = dmabuf->attributes();
spa_data->type = SPA_DATA_DmaBuf;
spa_data->fd = dmabuf->fd();
spa_data->fd = dmabufAttribs.fd[0];
spa_data->data = nullptr;
spa_data->maxsize = dmabuf->stride() * stream->m_resolution.height();
spa_data->maxsize = dmabufAttribs.pitch[0] * stream->m_resolution.height();
stream->m_dmabufDataForPwBuffer.insert(buffer, dmabuf);
#ifdef F_SEAL_SEAL // Disable memfd on systems that don't have it, like BSD < 12
......@@ -399,7 +401,7 @@ void ScreenCastStream::recordFrame(const QRegion &damagedRegion)
} else {
auto &buf = m_dmabufDataForPwBuffer[buffer];
spa_data->chunk->stride = buf->stride();
spa_data->chunk->stride = buf->attributes().pitch[0];
spa_data->chunk->size = spa_data->maxsize;
m_source->render(buf->framebuffer());
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment