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.
This commit is contained in:
Aleix Pol 2022-06-02 20:21:21 +02:00 committed by Aleix Pol Gonzalez
parent e04542995e
commit aa59344be7
19 changed files with 321 additions and 124 deletions

View file

@ -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")

View file

@ -624,40 +624,78 @@ void DrmBackend::removeVirtualOutput(Output *output)
primaryGpu()->removeVirtualOutput(virtualOutput);
}
std::shared_ptr<DmaBufTexture> DrmBackend::createDmaBufTexture(const QSize &size)
#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)
{
if (const auto eglBackend = dynamic_cast<EglGbmBackend *>(m_renderBackend); eglBackend && primaryGpu()->gbmDevice()) {
return gbm_bo_create_with_modifiers(gbm, width, height, format, modifiers, count);
}
#endif
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 int format = GBM_FORMAT_ARGB8888;
const uint64_t modifiers[] = {DRM_FORMAT_MOD_LINEAR};
gbm_bo *bo = gbm_bo_create_with_modifiers(primaryGpu()->gbmDevice(),
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, 1);
modifiers.constData(), modifiers.count(), 0);
}
// If modifiers are not supported fallback to gbm_bo_create().
if (!bo && errno == ENOSYS) {
if (!bo && (modifiers.isEmpty() || modifiers.contains(DRM_FORMAT_MOD_INVALID))) {
bo = gbm_bo_create(primaryGpu()->gbmDevice(),
size.width(),
size.height(),
format,
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
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 nullptr;
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);
} else {
return nullptr;
}
}
DrmGpu *DrmBackend::primaryGpu() const

View file

@ -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);
};
}

View file

@ -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)
std::shared_ptr<GLTexture> EglGbmBackend::importBufferObjectAsTexture(gbm_bo *bo)
{
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)
{
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 {

View file

@ -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();

View file

@ -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();
}

View file

@ -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)

View file

@ -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)

View file

@ -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();
}
}
}

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -33,3 +33,4 @@
#cmakedefine01 PipeWire_FOUND
#cmakedefine01 HAVE_XWAYLAND_LISTENFD
#cmakedefine01 GBM_CREATE_WITH_MODIFIERS2

View file

@ -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);

View file

@ -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.

View file

@ -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) {
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(m_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());
}
std::shared_ptr<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
{
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_OPENGL) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
return nullptr;
}
}
}

View file

@ -30,6 +30,7 @@ struct AbstractEglBackendFunctions
eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
};
struct DmaBufAttributes;
class EglDmabuf;
class Output;
@ -68,6 +69,9 @@ public:
virtual bool prefer10bpc() const;
EglDmabuf *dmabuf() const;
std::shared_ptr<GLTexture> importDmaBufAsTexture(const DmaBufAttributes &attributes) const;
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes) const;
protected:
AbstractEglBackend(dev_t deviceId = 0);
void setEglDisplay(const EGLDisplay &display);

View file

@ -39,6 +39,27 @@
namespace KWin
{
uint32_t spaVideoFormatToDrmFormat(spa_video_format spa_format)
{
switch (spa_format) {
case SPA_VIDEO_FORMAT_RGBA:
return DRM_FORMAT_ABGR8888;
case SPA_VIDEO_FORMAT_RGBx:
return DRM_FORMAT_XBGR8888;
case SPA_VIDEO_FORMAT_BGRA:
return DRM_FORMAT_ARGB8888;
case SPA_VIDEO_FORMAT_BGRx:
return DRM_FORMAT_XRGB8888;
case SPA_VIDEO_FORMAT_BGR:
return DRM_FORMAT_BGR888;
case SPA_VIDEO_FORMAT_RGB:
return DRM_FORMAT_RGB888;
default:
qCDebug(KWIN_SCREENCAST) << "unknown format" << spa_format;
return DRM_FORMAT_INVALID;
}
}
void ScreenCastStream::onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message)
{
ScreenCastStream *pw = static_cast<ScreenCastStream *>(data);
@ -134,26 +155,26 @@ void ScreenCastStream::onStreamParamChanged(void *data, uint32_t id, const struc
void ScreenCastStream::onStreamAddBuffer(void *data, pw_buffer *buffer)
{
std::shared_ptr<DmaBufTexture> dmabuf;
ScreenCastStream *stream = static_cast<ScreenCastStream *>(data);
struct spa_data *spa_data = buffer->buffer->datas;
spa_data->mapoffset = 0;
spa_data->flags = SPA_DATA_FLAG_READWRITE;
std::shared_ptr<DmaBufTexture> dmabuff;
if (spa_data[0].type != SPA_ID_INVALID && spa_data[0].type & (1 << SPA_DATA_DmaBuf)) {
dmabuf = kwinApp()->platform()->createDmaBufTexture(stream->m_resolution);
Q_ASSERT(stream->m_attribs);
dmabuff = kwinApp()->platform()->createDmaBufTexture(*stream->m_attribs);
}
if (dmabuf) {
const DmaBufAttributes dmabufAttribs = dmabuf->attributes();
if (dmabuff) {
spa_data->type = SPA_DATA_DmaBuf;
spa_data->fd = dmabufAttribs.fd[0];
spa_data->fd = dmabuff->attributes().fd[0];
spa_data->data = nullptr;
spa_data->maxsize = dmabufAttribs.pitch[0] * stream->m_resolution.height();
spa_data->maxsize = dmabuff->attributes().pitch[0] * stream->m_resolution.height();
stream->m_dmabufDataForPwBuffer.insert(buffer, dmabuf);
stream->m_dmabufDataForPwBuffer.insert(buffer, dmabuff);
#ifdef F_SEAL_SEAL // Disable memfd on systems that don't have it, like BSD < 12
} else {
if (!(spa_data[0].type & (1 << SPA_DATA_MemFd))) {
@ -285,8 +306,9 @@ bool ScreenCastStream::createStream()
int n_params;
const auto format = m_source->hasAlphaChannel() ? SPA_VIDEO_FORMAT_BGRA : SPA_VIDEO_FORMAT_BGR;
m_attribs = kwinApp()->platform()->testCreateDmaBuf(m_resolution, spaVideoFormatToDrmFormat(format), {DRM_FORMAT_MOD_INVALID});
if (kwinApp()->platform()->createDmaBufTexture(m_resolution) != nullptr) {
if (m_attribs) {
params[0] = buildFormat(&podBuilder, SPA_VIDEO_FORMAT_BGRA, &resolution, &defaultFramerate, &minFramerate, &maxFramerate, &modifier, 1);
params[1] = buildFormat(&podBuilder, format, &resolution, &defaultFramerate, &minFramerate, &maxFramerate, nullptr, 0);
n_params = 2;
@ -397,6 +419,7 @@ void ScreenCastStream::recordFrame(const QRegion &damagedRegion)
}
} else {
auto &buf = m_dmabufDataForPwBuffer[buffer];
Q_ASSERT(buf);
spa_data->chunk->stride = buf->attributes().pitch[0];
spa_data->chunk->size = spa_data->maxsize;

View file

@ -10,6 +10,7 @@
#include "config-kwin.h"
#include "dmabufattributes.h"
#include "kwinglobals.h"
#include "wayland/screencast_v1_interface.h"
@ -120,6 +121,7 @@ private:
EGLNativeFence *m_pendingFence = nullptr;
std::optional<std::chrono::nanoseconds> m_start;
quint64 m_sequential = 0;
std::optional<DmaBufAttributes> m_attribs;
};
} // namespace KWin