Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
linuxdmabufclientbufferintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5#include "linuxdmabuf.h"
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
9#include <QtWaylandCompositor/private/qwltextureorphanage_p.h>
10#include <qpa/qplatformnativeinterface.h>
11#include <QtOpenGL/QOpenGLTexture>
12#include <QtCore/QVarLengthArray>
13#include <QtGui/QGuiApplication>
14#include <QtGui/QOpenGLContext>
15
16#include <EGL/egl.h>
17#include <EGL/eglext.h>
18#include <unistd.h>
19#include <drm_fourcc.h>
20
22
24 switch (format) {
25 case DRM_FORMAT_RGB332:
26 case DRM_FORMAT_BGR233:
27 case DRM_FORMAT_XRGB4444:
28 case DRM_FORMAT_XBGR4444:
29 case DRM_FORMAT_RGBX4444:
30 case DRM_FORMAT_BGRX4444:
31 case DRM_FORMAT_XRGB1555:
32 case DRM_FORMAT_XBGR1555:
33 case DRM_FORMAT_RGBX5551:
34 case DRM_FORMAT_BGRX5551:
36 case DRM_FORMAT_BGR565:
39 case DRM_FORMAT_XRGB8888:
40 case DRM_FORMAT_XBGR8888:
41 case DRM_FORMAT_RGBX8888:
42 case DRM_FORMAT_BGRX8888:
43 case DRM_FORMAT_XRGB2101010:
44 case DRM_FORMAT_XBGR2101010:
45 case DRM_FORMAT_RGBX1010102:
46 case DRM_FORMAT_BGRX1010102:
48 case DRM_FORMAT_ARGB4444:
49 case DRM_FORMAT_ABGR4444:
50 case DRM_FORMAT_RGBA4444:
51 case DRM_FORMAT_BGRA4444:
52 case DRM_FORMAT_ARGB1555:
53 case DRM_FORMAT_ABGR1555:
54 case DRM_FORMAT_RGBA5551:
55 case DRM_FORMAT_BGRA5551:
56 case DRM_FORMAT_ARGB8888:
59 case DRM_FORMAT_BGRA8888:
60 case DRM_FORMAT_ARGB2101010:
61 case DRM_FORMAT_ABGR2101010:
62 case DRM_FORMAT_RGBA1010102:
65 case DRM_FORMAT_YUYV:
67 default:
68 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Buffer format" << Qt::hex << format << "not supported";
70 }
71}
72
83
84// Initialize the EGLImage for a dmabuf buffer which conceptually consists of a
85// single plane. Note that depending on the modifiers, the buffer may be actually
86// transported as multiple dmabuf planes which must be combined into a single
87// EGLImage. For formats where the buffer needs to be represented as multiple
88// EGLImages (e.g., various YUV formats) a different approach is required.
89bool LinuxDmabufClientBufferIntegration::initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer)
90{
91 bool success = true;
92
93 // Resolving GL functions may need a context current, so do it only here.
95 gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
96
97 if (dmabufBuffer->plane(0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) {
98 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported.";
99 success = false;
100 }
101
102 // 6 entries for the common attribs plus 10 per possible plane, plus 1 for
103 // the final EGL_NONE sentinel.
104 QVarLengthArray<EGLint, 6 + 10 * 4 + 1> attribs;
105
106 attribs.append(EGL_WIDTH);
107 attribs.append(dmabufBuffer->size().width());
108 attribs.append(EGL_HEIGHT);
109 attribs.append(dmabufBuffer->size().height());
110 attribs.append(EGL_LINUX_DRM_FOURCC_EXT);
111 attribs.append(EGLint(dmabufBuffer->drmFormat()));
112
113#define ADD_PLANE_ATTRIBS(plane_idx) { \
114 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT); \
115 attribs.append(dmabufBuffer->plane(plane_idx).fd); \
116 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT); \
117 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).offset)); \
118 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT); \
119 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).stride)); \
120 if (dmabufBuffer->plane(plane_idx).modifiers != DRM_FORMAT_MOD_INVALID) { \
121 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT); \
122 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).modifiers & 0xffffffff)); \
123 attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT); \
124 attribs.append(EGLint(dmabufBuffer->plane(plane_idx).modifiers >> 32)); \
125 } \
126}
127
128 switch (dmabufBuffer->planesNumber()) {
129 case 4:
132 case 3:
135 case 2:
138 case 1:
140 break;
141 default:
142 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses invalid number of planes:" << dmabufBuffer->planesNumber();
143 return false;
144 }
145
146 attribs.append(EGL_NONE);
147
148 // note: EGLImageKHR does NOT take ownership of the file descriptors
149 EGLImageKHR image = egl_create_image(m_eglDisplay,
150 EGL_NO_CONTEXT,
151 EGL_LINUX_DMA_BUF_EXT,
152 (EGLClientBuffer) nullptr,
153 attribs.constData());
154
155 if (image == EGL_NO_IMAGE_KHR) {
156 qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image from" <<
157 dmabufBuffer->planesNumber() << "plane(s)";
158 success = false;
159 }
160
161 dmabufBuffer->initImage(0, image);
162
163 return success;
164}
165
166bool LinuxDmabufClientBufferIntegration::initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer)
167{
168 bool success = true;
169
170 const YuvFormatConversion conversion = m_yuvFormats.value(dmabufBuffer->drmFormat());
171 if (conversion.inputPlanes != dmabufBuffer->planesNumber()) {
172 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer for this format must provide" << conversion.inputPlanes
173 << "planes but only" << dmabufBuffer->planesNumber() << "received";
174 return false;
175 }
176
177 // Resolving GL functions may need a context current, so do it only here.
179 gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
180
181
182 if (dmabufBuffer->plane(0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) {
183 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported.";
184 success = false;
185 }
186
187 for (uint32_t i = 0; i < conversion.outputPlanes; ++i) {
188 const YuvPlaneConversion plane = conversion.plane[i];
189
190 QVarLengthArray<EGLint, 17> attribs = {
191 EGL_WIDTH, dmabufBuffer->size().width() / plane.widthDivisor,
192 EGL_HEIGHT, dmabufBuffer->size().height() / plane.heightDivisor,
193 EGL_LINUX_DRM_FOURCC_EXT, plane.format,
194 EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(plane.planeIndex).fd,
195 EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).offset),
196 EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).stride),
197 EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).modifiers & 0xffffffff),
198 EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).modifiers >> 32),
199 EGL_NONE
200 };
201
202 // note: EGLImageKHR does NOT take ownership of the file descriptors
203 EGLImageKHR image = egl_create_image(m_eglDisplay,
204 EGL_NO_CONTEXT,
205 EGL_LINUX_DMA_BUF_EXT,
206 (EGLClientBuffer) nullptr,
207 attribs.constData());
208
209 if (image == EGL_NO_IMAGE_KHR) {
210 qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i;
211 success = false;
212 }
213
214 dmabufBuffer->initImage(i, image);
215 }
216 return success;
217}
218
220{
221 YuvPlaneConversion firstPlane;
222 firstPlane.format = DRM_FORMAT_GR88;
223 firstPlane.widthDivisor = 1;
224 firstPlane.heightDivisor = 1;
225 firstPlane.planeIndex = 0;
226
227 YuvPlaneConversion secondPlane;
228 secondPlane.format = DRM_FORMAT_ARGB8888;
229 secondPlane.widthDivisor = 2;
230 secondPlane.heightDivisor = 1;
231 secondPlane.planeIndex = 0;
232
233 YuvFormatConversion formatConversion;
234 formatConversion.inputPlanes = 1;
235 formatConversion.outputPlanes = 2;
236 formatConversion.plane[0] = firstPlane;
237 formatConversion.plane[1] = secondPlane;
238
239 m_yuvFormats.insert(DRM_FORMAT_YUYV, formatConversion);
240}
241
243{
244 m_importedBuffers.clear();
245
246 if (egl_unbind_wayland_display != nullptr && m_displayBound) {
247 Q_ASSERT(m_wlDisplay != nullptr);
248 if (!egl_unbind_wayland_display(m_eglDisplay, m_wlDisplay))
249 qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed";
250 }
251}
252
254{
255 m_linuxDmabuf.reset(new LinuxDmabuf(display, this));
256
257 const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty() && qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").toInt() != 0;
258
259 // initialize hardware extensions
260 egl_query_dmabuf_modifiers_ext = reinterpret_cast<PFNEGLQUERYDMABUFMODIFIERSEXTPROC>(eglGetProcAddress("eglQueryDmaBufModifiersEXT"));
261 egl_query_dmabuf_formats_ext = reinterpret_cast<PFNEGLQUERYDMABUFFORMATSEXTPROC>(eglGetProcAddress("eglQueryDmaBufFormatsEXT"));
262 if (!egl_query_dmabuf_modifiers_ext || !egl_query_dmabuf_formats_ext) {
263 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglQueryDmaBufModifiersEXT and eglQueryDmaBufFormatsEXT.";
264 return;
265 }
266
267 egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
268 egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
269 if ((!egl_bind_wayland_display || !egl_unbind_wayland_display) && !ignoreBindDisplay) {
270 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.";
271 return;
272 }
273
274 egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
275 egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
276 if (!egl_create_image || !egl_destroy_image) {
277 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR.";
278 return;
279 }
280
281 // initialize EGL display
283 if (!nativeInterface) {
284 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. No native platform interface available.";
285 return;
286 }
287
288 m_eglDisplay = nativeInterface->nativeResourceForIntegration("EglDisplay");
289 if (!m_eglDisplay) {
290 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not get EglDisplay for window.";
291 return;
292 }
293
294 const char *extensionString = eglQueryString(m_eglDisplay, EGL_EXTENSIONS);
295 if (!extensionString || !strstr(extensionString, "EGL_EXT_image_dma_buf_import")) {
296 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. There is no EGL_EXT_image_dma_buf_import extension.";
297 return;
298 }
299 if (strstr(extensionString, "EGL_EXT_image_dma_buf_import_modifiers"))
300 m_supportsDmabufModifiers = true;
301
302 if (egl_bind_wayland_display && egl_unbind_wayland_display) {
303 m_displayBound = egl_bind_wayland_display(m_eglDisplay, display);
304 if (!m_displayBound)
305 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration.";
306 m_wlDisplay = display;
307 }
308
309 // request and sent formats/modifiers only after egl_display is bound
310 QHash<uint32_t, QList<uint64_t>> modifiers;
311 for (const auto &format : supportedDrmFormats()) {
312 modifiers[format] = supportedDrmModifiers(format);
313 }
314 m_linuxDmabuf->setSupportedModifiers(modifiers);
315}
316
317QList<uint32_t> LinuxDmabufClientBufferIntegration::supportedDrmFormats()
318{
319 if (!egl_query_dmabuf_formats_ext)
320 return QList<uint32_t>();
321
322 // request total number of formats
323 EGLint count = 0;
324 EGLBoolean success = egl_query_dmabuf_formats_ext(m_eglDisplay, 0, nullptr, &count);
325
326 if (success && count > 0) {
327 QList<uint32_t> drmFormats(count);
328 if (egl_query_dmabuf_formats_ext(m_eglDisplay, count, (EGLint *) drmFormats.data(), &count))
329 return drmFormats;
330 }
331
332 return QList<uint32_t>();
333}
334
335QList<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32_t format)
336{
337 if (!egl_query_dmabuf_modifiers_ext)
338 return QList<uint64_t>();
339
340 // request total number of formats
341 EGLint count = 0;
342 EGLBoolean success = egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, 0, nullptr, nullptr, &count);
343
344 if (success && count > 0) {
345 QList<uint64_t> modifiers(count);
346 if (egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, count, modifiers.data(), nullptr, &count)) {
347 return modifiers;
348 }
349 }
350
351 return QList<uint64_t>();
352}
353
355{
356 egl_destroy_image(m_eglDisplay, image);
357}
358
360{
361 auto it = m_importedBuffers.find(resource);
362 if (it != m_importedBuffers.end()) {
363 m_importedBuffers.value(resource);
364 return new LinuxDmabufClientBuffer(this, it.value()->resource()->handle, m_importedBuffers.value(resource));
365 }
366
367 return nullptr;
368}
369
370bool LinuxDmabufClientBufferIntegration::importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer)
371{
372 if (m_importedBuffers.contains(resource)) {
373 qCWarning(qLcWaylandCompositorHardwareIntegration) << "buffer has already been added";
374 return false;
375 }
376 m_importedBuffers[resource] = linuxDmabufBuffer;
377 if (m_yuvFormats.contains(linuxDmabufBuffer->drmFormat()))
378 return initYuvTexture(linuxDmabufBuffer);
379 else
380 return initSimpleTexture(linuxDmabufBuffer);
381}
382
384{
385 m_importedBuffers.remove(resource);
386}
387
388LinuxDmabufClientBuffer::LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration *integration,
389 wl_resource *bufferResource,
390 LinuxDmabufWlBuffer *dmabufBuffer)
391 : ClientBuffer(bufferResource)
392 , m_integration(integration)
393{
394 d = dmabufBuffer;
395}
396
398{
399 // At this point we should have a valid OpenGL context, so it's safe to destroy textures
401
402 if (!m_buffer)
403 return nullptr;
404
405 QOpenGLTexture *texture = d->texture(plane);
406
407 const auto target = static_cast<QOpenGLTexture::Target>(GL_TEXTURE_2D);
408
409 if (!texture) {
412 texture->setSize(d->size().width(), d->size().height());
413 texture->create();
414 d->initTexture(plane, texture);
415 }
416
417 if (m_textureDirty) {
418 m_textureDirty = false;
419 texture->bind();
420 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
421 m_integration->gl_egl_image_target_texture_2d(target, d->image(plane));
422 }
423 return texture;
424}
425
427{
428 m_integration->removeBuffer(m_buffer);
429 ClientBuffer::setDestroyed();
430}
431
433{
434 // resources are deleted by buffer_destroy_resource
435 m_buffer = nullptr;
436 d = nullptr;
437}
438
443
445{
446 return d->size();
447}
448
450{
451 return (d->flags() & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) ? QWaylandSurface::OriginBottomLeft : QWaylandSurface::OriginTopLeft;
452}
453
QtWayland::ClientBuffer * createBufferFor(wl_resource *resource) override
bool importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer)
void initializeHardware(struct ::wl_display *display) override
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d
QOpenGLTexture * toOpenGlTexture(int plane) override
QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const override
QWaylandSurface::Origin origin() const override
uint32_t flags() const
QSize size() const
void initTexture(uint32_t plane, QOpenGLTexture *texture)
uint32_t drmFormat() const
QOpenGLTexture * texture(uint32_t plane) const
EGLImageKHR image(uint32_t plane)
void setSupportedModifiers(const QHash< uint32_t, QList< uint64_t > > &modifiers)
static QPlatformNativeInterface * platformNativeInterface()
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1291
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtGui
TextureFormat
This enum defines the possible texture formats.
Target
This enum defines the texture target of a QOpenGLTexture object.
The QPlatformNativeInterface class provides an abstraction for retrieving native resource handles.
virtual void * nativeResourceForIntegration(const QByteArray &resource)
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
Origin
This enum type is used to specify the origin of a QWaylandSurface's buffer.
struct::wl_resource * m_buffer
static QWaylandTextureOrphanage * instance()
EGLImageKHR int int EGLuint64KHR * modifiers
QSet< QString >::iterator it
struct wl_display * display
Definition linuxdmabuf.h:41
#define DRM_FORMAT_MOD_INVALID
Definition linuxdmabuf.h:36
#define ADD_PLANE_ATTRIBS(plane_idx)
static QT_BEGIN_NAMESPACE QWaylandBufferRef::BufferFormatEgl formatFromDrmFormat(EGLint format)
static QOpenGLTexture::TextureFormat openGLFormatFromBufferFormat(QWaylandBufferRef::BufferFormatEgl format)
QT_BEGIN_NAMESPACE typedef EGLBoolean(EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat)(EGLDisplay dpy
EGLint EGLint EGLuint64KHR * modifiers
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
Definition image.cpp:4
#define Q_FALLTHROUGH()
#define DRM_FORMAT_BGR888
#define DRM_FORMAT_RGB565
#define DRM_FORMAT_RGB888
#define DRM_FORMAT_ABGR8888
#define DRM_FORMAT_GR88
#define DRM_FORMAT_BGRA1010102
#define DRM_FORMAT_RGBA8888
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum GLenum GLsizei count
GLenum target
GLenum GLuint texture
GLint GLsizei GLsizei GLenum format
const GLint * attribs
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
struct YuvPlaneConversion plane[LinuxDmabufWlBuffer::MaxDmabufPlanes]