7#include <QtCore/QStandardPaths>
9#include <QtWaylandCompositor/QWaylandCompositor>
10#include <QtWaylandCompositor/private/qwltextureorphanage_p.h>
12#include <drm_fourcc.h>
21# include <sys/syscall.h>
24# define MFD_CLOEXEC 0x0001U
26# ifndef MFD_ALLOW_SEALING
27# define MFD_ALLOW_SEALING 0x0002U
31# define F_ADD_SEALS 1033
34# define F_SEAL_SEAL 0x0001
37# define F_SEAL_SHRINK 0x0002
43LinuxDmabuf::LinuxDmabuf(
int version,
45 LinuxDmabufClientBufferIntegration *clientBufferIntegration)
46 : zwp_linux_dmabuf_v1(display, version)
47 , m_clientBufferIntegration(clientBufferIntegration)
53 m_drmDevice = drmDevice;
58 Q_ASSERT(resourceMap().isEmpty());
59 m_modifiers = modifiers;
64 if (resource->version() >= ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK_SINCE_VERSION)
67 for (
auto it = m_modifiers.constBegin(); it != m_modifiers.constEnd(); ++it) {
68 auto format = it.key();
69 auto modifiers = it.value();
71 if (modifiers.isEmpty())
73 for (
const auto &modifier : std::as_const(modifiers)) {
74 if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
75 const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
76 const uint32_t modifier_hi = modifier >> 32;
77 send_modifier(resource->handle, format, modifier_hi, modifier_lo);
79 send_format(resource->handle, format);
87 wl_resource *r = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface,
88 wl_resource_get_version(resource->handle), params_id);
94 if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION)
97 wl_resource *r = wl_resource_create(resource->client(),
98 &zwp_linux_dmabuf_feedback_v1_interface,
99 wl_resource_get_version(resource->handle),
102 new LinuxDmabufFeedback(m_modifiers, m_drmDevice, m_clientBufferIntegration, r);
107 if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK_SINCE_VERSION)
111 wl_resource *r = wl_resource_create(resource->client(),
112 &zwp_linux_dmabuf_feedback_v1_interface,
113 wl_resource_get_version(resource->handle),
116 new LinuxDmabufFeedback(m_modifiers, m_drmDevice, m_clientBufferIntegration, r);
119LinuxDmabufFeedback::LinuxDmabufFeedback(QHash<uint32_t, QList<uint64_t>> modifiers,
120 const char *drmDevice,
121 LinuxDmabufClientBufferIntegration *clientBufferIntegration,
123 : zwp_linux_dmabuf_feedback_v1(res)
124 , m_modifiers(modifiers)
125 , m_drmDevice(drmDevice)
126 , m_clientBufferIntegration(clientBufferIntegration)
128 sendFeedback(resource());
134 munmap(m_data, m_size);
139 wl_resource_destroy(resource->handle);
150 sendFeedback(resource);
155 QList<std::pair<uint32_t, uint64_t> > formatModifierPairs;
156 for (
auto it = m_modifiers.constBegin(); it != m_modifiers.constEnd(); ++it) {
157 uint32_t format = it.key();
158 QList<uint64_t> modifiers = it.value();
160 if (!modifiers.isEmpty()) {
161 for (uint64_t modifier : modifiers)
162 formatModifierPairs.append(std::make_pair(format, modifier));
168 if (formatModifierPairs.isEmpty()) {
169 qCWarning(qLcWaylandCompositorHardwareIntegration) <<
"LinuxDmabufFeedback: No formats";
174#ifdef SYS_memfd_create
175 fd = syscall(SYS_memfd_create,
"wayland-dmabuf", MFD_CLOEXEC | MFD_ALLOW_SEALING);
177 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
180 std::unique_ptr<QFile> filePointer;
183 std::make_unique<QTemporaryFile>(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) +
184 QLatin1String(
"/wayland-dmabuf-XXXXXX"));
186 filePointer = std::move(tmpFile);
188 auto file = std::make_unique<QFile>();
189 if (file->open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFile::AutoCloseHandle))
190 filePointer = std::move(file);
193 m_size = formatModifierPairs.size() * 16;
194 if (!filePointer || !filePointer->resize(m_size)) {
195 qCWarning(qLcWaylandCompositorHardwareIntegration)
196 <<
"LinuxDmabufFeedback: failed: " << filePointer->errorString();
200 fd = filePointer->handle();
203 munmap(m_data, m_size);
204 m_data = (uchar *) mmap(
nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
206 QByteArray indicesArray;
207 indicesArray.resize(formatModifierPairs.size() * 2);
208 char *iptr = indicesArray.data();
211 for (quint16 i = 0; i < quint16(formatModifierPairs.size()); ++i) {
215 const auto &formatModifierPair = formatModifierPairs.at(i);
216 uint32_t format = formatModifierPair.first;
217 uint64_t modifiers = formatModifierPair.second;
218 memcpy(m_data, &format, 4);
223 memcpy(m_data, &modifiers, 8);
227 send_format_table(fd, m_size);
238 QByteArray indices = sendFormatTable(resource);
242 struct stat drmDeviceStat;
243 if (stat(m_drmDevice, &drmDeviceStat)) {
244 qCWarning(qLcWaylandCompositorHardwareIntegration)
245 <<
"Failed to access DRM device in linux-dmabuf-feedback";
249 dev_t mainDevice = drmDeviceStat.st_rdev;
250 QByteArray mainDeviceArray;
251 mainDeviceArray.setRawData(
reinterpret_cast<
const char *>(&mainDevice),
sizeof(dev_t));
252 send_main_device(mainDeviceArray);
255 send_tranche_target_device(mainDeviceArray);
256 send_tranche_flags(0);
257 send_tranche_formats(indices);
265 , m_clientBufferIntegration(clientBufferIntegration)
271 for (
auto it = m_planes.begin(); it != m_planes.end(); ++it) {
272 if (it.value().fd != -1)
273 close(it.value().fd);
278bool LinuxDmabufParams::handleCreateParams(Resource *resource,
int width,
int height, uint format, uint flags)
281 wl_resource_post_error(resource->handle,
282 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
283 "Params already used");
287 if (width <= 0 || height <= 0) {
288 wl_resource_post_error(resource->handle,
289 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
290 "Invalid dimensions in create request");
294 if (m_planes.isEmpty()) {
295 wl_resource_post_error(resource->handle,
296 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
297 "Cannot create a buffer with no planes");
302 auto planeIds = m_planes.keys();
303 std::sort(planeIds.begin(), planeIds.end());
304 for (
int i = 0; i < planeIds.size(); ++i) {
305 if (uint(i) != planeIds[i]) {
306 wl_resource_post_error(resource->handle,
307 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
308 "No dmabuf parameters provided for plane %i", i);
314 for (
auto it = m_planes.constBegin(); it != m_planes.constEnd(); ++it) {
315 const auto planeId = it.key();
316 const auto plane = it.value();
317 if (
static_cast<int64_t>(plane.offset) + plane.stride > UINT32_MAX) {
318 wl_resource_post_error(resource->handle,
319 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
320 "Size overflow for plane %i",
324 if (planeId == 0 &&
static_cast<int64_t>(plane.offset) + plane.stride *
static_cast<int64_t>(height) > UINT32_MAX) {
325 wl_resource_post_error(resource->handle,
326 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
327 "Size overflow for plane %i",
333 off_t size = lseek(plane.fd, 0, SEEK_END);
335 qCDebug(qLcWaylandCompositorHardwareIntegration) <<
"Seeking is not supported";
339 if (
static_cast<int64_t>(plane.offset) >= size) {
340 wl_resource_post_error(resource->handle,
341 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
342 "Invalid offset %i for plane %i",
343 plane.offset, planeId);
347 if (
static_cast<int64_t>(plane.offset) +
static_cast<int64_t>(plane.stride) > size) {
348 wl_resource_post_error(resource->handle,
349 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
350 "Invalid stride %i for plane %i",
351 plane.stride, planeId);
356 if (planeId == 0 && plane.offset +
static_cast<int64_t>(plane.stride) * height > size) {
357 wl_resource_post_error(resource->handle,
358 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
359 "Invalid buffer stride or height for plane %i", planeId);
364 m_size = QSize(width, height);
365 m_drmFormat = format;
374 wl_resource_destroy(resource->handle);
385 const uint64_t modifiers = (
static_cast<uint64_t>(modifier_hi) << 32) | modifier_lo;
387 wl_resource_post_error(resource->handle,
388 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
389 "Plane index %i is out of bounds", plane_idx);
392 if (m_planes.contains(plane_idx)) {
393 wl_resource_post_error(resource->handle,
394 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
395 "Plane already set");
403 m_planes.insert(plane_idx, plane);
408 if (!handleCreateParams(resource, width, height, format, flags))
412 buffer->m_size = m_size;
413 buffer->m_flags = m_flags;
414 buffer->m_drmFormat = m_drmFormat;
415 buffer->m_planesNumber = m_planes.size();
416 for (
auto it = m_planes.begin(); it != m_planes.end(); ++it) {
417 buffer->m_planes[it.key()] = it.value();
421 if (!m_clientBufferIntegration
->importBuffer(buffer->resource()->handle
, buffer
)) {
422 send_failed(resource->handle);
424 send_created(resource->handle, buffer->resource()->handle);
430 if (!handleCreateParams(resource, width, height, format, flags))
433 auto *buffer =
new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration, buffer_id);
434 buffer->m_size = m_size;
435 buffer->m_flags = m_flags;
436 buffer->m_drmFormat = m_drmFormat;
437 buffer->m_planesNumber = m_planes.size();
438 for (
auto it = m_planes.begin(); it != m_planes.end(); ++it) {
439 buffer->m_planes[it.key()] = it.value();
443 if (!m_clientBufferIntegration
->importBuffer(buffer->resource()->handle
, buffer
)) {
447 wl_resource_post_error(resource->handle,
448 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
449 "Import of the provided DMA buffer failed");
456 , m_clientBufferIntegration(clientBufferIntegration)
470 wl_resource_destroy(resource->handle);
475 QMutexLocker locker(&m_texturesLock);
477 for (uint32_t i = 0; i < m_planesNumber; ++i) {
478 if (m_textures[i] !=
nullptr) {
479 QtWayland::QWaylandTextureOrphanage::instance()->admitTexture(m_textures[i],
480 m_texturesContext[i]);
481 m_textures[i] =
nullptr;
482 m_texturesContext[i] =
nullptr;
483 QObject::disconnect(m_texturesAboutToBeDestroyedConnection[i]);
484 m_texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection();
486 if (m_eglImages[i] != EGL_NO_IMAGE_KHR) {
488 m_eglImages[i] = EGL_NO_IMAGE_KHR;
490 if (m_planes[i].fd != -1)
491 close(m_planes[i].fd);
499 Q_ASSERT(plane < m_planesNumber);
500 Q_ASSERT(m_eglImages.at(plane) == EGL_NO_IMAGE_KHR);
501 m_eglImages[plane] = image;
506 QMutexLocker locker(&m_texturesLock);
508 Q_ASSERT(plane < m_planesNumber);
509 Q_ASSERT(m_textures.at(plane) ==
nullptr);
510 Q_ASSERT(QOpenGLContext::currentContext());
511 m_textures[plane] = texture;
512 m_texturesContext[plane] = QOpenGLContext::currentContext();
514 m_texturesAboutToBeDestroyedConnection[plane] =
515 QObject::connect(m_texturesContext[plane], &QOpenGLContext::aboutToBeDestroyed,
516 m_texturesContext[plane], [
this, plane]() {
518 QMutexLocker locker(&
this->m_texturesLock);
522 if (
this->m_textures[plane] ==
nullptr)
525 delete this->m_textures[plane];
527 qCDebug(qLcWaylandCompositorHardwareIntegration)
529 <<
"texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
530 <<
"Pointer (now dead) was:" << (
void*)(
this->m_textures[plane])
531 <<
" Associated context (about to die too) is: " << (
void*)(
this->m_texturesContext[plane]);
533 this->m_textures[plane] =
nullptr;
534 this->m_texturesContext[plane] =
nullptr;
536 QObject::disconnect(
this->m_texturesAboutToBeDestroyedConnection[plane]);
537 this->m_texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
539 }, Qt::DirectConnection);
bool importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer)
void deleteImage(EGLImageKHR image)
void removeBuffer(wl_resource *resource)
void zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource) override
void zwp_linux_dmabuf_feedback_v1_destroy_resource(Resource *resource) override
~LinuxDmabufFeedback() override
void zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource) override
void zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) override
~LinuxDmabufParams() override
LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource)
void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override
void zwp_linux_buffer_params_v1_destroy(Resource *resource) override
void zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) override
void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override
void initTexture(uint32_t plane, QOpenGLTexture *texture)
void initImage(uint32_t plane, EGLImageKHR image)
void buffer_destroy(Resource *resource) override
~LinuxDmabufWlBuffer() override
LinuxDmabufWlBuffer(::wl_client *client, LinuxDmabufClientBufferIntegration *clientBufferIntegration, uint id=0)
static const uint32_t MaxDmabufPlanes
void buffer_destroy_resource(Resource *resource) override
void zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, struct ::wl_resource *surface) override
void setDrmDevice(const char *drmDevice)
void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override
void setSupportedModifiers(const QHash< uint32_t, QList< uint64_t > > &modifiers)
void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override
void zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id) override
#define DRM_FORMAT_MOD_LINEAR
#define DRM_FORMAT_MOD_INVALID