KWaylandServer

linuxdmabufv1clientbuffer.cpp
1 /*
2  SPDX-FileCopyrightText: 2018 Fredrik Höglund <[email protected]>
3  SPDX-FileCopyrightText: 2019 Roman Gilg <[email protected]>
4  SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <[email protected]>
5 
6  Based on the libweston implementation,
7  SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
8 
9  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
10 */
11 
12 #include "linuxdmabufv1clientbuffer.h"
13 #include "clientbuffer_p.h"
14 #include "display.h"
15 #include "display_p.h"
16 #include "drm_fourcc.h"
17 
18 #include "qwayland-server-linux-dmabuf-unstable-v1.h"
19 #include "qwayland-server-wayland.h"
20 
21 #include <QDebug>
22 #include <QVector>
23 
24 #include <unistd.h>
25 
26 namespace KWaylandServer
27 {
28 static const int s_version = 3;
29 
30 class LinuxDmaBufV1ClientBufferIntegrationPrivate : public QtWaylandServer::zwp_linux_dmabuf_v1
31 {
32 public:
33  LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display);
34 
35  LinuxDmaBufV1ClientBufferIntegration *q;
36  LinuxDmaBufV1ClientBufferIntegration::RendererInterface *rendererInterface = nullptr;
37  QHash<uint32_t, QSet<uint64_t>> supportedModifiers;
38 
39 protected:
40  void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override;
41  void zwp_linux_dmabuf_v1_destroy(Resource *resource) override;
42  void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override;
43 };
44 
45 class LinuxDmaBufV1ClientBufferPrivate : public ClientBufferPrivate, public QtWaylandServer::wl_buffer
46 {
47 public:
48  QSize size;
49  quint32 format;
50  quint32 flags;
52  bool hasAlphaChannel = false;
53 
54 protected:
55  void buffer_destroy(Resource *resource) override;
56 };
57 
58 class LinuxDmaBufParamsV1 : public QtWaylandServer::zwp_linux_buffer_params_v1
59 {
60 public:
61  LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource);
62  ~LinuxDmaBufParamsV1() override;
63 
64 protected:
65  void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override;
66  void zwp_linux_buffer_params_v1_destroy(Resource *resource) override;
67  void zwp_linux_buffer_params_v1_add(Resource *resource,
68  int32_t fd,
69  uint32_t plane_idx,
70  uint32_t offset,
71  uint32_t stride,
72  uint32_t modifier_hi,
73  uint32_t modifier_lo) override;
74  void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
75  void
76  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;
77 
78 private:
79  bool test(Resource *resource, uint32_t width, uint32_t height);
80 
81  LinuxDmaBufV1ClientBufferIntegration *m_integration;
83  int m_planeCount = 0;
84  bool m_isUsed = false;
85 };
86 
87 LinuxDmaBufV1ClientBufferIntegrationPrivate::LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display)
88  : QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version)
89  , q(q)
90 {
91 }
92 
93 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_bind_resource(Resource *resource)
94 {
95  for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) {
96  const uint32_t format = it.key();
97  QSet<uint64_t> modifiers = it.value();
98  if (modifiers.isEmpty()) {
99  modifiers.insert(DRM_FORMAT_MOD_INVALID);
100  }
101 
102  for (const uint64_t &modifier : qAsConst(modifiers)) {
103  if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
104  const uint32_t modifier_lo = modifier & 0xffffffff;
105  const uint32_t modifier_hi = modifier >> 32;
106  send_modifier(resource->handle, format, modifier_hi, modifier_lo);
107  } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
108  send_format(resource->handle, format);
109  }
110  }
111  }
112 }
113 
114 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_destroy(Resource *resource)
115 {
116  wl_resource_destroy(resource->handle);
117 }
118 
119 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
120 {
121  wl_resource *paramsResource = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface, resource->version(), params_id);
122  if (!paramsResource) {
123  wl_resource_post_no_memory(resource->handle);
124  return;
125  }
126  new LinuxDmaBufParamsV1(q, paramsResource);
127 }
128 
129 LinuxDmaBufParamsV1::LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource)
130  : QtWaylandServer::zwp_linux_buffer_params_v1(resource)
131  , m_integration(integration)
132  , m_planes(4)
133 {
134 }
135 
136 LinuxDmaBufParamsV1::~LinuxDmaBufParamsV1()
137 {
138  for (const LinuxDmaBufV1Plane &plane : m_planes) {
139  if (plane.fd != -1) {
140  close(plane.fd);
141  }
142  }
143 }
144 
145 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource)
146 {
147  Q_UNUSED(resource)
148  delete this;
149 }
150 
151 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy(Resource *resource)
152 {
153  wl_resource_destroy(resource->handle);
154 }
155 
156 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_add(Resource *resource,
157  int32_t fd,
158  uint32_t plane_idx,
159  uint32_t offset,
160  uint32_t stride,
161  uint32_t modifier_hi,
162  uint32_t modifier_lo)
163 {
164  if (Q_UNLIKELY(m_isUsed)) {
165  wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
166  close(fd);
167  return;
168  }
169 
170  if (Q_UNLIKELY(plane_idx >= uint(m_planes.size()))) {
171  wl_resource_post_error(resource->handle, error_plane_idx, "plane index %d is out of bounds", plane_idx);
172  close(fd);
173  return;
174  }
175 
176  LinuxDmaBufV1Plane &plane = m_planes[plane_idx];
177 
178  if (Q_UNLIKELY(plane.fd != -1)) {
179  wl_resource_post_error(resource->handle, error_plane_set, "the plane index %d was already set", plane_idx);
180  close(fd);
181  return;
182  }
183 
184  plane.fd = fd;
185  plane.modifier = (quint64(modifier_hi) << 32) | modifier_lo;
186  plane.offset = offset;
187  plane.stride = stride;
188 
189  m_planeCount++;
190 }
191 
192 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
193 {
194  if (Q_UNLIKELY(m_isUsed)) {
195  wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
196  return;
197  }
198 
199  if (Q_UNLIKELY(!test(resource, width, height))) {
200  return;
201  }
202 
203  m_isUsed = true;
204  m_planes.resize(m_planeCount);
205 
206  LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(m_planes, format, QSize(width, height), flags);
207  if (!clientBuffer) {
208  send_failed(resource->handle);
209  return;
210  }
211 
212  m_planes.clear(); // the ownership of file descriptors has been moved to the buffer
213 
214  wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0);
215  if (!bufferResource) {
216  delete clientBuffer;
217  wl_resource_post_no_memory(resource->handle);
218  return;
219  }
220 
221  clientBuffer->initialize(bufferResource);
222  send_created(resource->handle, bufferResource);
223 
224  DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
225  displayPrivate->registerClientBuffer(clientBuffer);
226 }
227 
228 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *resource,
229  uint32_t buffer_id,
230  int32_t width,
231  int32_t height,
232  uint32_t format,
233  uint32_t flags)
234 {
235  if (Q_UNLIKELY(m_isUsed)) {
236  wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
237  return;
238  }
239 
240  if (Q_UNLIKELY(!test(resource, width, height))) {
241  return;
242  }
243 
244  m_isUsed = true;
245  m_planes.resize(m_planeCount);
246 
247  LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(m_planes, format, QSize(width, height), flags);
248  if (!clientBuffer) {
249  wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
250  return;
251  }
252 
253  m_planes.clear(); // the ownership of file descriptors has been moved to the buffer
254 
255  wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id);
256  if (!bufferResource) {
257  delete clientBuffer;
258  wl_resource_post_no_memory(resource->handle);
259  return;
260  }
261 
262  clientBuffer->initialize(bufferResource);
263 
264  DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
265  displayPrivate->registerClientBuffer(clientBuffer);
266 }
267 
268 bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height)
269 {
270  if (Q_UNLIKELY(!m_planeCount)) {
271  wl_resource_post_error(resource->handle, error_incomplete, "no planes have been specified");
272  return false;
273  }
274 
275  // Check for holes in the dmabuf set (e.g. [0, 1, 3]).
276  for (int i = 0; i < m_planeCount; ++i) {
277  if (m_planes[i].fd == -1) {
278  wl_resource_post_error(resource->handle, error_incomplete, "no dmabuf has been added for plane %d", i);
279  return false;
280  }
281  }
282 
283  if (Q_UNLIKELY(width == 0 || height == 0)) {
284  wl_resource_post_error(resource->handle, error_invalid_dimensions, "invalid width %d or height %d", width, height);
285  return false;
286  }
287 
288  for (int i = 0; i < m_planeCount; ++i) {
289  const LinuxDmaBufV1Plane &plane = m_planes.at(i);
290 
291  // Check for overflows.
292  if (Q_UNLIKELY(uint64_t(plane.offset) + plane.stride > UINT32_MAX)) {
293  wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
294  return false;
295  }
296 
297  if (Q_UNLIKELY(i == 0 && uint64_t(plane.offset) + uint64_t(plane.stride) * height > UINT32_MAX)) {
298  wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
299  return false;
300  }
301 
302  // Don't report an error as it might be caused by the kernel not supporting
303  // seeking on dmabuf.
304  const off_t size = lseek(plane.fd, 0, SEEK_END);
305  if (size == -1) {
306  continue;
307  }
308 
309  if (Q_UNLIKELY(plane.offset >= size)) {
310  wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid offset %i for plane %d", plane.offset, i);
311  return false;
312  }
313 
314  if (Q_UNLIKELY(plane.offset + plane.stride > size)) {
315  wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid stride %i for plane %d", plane.stride, i);
316  return false;
317  }
318 
319  // Only valid for first plane as other planes might be sub-sampled according to
320  // fourcc format.
321  if (Q_UNLIKELY(i == 0 && plane.offset + plane.stride * height > size)) {
322  wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid buffer stride of height for plane %d", i);
323  return false;
324  }
325  }
326 
327  return true;
328 }
329 
330 LinuxDmaBufV1ClientBufferIntegration::LinuxDmaBufV1ClientBufferIntegration(Display *display)
331  : ClientBufferIntegration(display)
332  , d(new LinuxDmaBufV1ClientBufferIntegrationPrivate(this, display))
333 {
334 }
335 
336 LinuxDmaBufV1ClientBufferIntegration::~LinuxDmaBufV1ClientBufferIntegration()
337 {
338 }
339 
340 LinuxDmaBufV1ClientBufferIntegration::RendererInterface *LinuxDmaBufV1ClientBufferIntegration::rendererInterface() const
341 {
342  return d->rendererInterface;
343 }
344 
346 {
347  d->rendererInterface = rendererInterface;
348 }
349 
350 void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QHash<uint32_t, QSet<uint64_t>> &set)
351 {
352  d->supportedModifiers = set;
353 }
354 
355 static bool testAlphaChannel(uint32_t drmFormat)
356 {
357  switch (drmFormat) {
358  case DRM_FORMAT_ARGB4444:
359  case DRM_FORMAT_ABGR4444:
360  case DRM_FORMAT_RGBA4444:
361  case DRM_FORMAT_BGRA4444:
362 
363  case DRM_FORMAT_ARGB1555:
364  case DRM_FORMAT_ABGR1555:
365  case DRM_FORMAT_RGBA5551:
366  case DRM_FORMAT_BGRA5551:
367 
368  case DRM_FORMAT_ARGB8888:
369  case DRM_FORMAT_ABGR8888:
370  case DRM_FORMAT_RGBA8888:
371  case DRM_FORMAT_BGRA8888:
372 
373  case DRM_FORMAT_ARGB2101010:
374  case DRM_FORMAT_ABGR2101010:
375  case DRM_FORMAT_RGBA1010102:
376  case DRM_FORMAT_BGRA1010102:
377 
378  case DRM_FORMAT_XRGB8888_A8:
379  case DRM_FORMAT_XBGR8888_A8:
380  case DRM_FORMAT_RGBX8888_A8:
381  case DRM_FORMAT_BGRX8888_A8:
382  case DRM_FORMAT_RGB888_A8:
383  case DRM_FORMAT_BGR888_A8:
384  case DRM_FORMAT_RGB565_A8:
385  case DRM_FORMAT_BGR565_A8:
386  return true;
387  default:
388  return false;
389  }
390 }
391 
392 void LinuxDmaBufV1ClientBufferPrivate::buffer_destroy(Resource *resource)
393 {
394  wl_resource_destroy(resource->handle);
395 }
396 
397 LinuxDmaBufV1ClientBuffer::LinuxDmaBufV1ClientBuffer(const QSize &size, quint32 format, quint32 flags, const QVector<LinuxDmaBufV1Plane> &planes)
398  : ClientBuffer(*new LinuxDmaBufV1ClientBufferPrivate)
399 {
401  d->size = size;
402  d->format = format;
403  d->flags = flags;
404  d->planes = planes;
405  d->hasAlphaChannel = testAlphaChannel(format);
406 }
407 
408 LinuxDmaBufV1ClientBuffer::~LinuxDmaBufV1ClientBuffer()
409 {
411  for (int i = 0; i < d->planes.count(); ++i) {
412  if (d->planes[i].fd != -1) {
413  close(d->planes[i].fd);
414  d->planes[i].fd = -1;
415  }
416  }
417 }
418 
419 void LinuxDmaBufV1ClientBuffer::initialize(wl_resource *resource)
420 {
422  d->init(resource);
423  ClientBuffer::initialize(resource);
424 }
425 
426 quint32 LinuxDmaBufV1ClientBuffer::format() const
427 {
428  Q_D(const LinuxDmaBufV1ClientBuffer);
429  return d->format;
430 }
431 
432 quint32 LinuxDmaBufV1ClientBuffer::flags() const
433 {
434  Q_D(const LinuxDmaBufV1ClientBuffer);
435  return d->flags;
436 }
437 
438 QVector<LinuxDmaBufV1Plane> LinuxDmaBufV1ClientBuffer::planes() const
439 {
440  Q_D(const LinuxDmaBufV1ClientBuffer);
441  return d->planes;
442 }
443 
445 {
446  Q_D(const LinuxDmaBufV1ClientBuffer);
447  return d->size;
448 }
449 
450 bool LinuxDmaBufV1ClientBuffer::hasAlphaChannel() const
451 {
452  Q_D(const LinuxDmaBufV1ClientBuffer);
453  return d->hasAlphaChannel;
454 }
455 
456 ClientBuffer::Origin LinuxDmaBufV1ClientBuffer::origin() const
457 {
458  Q_D(const LinuxDmaBufV1ClientBuffer);
459  if (d->flags & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) {
460  return ClientBuffer::Origin::BottomLeft;
461  } else {
462  return ClientBuffer::Origin::TopLeft;
463  }
464 }
465 
466 } // namespace KWaylandServer
void setRendererInterface(RendererInterface *rendererInterface)
Sets the compositor implementation for the dmabuf interface.
The Iface class provides an interface from the LinuxDmabufInterface into the compositor.
QSet::iterator insert(const T &value)
const QList< QKeySequence > & close()
KGuiItem test()
Origin
This enum type is used to specify the corner where the origin is.
Definition: clientbuffer.h:41
QSize size() const override
Returns the size in the native pixels.
bool isEmpty() const const
The ClientBuffer class represents a client buffer.
Definition: clientbuffer.h:29
The LinuxDmaBufV1ClientBuffer class represents a linux dma-buf client buffer.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 23 2021 23:08:27 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.