1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "waylandeglclientbufferintegration_p.h"
5
6#include <QtWaylandCompositor/QWaylandCompositor>
7#include <qpa/qplatformnativeinterface.h>
8#include <QtOpenGL/QOpenGLTexture>
9#include <QtGui/QGuiApplication>
10#include <QtGui/QOpenGLContext>
11#include <QtGui/QOffscreenSurface>
12#include <qpa/qplatformscreen.h>
13#include <QtGui/QWindow>
14#include <QtCore/QPointer>
15#include <QDebug>
16
17#include <QMutex>
18#include <QMutexLocker>
19#include <QVarLengthArray>
20#include <QtCore/private/qcore_unix_p.h>
21#include <QtGui/private/qeglstreamconvenience_p.h>
22
23#ifndef GL_TEXTURE_EXTERNAL_OES
24#define GL_TEXTURE_EXTERNAL_OES 0x8D65
25#endif
26
27#ifndef EGL_WAYLAND_BUFFER_WL
28#define EGL_WAYLAND_BUFFER_WL 0x31D5
29#endif
30
31#ifndef EGL_WAYLAND_EGLSTREAM_WL
32#define EGL_WAYLAND_EGLSTREAM_WL 0x334B
33#endif
34
35#ifndef EGL_WAYLAND_PLANE_WL
36#define EGL_WAYLAND_PLANE_WL 0x31D6
37#endif
38
39#ifndef EGL_WAYLAND_Y_INVERTED_WL
40#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
41#endif
42
43#ifndef EGL_TEXTURE_RGB
44#define EGL_TEXTURE_RGB 0x305D
45#endif
46
47#ifndef EGL_TEXTURE_RGBA
48#define EGL_TEXTURE_RGBA 0x305E
49#endif
50
51#ifndef EGL_TEXTURE_EXTERNAL_WL
52#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
53#endif
54
55#ifndef EGL_TEXTURE_Y_U_V_WL
56#define EGL_TEXTURE_Y_U_V_WL 0x31D7
57#endif
58
59#ifndef EGL_TEXTURE_Y_UV_WL
60#define EGL_TEXTURE_Y_UV_WL 0x31D8
61#endif
62
63#ifndef EGL_TEXTURE_Y_XUXV_WL
64#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
65#endif
66
67#ifndef EGL_PLATFORM_X11_KHR
68#define EGL_PLATFORM_X11_KHR 0x31D5
69#endif
70
71/* Needed for compatibility with Mesa older than 10.0. */
72typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
73
74#ifndef EGL_WL_bind_wayland_display
75typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
76typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
77#endif
78
79#ifndef EGL_KHR_image
80typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
81typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image);
82#endif
83
84#ifndef GL_OES_EGL_image
85typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image);
86typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image);
87#endif
88
89QT_BEGIN_NAMESPACE
90
91static const char *
92egl_error_string(EGLint code)
93{
94#define MYERRCODE(x) case x: return #x;
95 switch (code) {
96 MYERRCODE(EGL_SUCCESS)
97 MYERRCODE(EGL_NOT_INITIALIZED)
98 MYERRCODE(EGL_BAD_ACCESS)
99 MYERRCODE(EGL_BAD_ALLOC)
100 MYERRCODE(EGL_BAD_ATTRIBUTE)
101 MYERRCODE(EGL_BAD_CONTEXT)
102 MYERRCODE(EGL_BAD_CONFIG)
103 MYERRCODE(EGL_BAD_CURRENT_SURFACE)
104 MYERRCODE(EGL_BAD_DISPLAY)
105 MYERRCODE(EGL_BAD_SURFACE)
106 MYERRCODE(EGL_BAD_MATCH)
107 MYERRCODE(EGL_BAD_PARAMETER)
108 MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
109 MYERRCODE(EGL_BAD_NATIVE_WINDOW)
110 MYERRCODE(EGL_CONTEXT_LOST)
111 default:
112 return "unknown";
113 }
114#undef MYERRCODE
115}
116
117struct BufferState
118{
119 BufferState() = default;
120
121 enum EglMode {
122 ModeUninitialized,
123 ModeEGLImage,
124 ModeEGLStream
125 };
126
127 EGLint egl_format = EGL_TEXTURE_RGBA;
128 QVarLengthArray<EGLImageKHR, 3> egl_images;
129 QOpenGLTexture *textures[3] = {nullptr, nullptr, nullptr};
130 QOpenGLContext *texturesContext[3] = {nullptr, nullptr, nullptr};
131 QMetaObject::Connection texturesAboutToBeDestroyedConnection[3] = {QMetaObject::Connection(), QMetaObject::Connection(), QMetaObject::Connection()};
132 QMutex texturesLock;
133
134 EGLStreamKHR egl_stream = EGL_NO_STREAM_KHR;
135
136 bool isYInverted = true;
137 QSize size;
138 EglMode eglMode = ModeUninitialized;
139};
140
141class WaylandEglClientBufferIntegrationPrivate
142{
143public:
144 WaylandEglClientBufferIntegrationPrivate();
145
146 void initBuffer(WaylandEglClientBuffer *buffer);
147 void initEglTexture(WaylandEglClientBuffer *buffer, EGLint format);
148 bool ensureContext();
149 bool initEglStream(WaylandEglClientBuffer *buffer, struct ::wl_resource *bufferHandle);
150 void setupBufferAndCleanup(BufferState *bs, QOpenGLTexture *texture, int plane);
151 void handleEglstreamTexture(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle);
152 void registerBuffer(struct ::wl_resource *buffer, BufferState state);
153 void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx);
154 void deleteOrphanedTextures();
155 void deleteSpecificOrphanedTexture(QOpenGLTexture *texture);
156
157 EGLDisplay egl_display = EGL_NO_DISPLAY;
158 bool display_bound = false;
159 ::wl_display *wlDisplay = nullptr;
160 QOffscreenSurface *offscreenSurface = nullptr;
161 QOpenGLContext *localContext = nullptr;
162
163 QMutex orphanedTexturesLock;
164 QList<QOpenGLTexture *> orphanedTextures;
165 QList<QMetaObject::Connection> orphanedTexturesAboutToBeDestroyedConnection;
166
167
168 PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
169 PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
170 PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer = nullptr;
171
172 PFNEGLCREATEIMAGEKHRPROC egl_create_image = nullptr;
173 PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image = nullptr;
174
175 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d = nullptr;
176
177 QEGLStreamConvenience *funcs = nullptr;
178 static WaylandEglClientBufferIntegrationPrivate *get(WaylandEglClientBufferIntegration *integration) {
179 return shuttingDown ? nullptr : integration->d_ptr.data();
180 }
181
182 static bool shuttingDown;
183};
184
185bool WaylandEglClientBufferIntegrationPrivate::shuttingDown = false;
186
187WaylandEglClientBufferIntegrationPrivate::WaylandEglClientBufferIntegrationPrivate()
188{
189}
190
191void WaylandEglClientBufferIntegrationPrivate::initBuffer(WaylandEglClientBuffer *buffer)
192{
193 EGLint format;
194
195 if (egl_query_wayland_buffer(egl_display, buffer->waylandBufferHandle(), EGL_TEXTURE_FORMAT, &format))
196 initEglTexture(buffer, format);
197}
198
199void WaylandEglClientBufferIntegrationPrivate::initEglTexture(WaylandEglClientBuffer *buffer, EGLint format)
200{
201// Non-streaming case
202
203 // Resolving GL functions may need a context current, so do it only here.
204 if (!gl_egl_image_target_texture_2d)
205 gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES"));
206
207 if (!gl_egl_image_target_texture_2d) {
208 qCWarning(qLcWaylandCompositorHardwareIntegration)
209 << "BindTextureToBuffer() failed. Could not find glEGLImageTargetTexture2DOES.";
210 return;
211 }
212
213 BufferState &state = *buffer->d;
214 state.egl_format = format;
215 state.eglMode = BufferState::ModeEGLImage;
216
217#if defined(EGL_WAYLAND_Y_INVERTED_WL)
218 EGLint isYInverted;
219 EGLBoolean ret = egl_query_wayland_buffer(egl_display, buffer->waylandBufferHandle(), EGL_WAYLAND_Y_INVERTED_WL, &isYInverted);
220 // Yes, this looks strange, but the specification says that EGL_FALSE return
221 // value (not supported) should be treated the same as EGL_TRUE return value
222 // and EGL_TRUE in value.
223 state.isYInverted = (ret == EGL_FALSE || isYInverted == EGL_TRUE);
224#endif
225
226 int planes = 1;
227
228 switch (format) {
229 default:
230 case EGL_TEXTURE_RGB:
231 case EGL_TEXTURE_RGBA:
232 case EGL_TEXTURE_EXTERNAL_WL:
233 planes = 1;
234 break;
235 case EGL_TEXTURE_Y_UV_WL:
236 planes = 2;
237 break;
238 case EGL_TEXTURE_Y_U_V_WL:
239 planes = 3;
240 break;
241 case EGL_TEXTURE_Y_XUXV_WL:
242 planes = 2;
243 break;
244 }
245
246 for (int i = 0; i < planes; i++) {
247 EGLint attribs[5] = { EGL_WAYLAND_PLANE_WL, i, EGL_NONE };
248#ifdef EGL_EXT_protected_content
249 if (buffer->isProtected()) {
250 attribs[2] = EGL_PROTECTED_CONTENT_EXT;
251 attribs[3] = EGL_TRUE;
252 attribs[4] = EGL_NONE;
253 }
254#endif
255 EGLImageKHR image = egl_create_image(egl_display,
256 EGL_NO_CONTEXT,
257 EGL_WAYLAND_BUFFER_WL,
258 buffer->waylandBufferHandle(),
259 attribs);
260
261 if (image == EGL_NO_IMAGE_KHR) {
262 qCWarning(qLcWaylandCompositorHardwareIntegration)
263 << "Failed to create EGL image for plane" << i;
264 }
265
266 state.egl_images << image;
267
268 QMutexLocker locker(&state.texturesLock);
269 state.textures[i] = nullptr;
270 }
271}
272
273bool WaylandEglClientBufferIntegrationPrivate::ensureContext()
274{
275 bool localContextNeeded = false;
276 if (!QOpenGLContext::currentContext()) {
277 if (!localContext && QOpenGLContext::globalShareContext()) {
278 localContext = new QOpenGLContext;
279 localContext->setShareContext(QOpenGLContext::globalShareContext());
280 localContext->create();
281 }
282 if (localContext) {
283 if (!offscreenSurface) {
284 offscreenSurface = new QOffscreenSurface;
285 offscreenSurface->setFormat(localContext->format());
286 offscreenSurface->create();
287 }
288 localContext->makeCurrent(surface: offscreenSurface);
289 localContextNeeded = true;
290 }
291 }
292 return localContextNeeded;
293}
294
295
296void WaylandEglClientBufferIntegrationPrivate::setupBufferAndCleanup(BufferState *bs, QOpenGLTexture *texture, int plane)
297{
298 QMutexLocker locker(&bs->texturesLock);
299
300 bs->textures[plane] = texture;
301 bs->texturesContext[plane] = QOpenGLContext::currentContext();
302
303 Q_ASSERT(bs->texturesContext[plane] != nullptr);
304
305 qCDebug(qLcWaylandCompositorHardwareIntegration)
306 << Q_FUNC_INFO
307 << "(egl) creating a cleanup-lambda for QOpenGLContext::aboutToBeDestroyed!"
308 << ", texture: " << bs->textures[plane]
309 << ", ctx: " << (void*)bs->texturesContext[plane];
310
311 bs->texturesAboutToBeDestroyedConnection[plane] =
312 QObject::connect(sender: bs->texturesContext[plane], signal: &QOpenGLContext::aboutToBeDestroyed,
313 context: bs->texturesContext[plane], slot: [bs, plane]() {
314
315 QMutexLocker locker(&bs->texturesLock);
316
317 // See above lock - there is a chance that this has already been removed from textures[plane]!
318 // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed!
319 if (bs->textures[plane] == nullptr)
320 return;
321
322 delete bs->textures[plane];
323
324 qCDebug(qLcWaylandCompositorHardwareIntegration)
325 << Q_FUNC_INFO
326 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
327 << "Pointer (now dead) was:" << (void*)(bs->textures[plane])
328 << " Associated context (about to die too) is: " << (void*)(bs->texturesContext[plane]);
329
330 bs->textures[plane] = nullptr;
331 bs->texturesContext[plane] = nullptr;
332
333 QObject::disconnect(bs->texturesAboutToBeDestroyedConnection[plane]);
334 bs->texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
335
336 }, type: Qt::DirectConnection);
337}
338
339bool WaylandEglClientBufferIntegrationPrivate::initEglStream(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle)
340{
341 BufferState &state = *buffer->d;
342 state.egl_format = EGL_TEXTURE_EXTERNAL_WL;
343 state.isYInverted = false;
344
345 EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR;
346
347 if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) {
348 state.egl_stream = funcs->create_stream_from_file_descriptor(egl_display, streamFd);
349 close(fd: streamFd);
350 } else {
351 EGLAttrib stream_attribs[] = {
352 EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle,
353 EGL_NONE
354 };
355 state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs);
356 }
357
358 if (state.egl_stream == EGL_NO_STREAM_KHR) {
359 qCWarning(qLcWaylandCompositorHardwareIntegration, "%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
360 return false;
361 }
362 state.eglMode = BufferState::ModeEGLStream;
363
364 if (!QOpenGLContext::currentContext()) {
365 qCWarning(qLcWaylandCompositorHardwareIntegration)
366 << "EglClientBufferIntegration: creating texture with no current context";
367 return false;
368 }
369
370 auto texture = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(GL_TEXTURE_EXTERNAL_OES));
371 texture->create();
372 setupBufferAndCleanup(bs: buffer->d, texture, plane: 0);
373
374 qCDebug(qLcWaylandCompositorHardwareIntegration)
375 << " NEW texture! It's pointer and ctx pointer: "
376 << (void*)state.textures[0] << "; " << (void*)state.texturesContext[0];
377
378 texture->bind();
379
380 auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream);
381 if (!newStream) {
382 EGLint code = eglGetError();
383 qCWarning(qLcWaylandCompositorHardwareIntegration) << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code;
384 funcs->destroy_stream(egl_display, state.egl_stream);
385 state.egl_stream = EGL_NO_STREAM_KHR;
386 return false;
387 }
388 return true;
389}
390
391void WaylandEglClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEglClientBuffer *buffer, struct ::wl_resource *bufferHandle)
392{
393 bool usingLocalContext = ensureContext();
394
395 if (buffer->d->eglMode == BufferState::ModeUninitialized) {
396 bool streamOK = initEglStream(buffer, bufferHandle);
397 if (!streamOK)
398 return;
399 }
400
401 BufferState &state = *buffer->d;
402 auto texture = state.textures[0];
403
404 // EGLStream requires calling acquire on every frame.
405 texture->bind();
406 EGLint stream_state;
407 funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state);
408
409 if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
410 if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE)
411 qCWarning(qLcWaylandCompositorHardwareIntegration,
412 "%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__,
413 eglGetError());
414 }
415
416 if (usingLocalContext)
417 localContext->doneCurrent();
418}
419
420void WaylandEglClientBufferIntegrationPrivate::deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx) {
421 QMutexLocker locker(&orphanedTexturesLock);
422
423 Q_ASSERT(ctx != nullptr);
424 Q_ASSERT(orphanedTextures.size() == orphanedTexturesAboutToBeDestroyedConnection.size());
425
426 qCDebug(qLcWaylandCompositorHardwareIntegration)
427 << Q_FUNC_INFO << " got texture and ctx to be deleted!"
428 << (void*)texture << "; " << (void*)ctx;
429
430 orphanedTextures << texture;
431 orphanedTexturesAboutToBeDestroyedConnection << QObject::connect(sender: ctx, signal: &QOpenGLContext::aboutToBeDestroyed,
432 context: ctx, slot: [this, texture]() {
433 this->deleteSpecificOrphanedTexture(texture);
434 }, type: Qt::DirectConnection);
435}
436
437void WaylandEglClientBufferIntegrationPrivate::deleteOrphanedTextures()
438{
439 Q_ASSERT(QOpenGLContext::currentContext());
440
441 QMutexLocker locker(&orphanedTexturesLock);
442
443 for (int i=0; i < orphanedTextures.size(); i++) {
444 qCDebug(qLcWaylandCompositorHardwareIntegration)
445 << Q_FUNC_INFO << " about to delete a texture: "
446 << (void*)orphanedTextures[i];
447 }
448
449 qDeleteAll(c: orphanedTextures);
450
451 for (QMetaObject::Connection con : orphanedTexturesAboutToBeDestroyedConnection)
452 QObject::disconnect(con);
453
454 orphanedTexturesAboutToBeDestroyedConnection.clear();
455 orphanedTextures.clear();
456}
457
458void WaylandEglClientBufferIntegrationPrivate::deleteSpecificOrphanedTexture(QOpenGLTexture *texture)
459{
460 Q_ASSERT(orphanedTextures.size() == orphanedTexturesAboutToBeDestroyedConnection.size());
461
462 QMutexLocker locker(&orphanedTexturesLock);
463
464 // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function!
465 if (orphanedTextures.length()==0) {
466 qCWarning(qLcWaylandCompositorHardwareIntegration)
467 << Q_FUNC_INFO
468 << "Looks like deleteOrphanedTextures() and this function where called simultaneously!"
469 << "This might cause issues!";
470 return;
471 }
472
473 int i = orphanedTextures.indexOf(t: texture);
474 Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture
475
476 orphanedTextures.removeAt(i);
477 QMetaObject::Connection con = orphanedTexturesAboutToBeDestroyedConnection.takeAt(i);
478
479 QObject::disconnect(con);
480 delete texture;
481
482 qCDebug(qLcWaylandCompositorHardwareIntegration)
483 << Q_FUNC_INFO
484 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
485 << "Pointer (now dead) was:" << (void*)texture;
486}
487
488WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration()
489 : d_ptr(new WaylandEglClientBufferIntegrationPrivate)
490{
491}
492
493WaylandEglClientBufferIntegration::~WaylandEglClientBufferIntegration()
494{
495 WaylandEglClientBufferIntegrationPrivate::shuttingDown = true;
496 Q_D(WaylandEglClientBufferIntegration);
497 if (d->egl_unbind_wayland_display && d->display_bound) {
498 Q_ASSERT(d->wlDisplay);
499 if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay))
500 qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed";
501 }
502}
503
504void WaylandEglClientBufferIntegration::initializeHardware(struct wl_display *display)
505{
506 Q_D(WaylandEglClientBufferIntegration);
507
508 const bool ignoreBindDisplay = !qgetenv(varName: "QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty();
509
510 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
511 if (!nativeInterface) {
512 qCWarning(qLcWaylandCompositorHardwareIntegration)
513 << "Failed to initialize EGL display. No native platform interface available.";
514 return;
515 }
516
517 d->egl_display = nativeInterface->nativeResourceForIntegration(resource: "EglDisplay");
518 if (!d->egl_display) {
519 qCWarning(qLcWaylandCompositorHardwareIntegration)
520 << "Failed to initialize EGL display. Could not get EglDisplay for window.";
521 return;
522 }
523
524 const char *extensionString = eglQueryString(dpy: d->egl_display, EGL_EXTENSIONS);
525 if ((!extensionString || !strstr(haystack: extensionString, needle: "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) {
526 qCWarning(qLcWaylandCompositorHardwareIntegration)
527 << "Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension.";
528 return;
529 }
530
531 d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglBindWaylandDisplayWL"));
532 d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglUnbindWaylandDisplayWL"));
533 if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) {
534 qCWarning(qLcWaylandCompositorHardwareIntegration)
535 << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.";
536 return;
537 }
538
539 d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress(procname: "eglQueryWaylandBufferWL"));
540 if (!d->egl_query_wayland_buffer) {
541 qCWarning(qLcWaylandCompositorHardwareIntegration)
542 << "Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL.";
543 return;
544 }
545
546 d->egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress(procname: "eglCreateImageKHR"));
547 d->egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress(procname: "eglDestroyImageKHR"));
548 if (!d->egl_create_image || !d->egl_destroy_image) {
549 qCWarning(qLcWaylandCompositorHardwareIntegration)
550 << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR.";
551 return;
552 }
553
554 if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) {
555 d->display_bound = d->egl_bind_wayland_display(d->egl_display, display);
556 if (!d->display_bound)
557 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration.";
558 d->wlDisplay = display;
559 }
560
561 d->funcs = new QEGLStreamConvenience;
562 d->funcs->initialize(dpy: d->egl_display);
563}
564
565QtWayland::ClientBuffer *WaylandEglClientBufferIntegration::createBufferFor(wl_resource *buffer)
566{
567 Q_D(WaylandEglClientBufferIntegration);
568 int w = -1;
569 bool q = d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_WIDTH, &w);
570 if (!q || w <= 0)
571 return nullptr;
572 return new WaylandEglClientBuffer(this, buffer);
573}
574
575WaylandEglClientBuffer::WaylandEglClientBuffer(WaylandEglClientBufferIntegration *integration, wl_resource *buffer)
576 : ClientBuffer(buffer)
577 , m_integration(integration)
578{
579 auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration);
580 d = new BufferState;
581 if (buffer && !wl_shm_buffer_get(resource: buffer)) {
582 EGLint width, height;
583 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width);
584 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height);
585 d->size = QSize(width, height);
586
587 p->initBuffer(buffer: this);
588 }
589}
590
591
592WaylandEglClientBuffer::~WaylandEglClientBuffer()
593{
594 auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration);
595
596 if (p) {
597 for (auto image : d->egl_images)
598 p->egl_destroy_image(p->egl_display, image);
599
600 if (d->egl_stream)
601 p->funcs->destroy_stream(p->egl_display, d->egl_stream);
602
603
604 QMutexLocker locker(&d->texturesLock);
605
606 for (int i=0; i<3; i++) {
607 if (d->textures[i] != nullptr) {
608
609 qCDebug(qLcWaylandCompositorHardwareIntegration)
610 << Q_FUNC_INFO << " handing over texture!"
611 << (void*)d->textures[i] << "; " << (void*)d->texturesContext[i]
612 << " ... current context might be the same: " << QOpenGLContext::currentContext();
613
614 p->deleteGLTextureWhenPossible(texture: d->textures[i], ctx: d->texturesContext[i]);
615 d->textures[i] = nullptr; // in case the aboutToBeDestroyed lambda is called while we where here
616 d->texturesContext[i] = nullptr;
617 QObject::disconnect(d->texturesAboutToBeDestroyedConnection[i]);
618 d->texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection();
619 }
620 }
621 }
622
623 delete d;
624}
625
626static QWaylandBufferRef::BufferFormatEgl formatFromEglFormat(EGLint format) {
627 switch (format) {
628 case EGL_TEXTURE_RGB:
629 return QWaylandBufferRef::BufferFormatEgl_RGB;
630 case EGL_TEXTURE_RGBA:
631 return QWaylandBufferRef::BufferFormatEgl_RGBA;
632 case EGL_TEXTURE_EXTERNAL_WL:
633 return QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES;
634 case EGL_TEXTURE_Y_UV_WL:
635 return QWaylandBufferRef::BufferFormatEgl_Y_UV;
636 case EGL_TEXTURE_Y_U_V_WL:
637 return QWaylandBufferRef::BufferFormatEgl_Y_U_V;
638 case EGL_TEXTURE_Y_XUXV_WL:
639 return QWaylandBufferRef::BufferFormatEgl_Y_XUXV;
640 }
641
642 return QWaylandBufferRef::BufferFormatEgl_RGBA;
643}
644
645static QOpenGLTexture::TextureFormat openGLFormatFromEglFormat(EGLint format) {
646 switch (format) {
647 case EGL_TEXTURE_RGB:
648 return QOpenGLTexture::RGBFormat;
649 case EGL_TEXTURE_RGBA:
650 return QOpenGLTexture::RGBAFormat;
651 default:
652 return QOpenGLTexture::NoFormat;
653 }
654}
655
656QWaylandBufferRef::BufferFormatEgl WaylandEglClientBuffer::bufferFormatEgl() const
657{
658 return formatFromEglFormat(format: d->egl_format);
659}
660
661QOpenGLTexture *WaylandEglClientBuffer::toOpenGlTexture(int plane)
662{
663 auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration);
664 // At this point we should have a valid OpenGL context, so it's safe to destroy textures
665 p->deleteOrphanedTextures();
666
667 if (!m_buffer)
668 return nullptr;
669
670 auto texture = d->textures[plane];
671 if (d->eglMode == BufferState::ModeEGLStream)
672 return texture; // EGLStreams texture is maintained by handle_eglstream_texture()
673
674 const auto target = static_cast<QOpenGLTexture::Target>(d->egl_format == EGL_TEXTURE_EXTERNAL_WL ? GL_TEXTURE_EXTERNAL_OES
675 : GL_TEXTURE_2D);
676 if (!texture) {
677 texture = new QOpenGLTexture(target);
678 texture->setFormat(openGLFormatFromEglFormat(format: d->egl_format));
679 texture->setSize(width: d->size.width(), height: d->size.height());
680 texture->create();
681 p->setupBufferAndCleanup(bs: this->d, texture, plane);
682 }
683
684 if (m_textureDirty) {
685 m_textureDirty = false;
686 texture->bind();
687 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
688 p->gl_egl_image_target_texture_2d(target, d->egl_images[plane]);
689#ifdef GL_EXT_protected_textures
690 if (isProtected())
691 glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT, GL_TRUE);
692#endif
693 }
694 return texture;
695}
696
697void WaylandEglClientBuffer::setCommitted(QRegion &damage)
698{
699 ClientBuffer::setCommitted(damage);
700 if (d->eglMode == BufferState::ModeEGLStream || d->eglMode == BufferState::ModeUninitialized) {
701 auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration);
702 p->handleEglstreamTexture(buffer: this, bufferHandle: waylandBufferHandle());
703 }
704}
705
706bool WaylandEglClientBuffer::isProtected()
707{
708 if (m_integration && m_buffer)
709 return m_integration->isProtected(buffer: m_buffer);
710
711 return false;
712}
713
714
715QWaylandSurface::Origin WaylandEglClientBuffer::origin() const
716{
717 return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft;
718}
719
720quintptr WaylandEglClientBuffer::lockNativeBuffer()
721{
722 auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration);
723
724 if (d->egl_stream != EGL_NO_STREAM_KHR)
725 return 0;
726
727 EGLImageKHR image = p->egl_create_image(p->egl_display, EGL_NO_CONTEXT,
728 EGL_WAYLAND_BUFFER_WL,
729 m_buffer, nullptr);
730 return reinterpret_cast<quintptr>(image);
731}
732
733void WaylandEglClientBuffer::unlockNativeBuffer(quintptr native_buffer) const
734{
735 if (!native_buffer)
736 return;
737
738 auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration);
739
740 EGLImageKHR image = reinterpret_cast<EGLImageKHR>(native_buffer);
741 p->egl_destroy_image(p->egl_display, image);
742}
743
744QSize WaylandEglClientBuffer::size() const
745{
746 return d->size;
747}
748
749QT_END_NAMESPACE
750

source code of qtwayland/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp