From baada964fa554bd06d6f8ce2d1afd88902ea73b5 Mon Sep 17 00:00:00 2001 From: Thomas Senyk Date: Thu, 3 Nov 2022 08:19:58 +0100 Subject: QOpenGLContext re-creation - orphanedTextures The texture clean-up in the HW-integration has two issues when QOpenGLContext is re-created. 1. texture going out-of-date (QOpenGLContext::aboutToBeDestroyed) and still being used/returned to be used 2. QOpenGLContext dies (QOpenGLContext::aboutToBeDestroyed) while an "discarded" QOpenGLTexture (an orphan) isn't deleted yet. (you can't delete a texture past it's ctx's QOpenGLContext::aboutToBeDestroyed) This patch fixes both issues with a helper and a lambda on 3 HW-integration-backends: wayland-egl, wayland-eglstream and linux-dmabuf Fix for 1.: Simple connection to a lambda that deletes the texture and removes them from the set of used textures. Signal is QOpenGLContext::aboutToBeDestroyed Fix for 2.: A function in each backend: deleteSpecificOrphanedTexture(QOpenGLTexture *texture) Also connected/called by QOpenGLContext::aboutToBeDestroyed Deletes the texture (before deleteOrphanedTextures() does it too late) and removes the dead pointer from the orphanage Pick-to: 6.5 Change-Id: Iccce8845bb669df93f1be43cbe9b9d25f7fd5235 Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../linux-dmabuf-unstable-v1/linuxdmabuf.cpp | 39 +++++- .../linux-dmabuf-unstable-v1/linuxdmabuf.h | 6 + .../linuxdmabufclientbufferintegration.cpp | 56 ++++++++ .../linuxdmabufclientbufferintegration.h | 8 +- .../waylandeglclientbufferintegration.cpp | 149 ++++++++++++++++++++- .../waylandeglstreamintegration.cpp | 136 ++++++++++++++++++- 6 files changed, 381 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp index f509809e..c0949698 100644 --- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp @@ -257,10 +257,16 @@ LinuxDmabufWlBuffer::~LinuxDmabufWlBuffer() void LinuxDmabufWlBuffer::buffer_destroy(Resource *resource) { Q_UNUSED(resource); + + QMutexLocker locker(&m_texturesLock); + for (uint32_t i = 0; i < m_planesNumber; ++i) { if (m_textures[i] != nullptr) { - m_clientBufferIntegration->deleteGLTextureWhenPossible(m_textures[i]); + m_clientBufferIntegration->deleteGLTextureWhenPossible(m_textures[i], m_texturesContext[i]); m_textures[i] = nullptr; + m_texturesContext[i] = nullptr; + QObject::disconnect(m_texturesAboutToBeDestroyedConnection[i]); + m_texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection(); } if (m_eglImages[i] != EGL_NO_IMAGE_KHR) { m_clientBufferIntegration->deleteImage(m_eglImages[i]); @@ -282,9 +288,40 @@ void LinuxDmabufWlBuffer::initImage(uint32_t plane, EGLImageKHR image) void LinuxDmabufWlBuffer::initTexture(uint32_t plane, QOpenGLTexture *texture) { + QMutexLocker locker(&m_texturesLock); + Q_ASSERT(plane < m_planesNumber); Q_ASSERT(m_textures.at(plane) == nullptr); + Q_ASSERT(QOpenGLContext::currentContext()); m_textures[plane] = texture; + m_texturesContext[plane] = QOpenGLContext::currentContext(); + + m_texturesAboutToBeDestroyedConnection[plane] = + QObject::connect(m_texturesContext[plane], &QOpenGLContext::aboutToBeDestroyed, + m_texturesContext[plane], [this, plane]() { + + QMutexLocker locker(&this->m_texturesLock); + + // See above lock - there is a chance that this has already been removed from m_textures[plane]! + // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed! + if (this->m_textures[plane] == nullptr) + return; + + delete this->m_textures[plane]; + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!" + << "Pointer (now dead) was:" << (void*)(this->m_textures[plane]) + << " Associated context (about to die too) is: " << (void*)(this->m_texturesContext[plane]); + + this->m_textures[plane] = nullptr; + this->m_texturesContext[plane] = nullptr; + + QObject::disconnect(this->m_texturesAboutToBeDestroyedConnection[plane]); + this->m_texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection(); + + }, Qt::DirectConnection); } void LinuxDmabufWlBuffer::buffer_destroy_resource(Resource *resource) diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h index 208e4400..dba4e398 100644 --- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include @@ -123,6 +125,10 @@ private: LinuxDmabufClientBufferIntegration *m_clientBufferIntegration = nullptr; std::array m_eglImages = { {EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR} }; std::array m_textures = { {nullptr, nullptr, nullptr, nullptr} }; + std::array m_texturesContext = { {nullptr, nullptr, nullptr, nullptr} }; + std::array m_texturesAboutToBeDestroyedConnection = { {QMetaObject::Connection(), QMetaObject::Connection(), QMetaObject::Connection(), QMetaObject::Connection()} }; + QMutex m_texturesLock; + void freeResources(); void buffer_destroy(Resource *resource) override; diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp index af613f2e..960ec461 100644 --- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp @@ -350,13 +350,69 @@ QList LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32 return QList(); } + +void LinuxDmabufClientBufferIntegration::deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx) { + QMutexLocker locker(&m_orphanedTexturesLock); + + Q_ASSERT(m_orphanedTextures.size() == m_orphanedTexturesAboutToBeDestroyedConnection.size()); + + m_orphanedTextures << texture; + m_orphanedTexturesAboutToBeDestroyedConnection << QObject::connect(ctx, &QOpenGLContext::aboutToBeDestroyed, + ctx, [this, texture]() { + this->deleteSpecificOrphanedTexture(texture); + }, Qt::DirectConnection); +} + + void LinuxDmabufClientBufferIntegration::deleteOrphanedTextures() { Q_ASSERT(QOpenGLContext::currentContext()); + + QMutexLocker locker(&m_orphanedTexturesLock); + + if (!m_orphanedTextures.isEmpty()) + qCDebug(qLcWaylandCompositorHardwareIntegration) << "About to delete some textures: " + << m_orphanedTextures; + qDeleteAll(m_orphanedTextures); + + for (QMetaObject::Connection con : m_orphanedTexturesAboutToBeDestroyedConnection) + QObject::disconnect(con); + + m_orphanedTexturesAboutToBeDestroyedConnection.clear(); m_orphanedTextures.clear(); } +void LinuxDmabufClientBufferIntegration::deleteSpecificOrphanedTexture(QOpenGLTexture *texture) +{ + Q_ASSERT(m_orphanedTextures.size() == m_orphanedTexturesAboutToBeDestroyedConnection.size()); + + QMutexLocker locker(&m_orphanedTexturesLock); + + // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function! + if (m_orphanedTextures.length()==0) { + qCWarning(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "Looks like deleteOrphanedTextures() and this function where called simultaneously!" + << "This might cause issues!"; + return; + } + + int i = m_orphanedTextures.indexOf(texture); + Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture + + m_orphanedTextures.removeAt(i); + QMetaObject::Connection con = m_orphanedTexturesAboutToBeDestroyedConnection.takeAt(i); + + QObject::disconnect(con); + delete texture; + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!" + << "Pointer (now dead) was:" << (void*)texture; +} + void LinuxDmabufClientBufferIntegration::deleteImage(EGLImageKHR image) { egl_destroy_image(m_eglDisplay, image); diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h index 24c4eeea..3749d88e 100644 --- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -46,8 +47,9 @@ public: bool importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer); void removeBuffer(wl_resource *resource); void deleteOrphanedTextures(); + void deleteSpecificOrphanedTexture(QOpenGLTexture *texture); void deleteImage(EGLImageKHR image); - void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { m_orphanedTextures << texture; } + void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext* ctx); PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d = nullptr; private: @@ -68,7 +70,11 @@ private: EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; ::wl_display *m_wlDisplay = nullptr; bool m_displayBound = false; + + QMutex m_orphanedTexturesLock; QList m_orphanedTextures; + QList m_orphanedTexturesAboutToBeDestroyedConnection; + QHash m_yuvFormats; bool m_supportsDmabufModifiers = false; QHash m_importedBuffers; diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp index 0c52c23f..6852c595 100644 --- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp +++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp @@ -126,7 +126,11 @@ struct BufferState EGLint egl_format = EGL_TEXTURE_RGBA; QVarLengthArray 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; @@ -145,15 +149,20 @@ public: bool initEglStream(WaylandEglClientBuffer *buffer, struct ::wl_resource *bufferHandle); void handleEglstreamTexture(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle); void registerBuffer(struct ::wl_resource *buffer, BufferState state); - void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; } + void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx); void deleteOrphanedTextures(); + void deleteSpecificOrphanedTexture(QOpenGLTexture *texture); EGLDisplay egl_display = EGL_NO_DISPLAY; bool display_bound = false; ::wl_display *wlDisplay = nullptr; QOffscreenSurface *offscreenSurface = nullptr; QOpenGLContext *localContext = nullptr; + + QMutex orphanedTexturesLock; QList orphanedTextures; + QList orphanedTexturesAboutToBeDestroyedConnection; + PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr; PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr; @@ -254,6 +263,8 @@ void WaylandEglClientBufferIntegrationPrivate::initEglTexture(WaylandEglClientBu } state.egl_images << image; + + QMutexLocker locker(&state.texturesLock); state.textures[i] = nullptr; } } @@ -280,6 +291,50 @@ bool WaylandEglClientBufferIntegrationPrivate::ensureContext() return localContextNeeded; } + +void setupWaylandEglClientBufferWithTextureContextAndAboutToBeDestroyedConnection(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; @@ -313,7 +368,11 @@ bool WaylandEglClientBufferIntegrationPrivate::initEglStream(WaylandEglClientBuf auto texture = new QOpenGLTexture(static_cast(GL_TEXTURE_EXTERNAL_OES)); texture->create(); - state.textures[0] = texture; // TODO: support multiple planes for the streaming case + setupWaylandEglClientBufferWithTextureContextAndAboutToBeDestroyedConnection(buffer->d, texture, 0); + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << " NEW texture! It's pointer and ctx pointer: " + << (void*)state.textures[0] << "; " << (void*)state.texturesContext[0]; texture->bind(); @@ -357,13 +416,74 @@ void WaylandEglClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEgl localContext->doneCurrent(); } +void WaylandEglClientBufferIntegrationPrivate::deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx) { + QMutexLocker locker(&orphanedTexturesLock); + + Q_ASSERT(ctx != nullptr); + Q_ASSERT(orphanedTextures.size() == orphanedTexturesAboutToBeDestroyedConnection.size()); + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO << " got texture and ctx to be deleted!" + << (void*)texture << "; " << (void*)ctx; + + orphanedTextures << texture; + orphanedTexturesAboutToBeDestroyedConnection << QObject::connect(ctx, &QOpenGLContext::aboutToBeDestroyed, + ctx, [this, texture]() { + this->deleteSpecificOrphanedTexture(texture); + }, Qt::DirectConnection); +} + void WaylandEglClientBufferIntegrationPrivate::deleteOrphanedTextures() { Q_ASSERT(QOpenGLContext::currentContext()); + + QMutexLocker locker(&orphanedTexturesLock); + + for (int i=0; i < orphanedTextures.size(); i++) { + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO << " about to delete a texture: " + << (void*)orphanedTextures[i]; + } + qDeleteAll(orphanedTextures); + + for (QMetaObject::Connection con : orphanedTexturesAboutToBeDestroyedConnection) + QObject::disconnect(con); + + orphanedTexturesAboutToBeDestroyedConnection.clear(); orphanedTextures.clear(); } +void WaylandEglClientBufferIntegrationPrivate::deleteSpecificOrphanedTexture(QOpenGLTexture *texture) +{ + Q_ASSERT(orphanedTextures.size() == orphanedTexturesAboutToBeDestroyedConnection.size()); + + QMutexLocker locker(&orphanedTexturesLock); + + // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function! + if (orphanedTextures.length()==0) { + qCWarning(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "Looks like deleteOrphanedTextures() and this function where called simultaneously!" + << "This might cause issues!"; + return; + } + + int i = orphanedTextures.indexOf(texture); + Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture + + orphanedTextures.removeAt(i); + QMetaObject::Connection con = orphanedTexturesAboutToBeDestroyedConnection.takeAt(i); + + QObject::disconnect(con); + delete texture; + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!" + << "Pointer (now dead) was:" << (void*)texture; +} + WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration() : d_ptr(new WaylandEglClientBufferIntegrationPrivate) { @@ -479,9 +599,26 @@ 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(); + + p->deleteGLTextureWhenPossible(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; } @@ -540,7 +677,7 @@ 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; + setupWaylandEglClientBufferWithTextureContextAndAboutToBeDestroyedConnection(this->d, texture, plane); } if (m_textureDirty) { diff --git a/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp index f43bfb09..2e2b2829 100644 --- a/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp +++ b/src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -109,7 +110,11 @@ struct BufferState BufferState() = default; EGLint egl_format = EGL_TEXTURE_EXTERNAL_WL; - 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 = false; @@ -124,15 +129,19 @@ public: bool ensureContext(); bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle); void handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer); - void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; } + void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx); void deleteOrphanedTextures(); + void deleteSpecificOrphanedTexture(QOpenGLTexture *texture); EGLDisplay egl_display = EGL_NO_DISPLAY; bool display_bound = false; ::wl_display *wlDisplay = nullptr; QOffscreenSurface *offscreenSurface = nullptr; QOpenGLContext *localContext = nullptr; + + QMutex orphanedTexturesLock; QList orphanedTextures; + QList orphanedTexturesAboutToBeDestroyedConnection; WaylandEglStreamController *eglStreamController = nullptr; @@ -150,13 +159,71 @@ public: bool WaylandEglStreamClientBufferIntegrationPrivate::shuttingDown = false; + +void WaylandEglStreamClientBufferIntegrationPrivate::deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx) { + + QMutexLocker locker(&orphanedTexturesLock); + + Q_ASSERT(orphanedTextures.size() == orphanedTexturesAboutToBeDestroyedConnection.size()); + + orphanedTextures << texture; + orphanedTexturesAboutToBeDestroyedConnection << QObject::connect(ctx, &QOpenGLContext::aboutToBeDestroyed, + ctx, [this, texture]() { + this->deleteSpecificOrphanedTexture(texture); + }, Qt::DirectConnection); +} + void WaylandEglStreamClientBufferIntegrationPrivate::deleteOrphanedTextures() { Q_ASSERT(QOpenGLContext::currentContext()); + + QMutexLocker locker(&orphanedTexturesLock); + + for (int i=0; i < orphanedTextures.size(); i++) { + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO << " about to delete a texture: " + << (void*)orphanedTextures[i]; + } + qDeleteAll(orphanedTextures); + + for (QMetaObject::Connection con : orphanedTexturesAboutToBeDestroyedConnection) + QObject::disconnect(con); + + orphanedTexturesAboutToBeDestroyedConnection.clear(); orphanedTextures.clear(); } +void WaylandEglStreamClientBufferIntegrationPrivate::deleteSpecificOrphanedTexture(QOpenGLTexture *texture) +{ + Q_ASSERT(orphanedTextures.size() == orphanedTexturesAboutToBeDestroyedConnection.size()); + + QMutexLocker locker(&orphanedTexturesLock); + + // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function! + if (orphanedTextures.length()==0) { + qCWarning(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "Looks like deleteOrphanedTextures() and this function where called simultaneously!" + << "This might cause issues!"; + return; + } + + int i = orphanedTextures.indexOf(texture); + Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture + + orphanedTextures.removeAt(i); + QMetaObject::Connection con = orphanedTexturesAboutToBeDestroyedConnection.takeAt(i); + + QObject::disconnect(con); + delete texture; + + qCDebug(qLcWaylandCompositorHardwareIntegration) + << Q_FUNC_INFO + << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!" + << "Pointer (now dead) was:" << (void*)texture; +} + bool WaylandEglStreamClientBufferIntegrationPrivate::ensureContext() { bool localContextNeeded = false; @@ -180,6 +247,49 @@ bool WaylandEglStreamClientBufferIntegrationPrivate::ensureContext() } +void setupWaylandEglClientBufferWithTextureContextAndAboutToBeDestroyedConnection(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 + << "(eglstream) 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 WaylandEglStreamClientBufferIntegrationPrivate::initEglStream(WaylandEglStreamClientBuffer *buffer, wl_resource *bufferHandle) { BufferState &state = *buffer->d; @@ -210,7 +320,7 @@ bool WaylandEglStreamClientBufferIntegrationPrivate::initEglStream(WaylandEglStr auto texture = new QOpenGLTexture(static_cast(GL_TEXTURE_EXTERNAL_OES)); texture->create(); - state.textures[0] = texture; // TODO: support multiple planes + setupWaylandEglClientBufferWithTextureContextAndAboutToBeDestroyedConnection(buffer->d, texture, 0); texture->bind(); @@ -360,9 +470,25 @@ WaylandEglStreamClientBuffer::~WaylandEglStreamClientBuffer() 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(); + + p->deleteGLTextureWhenPossible(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; } -- cgit v1.2.1