| 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. */ | 
| 73 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); | 
| 74 |  | 
| 75 | #ifndef EGL_WL_bind_wayland_display | 
| 76 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display); | 
| 77 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display); | 
| 78 | #endif | 
| 79 |  | 
| 80 | #ifndef EGL_KHR_image | 
| 81 | typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); | 
| 82 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); | 
| 83 | #endif | 
| 84 |  | 
| 85 | #ifndef GL_OES_EGL_image | 
| 86 | typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); | 
| 87 | typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); | 
| 88 | #endif | 
| 89 |  | 
| 90 | QT_BEGIN_NAMESPACE | 
| 91 |  | 
| 92 | static const char * | 
| 93 | egl_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 |  | 
| 118 | struct 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 |  | 
| 142 | class WaylandEglClientBufferIntegrationPrivate | 
| 143 | { | 
| 144 | public: | 
| 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 |  | 
| 178 | bool WaylandEglClientBufferIntegrationPrivate::shuttingDown = false; | 
| 179 |  | 
| 180 | WaylandEglClientBufferIntegrationPrivate::WaylandEglClientBufferIntegrationPrivate() | 
| 181 | { | 
| 182 | } | 
| 183 |  | 
| 184 | void 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 |  | 
| 192 | void 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 |  | 
| 266 | bool 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 |  | 
| 289 | void 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 |  | 
| 332 | bool 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 |  | 
| 384 | void 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 |  | 
| 413 | WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration() | 
| 414 |     : d_ptr(new WaylandEglClientBufferIntegrationPrivate) | 
| 415 | { | 
| 416 | } | 
| 417 |  | 
| 418 | WaylandEglClientBufferIntegration::~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 |  | 
| 429 | void 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 |  | 
| 490 | QtWayland::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 |  | 
| 500 | WaylandEglClientBuffer::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 |  | 
| 517 | WaylandEglClientBuffer::~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 |  | 
| 554 | static 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 |  | 
| 573 | static 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 |  | 
| 584 | QWaylandBufferRef::BufferFormatEgl WaylandEglClientBuffer::bufferFormatEgl() const | 
| 585 | { | 
| 586 |     return formatFromEglFormat(format: d->egl_format); | 
| 587 | } | 
| 588 |  | 
| 589 | QOpenGLTexture *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 |  | 
| 625 | void 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 |  | 
| 634 | bool WaylandEglClientBuffer::isProtected() | 
| 635 | { | 
| 636 |     if (m_integration && m_buffer) | 
| 637 |         return m_integration->isProtected(buffer: m_buffer); | 
| 638 |  | 
| 639 |     return false; | 
| 640 | } | 
| 641 |  | 
| 642 |  | 
| 643 | QWaylandSurface::Origin WaylandEglClientBuffer::origin() const | 
| 644 | { | 
| 645 |     return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft; | 
| 646 | } | 
| 647 |  | 
| 648 | quintptr 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 |  | 
| 661 | void 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 |  | 
| 672 | QSize WaylandEglClientBuffer::size() const | 
| 673 | { | 
| 674 |     return d->size; | 
| 675 | } | 
| 676 |  | 
| 677 | QT_END_NAMESPACE | 
| 678 |  |