KWayland

buffer_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "buffer_interface.h"
7 #include "display.h"
8 #include "linuxdmabuf_v1_interface.h"
9 #include "logging.h"
10 #include "surface_interface.h"
11 // Wayland
12 #include <wayland-server.h>
13 // EGL
14 #include <EGL/egl.h>
15 #include <QtGui/qopengl.h>
16 
17 #include "drm_fourcc.h"
18 
19 namespace KWayland
20 {
21 namespace Server
22 {
23 namespace EGL
24 {
25 typedef GLboolean (*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
26 eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
27 }
28 
29 class BufferInterface::Private
30 {
31 public:
32  Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent);
33  ~Private();
34  QImage::Format format() const;
35  QImage createImage();
36  wl_resource *buffer;
37  wl_shm_buffer *shmBuffer;
38  LinuxDmabufBuffer *dmabufBuffer;
39  SurfaceInterface *surface;
40  int refCount;
41  QSize size;
42  bool alpha;
43 
44  static BufferInterface *get(wl_resource *r);
45 
46 private:
47  static void destroyListenerCallback(wl_listener *listener, void *data);
48  static Private *cast(wl_resource *r);
49  static void imageBufferCleanupHandler(void *info);
50  static QList<Private *> s_buffers;
51  static Private *s_accessedBuffer;
52  static int s_accessCounter;
53 
54  BufferInterface *q;
55  wl_listener listener;
56 };
57 
58 QList<BufferInterface::Private *> BufferInterface::Private::s_buffers;
59 BufferInterface::Private *BufferInterface::Private::s_accessedBuffer = nullptr;
60 int BufferInterface::Private::s_accessCounter = 0;
61 
62 BufferInterface::Private *BufferInterface::Private::cast(wl_resource *r)
63 {
64  auto it = std::find_if(s_buffers.constBegin(), s_buffers.constEnd(), [r](Private *d) {
65  return d->buffer == r;
66  });
67  if (it == s_buffers.constEnd()) {
68  return nullptr;
69  }
70  return *it;
71 }
72 
73 BufferInterface *BufferInterface::Private::get(wl_resource *r)
74 {
75  Private *p = cast(r);
76  if (!p) {
77  return nullptr;
78  }
79  return p->q;
80 }
81 
82 void BufferInterface::Private::imageBufferCleanupHandler(void *info)
83 {
84  Private *p = reinterpret_cast<Private *>(info);
85  Q_ASSERT(p == s_accessedBuffer);
86  Q_ASSERT(s_accessCounter > 0);
87  s_accessCounter--;
88  if (s_accessCounter == 0) {
89  s_accessedBuffer = nullptr;
90  }
91  wl_shm_buffer_end_access(p->shmBuffer);
92 }
93 
94 BufferInterface::Private::Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent)
95  : buffer(resource)
96  , shmBuffer(wl_shm_buffer_get(resource))
97  , dmabufBuffer(nullptr)
98  , surface(parent)
99  , refCount(0)
100  , alpha(false)
101  , q(q)
102 {
103  if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) {
104  dmabufBuffer = static_cast<LinuxDmabufBuffer *>(wl_resource_get_user_data(resource));
105  }
106  s_buffers << this;
107  listener.notify = destroyListenerCallback;
108  listener.link.prev = nullptr;
109  listener.link.next = nullptr;
110  wl_resource_add_destroy_listener(resource, &listener);
111  if (shmBuffer) {
112  size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer));
113  // check alpha
114  switch (wl_shm_buffer_get_format(shmBuffer)) {
115  case WL_SHM_FORMAT_ARGB8888:
116  alpha = true;
117  break;
118  case WL_SHM_FORMAT_XRGB8888:
119  default:
120  alpha = false;
121  break;
122  }
123  } else if (dmabufBuffer) {
124  switch (dmabufBuffer->format()) {
125  case DRM_FORMAT_ARGB4444:
126  case DRM_FORMAT_ABGR4444:
127  case DRM_FORMAT_RGBA4444:
128  case DRM_FORMAT_BGRA4444:
129 
130  case DRM_FORMAT_ARGB1555:
131  case DRM_FORMAT_ABGR1555:
132  case DRM_FORMAT_RGBA5551:
133  case DRM_FORMAT_BGRA5551:
134 
135  case DRM_FORMAT_ARGB8888:
136  case DRM_FORMAT_ABGR8888:
137  case DRM_FORMAT_RGBA8888:
138  case DRM_FORMAT_BGRA8888:
139 
140  case DRM_FORMAT_ARGB2101010:
141  case DRM_FORMAT_ABGR2101010:
142  case DRM_FORMAT_RGBA1010102:
143  case DRM_FORMAT_BGRA1010102:
144 
145  case DRM_FORMAT_XRGB8888_A8:
146  case DRM_FORMAT_XBGR8888_A8:
147  case DRM_FORMAT_RGBX8888_A8:
148  case DRM_FORMAT_BGRX8888_A8:
149  case DRM_FORMAT_RGB888_A8:
150  case DRM_FORMAT_BGR888_A8:
151  case DRM_FORMAT_RGB565_A8:
152  case DRM_FORMAT_BGR565_A8:
153  alpha = true;
154  break;
155  default:
156  alpha = false;
157  break;
158  }
159  size = dmabufBuffer->size();
160  } else if (parent) {
161  EGLDisplay eglDisplay = parent->global()->display()->eglDisplay();
162  static bool resolved = false;
163  using namespace EGL;
164  if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
165  eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
166  resolved = true;
167  }
168  if (eglQueryWaylandBufferWL) {
169  EGLint width;
170  EGLint height;
171  bool valid = false;
172  valid = eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_WIDTH, &width);
173  valid = valid && eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_HEIGHT, &height);
174  if (valid) {
175  size = QSize(width, height);
176  }
177  // check alpha
178  EGLint format;
179  if (eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_TEXTURE_FORMAT, &format)) {
180  switch (format) {
181  case EGL_TEXTURE_RGBA:
182  alpha = true;
183  break;
184  case EGL_TEXTURE_RGB:
185  default:
186  alpha = false;
187  break;
188  }
189  }
190  }
191  }
192 }
193 
194 BufferInterface::Private::~Private()
195 {
196  wl_list_remove(&listener.link);
197  s_buffers.removeAll(this);
198 }
199 
200 BufferInterface *BufferInterface::get(wl_resource *r)
201 {
202  if (!r) {
203  return nullptr;
204  }
205  // TODO: verify it's a buffer
206  BufferInterface *b = Private::get(r);
207  if (b) {
208  return b;
209  }
210  return new BufferInterface(r, nullptr);
211 }
212 
213 BufferInterface::BufferInterface(wl_resource *resource, SurfaceInterface *parent)
214  : QObject()
215  , d(new Private(this, resource, parent))
216 {
217 }
218 
219 BufferInterface::~BufferInterface()
220 {
221  if (d->refCount != 0) {
222  qCWarning(KWAYLAND_SERVER) << "Buffer destroyed while still being referenced, ref count:" << d->refCount;
223  }
224 }
225 
226 void BufferInterface::Private::destroyListenerCallback(wl_listener *listener, void *data)
227 {
228  Q_UNUSED(listener);
229  auto b = cast(reinterpret_cast<wl_resource *>(data));
230  b->buffer = nullptr;
231  Q_EMIT b->q->aboutToBeDestroyed(b->q);
232  delete b->q;
233 }
234 
235 void BufferInterface::ref()
236 {
237  d->refCount++;
238 }
239 
240 void BufferInterface::unref()
241 {
242  Q_ASSERT(d->refCount > 0);
243  d->refCount--;
244  if (d->refCount == 0) {
245  if (d->buffer) {
246  wl_buffer_send_release(d->buffer);
247  wl_client_flush(wl_resource_get_client(d->buffer));
248  }
249  deleteLater();
250  }
251 }
252 
253 QImage::Format BufferInterface::Private::format() const
254 {
255  if (!shmBuffer) {
256  return QImage::Format_Invalid;
257  }
258  switch (wl_shm_buffer_get_format(shmBuffer)) {
259  case WL_SHM_FORMAT_ARGB8888:
261  case WL_SHM_FORMAT_XRGB8888:
262  return QImage::Format_RGB32;
263  default:
264  return QImage::Format_Invalid;
265  }
266 }
267 
268 QImage BufferInterface::data()
269 {
270  return d->createImage();
271 }
272 
273 QImage BufferInterface::Private::createImage()
274 {
275  if (!shmBuffer) {
276  return QImage();
277  }
278  if (s_accessedBuffer != nullptr && s_accessedBuffer != this) {
279  return QImage();
280  }
281  const QImage::Format imageFormat = format();
282  if (imageFormat == QImage::Format_Invalid) {
283  return QImage();
284  }
285  s_accessedBuffer = this;
286  s_accessCounter++;
287  wl_shm_buffer_begin_access(shmBuffer);
288  return QImage((const uchar *)wl_shm_buffer_get_data(shmBuffer),
289  size.width(),
290  size.height(),
291  wl_shm_buffer_get_stride(shmBuffer),
292  imageFormat,
293  &imageBufferCleanupHandler,
294  this);
295 }
296 
297 bool BufferInterface::isReferenced() const
298 {
299  return d->refCount > 0;
300 }
301 
302 SurfaceInterface *BufferInterface::surface() const
303 {
304  return d->surface;
305 }
306 
307 wl_shm_buffer *BufferInterface::shmBuffer()
308 {
309  return d->shmBuffer;
310 }
311 
312 LinuxDmabufBuffer *BufferInterface::linuxDmabufBuffer()
313 {
314  return d->dmabufBuffer;
315 }
316 
317 wl_resource *BufferInterface::resource() const
318 {
319  return d->buffer;
320 }
321 
322 QSize BufferInterface::size() const
323 {
324  return d->size;
325 }
326 
327 void BufferInterface::setSize(const QSize &size)
328 {
329  if (d->shmBuffer || d->size == size) {
330  return;
331  }
332  d->size = size;
333  Q_EMIT sizeChanged();
334 }
335 
336 bool BufferInterface::hasAlphaChannel() const
337 {
338  return d->alpha;
339 }
340 
341 }
342 }
int width() const const
Format format() const
Definition: buffer.cpp:115
Resource representing a wl_surface.
int height() const const
The base class for linux-dmabuf buffers.
QSize size() const
Definition: buffer.cpp:95
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Sep 25 2021 22:51:28 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.