Directory: | ./ |
---|---|
File: | server/buffer.cpp |
Date: | 2024-01-22 17:25:27 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 137 | 205 | 66.8% |
Branches: | 46 | 118 | 39.0% |
Line | Branch | Exec | Source |
---|---|---|---|
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 | |||
50 | 2362 | ShmImage::Private::Private(Buffer* buffer, ShmImage::Format format) | |
51 | 1181 | : format{format} | |
52 | 1181 | , stride{wl_shm_buffer_get_stride(buffer->d_ptr->shmBuffer)} | |
53 | 1181 | , bpp{default_bpp} | |
54 | 1181 | , data{static_cast<uchar*>(wl_shm_buffer_get_data(buffer->d_ptr->shmBuffer))} | |
55 | 1181 | , buffer{buffer} | |
56 | 1181 | , display{buffer->d_ptr->display} | |
57 | { | ||
58 | 1181 | } | |
59 | |||
60 | 1181 | ShmImage::Private::~Private() | |
61 | { | ||
62 |
2/4✓ Branch 0 taken 1181 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1181 times.
✗ Branch 3 not taken.
|
1181 | display->bufferManager()->endShmAccess(); |
63 | 1181 | } | |
64 | |||
65 | 1180 | QImage ShmImage::Private::createQImage() | |
66 | { | ||
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1180 times.
|
1256 | if (!image.isNull()) { |
68 | ✗ | return image; | |
69 | } | ||
70 | 76 | ||
71 | 1180 | [[maybe_unused]] auto const hasAccess | |
72 | 1256 | = display->bufferManager()->beginShmAccess(buffer->d_ptr->shmBuffer); | |
73 |
1/2✓ Branch 0 taken 1180 times.
✗ Branch 1 not taken.
|
1256 | assert(hasAccess); |
74 | |||
75 | 1180 | QImage::Format qtFormat{QImage::Format_Invalid}; | |
76 |
2/3✗ Branch 0 not taken.
✓ Branch 1 taken 1174 times.
✓ Branch 2 taken 6 times.
|
1180 | switch (format) { |
77 | case ShmImage::Format::argb8888: | ||
78 | 1174 | qtFormat = QImage::Format_ARGB32_Premultiplied; | |
79 | 1174 | break; | |
80 | case ShmImage::Format::xrgb8888: | ||
81 | 6 | qtFormat = QImage::Format_RGB32; | |
82 | 6 | break; | |
83 | default: | ||
84 | ✗ | assert(false); | |
85 | } | ||
86 | |||
87 | 1180 | auto const size = buffer->size(); | |
88 | 1180 | return { | |
89 | 1180 | data, size.width(), size.height(), stride, qtFormat, &imageBufferCleanupHandler, display}; | |
90 | 1180 | } | |
91 | |||
92 | 1180 | void ShmImage::Private::imageBufferCleanupHandler(void* info) | |
93 | { | ||
94 | 1180 | auto display = static_cast<Wayland::Display*>(info); | |
95 | 1180 | display->bufferManager()->endShmAccess(); | |
96 | 1180 | } | |
97 | |||
98 | 1181 | ShmImage::ShmImage(Buffer* buffer, ShmImage::Format format) | |
99 |
1/2✓ Branch 0 taken 1181 times.
✗ Branch 1 not taken.
|
1181 | : d_ptr{new Private(buffer, format)} |
100 | { | ||
101 | 1181 | } | |
102 | |||
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 | |||
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 | |||
126 | 1182 | ShmImage::ShmImage(ShmImage&& img) noexcept | |
127 | 1182 | : d_ptr{std::move(img.d_ptr)} | |
128 | { | ||
129 | 1182 | } | |
130 | |||
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 | 2363 | ShmImage::~ShmImage() = default; | |
141 | |||
142 | 1 | ShmImage::Format ShmImage::format() const | |
143 | { | ||
144 | 1 | return d_ptr->format; | |
145 | } | ||
146 | |||
147 | ✗ | int32_t ShmImage::stride() const | |
148 | { | ||
149 | ✗ | return d_ptr->stride; | |
150 | } | ||
151 | |||
152 | ✗ | int32_t ShmImage::bpp() const | |
153 | { | ||
154 | ✗ | return d_ptr->bpp; | |
155 | } | ||
156 | |||
157 | ✗ | uchar* ShmImage::data() const | |
158 | { | ||
159 | ✗ | return d_ptr->data; | |
160 | } | ||
161 | |||
162 | 1180 | QImage ShmImage::createQImage() | |
163 | { | ||
164 | 1180 | return d_ptr->createQImage(); | |
165 | } | ||
166 | |||
167 | 1181 | ShmImage::Format getFormat(wl_shm_buffer* shmBuffer) | |
168 | { | ||
169 |
2/3✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1174 times.
|
1181 | switch (wl_shm_buffer_get_format(shmBuffer)) { |
170 | case WL_SHM_FORMAT_ARGB8888: | ||
171 | 1174 | return ShmImage::Format::argb8888; | |
172 | case WL_SHM_FORMAT_XRGB8888: | ||
173 | 7 | return ShmImage::Format::xrgb8888; | |
174 | default: | ||
175 | ✗ | return ShmImage::Format::invalid; | |
176 | } | ||
177 | 1181 | } | |
178 | |||
179 | 1182 | std::optional<ShmImage> ShmImage::get(Buffer* buffer) | |
180 | { | ||
181 | 1182 | auto display = buffer->d_ptr->display; | |
182 | 1182 | auto shmBuffer = buffer->d_ptr->shmBuffer; | |
183 | |||
184 |
1/2✓ Branch 0 taken 1182 times.
✗ Branch 1 not taken.
|
1182 | if (!shmBuffer) { |
185 | ✗ | return std::nullopt; | |
186 | } | ||
187 | |||
188 |
2/2✓ Branch 0 taken 1181 times.
✓ Branch 1 taken 1 times.
|
1182 | if (!display->bufferManager()->beginShmAccess(shmBuffer)) { |
189 | 1 | return std::nullopt; | |
190 | } | ||
191 | |||
192 | 1181 | auto const imageFormat = getFormat(shmBuffer); | |
193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1181 times.
|
1181 | if (imageFormat == ShmImage::Format::invalid) { |
194 | ✗ | display->bufferManager()->endShmAccess(); | |
195 | ✗ | return std::nullopt; | |
196 | } | ||
197 | |||
198 | 1181 | return ShmImage(buffer, imageFormat); | |
199 | 1182 | } | |
200 | |||
201 | 228 | Buffer::Private::Private(Buffer* q_ptr, | |
202 | wl_resource* wlResource, | ||
203 | Surface* surface, | ||
204 | Wayland::Display* display) | ||
205 | 76 | : resource(wlResource) | |
206 | 76 | , shmBuffer(wl_shm_buffer_get(wlResource)) | |
207 | 76 | , surface(surface) | |
208 | 76 | , display(display) | |
209 | 76 | , q_ptr{q_ptr} | |
210 | { | ||
211 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
76 | if (!shmBuffer |
212 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
|
76 | && wl_resource_instance_of( |
213 | ✗ | resource, &wl_buffer_interface, &linux_dmabuf_buffer_v1_res_impl::s_interface)) { | |
214 | ✗ | dmabufBuffer | |
215 | ✗ | = Wayland::Resource<linux_dmabuf_buffer_v1_res>::get_handle(resource)->handle.get(); | |
216 | } | ||
217 | |||
218 | 76 | destroyWrapper.buffer = q_ptr; | |
219 | 76 | destroyWrapper.listener.notify = destroyListenerCallback; | |
220 | 76 | destroyWrapper.listener.link.prev = nullptr; | |
221 | 76 | destroyWrapper.listener.link.next = nullptr; | |
222 | 76 | wl_resource_add_destroy_listener(resource, &destroyWrapper.listener); | |
223 | |||
224 |
1/2✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
|
76 | if (shmBuffer) { |
225 | 76 | size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer)); | |
226 | // check alpha | ||
227 |
2/3✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 10 times.
|
76 | switch (wl_shm_buffer_get_format(shmBuffer)) { |
228 | case WL_SHM_FORMAT_ARGB8888: | ||
229 | 66 | alpha = true; | |
230 | 66 | break; | |
231 | 10 | case WL_SHM_FORMAT_XRGB8888: | |
232 | default: | ||
233 | 10 | alpha = false; | |
234 | 10 | break; | |
235 | } | ||
236 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
76 | } else if (dmabufBuffer) { |
237 | ✗ | switch (dmabufBuffer->format) { | |
238 | case DRM_FORMAT_ARGB4444: | ||
239 | case DRM_FORMAT_ABGR4444: | ||
240 | case DRM_FORMAT_RGBA4444: | ||
241 | case DRM_FORMAT_BGRA4444: | ||
242 | |||
243 | case DRM_FORMAT_ARGB1555: | ||
244 | case DRM_FORMAT_ABGR1555: | ||
245 | case DRM_FORMAT_RGBA5551: | ||
246 | case DRM_FORMAT_BGRA5551: | ||
247 | |||
248 | case DRM_FORMAT_ARGB8888: | ||
249 | case DRM_FORMAT_ABGR8888: | ||
250 | case DRM_FORMAT_RGBA8888: | ||
251 | case DRM_FORMAT_BGRA8888: | ||
252 | |||
253 | case DRM_FORMAT_ARGB2101010: | ||
254 | case DRM_FORMAT_ABGR2101010: | ||
255 | case DRM_FORMAT_RGBA1010102: | ||
256 | case DRM_FORMAT_BGRA1010102: | ||
257 | |||
258 | case DRM_FORMAT_XRGB8888_A8: | ||
259 | case DRM_FORMAT_XBGR8888_A8: | ||
260 | case DRM_FORMAT_RGBX8888_A8: | ||
261 | case DRM_FORMAT_BGRX8888_A8: | ||
262 | case DRM_FORMAT_RGB888_A8: | ||
263 | case DRM_FORMAT_BGR888_A8: | ||
264 | case DRM_FORMAT_RGB565_A8: | ||
265 | case DRM_FORMAT_BGR565_A8: | ||
266 | ✗ | alpha = true; | |
267 | ✗ | break; | |
268 | default: | ||
269 | ✗ | alpha = false; | |
270 | ✗ | break; | |
271 | } | ||
272 | ✗ | size = dmabufBuffer->size; | |
273 | ✗ | } else if (surface) { | |
274 | ✗ | EGLDisplay eglDisplay = surface->client()->display()->eglDisplay(); | |
275 | |||
276 | using namespace EGL; | ||
277 | static eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL{nullptr}; | ||
278 | static bool resolved{false}; | ||
279 | |||
280 | ✗ | if (!resolved && eglDisplay != EGL_NO_DISPLAY) { | |
281 | ✗ | eglQueryWaylandBufferWL = reinterpret_cast<eglQueryWaylandBufferWL_func>( | |
282 | ✗ | eglGetProcAddress("eglQueryWaylandBufferWL")); | |
283 | ✗ | resolved = true; | |
284 | } | ||
285 | |||
286 | ✗ | if (eglQueryWaylandBufferWL) { | |
287 | ✗ | EGLint width = 0; | |
288 | ✗ | EGLint height = 0; | |
289 | ✗ | bool valid = false; | |
290 | ✗ | valid = eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WIDTH, &width); | |
291 | ✗ | valid = valid && eglQueryWaylandBufferWL(eglDisplay, resource, EGL_HEIGHT, &height); | |
292 | ✗ | if (valid) { | |
293 | ✗ | size = QSize(width, height); | |
294 | } | ||
295 | // check alpha | ||
296 | ✗ | EGLint format = 0; | |
297 | ✗ | if (eglQueryWaylandBufferWL(eglDisplay, resource, EGL_TEXTURE_FORMAT, &format)) { | |
298 | ✗ | switch (format) { | |
299 | case EGL_TEXTURE_RGBA: | ||
300 | ✗ | alpha = true; | |
301 | ✗ | break; | |
302 | ✗ | case EGL_TEXTURE_RGB: | |
303 | default: | ||
304 | ✗ | alpha = false; | |
305 | ✗ | break; | |
306 | } | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | 76 | } | |
311 | |||
312 | 76 | Buffer::Private::~Private() | |
313 | { | ||
314 |
1/2✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
|
76 | wl_list_remove(&destroyWrapper.listener.link); |
315 |
2/4✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
|
76 | display->bufferManager()->removeBuffer(q_ptr); |
316 | 76 | } | |
317 | |||
318 | 67 | std::shared_ptr<Buffer> Buffer::make(wl_resource* wlResource, Surface* surface) | |
319 | { | ||
320 | 67 | auto backendDisplay = Wayland::Display::backendCast(surface->client()->display()); | |
321 |
1/2✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
|
67 | auto buffer = std::shared_ptr<Buffer>{new Buffer(wlResource, surface)}; |
322 |
2/4✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
|
67 | backendDisplay->bufferManager()->addBuffer(buffer); |
323 | 67 | return buffer; | |
324 |
1/2✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
|
67 | } |
325 | |||
326 | 9 | std::shared_ptr<Buffer> Buffer::make(wl_resource* wlResource, Display* display) | |
327 | { | ||
328 | 9 | auto backendDisplay = Wayland::Display::backendCast(display); | |
329 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | auto buffer = std::shared_ptr<Buffer>{new Buffer(wlResource, display)}; |
330 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
|
9 | backendDisplay->bufferManager()->addBuffer(buffer); |
331 | 9 | return buffer; | |
332 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | } |
333 | |||
334 | 12 | void Buffer::Private::destroyListenerCallback(wl_listener* listener, [[maybe_unused]] void* data) | |
335 | { | ||
336 | // The wl_container_of macro can not be used with auto keyword and in the macro from libwayland | ||
337 | // the alignment is increased. | ||
338 | // Relevant clang-tidy checks are: | ||
339 | // * clang-diagnostic-cast-align | ||
340 | // * cppcoreguidelines-pro-bounds-pointer-arithmetic | ||
341 | // * hicpp-use-auto | ||
342 | // * modernize-use-auto | ||
343 | // NOLINTNEXTLINE | ||
344 | 12 | DestroyWrapper* wrapper = wl_container_of(listener, wrapper, listener); | |
345 | |||
346 | 12 | wrapper->buffer->d_ptr->resource = nullptr; | |
347 | 12 | Q_EMIT wrapper->buffer->resourceDestroyed(); | |
348 | 12 | } | |
349 | |||
350 | 67 | Buffer::Buffer(wl_resource* wlResource, Surface* surface) | |
351 | 67 | : QObject(nullptr) | |
352 |
2/4✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
|
134 | , d_ptr(new Private(this, |
353 | 67 | wlResource, | |
354 | 67 | surface, | |
355 |
3/6✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 67 times.
✗ Branch 5 not taken.
|
67 | Wayland::Display::backendCast(surface->client()->display()))) |
356 | 67 | { | |
357 | 67 | } | |
358 | |||
359 | 9 | Buffer::Buffer(wl_resource* wlResource, Display* display) | |
360 | 9 | : QObject(nullptr) | |
361 |
3/6✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
|
9 | , d_ptr(new Private(this, wlResource, nullptr, Wayland::Display::backendCast(display))) |
362 | 9 | { | |
363 | 9 | } | |
364 | |||
365 | 9 | std::shared_ptr<Buffer> Buffer::get(Display* display, wl_resource* resource) | |
366 | { | ||
367 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (!resource) { |
368 | ✗ | return nullptr; | |
369 | } | ||
370 | // TODO(unknown author): verify resource is a buffer | ||
371 | 9 | auto buffer = Wayland::Display::backendCast(display)->bufferManager()->fromResource(resource); | |
372 |
2/6✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
|
9 | return buffer ? buffer.value() : make(resource, display); |
373 | 9 | } | |
374 | |||
375 | 152 | Buffer::~Buffer() | |
376 | 152 | { | |
377 |
4/4✓ Branch 0 taken 64 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 62 times.
|
76 | if (d_ptr->committed && d_ptr->resource) { |
378 |
1/2✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
|
62 | wl_buffer_send_release(d_ptr->resource); |
379 |
2/4✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
62 | wl_client_flush(wl_resource_get_client(d_ptr->resource)); |
380 | 62 | } | |
381 | 152 | } | |
382 | |||
383 | 1182 | std::optional<ShmImage> Buffer::shmImage() | |
384 | { | ||
385 | 1182 | return ShmImage::get(this); | |
386 | } | ||
387 | |||
388 | ✗ | Surface* Buffer::surface() const | |
389 | { | ||
390 | ✗ | return d_ptr->surface; | |
391 | } | ||
392 | |||
393 | 3 | wl_shm_buffer* Buffer::shmBuffer() | |
394 | { | ||
395 | 3 | return d_ptr->shmBuffer; | |
396 | } | ||
397 | |||
398 | ✗ | linux_dmabuf_buffer_v1* Buffer::linuxDmabufBuffer() | |
399 | { | ||
400 | ✗ | return d_ptr->dmabufBuffer; | |
401 | } | ||
402 | |||
403 | 36 | wl_resource* Buffer::resource() const | |
404 | { | ||
405 | 36 | return d_ptr->resource; | |
406 | } | ||
407 | |||
408 | 1652 | QSize Buffer::size() const | |
409 | { | ||
410 | 1652 | return d_ptr->size; | |
411 | } | ||
412 | |||
413 | ✗ | bool Buffer::hasAlphaChannel() const | |
414 | { | ||
415 | ✗ | return d_ptr->alpha; | |
416 | } | ||
417 | |||
418 | 66 | void Buffer::setCommitted() | |
419 | { | ||
420 | 66 | d_ptr->committed = true; | |
421 | 66 | } | |
422 | |||
423 | } | ||
424 |