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

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