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

KDE's Doxygen guidelines are available online.