Commit aa59344b authored by Aleix Pol Gonzalez's avatar Aleix Pol Gonzalez 🐧 Committed by Aleix Pol Gonzalez
Browse files

Platform: Improve our infrastructure to support dmabufs in backends

Allows creating buffers with modifiers.
Lets the windowed wayland backend support it, which makes testing
everything else much simpler.
parent e0454299
......@@ -245,6 +245,13 @@ else()
set(HAVE_GBM_BO_GET_FD_FOR_PLANE 0)
endif()
if (gbm_VERSION_STRING VERSION_GREATER_EQUAL 21.3.0)
set(GBM_CREATE_WITH_MODIFIERS2 1)
else()
set(GBM_CREATE_WITH_MODIFIERS2 0)
endif()
pkg_check_modules(Libxcvt IMPORTED_TARGET libxcvt>=0.1.1 REQUIRED)
add_feature_info(Libxcvt Libxcvt_FOUND "Required for generating modes in the drm backend")
......
......@@ -624,40 +624,78 @@ void DrmBackend::removeVirtualOutput(Output *output)
primaryGpu()->removeVirtualOutput(virtualOutput);
}
std::shared_ptr<DmaBufTexture> DrmBackend::createDmaBufTexture(const QSize &size)
{
if (const auto eglBackend = dynamic_cast<EglGbmBackend *>(m_renderBackend); eglBackend && primaryGpu()->gbmDevice()) {
eglBackend->makeCurrent();
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);
#if !GBM_CREATE_WITH_MODIFIERS2
struct gbm_bo *
gbm_bo_create_with_modifiers2(struct gbm_device *gbm,
uint32_t width, uint32_t height,
uint32_t format,
const uint64_t *modifiers,
const unsigned int count, quint32 flags)
{
return gbm_bo_create_with_modifiers(gbm, width, height, format, modifiers, count);
}
#endif
return std::make_shared<DmaBufTexture>(eglBackend->importDmaBufAsTexture(attributes), attributes);
} else {
gbm_bo *DrmBackend::createBo(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers)
{
const auto eglBackend = dynamic_cast<EglGbmBackend *>(m_renderBackend);
if (!eglBackend || !primaryGpu()->gbmDevice()) {
return nullptr;
}
eglBackend->makeCurrent();
const uint32_t flags = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
gbm_bo *bo = nullptr;
if (modifiers.count() > 0 && !(modifiers.count() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID)) {
bo = gbm_bo_create_with_modifiers2(primaryGpu()->gbmDevice(),
size.width(),
size.height(),
format,
modifiers.constData(), modifiers.count(), 0);
}
if (!bo && (modifiers.isEmpty() || modifiers.contains(DRM_FORMAT_MOD_INVALID))) {
bo = gbm_bo_create(primaryGpu()->gbmDevice(),
size.width(),
size.height(),
format,
flags);
Q_ASSERT(!bo || gbm_bo_get_modifier(bo) == DRM_FORMAT_MOD_INVALID);
}
return bo;
}
std::optional<DmaBufAttributes> DrmBackend::testCreateDmaBuf(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers)
{
gbm_bo *bo = createBo(size, format, modifiers);
if (!bo) {
return {};
}
auto ret = dmaBufAttributesForBo(bo);
gbm_bo_destroy(bo);
// We are just testing to know it works and check the modifier, no need to keep the fd
for (int i = 0, c = ret.planeCount; i < c; ++i) {
close(ret.fd[i]);
}
return ret;
}
std::shared_ptr<DmaBufTexture> DrmBackend::createDmaBufTexture(const QSize &size, quint32 format, uint64_t modifier)
{
const auto eglBackend = dynamic_cast<EglGbmBackend *>(m_renderBackend);
QVector<uint64_t> mods = {modifier};
gbm_bo *bo = createBo(size, format, mods);
if (!bo) {
return {};
}
// The bo will be kept around until the last fd is closed.
const DmaBufAttributes attributes = dmaBufAttributesForBo(bo);
gbm_bo_destroy(bo);
return std::make_shared<DmaBufTexture>(eglBackend->importDmaBufAsTexture(attributes), attributes);
}
DrmGpu *DrmBackend::primaryGpu() const
......
......@@ -19,6 +19,8 @@
#include <memory>
struct gbm_bo;
namespace KWin
{
......@@ -44,7 +46,9 @@ public:
InputBackend *createInputBackend() override;
QPainterBackend *createQPainterBackend() override;
OpenGLBackend *createOpenGLBackend() override;
std::shared_ptr<DmaBufTexture> createDmaBufTexture(const QSize &size) override;
std::optional<DmaBufAttributes> testCreateDmaBuf(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers) override;
std::shared_ptr<DmaBufTexture> createDmaBufTexture(const QSize &size, quint32 format, const uint64_t modifier) override;
Session *session() const override;
bool initialize() override;
......@@ -112,6 +116,8 @@ private:
std::unique_ptr<DpmsInputEventFilter> m_dpmsFilter;
std::unique_ptr<PlaceholderInputEventFilter> m_placeholderFilter;
DrmRenderBackend *m_renderBackend = nullptr;
gbm_bo *createBo(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers);
};
}
......
......@@ -268,68 +268,7 @@ 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 != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
<< EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 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 != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
<< EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 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 != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
<< EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 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 != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
<< EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
}
}
attribs << EGL_NONE;
return eglCreateImageKHR(eglDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());
}
EGLImageKHR EglGbmBackend::importDmaBufAsImage(gbm_bo *bo)
EGLImageKHR EglGbmBackend::importBufferObjectAsImage(gbm_bo *bo)
{
const DmaBufAttributes dmabuf = dmaBufAttributesForBo(bo);
EGLImage image = importDmaBufAsImage(dmabuf);
......@@ -341,20 +280,9 @@ EGLImageKHR EglGbmBackend::importDmaBufAsImage(gbm_bo *bo)
return image;
}
std::shared_ptr<GLTexture> EglGbmBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes)
{
EGLImageKHR image = importDmaBufAsImage(attributes);
if (image != EGL_NO_IMAGE_KHR) {
return std::make_shared<EGLImageTexture>(eglDisplay(), image, GL_RGBA8, QSize(attributes.width, attributes.height));
} else {
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
return nullptr;
}
}
std::shared_ptr<GLTexture> EglGbmBackend::importDmaBufAsTexture(gbm_bo *bo)
std::shared_ptr<GLTexture> EglGbmBackend::importBufferObjectAsTexture(gbm_bo *bo)
{
EGLImageKHR image = importDmaBufAsImage(bo);
EGLImageKHR image = importBufferObjectAsImage(bo);
if (image != EGL_NO_IMAGE_KHR) {
return std::make_shared<EGLImageTexture>(eglDisplay(), image, GL_RGBA8, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)));
} else {
......
......@@ -80,10 +80,8 @@ public:
std::optional<GbmFormat> gbmFormatForDrmFormat(uint32_t format) const;
DrmGpu *gpu() const;
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes);
EGLImageKHR importDmaBufAsImage(gbm_bo *bo);
std::shared_ptr<GLTexture> importDmaBufAsTexture(const DmaBufAttributes &attributes);
std::shared_ptr<GLTexture> importDmaBufAsTexture(gbm_bo *bo);
EGLImageKHR importBufferObjectAsImage(gbm_bo *bo);
std::shared_ptr<GLTexture> importBufferObjectAsTexture(gbm_bo *bo);
private:
bool initializeEgl();
......
......@@ -82,7 +82,7 @@ bool EglGbmLayer::checkTestBuffer()
std::shared_ptr<GLTexture> EglGbmLayer::texture() const
{
if (m_scanoutBuffer) {
return m_surface.eglBackend()->importDmaBufAsTexture(static_cast<GbmBuffer *>(m_scanoutBuffer->buffer())->bo());
return m_surface.eglBackend()->importBufferObjectAsTexture(static_cast<GbmBuffer *>(m_scanoutBuffer->buffer())->bo());
} else {
return m_surface.texture();
}
......
......@@ -336,7 +336,7 @@ std::shared_ptr<GLTexture> EglGbmLayerSurface::texture() const
qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!";
return nullptr;
}
return m_eglBackend->importDmaBufAsTexture(m_currentBuffer->bo());
return m_eglBackend->importBufferObjectAsTexture(m_currentBuffer->bo());
}
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::renderTestBuffer(const QSize &bufferSize, const QMap<uint32_t, QVector<uint64_t>> &formats, uint32_t additionalFlags)
......
......@@ -139,7 +139,7 @@ std::shared_ptr<GLTexture> VirtualEglGbmLayer::texture() const
qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!";
return nullptr;
}
return m_eglBackend->importDmaBufAsTexture(m_currentBuffer->bo());
return m_eglBackend->importBufferObjectAsTexture(m_currentBuffer->bo());
}
bool VirtualEglGbmLayer::scanout(SurfaceItem *surfaceItem)
......
......@@ -38,6 +38,8 @@
#include <QOpenGLContext>
#include <cmath>
#include <drm_fourcc.h>
#include <gbm.h>
namespace KWin
{
......@@ -229,6 +231,8 @@ EglWaylandBackend::EglWaylandBackend(WaylandBackend *b)
}
m_outputs.erase(it);
});
b->setEglBackend(this);
}
EglWaylandBackend::~EglWaylandBackend()
......@@ -391,5 +395,6 @@ OutputLayer *EglWaylandBackend::primaryLayer(Output *output)
{
return m_outputs[output].get();
}
}
}
......@@ -13,6 +13,8 @@
#include "outputlayer.h"
#include "utils/damagejournal.h"
// wayland
#include <dmabuftexture.h>
#include <optional>
#include <wayland-egl.h>
#include <memory>
......@@ -20,6 +22,7 @@
class QTemporaryFile;
struct wl_buffer;
struct wl_shm;
struct gbm_bo;
namespace KWin
{
......@@ -96,6 +99,7 @@ public:
std::shared_ptr<KWin::GLTexture> textureForOutput(KWin::Output *output) const override;
private:
gbm_bo *createBo(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers);
bool initializeEgl();
bool initBufferConfigs();
bool initRenderingContext();
......
......@@ -56,7 +56,9 @@
#include <linux/input.h>
#include <unistd.h>
#include "../drm/gbm_dmabuf.h"
#include <cmath>
#include <drm_fourcc.h>
namespace KWin
{
......@@ -1026,6 +1028,72 @@ void WaylandBackend::removeVirtualOutput(Output *output)
}
}
std::optional<DmaBufAttributes> WaylandBackend::testCreateDmaBuf(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers)
{
gbm_bo *bo = createBo(size, format, modifiers);
if (!bo) {
return {};
}
auto ret = dmaBufAttributesForBo(bo);
gbm_bo_destroy(bo);
// We are just testing to know it works and check the modifier, no need to keep the fd
for (int i = 0, c = ret.planeCount; i < c; ++i) {
close(ret.fd[i]);
}
return ret;
}
std::shared_ptr<DmaBufTexture> WaylandBackend::createDmaBufTexture(const QSize &size, quint32 format, uint64_t modifier)
{
gbm_bo *bo = createBo(size, format, {modifier});
if (!bo) {
return {};
}
// The bo will be kept around until the last fd is closed.
const DmaBufAttributes attributes = dmaBufAttributesForBo(bo);
gbm_bo_destroy(bo);
return std::make_shared<DmaBufTexture>(m_eglBackend->importDmaBufAsTexture(attributes), attributes);
}
#if !GBM_CREATE_WITH_MODIFIERS2
struct gbm_bo *
gbm_bo_create_with_modifiers2(struct gbm_device *gbm,
uint32_t width, uint32_t height,
uint32_t format,
const uint64_t *modifiers,
const unsigned int count, quint32 flags)
{
return gbm_bo_create_with_modifiers(gbm, width, height, format, modifiers, count);
}
#endif
gbm_bo *WaylandBackend::createBo(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers)
{
Q_ASSERT(!modifiers.isEmpty());
const uint32_t flags = GBM_BO_USE_RENDERING;
gbm_bo *bo = nullptr;
if (!modifiers.isEmpty() && !(modifiers.count() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID)) {
bo = gbm_bo_create_with_modifiers2(m_gbmDevice,
size.width(),
size.height(),
format,
modifiers.constData(), modifiers.count(), 0);
}
if (!bo && modifiers.contains(DRM_FORMAT_MOD_INVALID)) {
bo = gbm_bo_create(m_gbmDevice,
size.width(),
size.height(),
format,
flags | GBM_BO_USE_LINEAR);
Q_ASSERT(!bo || gbm_bo_get_modifier(bo) == DRM_FORMAT_MOD_INVALID);
}
return bo;
}
}
} // KWin
......@@ -28,6 +28,7 @@ struct wl_display;
struct wl_event_queue;
struct wl_seat;
struct gbm_device;
struct gbm_bo;
namespace KWayland
{
......@@ -67,6 +68,7 @@ namespace Wayland
class WaylandBackend;
class WaylandSeat;
class WaylandOutput;
class EglWaylandBackend;
class WaylandCursor : public QObject
{
......@@ -301,6 +303,19 @@ public:
Output *createVirtualOutput(const QString &name, const QSize &size, double scale) override;
void removeVirtualOutput(Output *output) override;
std::optional<DmaBufAttributes> testCreateDmaBuf(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers) override;
std::shared_ptr<DmaBufTexture> createDmaBufTexture(const QSize &size, quint32 format, uint64_t modifier) override;
gbm_device *gbmDevice() const
{
return m_gbmDevice;
}
void setEglBackend(EglWaylandBackend *eglBackend)
{
m_eglBackend = eglBackend;
}
Q_SIGNALS:
void systemCompositorDied();
void connectionFailed();
......@@ -316,6 +331,7 @@ private:
void updateScreenSize(WaylandOutput *output);
WaylandOutput *createOutput(const QPoint &position, const QSize &size);
gbm_bo *createBo(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers);
Session *m_session;
wl_display *m_display;
......@@ -331,6 +347,7 @@ private:
KWayland::Client::RelativePointerManager *m_relativePointerManager = nullptr;
KWayland::Client::PointerConstraints *m_pointerConstraints = nullptr;
KWayland::Client::PointerGestures *m_pointerGestures = nullptr;
EglWaylandBackend *m_eglBackend = nullptr;
QThread *m_connectionThread;
QVector<WaylandOutput *> m_outputs;
......
......@@ -33,3 +33,4 @@
#cmakedefine01 PipeWire_FOUND
#cmakedefine01 HAVE_XWAYLAND_LISTENFD
#cmakedefine01 GBM_CREATE_WITH_MODIFIERS2
......@@ -13,6 +13,7 @@
#include "composite.h"
#include "cursor.h"
#include "dmabuftexture.h"
#include "effects.h"
#include "outline.h"
#include "output.h"
......@@ -80,12 +81,27 @@ QPainterBackend *Platform::createQPainterBackend()
return nullptr;
}
std::shared_ptr<DmaBufTexture> Platform::createDmaBufTexture(const QSize &size)
std::optional<DmaBufAttributes> Platform::testCreateDmaBuf(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers)
{
Q_UNUSED(size)
Q_UNUSED(format)
Q_UNUSED(modifiers)
return {};
}
std::shared_ptr<DmaBufTexture> Platform::createDmaBufTexture(const QSize &size, quint32 format, uint64_t modifier)
{
Q_UNUSED(size)
Q_UNUSED(format)
Q_UNUSED(modifier)
return {};
}
std::shared_ptr<DmaBufTexture> Platform::createDmaBufTexture(const DmaBufAttributes &attribs)
{
return createDmaBufTexture({attribs.width, attribs.height}, attribs.format, attribs.modifier);
}
Edge *Platform::createScreenEdge(ScreenEdges *edges)
{
return new Edge(edges);
......
......@@ -17,6 +17,7 @@
#include <functional>
#include <memory>
#include <optional>
class QAction;
......@@ -43,6 +44,7 @@ class Scene;
class ScreenEdges;
class Session;
class OutputConfiguration;
struct DmaBufAttributes;
class KWIN_EXPORT Outputs : public QVector<Output *>
{
......@@ -67,7 +69,9 @@ public:
virtual InputBackend *createInputBackend();
virtual OpenGLBackend *createOpenGLBackend();
virtual QPainterBackend *createQPainterBackend();
virtual std::shared_ptr<DmaBufTexture> createDmaBufTexture(const QSize &size);
virtual std::optional<DmaBufAttributes> testCreateDmaBuf(const QSize &size, quint32 format, const QVector<uint64_t> &modifiers);
virtual std::shared_ptr<DmaBufTexture> createDmaBufTexture(const QSize &size, quint32 format, const uint64_t modifier);
std::shared_ptr<DmaBufTexture> createDmaBufTexture(const DmaBufAttributes &attributes);
/**
* Allows the platform to create a platform specific screen edge.
......
......@@ -16,6 +16,7 @@
#include "utils/egl_context_attribute_builder.h"
#include "wayland/display.h"
#include "wayland_server.h"
#include "dmabuftexture.h"
// kwin libs
#include <kwinglplatform.h>
#include <kwinglutils.h>
......@@ -23,6 +24,9 @@
#include <QOpenGLContext>
#include <memory>
#include "drm_fourcc.h"
#include <kwineglimagetexture.h>
#include <kwineglutils_p.h>
namespace KWin
{
......@@ -392,4 +396,76 @@ EglDmabuf *AbstractEglBackend::dmabuf() const
return m_dmaBuf;
}
EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf) const
{
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 != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
<< EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 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 != DRM_FORMAT_MOD_INVALID) {
attribs
<< EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
<< EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
}
}
if (dmabuf.planeCount > 2) {