summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Senyk <thomas.senyk@qt.io>2022-11-03 08:19:58 +0100
committerThomas Senyk <thomas.senyk@qt.io>2023-02-20 17:30:56 +0100
commitbaada964fa554bd06d6f8ce2d1afd88902ea73b5 (patch)
tree5ba0ec856db08a02f2dcf3f32eadd274b6fee764 /src
parentacb68bd3bbbc0001bcf460dd175ff92cc3ce594a (diff)
downloadqtwayland-baada964fa554bd06d6f8ce2d1afd88902ea73b5.tar.gz
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 <eskil.abrahamsen-blomfeldt@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp39
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h6
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp56
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h8
-rw-r--r--src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp149
-rw-r--r--src/hardwareintegration/compositor/wayland-eglstream-controller/waylandeglstreamintegration.cpp136
6 files changed, 381 insertions, 13 deletions
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 <QtCore/QTextStream>
#include <array>
+#include <QtGui/QOpenGLContext>
+#include <QtCore/QMutex>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -123,6 +125,10 @@ private:
LinuxDmabufClientBufferIntegration *m_clientBufferIntegration = nullptr;
std::array<EGLImageKHR, MaxDmabufPlanes> m_eglImages = { {EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR} };
std::array<QOpenGLTexture *, MaxDmabufPlanes> m_textures = { {nullptr, nullptr, nullptr, nullptr} };
+ std::array<QOpenGLContext *, MaxDmabufPlanes> m_texturesContext = { {nullptr, nullptr, nullptr, nullptr} };
+ std::array<QMetaObject::Connection, MaxDmabufPlanes> 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<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32
return QList<uint64_t>();
}
+
+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 <QtWaylandCompositor/private/qwlclientbufferintegration_p.h>
#include <QtWaylandCompositor/private/qwlclientbuffer_p.h>
#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#include <QtCore/QMutex>
#include <drm_fourcc.h>
@@ -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<QOpenGLTexture *> m_orphanedTextures;
+ QList<QMetaObject::Connection> m_orphanedTexturesAboutToBeDestroyedConnection;
+
QHash<EGLint, YuvFormatConversion> m_yuvFormats;
bool m_supportsDmabufModifiers = false;
QHash<struct ::wl_resource *, LinuxDmabufWlBuffer *> 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<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;
@@ -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<QOpenGLTexture *> orphanedTextures;
+ QList<QMetaObject::Connection> 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<QOpenGLTexture::Target>(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 <QtGui/QGuiApplication>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOffscreenSurface>
+#include <QtCore/QMutexLocker>
#include <QtGui/private/qeglstreamconvenience_p.h>
#include <qpa/qplatformnativeinterface.h>
@@ -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<QOpenGLTexture *> orphanedTextures;
+ QList<QMetaObject::Connection> 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<QOpenGLTexture::Target>(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;
}