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

KDE's Doxygen guidelines are available online.