1 /********************************************************************
2 Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
3 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
21 #include "buffer_p.h"
22
23 #include "client.h"
24 #include "display.h"
25 #include "logging.h"
26 #include "surface.h"
27
28 #include "wayland/buffer_manager.h"
29 #include "wayland/display.h"
30
31 #include "linux_dmabuf_v1.h"
32 #include "linux_dmabuf_v1_p.h"
33
34 #include <drm_fourcc.h>
35
36 #include <EGL/egl.h>
37 #include <QtGui/qopengl.h>
38
39 namespace EGL
40 {
41 using eglQueryWaylandBufferWL_func
42 = GLboolean (*)(EGLDisplay dpy, struct wl_resource* buffer, EGLint attribute, EGLint* value);
43 }
44
45 namespace Wrapland::Server
46 {
47
48 int constexpr default_bpp{32};
49
Private(Buffer * buffer,ShmImage::Format format)50 ShmImage::Private::Private(Buffer* buffer, ShmImage::Format format)
51 : format{format}
52 , stride{wl_shm_buffer_get_stride(buffer->d_ptr->shmBuffer)}
53 , bpp{default_bpp}
54 , data{static_cast<uchar*>(wl_shm_buffer_get_data(buffer->d_ptr->shmBuffer))}
55 , buffer{buffer}
56 , display{buffer->d_ptr->display}
57 {
58 }
59
~Private()60 ShmImage::Private::~Private()
61 {
62 display->bufferManager()->endShmAccess();
63 }
64
createQImage()65 QImage ShmImage::Private::createQImage()
66 {
67 if (!image.isNull()) {
68 return image;
69 }
70
71 [[maybe_unused]] auto const hasAccess
72 = display->bufferManager()->beginShmAccess(buffer->d_ptr->shmBuffer);
73 assert(hasAccess);
74
75 QImage::Format qtFormat{QImage::Format_Invalid};
76 switch (format) {
77 case ShmImage::Format::argb8888:
78 qtFormat = QImage::Format_ARGB32_Premultiplied;
79 break;
80 case ShmImage::Format::xrgb8888:
81 qtFormat = QImage::Format_RGB32;
82 break;
83 default:
84 assert(false);
85 }
86
87 auto const size = buffer->size();
88 return QImage(
89 data, size.width(), size.height(), stride, qtFormat, &imageBufferCleanupHandler, display);
90 }
91
imageBufferCleanupHandler(void * info)92 void ShmImage::Private::imageBufferCleanupHandler(void* info)
93 {
94 auto display = static_cast<Wayland::Display*>(info);
95 display->bufferManager()->endShmAccess();
96 }
97
ShmImage(Buffer * buffer,ShmImage::Format format)98 ShmImage::ShmImage(Buffer* buffer, ShmImage::Format format)
99 : d_ptr{new Private(buffer, format)}
100 {
101 }
102
ShmImage(ShmImage const & img)103 ShmImage::ShmImage(ShmImage const& img)
104 : d_ptr{new Private(img.d_ptr->buffer, img.d_ptr->format)}
105 {
106 d_ptr->display->bufferManager()->beginShmAccess(d_ptr->buffer->d_ptr->shmBuffer);
107 }
108
operator =(ShmImage const & img)109 ShmImage& ShmImage::operator=(ShmImage const& img)
110 {
111 if (this != &img) {
112 d_ptr->display->bufferManager()->endShmAccess();
113 img.d_ptr->display->bufferManager()->beginShmAccess(img.d_ptr->buffer->d_ptr->shmBuffer);
114
115 d_ptr->format = img.d_ptr->format;
116 d_ptr->stride = img.d_ptr->stride;
117 d_ptr->bpp = img.d_ptr->bpp;
118 d_ptr->data = img.d_ptr->data;
119 d_ptr->buffer = img.d_ptr->buffer;
120 d_ptr->display = img.d_ptr->display;
121 }
122
123 return *this;
124 }
125
ShmImage(ShmImage && img)126 ShmImage::ShmImage(ShmImage&& img) noexcept
127 : d_ptr{std::move(img.d_ptr)}
128 {
129 }
130
operator =(ShmImage && img)131 ShmImage& ShmImage::operator=(ShmImage&& img) noexcept
132 {
133 if (this != &img) {
134 d_ptr = std::move(img.d_ptr);
135 }
136
137 return *this;
138 }
139
140 ShmImage::~ShmImage() = default;
141
format() const142 ShmImage::Format ShmImage::format() const
143 {
144 return d_ptr->format;
145 }
146
stride() const147 int32_t ShmImage::stride() const
148 {
149 return d_ptr->stride;
150 }
151
bpp() const152 int32_t ShmImage::bpp() const
153 {
154 return d_ptr->bpp;
155 }
156
data() const157 uchar* ShmImage::data() const
158 {
159 return d_ptr->data;
160 }
161
createQImage()162 QImage ShmImage::createQImage()
163 {
164 return d_ptr->createQImage();
165 }
166
getFormat(wl_shm_buffer * shmBuffer)167 ShmImage::Format getFormat(wl_shm_buffer* shmBuffer)
168 {
169 switch (wl_shm_buffer_get_format(shmBuffer)) {
170 case WL_SHM_FORMAT_ARGB8888:
171 return ShmImage::Format::argb8888;
172 case WL_SHM_FORMAT_XRGB8888:
173 return ShmImage::Format::xrgb8888;
174 default:
175 return ShmImage::Format::invalid;
176 }
177 }
178
get(Buffer * buffer)179 std::optional<ShmImage> ShmImage::get(Buffer* buffer)
180 {
181 auto display = buffer->d_ptr->display;
182 auto shmBuffer = buffer->d_ptr->shmBuffer;
183
184 if (!shmBuffer) {
185 return std::nullopt;
186 }
187
188 if (!display->bufferManager()->beginShmAccess(shmBuffer)) {
189 return std::nullopt;
190 }
191
192 auto const imageFormat = getFormat(shmBuffer);
193 if (imageFormat == ShmImage::Format::invalid) {
194 display->bufferManager()->endShmAccess();
195 return std::nullopt;
196 }
197
198 return ShmImage(buffer, imageFormat);
199 }
200
Private(Buffer * q,wl_resource * wlResource,Surface * surface,Wayland::Display * display)201 Buffer::Private::Private(Buffer* q,
202 wl_resource* wlResource,
203 Surface* surface,
204 Wayland::Display* display)
205 : resource(wlResource)
206 , shmBuffer(wl_shm_buffer_get(wlResource))
207 , surface(surface)
208 , display(display)
209 , q_ptr{q}
210 {
211 if (!shmBuffer
212 && wl_resource_instance_of(resource, &wl_buffer_interface, BufferV1::interface())) {
213 dmabufBuffer = Wayland::Resource<LinuxDmabufBufferV1>::handle(resource);
214 }
215
216 destroyWrapper.buffer = q;
217 destroyWrapper.listener.notify = destroyListenerCallback;
218 destroyWrapper.listener.link.prev = nullptr;
219 destroyWrapper.listener.link.next = nullptr;
220 wl_resource_add_destroy_listener(resource, &destroyWrapper.listener);
221
222 if (shmBuffer) {
223 size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer));
224 // check alpha
225 switch (wl_shm_buffer_get_format(shmBuffer)) {
226 case WL_SHM_FORMAT_ARGB8888:
227 alpha = true;
228 break;
229 case WL_SHM_FORMAT_XRGB8888:
230 default:
231 alpha = false;
232 break;
233 }
234 } else if (dmabufBuffer) {
235 switch (dmabufBuffer->format()) {
236 case DRM_FORMAT_ARGB4444:
237 case DRM_FORMAT_ABGR4444:
238 case DRM_FORMAT_RGBA4444:
239 case DRM_FORMAT_BGRA4444:
240
241 case DRM_FORMAT_ARGB1555:
242 case DRM_FORMAT_ABGR1555:
243 case DRM_FORMAT_RGBA5551:
244 case DRM_FORMAT_BGRA5551:
245
246 case DRM_FORMAT_ARGB8888:
247 case DRM_FORMAT_ABGR8888:
248 case DRM_FORMAT_RGBA8888:
249 case DRM_FORMAT_BGRA8888:
250
251 case DRM_FORMAT_ARGB2101010:
252 case DRM_FORMAT_ABGR2101010:
253 case DRM_FORMAT_RGBA1010102:
254 case DRM_FORMAT_BGRA1010102:
255
256 case DRM_FORMAT_XRGB8888_A8:
257 case DRM_FORMAT_XBGR8888_A8:
258 case DRM_FORMAT_RGBX8888_A8:
259 case DRM_FORMAT_BGRX8888_A8:
260 case DRM_FORMAT_RGB888_A8:
261 case DRM_FORMAT_BGR888_A8:
262 case DRM_FORMAT_RGB565_A8:
263 case DRM_FORMAT_BGR565_A8:
264 alpha = true;
265 break;
266 default:
267 alpha = false;
268 break;
269 }
270 size = dmabufBuffer->size();
271 } else if (surface) {
272 EGLDisplay eglDisplay = surface->client()->display()->eglDisplay();
273
274 using namespace EGL;
275 static eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL{nullptr};
276 static bool resolved{false};
277
278 if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
279 eglQueryWaylandBufferWL = reinterpret_cast<eglQueryWaylandBufferWL_func>(
280 eglGetProcAddress("eglQueryWaylandBufferWL"));
281 resolved = true;
282 }
283
284 if (eglQueryWaylandBufferWL) {
285 EGLint width = 0;
286 EGLint height = 0;
287 bool valid = false;
288 valid = eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WIDTH, &width);
289 valid = valid && eglQueryWaylandBufferWL(eglDisplay, resource, EGL_HEIGHT, &height);
290 if (valid) {
291 size = QSize(width, height);
292 }
293 // check alpha
294 EGLint format = 0;
295 if (eglQueryWaylandBufferWL(eglDisplay, resource, EGL_TEXTURE_FORMAT, &format)) {
296 switch (format) {
297 case EGL_TEXTURE_RGBA:
298 alpha = true;
299 break;
300 case EGL_TEXTURE_RGB:
301 default:
302 alpha = false;
303 break;
304 }
305 }
306 }
307 }
308 }
309
~Private()310 Buffer::Private::~Private()
311 {
312 wl_list_remove(&destroyWrapper.listener.link);
313 display->bufferManager()->removeBuffer(q_ptr);
314 }
315
make(wl_resource * wlResource,Surface * surface)316 std::shared_ptr<Buffer> Buffer::make(wl_resource* wlResource, Surface* surface)
317 {
318 auto backendDisplay = Wayland::Display::backendCast(surface->client()->display());
319 auto buffer = std::shared_ptr<Buffer>{new Buffer(wlResource, surface)};
320 backendDisplay->bufferManager()->addBuffer(buffer);
321 return buffer;
322 }
323
make(wl_resource * wlResource,Display * display)324 std::shared_ptr<Buffer> Buffer::make(wl_resource* wlResource, Display* display)
325 {
326 auto backendDisplay = Wayland::Display::backendCast(display);
327 auto buffer = std::shared_ptr<Buffer>{new Buffer(wlResource, display)};
328 backendDisplay->bufferManager()->addBuffer(buffer);
329 return buffer;
330 }
331
destroyListenerCallback(wl_listener * listener,void * data)332 void Buffer::Private::destroyListenerCallback(wl_listener* listener, [[maybe_unused]] void* data)
333 {
334 // The wl_container_of macro can not be used with auto keyword and in the macro from libwayland
335 // the alignment is increased.
336 // Relevant clang-tidy checks are:
337 // * clang-diagnostic-cast-align
338 // * cppcoreguidelines-pro-bounds-pointer-arithmetic
339 // * hicpp-use-auto
340 // * modernize-use-auto
341 // NOLINTNEXTLINE
342 DestroyWrapper* wrapper = wl_container_of(listener, wrapper, listener);
343
344 wrapper->buffer->d_ptr->resource = nullptr;
345 Q_EMIT wrapper->buffer->resourceDestroyed();
346 }
347
Buffer(wl_resource * wlResource,Surface * surface)348 Buffer::Buffer(wl_resource* wlResource, Surface* surface)
349 : QObject(nullptr)
350 , d_ptr(new Private(this,
351 wlResource,
352 surface,
353 Wayland::Display::backendCast(surface->client()->display())))
354 {
355 }
356
Buffer(wl_resource * wlResource,Display * display)357 Buffer::Buffer(wl_resource* wlResource, Display* display)
358 : QObject(nullptr)
359 , d_ptr(new Private(this, wlResource, nullptr, Wayland::Display::backendCast(display)))
360 {
361 }
362
get(Display * display,wl_resource * resource)363 std::shared_ptr<Buffer> Buffer::get(Display* display, wl_resource* resource)
364 {
365 if (!resource) {
366 return nullptr;
367 }
368 // TODO(unknown author): verify resource is a buffer
369 auto buffer = Wayland::Display::backendCast(display)->bufferManager()->fromResource(resource);
370 return buffer ? buffer.value() : make(resource, display);
371 }
372
~Buffer()373 Buffer::~Buffer()
374 {
375 if (d_ptr->committed && d_ptr->resource) {
376 wl_buffer_send_release(d_ptr->resource);
377 wl_client_flush(wl_resource_get_client(d_ptr->resource));
378 }
379 }
380
shmImage()381 std::optional<ShmImage> Buffer::shmImage()
382 {
383 return ShmImage::get(this);
384 }
385
surface() const386 Surface* Buffer::surface() const
387 {
388 return d_ptr->surface;
389 }
390
shmBuffer()391 wl_shm_buffer* Buffer::shmBuffer()
392 {
393 return d_ptr->shmBuffer;
394 }
395
linuxDmabufBuffer()396 LinuxDmabufBufferV1* Buffer::linuxDmabufBuffer()
397 {
398 return d_ptr->dmabufBuffer;
399 }
400
resource() const401 wl_resource* Buffer::resource() const
402 {
403 return d_ptr->resource;
404 }
405
size() const406 QSize Buffer::size() const
407 {
408 return d_ptr->size;
409 }
410
hasAlphaChannel() const411 bool Buffer::hasAlphaChannel() const
412 {
413 return d_ptr->alpha;
414 }
415
setCommitted()416 void Buffer::setCommitted()
417 {
418 d_ptr->committed = true;
419 }
420
421 }
422