Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
linuxdmabuf.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "linuxdmabuf.h"
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8
9#include <drm_fourcc.h>
10#include <drm_mode.h>
11#include <unistd.h>
12
14
16 : zwp_linux_dmabuf_v1(display, 3 /*version*/)
17 , m_clientBufferIntegration(clientBufferIntegration)
18{
19}
20
22{
23 Q_ASSERT(resourceMap().isEmpty());
24 m_modifiers = modifiers;
25}
26
28{
29 for (auto it = m_modifiers.constBegin(); it != m_modifiers.constEnd(); ++it) {
30 auto format = it.key();
31 auto modifiers = it.value();
32 // send DRM_FORMAT_MOD_INVALID when no modifiers are supported for a format
33 if (modifiers.isEmpty())
35 for (const auto &modifier : std::as_const(modifiers)) {
36 if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
37 const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
38 const uint32_t modifier_hi = modifier >> 32;
39 send_modifier(resource->handle, format, modifier_hi, modifier_lo);
40 } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
41 send_format(resource->handle, format);
42 }
43 }
44 }
45}
46
47void LinuxDmabuf::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
48{
49 wl_resource *r = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface,
50 wl_resource_get_version(resource->handle), params_id);
51 new LinuxDmabufParams(m_clientBufferIntegration, r); // deleted by the client, or when it disconnects
52}
53
54LinuxDmabufParams::LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource)
55 : zwp_linux_buffer_params_v1(resource)
56 , m_clientBufferIntegration(clientBufferIntegration)
57{
58}
59
61{
62 for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
63 if (it.value().fd != -1)
64 close(it.value().fd);
65 it.value().fd = -1;
66 }
67}
68
69bool LinuxDmabufParams::handleCreateParams(Resource *resource, int width, int height, uint format, uint flags)
70{
71 if (m_used) {
72 wl_resource_post_error(resource->handle,
73 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
74 "Params already used");
75 return false;
76 }
77
78 if (width <= 0 || height <= 0) {
79 wl_resource_post_error(resource->handle,
80 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
81 "Invalid dimensions in create request");
82 return false;
83 }
84
85 if (m_planes.isEmpty()) {
86 wl_resource_post_error(resource->handle,
87 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
88 "Cannot create a buffer with no planes");
89 return false;
90 }
91
92 // check for holes in plane sequence
93 auto planeIds = m_planes.keys();
94 std::sort(planeIds.begin(), planeIds.end());
95 for (int i = 0; i < planeIds.size(); ++i) {
96 if (uint(i) != planeIds[i]) {
97 wl_resource_post_error(resource->handle,
98 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
99 "No dmabuf parameters provided for plane %i", i);
100 return false;
101 }
102 }
103
104 // check for overflows
105 for (auto it = m_planes.constBegin(); it != m_planes.constEnd(); ++it) {
106 const auto planeId = it.key();
107 const auto plane = it.value();
108 if (static_cast<int64_t>(plane.offset) + plane.stride > UINT32_MAX) {
109 wl_resource_post_error(resource->handle,
110 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
111 "Size overflow for plane %i",
112 planeId);
113 return false;
114 }
115 if (planeId == 0 && static_cast<int64_t>(plane.offset) + plane.stride * static_cast<int64_t>(height) > UINT32_MAX) {
116 wl_resource_post_error(resource->handle,
117 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
118 "Size overflow for plane %i",
119 planeId);
120 return false;
121 }
122
123 // do not report an error as it might be caused by the kernel not supporting seeking on dmabuf
124 off_t size = lseek(plane.fd, 0, SEEK_END);
125 if (size == -1) {
126 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Seeking is not supported";
127 continue;
128 }
129
130 if (static_cast<int64_t>(plane.offset) >= size) {
131 wl_resource_post_error(resource->handle,
132 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
133 "Invalid offset %i for plane %i",
134 plane.offset, planeId);
135 return false;
136 }
137
138 if (static_cast<int64_t>(plane.offset) + static_cast<int64_t>(plane.stride) > size) {
139 wl_resource_post_error(resource->handle,
140 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
141 "Invalid stride %i for plane %i",
142 plane.stride, planeId);
143 return false;
144 }
145
146 // only valid for first plane as other planes might be sub-sampled
147 if (planeId == 0 && plane.offset + static_cast<int64_t>(plane.stride) * height > size) {
148 wl_resource_post_error(resource->handle,
149 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
150 "Invalid buffer stride or height for plane %i", planeId);
151 return false;
152 }
153 }
154
155 m_size = QSize(width, height);
156 m_drmFormat = format;
157 m_flags = flags;
158 m_used = true;
159
160 return true;
161}
162
164{
165 wl_resource_destroy(resource->handle);
166}
167
169{
170 Q_UNUSED(resource);
171 delete this;
172}
173
174void LinuxDmabufParams::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)
175{
176 const uint64_t modifiers = (static_cast<uint64_t>(modifier_hi) << 32) | modifier_lo;
177 if (plane_idx >= LinuxDmabufWlBuffer::MaxDmabufPlanes) {
178 wl_resource_post_error(resource->handle,
179 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
180 "Plane index %i is out of bounds", plane_idx);
181 }
182
183 if (m_planes.contains(plane_idx)) {
184 wl_resource_post_error(resource->handle,
185 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
186 "Plane already set");
187 }
188
189 Plane plane;
190 plane.fd = fd;
191 plane.modifiers = modifiers;
192 plane.offset = offset;
193 plane.stride = stride;
194 m_planes.insert(plane_idx, plane);
195}
196
197void LinuxDmabufParams::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
198{
199 if (!handleCreateParams(resource, width, height, format, flags))
200 return;
201
202 auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration);
203 buffer->m_size = m_size;
204 buffer->m_flags = m_flags;
205 buffer->m_drmFormat = m_drmFormat;
206 buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence
207 for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
208 buffer->m_planes[it.key()] = it.value();
209 it.value().fd = -1; // ownership is moved
210 }
211
212 if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) {
213 send_failed(resource->handle);
214 } else {
215 send_created(resource->handle, buffer->resource()->handle);
216 }
217}
218
219void LinuxDmabufParams::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)
220{
221 if (!handleCreateParams(resource, width, height, format, flags))
222 return;
223
224 auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration, buffer_id);
225 buffer->m_size = m_size;
226 buffer->m_flags = m_flags;
227 buffer->m_drmFormat = m_drmFormat;
228 buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence
229 for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
230 buffer->m_planes[it.key()] = it.value();
231 it.value().fd = -1; // ownership is moved
232 }
233
234 if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) {
235 // for the 'create_immed' request, the implementation can decide
236 // how to handle the failure by an unknown cause; we decide
237 // to raise a fatal error at the client
238 wl_resource_post_error(resource->handle,
239 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
240 "Import of the provided DMA buffer failed");
241 }
242 // note: create signal shall not be sent for the 'create_immed' request
243}
244
246 : wl_buffer(client, id, 1 /*version*/)
247 , m_clientBufferIntegration(clientBufferIntegration)
248{
249}
250
252{
253 m_clientBufferIntegration->removeBuffer(resource()->handle);
254 buffer_destroy(resource());
255}
256
258{
259 Q_UNUSED(resource);
260
261 QMutexLocker locker(&m_texturesLock);
262
263 for (uint32_t i = 0; i < m_planesNumber; ++i) {
264 if (m_textures[i] != nullptr) {
265 m_clientBufferIntegration->deleteGLTextureWhenPossible(m_textures[i], m_texturesContext[i]);
266 m_textures[i] = nullptr;
267 m_texturesContext[i] = nullptr;
268 QObject::disconnect(m_texturesAboutToBeDestroyedConnection[i]);
269 m_texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection();
270 }
271 if (m_eglImages[i] != EGL_NO_IMAGE_KHR) {
272 m_clientBufferIntegration->deleteImage(m_eglImages[i]);
273 m_eglImages[i] = EGL_NO_IMAGE_KHR;
274 }
275 if (m_planes[i].fd != -1)
276 close(m_planes[i].fd);
277 m_planes[i].fd = -1;
278 }
279 m_planesNumber = 0;
280}
281
283{
284 Q_ASSERT(plane < m_planesNumber);
285 Q_ASSERT(m_eglImages.at(plane) == EGL_NO_IMAGE_KHR);
286 m_eglImages[plane] = image;
287}
288
290{
291 QMutexLocker locker(&m_texturesLock);
292
293 Q_ASSERT(plane < m_planesNumber);
294 Q_ASSERT(m_textures.at(plane) == nullptr);
296 m_textures[plane] = texture;
297 m_texturesContext[plane] = QOpenGLContext::currentContext();
298
299 m_texturesAboutToBeDestroyedConnection[plane] =
301 m_texturesContext[plane], [this, plane]() {
302
303 QMutexLocker locker(&this->m_texturesLock);
304
305 // See above lock - there is a chance that this has already been removed from m_textures[plane]!
306 // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed!
307 if (this->m_textures[plane] == nullptr)
308 return;
309
310 delete this->m_textures[plane];
311
312 qCDebug(qLcWaylandCompositorHardwareIntegration)
313 << Q_FUNC_INFO
314 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
315 << "Pointer (now dead) was:" << (void*)(this->m_textures[plane])
316 << " Associated context (about to die too) is: " << (void*)(this->m_texturesContext[plane]);
317
318 this->m_textures[plane] = nullptr;
319 this->m_texturesContext[plane] = nullptr;
320
321 QObject::disconnect(this->m_texturesAboutToBeDestroyedConnection[plane]);
322 this->m_texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
323
325}
326
328{
329 Q_UNUSED(resource);
330 delete this;
331}
332
void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx)
bool importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer)
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
Plane & plane(uint index)
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_bind_resource(Resource *resource) override
LinuxDmabuf(wl_display *display, LinuxDmabufClientBufferIntegration *clientBufferIntegration)
void setSupportedModifiers(const QHash< uint32_t, QList< uint64_t > > &modifiers)
void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override
\inmodule QtCore
Definition qhash.h:818
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1205
Definition qlist.h:74
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
bool contains(const Key &key) const
Definition qmap.h:340
QList< Key > keys() const
Definition qmap.h:382
bool isEmpty() const
Definition qmap.h:268
iterator begin()
Definition qmap.h:597
iterator end()
Definition qmap.h:601
const_iterator constBegin() const
Definition qmap.h:599
size_type size() const
Definition qmap.h:266
const_iterator constEnd() const
Definition qmap.h:603
\inmodule QtCore
Definition qmutex.h:317
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
void aboutToBeDestroyed()
This signal is emitted before the underlying native OpenGL context is destroyed, such that users may ...
static QOpenGLContext * currentContext()
Returns the last context which called makeCurrent in the current thread, or \nullptr,...
\inmodule QtGui
\inmodule QtCore
Definition qsize.h:25
EGLImageKHR int int EGLuint64KHR * modifiers
QSet< QString >::iterator it
struct wl_display * display
Definition linuxdmabuf.h:41
#define DRM_FORMAT_MOD_LINEAR
Definition linuxdmabuf.h:33
#define DRM_FORMAT_MOD_INVALID
Definition linuxdmabuf.h:36
Combined button and popup list for selecting options.
@ DirectConnection
Definition image.cpp:4
#define Q_FUNC_INFO
#define qCDebug(category,...)
GLuint64 GLenum void * handle
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLenum GLuint id
[7]
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLint GLsizei width
GLbitfield flags
GLenum GLuint texture
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLint GLsizei GLsizei GLenum format
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:29
int fd
Definition linuxdmabuf.h:53
uint32_t stride
Definition linuxdmabuf.h:55
uint64_t modifiers
Definition linuxdmabuf.h:56
uint32_t offset
Definition linuxdmabuf.h:54