1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "linuxdmabufclientbufferintegration.h"
5#include "linuxdmabuf.h"
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
9#include <qpa/qplatformnativeinterface.h>
10#include <QtOpenGL/QOpenGLTexture>
11#include <QtCore/QVarLengthArray>
12#include <QtGui/QGuiApplication>
13#include <QtGui/QOpenGLContext>
14
15#include <EGL/egl.h>
16#include <EGL/eglext.h>
17#include <unistd.h>
18#include <drm_fourcc.h>
19
20QT_BEGIN_NAMESPACE
21
22static QWaylandBufferRef::BufferFormatEgl formatFromDrmFormat(EGLint format) {
23 switch (format) {
24 case DRM_FORMAT_RGB332:
25 case DRM_FORMAT_BGR233:
26 case DRM_FORMAT_XRGB4444:
27 case DRM_FORMAT_XBGR4444:
28 case DRM_FORMAT_RGBX4444:
29 case DRM_FORMAT_BGRX4444:
30 case DRM_FORMAT_XRGB1555:
31 case DRM_FORMAT_XBGR1555:
32 case DRM_FORMAT_RGBX5551:
33 case DRM_FORMAT_BGRX5551:
34 case DRM_FORMAT_RGB565:
35 case DRM_FORMAT_BGR565:
36 case DRM_FORMAT_RGB888:
37 case DRM_FORMAT_BGR888:
38 case DRM_FORMAT_XRGB8888:
39 case DRM_FORMAT_XBGR8888:
40 case DRM_FORMAT_RGBX8888:
41 case DRM_FORMAT_BGRX8888:
42 case DRM_FORMAT_XRGB2101010:
43 case DRM_FORMAT_XBGR2101010:
44 case DRM_FORMAT_RGBX1010102:
45 case DRM_FORMAT_BGRX1010102:
46 return QWaylandBufferRef::BufferFormatEgl_RGB;
47 case DRM_FORMAT_ARGB4444:
48 case DRM_FORMAT_ABGR4444:
49 case DRM_FORMAT_RGBA4444:
50 case DRM_FORMAT_BGRA4444:
51 case DRM_FORMAT_ARGB1555:
52 case DRM_FORMAT_ABGR1555:
53 case DRM_FORMAT_RGBA5551:
54 case DRM_FORMAT_BGRA5551:
55 case DRM_FORMAT_ARGB8888:
56 case DRM_FORMAT_ABGR8888:
57 case DRM_FORMAT_RGBA8888:
58 case DRM_FORMAT_BGRA8888:
59 case DRM_FORMAT_ARGB2101010:
60 case DRM_FORMAT_ABGR2101010:
61 case DRM_FORMAT_RGBA1010102:
62 case DRM_FORMAT_BGRA1010102:
63 return QWaylandBufferRef::BufferFormatEgl_RGBA;
64 case DRM_FORMAT_YUYV:
65 return QWaylandBufferRef::BufferFormatEgl_Y_XUXV;
66 default:
67 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Buffer format" << Qt::hex << format << "not supported";
68 return QWaylandBufferRef::BufferFormatEgl_Null;
69 }
70}
71
72static QOpenGLTexture::TextureFormat openGLFormatFromBufferFormat(QWaylandBufferRef::BufferFormatEgl format) {
73 switch (format) {
74 case QWaylandBufferRef::BufferFormatEgl_RGB:
75 return QOpenGLTexture::RGBFormat;
76 case QWaylandBufferRef::BufferFormatEgl_RGBA:
77 return QOpenGLTexture::RGBAFormat;
78 default:
79 return QOpenGLTexture::NoFormat;
80 }
81}
82
83// Initialize the EGLImage for a dmabuf buffer which conceptually consists of a
84// single plane. Note that depending on the modifiers, the buffer may be actually
85// transported as multiple dmabuf planes which must be combined into a single
86// EGLImage. For formats where the buffer needs to be represented as multiple
87// EGLImages (e.g., various YUV formats) a different approach is required.
88bool LinuxDmabufClientBufferIntegration::initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer)
89{
90 bool success = true;
91
92 // Resolving GL functions may need a context current, so do it only here.
93 if (!gl_egl_image_target_texture_2d)
94 gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES"));
95
96 if (dmabufBuffer->plane(index: 0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) {
97 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported.";
98 success = false;
99 }
100
101 // 6 entries for the common attribs plus 10 per possible plane, plus 1 for
102 // the final EGL_NONE sentinel.
103 QVarLengthArray<EGLint, 6 + 10 * 4 + 1> attribs;
104
105 attribs.append(EGL_WIDTH);
106 attribs.append(t: dmabufBuffer->size().width());
107 attribs.append(EGL_HEIGHT);
108 attribs.append(t: dmabufBuffer->size().height());
109 attribs.append(EGL_LINUX_DRM_FOURCC_EXT);
110 attribs.append(t: EGLint(dmabufBuffer->drmFormat()));
111
112#define ADD_PLANE_ATTRIBS(plane_idx) { \
113 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT); \
114 attribs.append(dmabufBuffer->plane(plane_idx).fd); \
115 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT); \
116 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).offset)); \
117 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT); \
118 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).stride)); \
119 if (dmabufBuffer->plane(plane_idx).modifiers != DRM_FORMAT_MOD_INVALID) { \
120 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT); \
121 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).modifiers & 0xffffffff)); \
122 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT); \
123 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).modifiers >> 32)); \
124 } \
125}
126
127 switch (dmabufBuffer->planesNumber()) {
128 case 4:
129 ADD_PLANE_ATTRIBS(3);
130 Q_FALLTHROUGH();
131 case 3:
132 ADD_PLANE_ATTRIBS(2);
133 Q_FALLTHROUGH();
134 case 2:
135 ADD_PLANE_ATTRIBS(1);
136 Q_FALLTHROUGH();
137 case 1:
138 ADD_PLANE_ATTRIBS(0);
139 break;
140 default:
141 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses invalid number of planes:" << dmabufBuffer->planesNumber();
142 return false;
143 }
144
145 attribs.append(EGL_NONE);
146
147 // note: EGLImageKHR does NOT take ownership of the file descriptors
148 EGLImageKHR image = egl_create_image(m_eglDisplay,
149 EGL_NO_CONTEXT,
150 EGL_LINUX_DMA_BUF_EXT,
151 (EGLClientBuffer) nullptr,
152 attribs.constData());
153
154 if (image == EGL_NO_IMAGE_KHR) {
155 qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image from" <<
156 dmabufBuffer->planesNumber() << "plane(s)";
157 success = false;
158 }
159
160 dmabufBuffer->initImage(plane: 0, image);
161
162 return success;
163}
164
165bool LinuxDmabufClientBufferIntegration::initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer)
166{
167 bool success = true;
168
169 const YuvFormatConversion conversion = m_yuvFormats.value(key: dmabufBuffer->drmFormat());
170 if (conversion.inputPlanes != dmabufBuffer->planesNumber()) {
171 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer for this format must provide" << conversion.inputPlanes
172 << "planes but only" << dmabufBuffer->planesNumber() << "received";
173 return false;
174 }
175
176 // Resolving GL functions may need a context current, so do it only here.
177 if (!gl_egl_image_target_texture_2d)
178 gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES"));
179
180
181 if (dmabufBuffer->plane(index: 0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) {
182 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported.";
183 success = false;
184 }
185
186 for (uint32_t i = 0; i < conversion.outputPlanes; ++i) {
187 const YuvPlaneConversion plane = conversion.plane[i];
188
189 QVarLengthArray<EGLint, 17> attribs = {
190 EGL_WIDTH, dmabufBuffer->size().width() / plane.widthDivisor,
191 EGL_HEIGHT, dmabufBuffer->size().height() / plane.heightDivisor,
192 EGL_LINUX_DRM_FOURCC_EXT, plane.format,
193 EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(index: plane.planeIndex).fd,
194 EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).offset),
195 EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).stride),
196 EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).modifiers & 0xffffffff),
197 EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).modifiers >> 32),
198 EGL_NONE
199 };
200
201 // note: EGLImageKHR does NOT take ownership of the file descriptors
202 EGLImageKHR image = egl_create_image(m_eglDisplay,
203 EGL_NO_CONTEXT,
204 EGL_LINUX_DMA_BUF_EXT,
205 (EGLClientBuffer) nullptr,
206 attribs.constData());
207
208 if (image == EGL_NO_IMAGE_KHR) {
209 qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i;
210 success = false;
211 }
212
213 dmabufBuffer->initImage(plane: i, image);
214 }
215 return success;
216}
217
218LinuxDmabufClientBufferIntegration::LinuxDmabufClientBufferIntegration()
219{
220 YuvPlaneConversion firstPlane;
221 firstPlane.format = DRM_FORMAT_GR88;
222 firstPlane.widthDivisor = 1;
223 firstPlane.heightDivisor = 1;
224 firstPlane.planeIndex = 0;
225
226 YuvPlaneConversion secondPlane;
227 secondPlane.format = DRM_FORMAT_ARGB8888;
228 secondPlane.widthDivisor = 2;
229 secondPlane.heightDivisor = 1;
230 secondPlane.planeIndex = 0;
231
232 YuvFormatConversion formatConversion;
233 formatConversion.inputPlanes = 1;
234 formatConversion.outputPlanes = 2;
235 formatConversion.plane[0] = firstPlane;
236 formatConversion.plane[1] = secondPlane;
237
238 m_yuvFormats.insert(DRM_FORMAT_YUYV, value: formatConversion);
239}
240
241LinuxDmabufClientBufferIntegration::~LinuxDmabufClientBufferIntegration()
242{
243 m_importedBuffers.clear();
244
245 if (egl_unbind_wayland_display != nullptr && m_displayBound) {
246 Q_ASSERT(m_wlDisplay != nullptr);
247 if (!egl_unbind_wayland_display(m_eglDisplay, m_wlDisplay))
248 qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed";
249 }
250}
251
252void LinuxDmabufClientBufferIntegration::initializeHardware(struct ::wl_display *display)
253{
254 m_linuxDmabuf.reset(other: new LinuxDmabuf(display, this));
255
256 const bool ignoreBindDisplay = !qgetenv(varName: "QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty() && qgetenv(varName: "QT_WAYLAND_IGNORE_BIND_DISPLAY").toInt() != 0;
257
258 // initialize hardware extensions
259 egl_query_dmabuf_modifiers_ext = reinterpret_cast<PFNEGLQUERYDMABUFMODIFIERSEXTPROC>(eglGetProcAddress(procname: "eglQueryDmaBufModifiersEXT"));
260 egl_query_dmabuf_formats_ext = reinterpret_cast<PFNEGLQUERYDMABUFFORMATSEXTPROC>(eglGetProcAddress(procname: "eglQueryDmaBufFormatsEXT"));
261 if (!egl_query_dmabuf_modifiers_ext || !egl_query_dmabuf_formats_ext) {
262 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglQueryDmaBufModifiersEXT and eglQueryDmaBufFormatsEXT.";
263 return;
264 }
265
266 egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglBindWaylandDisplayWL"));
267 egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglUnbindWaylandDisplayWL"));
268 if ((!egl_bind_wayland_display || !egl_unbind_wayland_display) && !ignoreBindDisplay) {
269 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.";
270 return;
271 }
272
273 egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress(procname: "eglCreateImageKHR"));
274 egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress(procname: "eglDestroyImageKHR"));
275 if (!egl_create_image || !egl_destroy_image) {
276 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR.";
277 return;
278 }
279
280 // initialize EGL display
281 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
282 if (!nativeInterface) {
283 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. No native platform interface available.";
284 return;
285 }
286
287 m_eglDisplay = nativeInterface->nativeResourceForIntegration(resource: "EglDisplay");
288 if (!m_eglDisplay) {
289 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not get EglDisplay for window.";
290 return;
291 }
292
293 const char *extensionString = eglQueryString(dpy: m_eglDisplay, EGL_EXTENSIONS);
294 if (!extensionString || !strstr(haystack: extensionString, needle: "EGL_EXT_image_dma_buf_import")) {
295 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. There is no EGL_EXT_image_dma_buf_import extension.";
296 return;
297 }
298 if (strstr(haystack: extensionString, needle: "EGL_EXT_image_dma_buf_import_modifiers"))
299 m_supportsDmabufModifiers = true;
300
301 if (egl_bind_wayland_display && egl_unbind_wayland_display) {
302 m_displayBound = egl_bind_wayland_display(m_eglDisplay, display);
303 if (!m_displayBound)
304 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration.";
305 m_wlDisplay = display;
306 }
307
308 // request and sent formats/modifiers only after egl_display is bound
309 QHash<uint32_t, QList<uint64_t>> modifiers;
310 for (const auto &format : supportedDrmFormats()) {
311 modifiers[format] = supportedDrmModifiers(format);
312 }
313 m_linuxDmabuf->setSupportedModifiers(modifiers);
314}
315
316QList<uint32_t> LinuxDmabufClientBufferIntegration::supportedDrmFormats()
317{
318 if (!egl_query_dmabuf_formats_ext)
319 return QList<uint32_t>();
320
321 // request total number of formats
322 EGLint count = 0;
323 EGLBoolean success = egl_query_dmabuf_formats_ext(m_eglDisplay, 0, nullptr, &count);
324
325 if (success && count > 0) {
326 QList<uint32_t> drmFormats(count);
327 if (egl_query_dmabuf_formats_ext(m_eglDisplay, count, (EGLint *) drmFormats.data(), &count))
328 return drmFormats;
329 }
330
331 return QList<uint32_t>();
332}
333
334QList<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32_t format)
335{
336 if (!egl_query_dmabuf_modifiers_ext)
337 return QList<uint64_t>();
338
339 // request total number of formats
340 EGLint count = 0;
341 EGLBoolean success = egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, 0, nullptr, nullptr, &count);
342
343 if (success && count > 0) {
344 QList<uint64_t> modifiers(count);
345 if (egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, count, modifiers.data(), nullptr, &count)) {
346 return modifiers;
347 }
348 }
349
350 return QList<uint64_t>();
351}
352
353
354void LinuxDmabufClientBufferIntegration::deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx) {
355 QMutexLocker locker(&m_orphanedTexturesLock);
356
357 Q_ASSERT(m_orphanedTextures.size() == m_orphanedTexturesAboutToBeDestroyedConnection.size());
358
359 m_orphanedTextures << texture;
360 m_orphanedTexturesAboutToBeDestroyedConnection << QObject::connect(sender: ctx, signal: &QOpenGLContext::aboutToBeDestroyed,
361 context: ctx, slot: [this, texture]() {
362 this->deleteSpecificOrphanedTexture(texture);
363 }, type: Qt::DirectConnection);
364}
365
366
367void LinuxDmabufClientBufferIntegration::deleteOrphanedTextures()
368{
369 Q_ASSERT(QOpenGLContext::currentContext());
370
371 QMutexLocker locker(&m_orphanedTexturesLock);
372
373 if (!m_orphanedTextures.isEmpty())
374 qCDebug(qLcWaylandCompositorHardwareIntegration) << "About to delete some textures: "
375 << m_orphanedTextures;
376
377 qDeleteAll(c: m_orphanedTextures);
378
379 for (QMetaObject::Connection con : m_orphanedTexturesAboutToBeDestroyedConnection)
380 QObject::disconnect(con);
381
382 m_orphanedTexturesAboutToBeDestroyedConnection.clear();
383 m_orphanedTextures.clear();
384}
385
386void LinuxDmabufClientBufferIntegration::deleteSpecificOrphanedTexture(QOpenGLTexture *texture)
387{
388 Q_ASSERT(m_orphanedTextures.size() == m_orphanedTexturesAboutToBeDestroyedConnection.size());
389
390 QMutexLocker locker(&m_orphanedTexturesLock);
391
392 // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function!
393 if (m_orphanedTextures.length()==0) {
394 qCWarning(qLcWaylandCompositorHardwareIntegration)
395 << Q_FUNC_INFO
396 << "Looks like deleteOrphanedTextures() and this function where called simultaneously!"
397 << "This might cause issues!";
398 return;
399 }
400
401 int i = m_orphanedTextures.indexOf(t: texture);
402 Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture
403
404 m_orphanedTextures.removeAt(i);
405 QMetaObject::Connection con = m_orphanedTexturesAboutToBeDestroyedConnection.takeAt(i);
406
407 QObject::disconnect(con);
408 delete texture;
409
410 qCDebug(qLcWaylandCompositorHardwareIntegration)
411 << Q_FUNC_INFO
412 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
413 << "Pointer (now dead) was:" << (void*)texture;
414}
415
416void LinuxDmabufClientBufferIntegration::deleteImage(EGLImageKHR image)
417{
418 egl_destroy_image(m_eglDisplay, image);
419}
420
421QtWayland::ClientBuffer *LinuxDmabufClientBufferIntegration::createBufferFor(wl_resource *resource)
422{
423 auto it = m_importedBuffers.find(key: resource);
424 if (it != m_importedBuffers.end()) {
425 m_importedBuffers.value(key: resource);
426 return new LinuxDmabufClientBuffer(this, it.value()->resource()->handle, m_importedBuffers.value(key: resource));
427 }
428
429 return nullptr;
430}
431
432bool LinuxDmabufClientBufferIntegration::importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer)
433{
434 if (m_importedBuffers.contains(key: resource)) {
435 qCWarning(qLcWaylandCompositorHardwareIntegration) << "buffer has already been added";
436 return false;
437 }
438 m_importedBuffers[resource] = linuxDmabufBuffer;
439 if (m_yuvFormats.contains(key: linuxDmabufBuffer->drmFormat()))
440 return initYuvTexture(dmabufBuffer: linuxDmabufBuffer);
441 else
442 return initSimpleTexture(dmabufBuffer: linuxDmabufBuffer);
443}
444
445void LinuxDmabufClientBufferIntegration::removeBuffer(wl_resource *resource)
446{
447 m_importedBuffers.remove(key: resource);
448}
449
450LinuxDmabufClientBuffer::LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration *integration,
451 wl_resource *bufferResource,
452 LinuxDmabufWlBuffer *dmabufBuffer)
453 : ClientBuffer(bufferResource)
454 , m_integration(integration)
455{
456 d = dmabufBuffer;
457}
458
459QOpenGLTexture *LinuxDmabufClientBuffer::toOpenGlTexture(int plane)
460{
461 // At this point we should have a valid OpenGL context, so it's safe to destroy textures
462 m_integration->deleteOrphanedTextures();
463
464 if (!m_buffer)
465 return nullptr;
466
467 QOpenGLTexture *texture = d->texture(plane);
468
469 const auto target = static_cast<QOpenGLTexture::Target>(GL_TEXTURE_2D);
470
471 if (!texture) {
472 texture = new QOpenGLTexture(target);
473 texture->setFormat(openGLFormatFromBufferFormat(format: formatFromDrmFormat(format: d->drmFormat())));
474 texture->setSize(width: d->size().width(), height: d->size().height());
475 texture->create();
476 d->initTexture(plane, texture);
477 }
478
479 if (m_textureDirty) {
480 m_textureDirty = false;
481 texture->bind();
482 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
483 m_integration->gl_egl_image_target_texture_2d(target, d->image(plane));
484 }
485 return texture;
486}
487
488void LinuxDmabufClientBuffer::setDestroyed()
489{
490 m_integration->removeBuffer(resource: m_buffer);
491 ClientBuffer::setDestroyed();
492}
493
494LinuxDmabufClientBuffer::~LinuxDmabufClientBuffer()
495{
496 // resources are deleted by buffer_destroy_resource
497 m_buffer = nullptr;
498 d = nullptr;
499}
500
501QWaylandBufferRef::BufferFormatEgl LinuxDmabufClientBuffer::bufferFormatEgl() const
502{
503 return formatFromDrmFormat(format: d->drmFormat());
504}
505
506QSize LinuxDmabufClientBuffer::size() const
507{
508 return d->size();
509}
510
511QWaylandSurface::Origin LinuxDmabufClientBuffer::origin() const
512{
513 return (d->flags() & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) ? QWaylandSurface::OriginBottomLeft : QWaylandSurface::OriginTopLeft;
514}
515
516QT_END_NAMESPACE
517

source code of qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp