diff options
Diffstat (limited to 'src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp')
-rw-r--r-- | src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp | 228 |
1 files changed, 143 insertions, 85 deletions
diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp index 88dab2ab2..c8819ba1f 100644 --- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp +++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp @@ -1,49 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandCompositor module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "waylandeglclientbufferintegration.h" +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "waylandeglclientbufferintegration_p.h" #include <QtWaylandCompositor/QWaylandCompositor> +#include <QtWaylandCompositor/private/qwltextureorphanage_p.h> #include <qpa/qplatformnativeinterface.h> +#include <QtOpenGL/QOpenGLTexture> #include <QtGui/QGuiApplication> #include <QtGui/QOpenGLContext> -#include <QtGui/QOpenGLTexture> #include <QtGui/QOffscreenSurface> #include <qpa/qplatformscreen.h> #include <QtGui/QWindow> @@ -52,8 +17,9 @@ #include <QMutex> #include <QMutexLocker> +#include <QVarLengthArray> #include <QtCore/private/qcore_unix_p.h> -#include <QtEglSupport/private/qeglstreamconvenience_p.h> +#include <QtGui/private/qeglstreamconvenience_p.h> #ifndef GL_TEXTURE_EXTERNAL_OES #define GL_TEXTURE_EXTERNAL_OES 0x8D65 @@ -161,7 +127,11 @@ struct BufferState EGLint egl_format = EGL_TEXTURE_RGBA; QVarLengthArray<EGLImageKHR, 3> egl_images; - QOpenGLTexture *textures[3] = {}; + QOpenGLTexture *textures[3] = {nullptr, nullptr, nullptr}; + QOpenGLContext *texturesContext[3] = {nullptr, nullptr, nullptr}; + QMetaObject::Connection texturesAboutToBeDestroyedConnection[3] = {QMetaObject::Connection(), QMetaObject::Connection(), QMetaObject::Connection()}; + QMutex texturesLock; + EGLStreamKHR egl_stream = EGL_NO_STREAM_KHR; bool isYInverted = true; @@ -178,18 +148,15 @@ public: void initEglTexture(WaylandEglClientBuffer *buffer, EGLint format); bool ensureContext(); bool initEglStream(WaylandEglClientBuffer *buffer, struct ::wl_resource *bufferHandle); + void setupBufferAndCleanup(BufferState *bs, QOpenGLTexture *texture, int plane); void handleEglstreamTexture(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle); void registerBuffer(struct ::wl_resource *buffer, BufferState state); - void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; } - void deleteOrphanedTextures(); EGLDisplay egl_display = EGL_NO_DISPLAY; - bool valid = false; bool display_bound = false; ::wl_display *wlDisplay = nullptr; QOffscreenSurface *offscreenSurface = nullptr; QOpenGLContext *localContext = nullptr; - QVector<QOpenGLTexture *> orphanedTextures; PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr; PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr; @@ -231,7 +198,8 @@ void WaylandEglClientBufferIntegrationPrivate::initEglTexture(WaylandEglClientBu gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); if (!gl_egl_image_target_texture_2d) { - qWarning("QtCompositor: bindTextureToBuffer() failed. Could not find glEGLImageTargetTexture2DOES."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "BindTextureToBuffer() failed. Could not find glEGLImageTargetTexture2DOES."; return; } @@ -269,17 +237,28 @@ void WaylandEglClientBufferIntegrationPrivate::initEglTexture(WaylandEglClientBu } for (int i = 0; i < planes; i++) { - const EGLint attribs[] = { EGL_WAYLAND_PLANE_WL, i, EGL_NONE }; + EGLint attribs[5] = { EGL_WAYLAND_PLANE_WL, i, EGL_NONE }; +#ifdef EGL_EXT_protected_content + if (buffer->isProtected()) { + attribs[2] = EGL_PROTECTED_CONTENT_EXT; + attribs[3] = EGL_TRUE; + attribs[4] = EGL_NONE; + } +#endif EGLImageKHR image = egl_create_image(egl_display, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, buffer->waylandBufferHandle(), attribs); - if (image == EGL_NO_IMAGE_KHR) - qWarning("failed to create EGL image for plane %d", i); + if (image == EGL_NO_IMAGE_KHR) { + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to create EGL image for plane" << i; + } state.egl_images << image; + + QMutexLocker locker(&state.texturesLock); state.textures[i] = nullptr; } } @@ -306,6 +285,50 @@ bool WaylandEglClientBufferIntegrationPrivate::ensureContext() return localContextNeeded; } + +void WaylandEglClientBufferIntegrationPrivate::setupBufferAndCleanup(BufferState *bs, QOpenGLTexture *texture, int plane) +{ + QMutexLocker locker(&bs->texturesLock); + + bs->textures[plane] = texture; + bs->texturesContext[plane] = QOpenGLContext::currentContext(); + + Q_ASSERT(bs->texturesContext[plane] != nullptr); + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "(egl) creating a cleanup-lambda for QOpenGLContext::aboutToBeDestroyed!" + << ", texture: " << bs->textures[plane] + << ", ctx: " << (void*)bs->texturesContext[plane]; + + bs->texturesAboutToBeDestroyedConnection[plane] = + QObject::connect(bs->texturesContext[plane], &QOpenGLContext::aboutToBeDestroyed, + bs->texturesContext[plane], [bs, plane]() { + + QMutexLocker locker(&bs->texturesLock); + + // See above lock - there is a chance that this has already been removed from textures[plane]! + // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed! + if (bs->textures[plane] == nullptr) + return; + + delete bs->textures[plane]; + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!" + << "Pointer (now dead) was:" << (void*)(bs->textures[plane]) + << " Associated context (about to die too) is: " << (void*)(bs->texturesContext[plane]); + + bs->textures[plane] = nullptr; + bs->texturesContext[plane] = nullptr; + + QObject::disconnect(bs->texturesAboutToBeDestroyedConnection[plane]); + bs->texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection(); + + }, Qt::DirectConnection); +} + bool WaylandEglClientBufferIntegrationPrivate::initEglStream(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle) { BufferState &state = *buffer->d; @@ -326,26 +349,31 @@ bool WaylandEglClientBufferIntegrationPrivate::initEglStream(WaylandEglClientBuf } if (state.egl_stream == EGL_NO_STREAM_KHR) { - qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); + qCWarning(qLcWaylandCompositorHardwareIntegration, "%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); return false; } state.eglMode = BufferState::ModeEGLStream; if (!QOpenGLContext::currentContext()) { - qWarning("EglClientBufferIntegration: creating texture with no current context"); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "EglClientBufferIntegration: creating texture with no current context"; return false; } auto texture = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(GL_TEXTURE_EXTERNAL_OES)); texture->create(); - state.textures[0] = texture; // TODO: support multiple planes for the streaming case + setupBufferAndCleanup(buffer->d, texture, 0); + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << " NEW texture! It's pointer and ctx pointer: " + << (void*)state.textures[0] << "; " << (void*)state.texturesContext[0]; texture->bind(); auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream); if (!newStream) { EGLint code = eglGetError(); - qWarning() << "Could not initialize EGLStream:" << egl_error_string(code) << hex << (long)code; + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code; funcs->destroy_stream(egl_display, state.egl_stream); state.egl_stream = EGL_NO_STREAM_KHR; return false; @@ -373,20 +401,15 @@ void WaylandEglClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEgl if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) { if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE) - qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError()); + qCWarning(qLcWaylandCompositorHardwareIntegration, + "%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, + eglGetError()); } if (usingLocalContext) localContext->doneCurrent(); } -void WaylandEglClientBufferIntegrationPrivate::deleteOrphanedTextures() -{ - Q_ASSERT(QOpenGLContext::currentContext()); - qDeleteAll(orphanedTextures); - orphanedTextures.clear(); -} - WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration() : d_ptr(new WaylandEglClientBufferIntegrationPrivate) { @@ -399,7 +422,7 @@ WaylandEglClientBufferIntegration::~WaylandEglClientBufferIntegration() if (d->egl_unbind_wayland_display && d->display_bound) { Q_ASSERT(d->wlDisplay); if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay)) - qWarning() << "Qt Wayland Compositor: eglUnbindWaylandDisplayWL failed"; + qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed"; } } @@ -411,64 +434,65 @@ void WaylandEglClientBufferIntegration::initializeHardware(struct wl_display *di QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); if (!nativeInterface) { - qWarning("QtCompositor: Failed to initialize EGL display. No native platform interface available."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize EGL display. No native platform interface available."; return; } d->egl_display = nativeInterface->nativeResourceForIntegration("EglDisplay"); if (!d->egl_display) { - qWarning("QtCompositor: Failed to initialize EGL display. Could not get EglDisplay for window."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize EGL display. Could not get EglDisplay for window."; return; } const char *extensionString = eglQueryString(d->egl_display, EGL_EXTENSIONS); if ((!extensionString || !strstr(extensionString, "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) { - qWarning("QtCompositor: Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension."; return; } d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL")); d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL")); if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) { - qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL."; return; } d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress("eglQueryWaylandBufferWL")); if (!d->egl_query_wayland_buffer) { - qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL."; return; } d->egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); d->egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); if (!d->egl_create_image || !d->egl_destroy_image) { - qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR."); + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR."; return; } if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) { d->display_bound = d->egl_bind_wayland_display(d->egl_display, display); - if (!d->display_bound) { - if (!ignoreBindDisplay) { - qWarning("QtCompositor: Failed to initialize EGL display. Could not bind Wayland display."); - return; - } else { - qWarning("QtCompositor: Could not bind Wayland display. Ignoring."); - } - } + if (!d->display_bound) + qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration."; d->wlDisplay = display; } d->funcs = new QEGLStreamConvenience; d->funcs->initialize(d->egl_display); - - d->valid = true; } QtWayland::ClientBuffer *WaylandEglClientBufferIntegration::createBufferFor(wl_resource *buffer) { - if (wl_shm_buffer_get(buffer)) + Q_D(WaylandEglClientBufferIntegration); + int w = -1; + bool q = d->egl_query_wayland_buffer(d->egl_display, buffer, EGL_WIDTH, &w); + if (!q || w <= 0) return nullptr; return new WaylandEglClientBuffer(this, buffer); } @@ -501,9 +525,29 @@ WaylandEglClientBuffer::~WaylandEglClientBuffer() if (d->egl_stream) p->funcs->destroy_stream(p->egl_display, d->egl_stream); - for (auto *texture : d->textures) - p->deleteGLTextureWhenPossible(texture); } + + { + QMutexLocker locker(&d->texturesLock); + + for (int i=0; i<3; i++) { + if (d->textures[i] != nullptr) { + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO << " handing over texture!" + << (void*)d->textures[i] << "; " << (void*)d->texturesContext[i] + << " ... current context might be the same: " << QOpenGLContext::currentContext(); + + QtWayland::QWaylandTextureOrphanage::instance()->admitTexture( + d->textures[i], d->texturesContext[i]); + d->textures[i] = nullptr; // in case the aboutToBeDestroyed lambda is called while we where here + d->texturesContext[i] = nullptr; + QObject::disconnect(d->texturesAboutToBeDestroyedConnection[i]); + d->texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection(); + } + } + } + delete d; } @@ -546,7 +590,7 @@ QOpenGLTexture *WaylandEglClientBuffer::toOpenGlTexture(int plane) { auto *p = WaylandEglClientBufferIntegrationPrivate::get(m_integration); // At this point we should have a valid OpenGL context, so it's safe to destroy textures - p->deleteOrphanedTextures(); + QtWayland::QWaylandTextureOrphanage::instance()->deleteTextures(); if (!m_buffer) return nullptr; @@ -562,13 +606,18 @@ QOpenGLTexture *WaylandEglClientBuffer::toOpenGlTexture(int plane) texture->setFormat(openGLFormatFromEglFormat(d->egl_format)); texture->setSize(d->size.width(), d->size.height()); texture->create(); - d->textures[plane] = texture; + p->setupBufferAndCleanup(this->d, texture, plane); } if (m_textureDirty) { + m_textureDirty = false; texture->bind(); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); p->gl_egl_image_target_texture_2d(target, d->egl_images[plane]); +#ifdef GL_EXT_protected_textures + if (isProtected()) + glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT, GL_TRUE); +#endif } return texture; } @@ -582,6 +631,15 @@ void WaylandEglClientBuffer::setCommitted(QRegion &damage) } } +bool WaylandEglClientBuffer::isProtected() +{ + if (m_integration && m_buffer) + return m_integration->isProtected(m_buffer); + + return false; +} + + QWaylandSurface::Origin WaylandEglClientBuffer::origin() const { return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft; |