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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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