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

KDE's Doxygen guidelines are available online.