KWayland

linuxdmabuf_v1_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2019 Roman Gilg <[email protected]>
3  SPDX-FileCopyrightText: 2018 Fredrik Höglund <[email protected]>
4 
5  Based on the libweston implementation,
6  SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
7 
8  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
9 */
10 #include "linuxdmabuf_v1_interface.h"
11 
12 #include "drm_fourcc.h"
13 #include "global_p.h"
14 #include "wayland-linux-dmabuf-unstable-v1-server-protocol.h"
15 #include "wayland-server-protocol.h"
16 
17 #include <QVector>
18 
19 #include <array>
20 #include <assert.h>
21 #include <unistd.h>
22 
23 namespace KWayland
24 {
25 namespace Server
26 {
27 class LinuxDmabufBuffer::Private
28 {
29 public:
30  Private(LinuxDmabufBuffer *_q)
31  : q(_q)
32  {
33  q->d = this;
34  }
35  virtual ~Private() = default;
36 
37  virtual uint32_t format() const = 0;
38  virtual QSize size() const = 0;
39 
40  LinuxDmabufBuffer *q;
41 };
42 
43 LinuxDmabufBuffer::LinuxDmabufBuffer()
44 {
45 }
46 
47 uint32_t LinuxDmabufBuffer::format() const
48 {
49  return d->format();
50 }
51 
53 {
54  return d->size();
55 }
56 
57 class LinuxDmabufUnstableV1Buffer::Private : LinuxDmabufBuffer::Private
58 {
59 public:
60  Private(LinuxDmabufUnstableV1Buffer *_q)
61  : LinuxDmabufBuffer::Private(_q)
62  , q(_q)
63  {
64  }
65  ~Private() override = default;
66 
67  uint32_t format() const override
68  {
69  return m_format;
70  }
71  QSize size() const override
72  {
73  return m_size;
74  }
75 
76  uint32_t m_format;
77  QSize m_size;
78 
79  LinuxDmabufUnstableV1Buffer *q;
80 };
81 
82 LinuxDmabufUnstableV1Buffer::LinuxDmabufUnstableV1Buffer(uint32_t format, const QSize &size)
84  , d(new LinuxDmabufUnstableV1Buffer::Private(this))
85 {
86  d->m_format = format;
87  d->m_size = size;
88 }
89 
91 
92 class V1Iface::Private : public Global::Private
93 {
94 public:
95  Private(V1Iface *q, Display *display);
96  ~Private();
97 
98  static const struct wl_buffer_interface *bufferImplementation()
99  {
100  return &s_bufferImplementation;
101  }
102  V1Iface::Impl *impl;
103  QHash<uint32_t, QSet<uint64_t>> supportedFormatsWithModifiers;
104  V1Iface *const q;
105  static const uint32_t s_version;
106 
107  void bind(wl_client *client, uint32_t version, uint32_t id) override final;
108  void createParams(wl_client *client, wl_resource *resource, uint32_t id);
109 
110  static void unbind(wl_client *client, wl_resource *resource);
111  static void createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id);
112 
113 private:
114  class Params
115  {
116  public:
117  Params(V1Iface::Private *dmabufInterface, wl_client *client, uint32_t version, uint32_t id);
118  ~Params();
119 
120  void postNoMemory()
121  {
122  wl_resource_post_no_memory(m_resource);
123  }
124 
125  wl_resource *resource() const
126  {
127  return m_resource;
128  }
129 
130  void add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier);
131  void create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags);
132 
133  static void destroy(wl_client *client, wl_resource *resource);
134  static void
135  add(wl_client *client, wl_resource *resource, int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo);
136  static void create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags);
137  static void createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags);
138 
139  private:
140  static const struct zwp_linux_buffer_params_v1_interface s_interface;
141 
142  wl_resource *m_resource;
143  V1Iface::Private *m_dmabufInterface;
144  std::array<V1Iface::Plane, 4> m_planes;
145  size_t m_planeCount = 0;
146  bool m_createRequested = false;
147  };
148 
149  static const struct zwp_linux_dmabuf_v1_interface s_implementation;
150  static const struct wl_buffer_interface s_bufferImplementation;
151 };
152 
153 void V1Iface::Private::Params::create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags)
154 {
155  Q_UNUSED(client)
156 
157  V1Iface::Private::Params *params = static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
158  assert(params->m_resource == resource);
159  params->create(client, 0, QSize(width, height), format, flags);
160 }
161 
162 void V1Iface::Private::Params::createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags)
163 {
164  Q_UNUSED(client)
165 
166  V1Iface::Private::Params *params = static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
167  assert(params->m_resource == resource);
168  params->create(client, new_id, QSize(width, height), format, flags);
169 }
170 
171 #ifndef K_DOXYGEN
172 const struct zwp_linux_dmabuf_v1_interface V1Iface::Private::s_implementation = {[](wl_client *, wl_resource *resource) {
173  wl_resource_destroy(resource);
174  }, // unbind
175  createParamsCallback};
176 
177 const struct wl_buffer_interface V1Iface::Private::s_bufferImplementation = {
178  [](wl_client *, wl_resource *resource) {
179  wl_resource_destroy(resource);
180  } // destroy
181 };
182 
183 #ifndef K_DOXYGEN
184 const struct zwp_linux_buffer_params_v1_interface V1Iface::Private::Params::s_interface = {destroy, add, create, createImmed};
185 #endif
186 
187 V1Iface::Private::Params::Params(V1Iface::Private *dmabufInterface, wl_client *client, uint32_t version, uint32_t id)
188  : m_dmabufInterface(dmabufInterface)
189 {
190  m_resource = wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, version, id);
191  if (!m_resource) {
192  return;
193  }
194 
195  wl_resource_set_implementation(m_resource, &s_interface, this, [](wl_resource *resource) {
196  delete static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
197  });
198 
199  for (auto &plane : m_planes) {
200  plane.fd = -1;
201  plane.offset = 0;
202  plane.stride = 0;
203  plane.modifier = 0;
204  }
205 }
206 
207 V1Iface::Private::Params::~Params()
208 {
209  // Close the file descriptors
210  for (auto &plane : m_planes) {
211  if (plane.fd != -1) {
212  ::close(plane.fd);
213  }
214  }
215 }
216 
217 void V1Iface::Private::Params::create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags)
218 {
219  // Validate the parameters
220  // -----------------------
221  const uint32_t width = size.width();
222  const uint32_t height = size.height();
223 
224  if (m_createRequested) {
225  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params was already used to create a wl_buffer");
226  return;
227  }
228  m_createRequested = true;
229 
230  if (m_planeCount == 0) {
231  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no dmabuf has been added to the params");
232  return;
233  }
234 
235  // Check for holes in the dmabufs set (e.g. [0, 1, 3])
236  for (uint32_t i = 0; i < m_planeCount; i++) {
237  if (m_planes[i].fd != -1) {
238  continue;
239  }
240 
241  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no dmabuf has been added for plane %i", i);
242  return;
243  }
244 
245  if (width < 1 || height < 1) {
246  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, "invalid width %d or height %d", width, height);
247  return;
248  }
249 
250  for (uint32_t i = 0; i < m_planeCount; i++) {
251  auto &plane = m_planes[i];
252 
253  if (uint64_t(plane.offset) + plane.stride > UINT32_MAX) {
254  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "size overflow for plane %i", i);
255  return;
256  }
257 
258  if (i == 0 && uint64_t(plane.offset) + plane.stride * height > UINT32_MAX) {
259  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "size overflow for plane %i", i);
260  return;
261  }
262 
263  // Don't report an error as it might be caused by the kernel not supporting seeking on dmabuf
264  off_t size = ::lseek(plane.fd, 0, SEEK_END);
265  if (size == -1) {
266  continue;
267  }
268 
269  if (plane.offset >= size) {
270  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid offset %i for plane %i", plane.offset, i);
271  return;
272  }
273 
274  if (plane.offset + plane.stride > size) {
275  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid stride %i for plane %i", plane.stride, i);
276  return;
277  }
278 
279  // Only valid for first plane as other planes might be
280  // sub-sampled according to fourcc format
281  if (i == 0 && plane.offset + plane.stride * height > size) {
282  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid buffer stride or height for plane %i", i);
283  return;
284  }
285  }
286 
287  // Import the buffer
288  // -----------------
290  planes.reserve(m_planeCount);
291  for (uint32_t i = 0; i < m_planeCount; i++) {
292  planes << m_planes[i];
293  }
294 
295  LinuxDmabufUnstableV1Buffer *buffer = m_dmabufInterface->impl->importBuffer(planes, format, size, (V1Iface::Flags)flags);
296  if (buffer) {
297  // The buffer has ownership of the file descriptors now
298  for (auto &plane : m_planes) {
299  plane.fd = -1;
300  }
301 
302  wl_resource *resource = wl_resource_create(client, &wl_buffer_interface, 1, bufferId);
303  if (!resource) {
304  postNoMemory();
305  delete buffer;
306  return;
307  }
308 
309  wl_resource_set_implementation(resource,
310  m_dmabufInterface->q->bufferImplementation(),
311  buffer,
312  [](wl_resource *resource) { // Destructor
313  delete static_cast<LinuxDmabufUnstableV1Buffer *>(wl_resource_get_user_data(resource));
314  });
315 
316  // XXX Do we need this?
317  // buffer->setResource(resource);
318 
319  // Send a 'created' event when the request is not for an immediate import, i.e. bufferId is zero
320  if (bufferId == 0) {
321  zwp_linux_buffer_params_v1_send_created(m_resource, resource);
322  }
323  } else {
324  if (bufferId == 0) {
325  zwp_linux_buffer_params_v1_send_failed(m_resource);
326  } else {
327  // since the behavior is left implementation defined by the
328  // protocol in case of create_immed failure due to an unknown cause,
329  // we choose to treat it as a fatal error and immediately kill the
330  // client instead of creating an invalid handle and waiting for it
331  // to be used.
332  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, "importing the supplied dmabufs failed");
333  }
334  }
335 }
336 
337 void V1Iface::Private::Params::add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier)
338 {
339  if (m_createRequested) {
340  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params was already used to create a wl_buffer");
341  ::close(fd);
342  return;
343  }
344 
345  if (plane_idx >= m_planes.size()) {
346  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "plane index %u is too high", plane_idx);
347  ::close(fd);
348  return;
349  }
350 
351  auto &plane = m_planes[plane_idx];
352 
353  if (plane.fd != -1) {
354  wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, "a dmabuf has already been added for plane %u", plane_idx);
355  ::close(fd);
356  return;
357  }
358 
359  plane.fd = fd;
360  plane.offset = offset;
361  plane.stride = stride;
362  plane.modifier = modifier;
363 
364  m_planeCount++;
365 }
366 
367 void V1Iface::Private::Params::destroy(wl_client *client, wl_resource *resource)
368 {
369  Q_UNUSED(client)
370  wl_resource_destroy(resource);
371 }
372 
373 void V1Iface::Private::Params::add(wl_client *client,
374  wl_resource *resource,
375  int fd,
376  uint32_t plane_idx,
377  uint32_t offset,
378  uint32_t stride,
379  uint32_t modifier_hi,
380  uint32_t modifier_lo)
381 {
382  Q_UNUSED(client)
383 
384  V1Iface::Private::Params *params = static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
385  assert(params->m_resource == resource);
386  params->add(fd, plane_idx, offset, stride, (uint64_t(modifier_hi) << 32) | modifier_lo);
387 }
388 
389 const uint32_t V1Iface::Private::s_version = 3;
390 #endif
391 
392 V1Iface::Private::Private(V1Iface *q, Display *display)
393  : Global::Private(display, &zwp_linux_dmabuf_v1_interface, s_version)
394  , q(q)
395 {
396 }
397 
398 V1Iface::Private::~Private() = default;
399 
400 void V1Iface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
401 {
402  wl_resource *resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, std::min(s_version, version), id);
403  if (!resource) {
404  wl_client_post_no_memory(client);
405  return;
406  }
407 
408  wl_resource_set_implementation(resource, &s_implementation, this, nullptr);
409 
410  // Send formats & modifiers
411  // ------------------------
412 
413  QHash<uint32_t, QSet<uint64_t>>::const_iterator it = supportedFormatsWithModifiers.constBegin();
414  while (it != supportedFormatsWithModifiers.constEnd()) {
415  QSet<uint64_t> modifiers = it.value();
416  if (modifiers.isEmpty()) {
417  modifiers << DRM_FORMAT_MOD_INVALID;
418  }
419 
420  for (uint64_t modifier : qAsConst(modifiers)) {
421  if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
422  const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
423  const uint32_t modifier_hi = modifier >> 32;
424  zwp_linux_dmabuf_v1_send_modifier(resource, it.key(), modifier_hi, modifier_lo);
425  } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
426  zwp_linux_dmabuf_v1_send_format(resource, it.key());
427  }
428  }
429  it++;
430  }
431 }
432 
433 void V1Iface::Private::createParams(wl_client *client, wl_resource *resource, uint32_t id)
434 {
435  Params *params = new Params(this, client, wl_resource_get_version(resource), id);
436  if (!params->resource()) {
437  wl_resource_post_no_memory(resource);
438  delete params;
439  }
440 }
441 
442 void V1Iface::Private::createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id)
443 {
444  V1Iface::Private *global = static_cast<V1Iface::Private *>(wl_resource_get_user_data(resource));
445  global->createParams(client, resource, id);
446 }
447 
448 V1Iface::LinuxDmabufUnstableV1Interface(Display *display, QObject *parent)
449  : Global(new Private(this, display), parent)
450 {
451 }
452 
454 
456 {
457  d_func()->impl = impl;
458 }
459 
460 void V1Iface::setSupportedFormatsWithModifiers(QHash<uint32_t, QSet<uint64_t>> set)
461 {
462  d_func()->supportedFormatsWithModifiers = set;
463 }
464 
465 const struct wl_buffer_interface *V1Iface::bufferImplementation()
466 {
467  return V1Iface::Private::bufferImplementation();
468 }
469 
470 V1Iface::Private *V1Iface::d_func() const
471 {
472  return reinterpret_cast<Private *>(d.data());
473 }
474 
475 }
476 }
virtual ~LinuxDmabufUnstableV1Interface()
Destroys the LinuxDmabufUnstableV1Interface.
int width() const const
const Key key(const T &value) const const
Represents the global zpw_linux_dmabuf_v1 interface.
QSize size() const
Returns the size of the buffer.
void setImpl(Impl *impl)
Sets the compositor implementation for the dmabuf interface.
QHash::const_iterator constEnd() const const
Class holding the Wayland server display loop.
Definition: display.h:86
The Iface class provides an interface from the LinuxDmabufInterface into the compositor.
const T value(const Key &key) const const
void reserve(int size)
Base class for all Globals.
Definition: global.h:46
QHash::const_iterator constBegin() const const
int height() const const
bool isEmpty() const const
The base class for linux-dmabuf buffers.
uint32_t format() const
Returns the DRM format code for the buffer.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Sep 23 2021 22:51:08 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.