Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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#include <QtWaylandCompositor/private/qwltextureorphanage_p.h>
9
10#include <drm_fourcc.h>
11#include <drm_mode.h>
12#include <unistd.h>
13
15
17 : zwp_linux_dmabuf_v1(display, 3 /*version*/)
18 , m_clientBufferIntegration(clientBufferIntegration)
19{
20}
21
22void LinuxDmabuf::setSupportedModifiers(const QHash<uint32_t, QList<uint64_t>> &modifiers)
23{
24 Q_ASSERT(resourceMap().isEmpty());
25 m_modifiers = modifiers;
26}
27
29{
30 for (auto it = m_modifiers.constBegin(); it != m_modifiers.constEnd(); ++it) {
31 auto format = it.key();
32 auto modifiers = it.value();
33 // send DRM_FORMAT_MOD_INVALID when no modifiers are supported for a format
34 if (modifiers.isEmpty())
36 for (const auto &modifier : std::as_const(modifiers)) {
37 if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
38 const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
39 const uint32_t modifier_hi = modifier >> 32;
40 send_modifier(resource->handle, format, modifier_hi, modifier_lo);
41 } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
42 send_format(resource->handle, format);
43 }
44 }
45 }
46}
47
48void LinuxDmabuf::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
49{
50 wl_resource *r = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface,
51 wl_resource_get_version(resource->handle), params_id);
52 new LinuxDmabufParams(m_clientBufferIntegration, r); // deleted by the client, or when it disconnects
53}
54
55LinuxDmabufParams::LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource)
56 : zwp_linux_buffer_params_v1(resource)
57 , m_clientBufferIntegration(clientBufferIntegration)
58{
59}
60
62{
63 for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
64 if (it.value().fd != -1)
65 close(it.value().fd);
66 it.value().fd = -1;
67 }
68}
69
70bool LinuxDmabufParams::handleCreateParams(Resource *resource, int width, int height, uint format, uint flags)
71{
72 if (m_used) {
73 wl_resource_post_error(resource->handle,
74 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
75 "Params already used");
76 return false;
77 }
78
79 if (width <= 0 || height <= 0) {
80 wl_resource_post_error(resource->handle,
81 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
82 "Invalid dimensions in create request");
83 return false;
84 }
85
86 if (m_planes.isEmpty()) {
87 wl_resource_post_error(resource->handle,
88 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
89 "Cannot create a buffer with no planes");
90 return false;
91 }
92
93 // check for holes in plane sequence
94 auto planeIds = m_planes.keys();
95 std::sort(planeIds.begin(), planeIds.end());
96 for (int i = 0; i < planeIds.size(); ++i) {
97 if (uint(i) != planeIds[i]) {
98 wl_resource_post_error(resource->handle,
99 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
100 "No dmabuf parameters provided for plane %i", i);
101 return false;
102 }
103 }
104
105 // check for overflows
106 for (auto it = m_planes.constBegin(); it != m_planes.constEnd(); ++it) {
107 const auto planeId = it.key();
108 const auto plane = it.value();
109 if (static_cast<int64_t>(plane.offset) + plane.stride > UINT32_MAX) {
110 wl_resource_post_error(resource->handle,
111 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
112 "Size overflow for plane %i",
113 planeId);
114 return false;
115 }
116 if (planeId == 0 && static_cast<int64_t>(plane.offset) + plane.stride * static_cast<int64_t>(height) > UINT32_MAX) {
117 wl_resource_post_error(resource->handle,
118 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
119 "Size overflow for plane %i",
120 planeId);
121 return false;
122 }
123
124 // do not report an error as it might be caused by the kernel not supporting seeking on dmabuf
125 off_t size = lseek(plane.fd, 0, SEEK_END);
126 if (size == -1) {
127 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Seeking is not supported";
128 continue;
129 }
130
131 if (static_cast<int64_t>(plane.offset) >= size) {
132 wl_resource_post_error(resource->handle,
133 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
134 "Invalid offset %i for plane %i",
135 plane.offset, planeId);
136 return false;
137 }
138
139 if (static_cast<int64_t>(plane.offset) + static_cast<int64_t>(plane.stride) > size) {
140 wl_resource_post_error(resource->handle,
141 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
142 "Invalid stride %i for plane %i",
143 plane.stride, planeId);
144 return false;
145 }
146
147 // only valid for first plane as other planes might be sub-sampled
148 if (planeId == 0 && plane.offset + static_cast<int64_t>(plane.stride) * height > size) {
149 wl_resource_post_error(resource->handle,
150 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
151 "Invalid buffer stride or height for plane %i", planeId);
152 return false;
153 }
154 }
155
156 m_size = QSize(width, height);
157 m_drmFormat = format;
158 m_flags = flags;
159 m_used = true;
160
161 return true;
162}
163
165{
166 wl_resource_destroy(resource->handle);
167}
168
170{
171 Q_UNUSED(resource);
172 delete this;
173}
174
175void 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)
176{
177 const uint64_t modifiers = (static_cast<uint64_t>(modifier_hi) << 32) | modifier_lo;
178 if (plane_idx >= LinuxDmabufWlBuffer::MaxDmabufPlanes) {
179 wl_resource_post_error(resource->handle,
180 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
181 "Plane index %i is out of bounds", plane_idx);
182 }
183
184 if (m_planes.contains(plane_idx)) {
185 wl_resource_post_error(resource->handle,
186 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
187 "Plane already set");
188 }
189
190 Plane plane;
191 plane.fd = fd;
192 plane.modifiers = modifiers;
193 plane.offset = offset;
194 plane.stride = stride;
195 m_planes.insert(plane_idx, plane);
196}
197
198void LinuxDmabufParams::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
199{
200 if (!handleCreateParams(resource, width, height, format, flags))
201 return;
202
203 auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration);
204 buffer->m_size = m_size;
205 buffer->m_flags = m_flags;
206 buffer->m_drmFormat = m_drmFormat;
207 buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence
208 for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
209 buffer->m_planes[it.key()] = it.value();
210 it.value().fd = -1; // ownership is moved
211 }
212
213 if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) {
214 send_failed(resource->handle);
215 } else {
216 send_created(resource->handle, buffer->resource()->handle);
217 }
218}
219
220void 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)
221{
222 if (!handleCreateParams(resource, width, height, format, flags))
223 return;
224
225 auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration, buffer_id);
226 buffer->m_size = m_size;
227 buffer->m_flags = m_flags;
228 buffer->m_drmFormat = m_drmFormat;
229 buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence
230 for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
231 buffer->m_planes[it.key()] = it.value();
232 it.value().fd = -1; // ownership is moved
233 }
234
235 if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) {
236 // for the 'create_immed' request, the implementation can decide
237 // how to handle the failure by an unknown cause; we decide
238 // to raise a fatal error at the client
239 wl_resource_post_error(resource->handle,
240 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
241 "Import of the provided DMA buffer failed");
242 }
243 // note: create signal shall not be sent for the 'create_immed' request
244}
245
247 : wl_buffer(client, id, 1 /*version*/)
248 , m_clientBufferIntegration(clientBufferIntegration)
249{
250}
251
253{
254 m_clientBufferIntegration->removeBuffer(resource()->handle);
255 buffer_destroy(resource());
256}
257
259{
260 Q_UNUSED(resource);
261
262 QMutexLocker locker(&m_texturesLock);
263
264 for (uint32_t i = 0; i < m_planesNumber; ++i) {
265 if (m_textures[i] != nullptr) {
266 QtWayland::QWaylandTextureOrphanage::instance()->admitTexture(m_textures[i],
267 m_texturesContext[i]);
268 m_textures[i] = nullptr;
269 m_texturesContext[i] = nullptr;
270 QObject::disconnect(m_texturesAboutToBeDestroyedConnection[i]);
271 m_texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection();
272 }
273 if (m_eglImages[i] != EGL_NO_IMAGE_KHR) {
274 m_clientBufferIntegration->deleteImage(m_eglImages[i]);
275 m_eglImages[i] = EGL_NO_IMAGE_KHR;
276 }
277 if (m_planes[i].fd != -1)
278 close(m_planes[i].fd);
279 m_planes[i].fd = -1;
280 }
281 m_planesNumber = 0;
282}
283
285{
286 Q_ASSERT(plane < m_planesNumber);
287 Q_ASSERT(m_eglImages.at(plane) == EGL_NO_IMAGE_KHR);
288 m_eglImages[plane] = image;
289}
290
292{
293 QMutexLocker locker(&m_texturesLock);
294
295 Q_ASSERT(plane < m_planesNumber);
296 Q_ASSERT(m_textures.at(plane) == nullptr);
298 m_textures[plane] = texture;
299 m_texturesContext[plane] = QOpenGLContext::currentContext();
300
301 m_texturesAboutToBeDestroyedConnection[plane] =
303 m_texturesContext[plane], [this, plane]() {
304
305 QMutexLocker locker(&this->m_texturesLock);
306
307 // See above lock - there is a chance that this has already been removed from m_textures[plane]!
308 // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed!
309 if (this->m_textures[plane] == nullptr)
310 return;
311
312 delete this->m_textures[plane];
313
314 qCDebug(qLcWaylandCompositorHardwareIntegration)
315 << Q_FUNC_INFO
316 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
317 << "Pointer (now dead) was:" << (void*)(this->m_textures[plane])
318 << " Associated context (about to die too) is: " << (void*)(this->m_texturesContext[plane]);
319
320 this->m_textures[plane] = nullptr;
321 this->m_texturesContext[plane] = nullptr;
322
323 QObject::disconnect(this->m_texturesAboutToBeDestroyedConnection[plane]);
324 this->m_texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
325
327}
328
330{
331 Q_UNUSED(resource);
332 delete this;
333}
334
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:820
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:1219
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:1215
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
QList< Key > keys() const
Definition qmap.h:383
bool isEmpty() const
Definition qmap.h:269
iterator begin()
Definition qmap.h:598
iterator end()
Definition qmap.h:602
const_iterator constBegin() const
Definition qmap.h:600
size_type size() const
Definition qmap.h:267
const_iterator constEnd() const
Definition qmap.h:604
\inmodule QtCore
Definition qmutex.h:313
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
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
static QWaylandTextureOrphanage * instance()
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:34
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