From 2c4ff29bf3072be12e269a0e05cfca010316137a Mon Sep 17 00:00:00 2001
From: Andreas Cord-Landwehr <cordlandwehr@kde.org>
Date: Sat, 24 Oct 2020 17:38:08 +0200
Subject: [PATCH] Destroy textures only when associated GL context is current

Change-Id: I8a847c8edae3a7340f6d7a28113e08cd586874a6
---
 .../linux-dmabuf-unstable-v1/linuxdmabuf.cpp  |  2 +-
 .../linux-dmabuf-unstable-v1/linuxdmabuf.h    |  2 ++
 .../linuxdmabufclientbufferintegration.cpp    | 12 +++++++++--
 .../linuxdmabufclientbufferintegration.h      |  4 ++--
 .../waylandeglclientbufferintegration.cpp     | 20 ++++++++++++++-----
 5 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp
index 28015d35..887e969a 100644
--- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp
@@ -285,7 +285,7 @@ void LinuxDmabufWlBuffer::buffer_destroy(Resource *resource)
     Q_UNUSED(resource);
     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_associatedGLContext);
             m_textures[i] = nullptr;
         }
         if (m_eglImages[i] != EGL_NO_IMAGE_KHR) {
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
index 56a710c3..5e0648a9 100644
--- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
@@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE
 
 class QWaylandCompositor;
 class QWaylandResource;
+class QOpenGLContext;
 class LinuxDmabufParams;
 class LinuxDmabufClientBufferIntegration;
 
@@ -147,6 +148,7 @@ 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} };
+    QOpenGLContext *m_associatedGLContext{ nullptr };
     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 74417586..21327167 100644
--- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
@@ -384,8 +384,16 @@ QVector<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint
 void LinuxDmabufClientBufferIntegration::deleteOrphanedTextures()
 {
     Q_ASSERT(QOpenGLContext::currentContext());
-    qDeleteAll(m_orphanedTextures);
-    m_orphanedTextures.clear();
+    for (auto iter = m_orphanedTextures.begin(); iter != m_orphanedTextures.end(); ++iter) {
+        if (iter->second == QOpenGLContext::currentContext()) {
+            delete iter->first;
+            iter = m_orphanedTextures.erase(iter);
+        }
+    }
+    m_orphanedTextures.erase(std::remove_if(m_orphanedTextures.begin(),
+                                            m_orphanedTextures.end(),
+                                            [](std::pair<QOpenGLTexture *, QOpenGLContext *> texture) { return texture.first == nullptr; }),
+            m_orphanedTextures.end());
 }
 
 void LinuxDmabufClientBufferIntegration::deleteImage(EGLImageKHR image)
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h
index cbbd4d9b..1fe5c629 100644
--- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h
@@ -73,7 +73,7 @@ public:
     void removeBuffer(wl_resource *resource);
     void deleteOrphanedTextures();
     void deleteImage(EGLImageKHR image);
-    void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { m_orphanedTextures << texture; }
+    void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *associatedContext) { m_orphanedTextures << std::make_pair(texture, associatedContext); }
     PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d = nullptr;
 
 private:
@@ -93,7 +93,7 @@ private:
 
     EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
     bool m_displayBound = false;
-    QVector<QOpenGLTexture *> m_orphanedTextures;
+    QVector<std::pair<QOpenGLTexture *, QOpenGLContext *>> m_orphanedTextures;
     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 1a6bf374..6a32cc29 100644
--- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp
+++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp
@@ -151,6 +151,7 @@ struct BufferState
 
     EGLint egl_format = EGL_TEXTURE_RGBA;
     QVarLengthArray<EGLImageKHR, 3> egl_images;
+    QOpenGLContext *associatedGLContext { nullptr };
     QOpenGLTexture *textures[3] = {};
     EGLStreamKHR egl_stream = EGL_NO_STREAM_KHR;
 
@@ -170,7 +171,7 @@ 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 *associatedContext) { m_orphanedTextures << std::make_pair(texture, associatedContext); }
     void deleteOrphanedTextures();
 
     EGLDisplay egl_display = EGL_NO_DISPLAY;
@@ -178,7 +179,7 @@ public:
     ::wl_display *wlDisplay = nullptr;
     QOffscreenSurface *offscreenSurface = nullptr;
     QOpenGLContext *localContext = nullptr;
-    QVector<QOpenGLTexture *> orphanedTextures;
+    QVector<std::pair<QOpenGLTexture *, QOpenGLContext *>> m_orphanedTextures;
 
     PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
     PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
@@ -332,6 +333,7 @@ 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
+    state.associatedGLContext = QOpenGLContext::currentContext();
 
     texture->bind();
 
@@ -378,8 +380,16 @@ void WaylandEglClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEgl
 void WaylandEglClientBufferIntegrationPrivate::deleteOrphanedTextures()
 {
     Q_ASSERT(QOpenGLContext::currentContext());
-    qDeleteAll(orphanedTextures);
-    orphanedTextures.clear();
+    for (auto iter = m_orphanedTextures.begin(); iter != m_orphanedTextures.end(); ++iter) {
+        if (iter->second == QOpenGLContext::currentContext()) {
+            delete iter->first;
+            iter = m_orphanedTextures.erase(iter);
+        }
+    }
+    m_orphanedTextures.erase(std::remove_if(m_orphanedTextures.begin(),
+                                            m_orphanedTextures.end(),
+                                            [](std::pair<QOpenGLTexture *, QOpenGLContext *> texture) { return texture.first == nullptr; }),
+            m_orphanedTextures.end());
 }
 
 WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration()
@@ -502,7 +512,7 @@ WaylandEglClientBuffer::~WaylandEglClientBuffer()
             p->funcs->destroy_stream(p->egl_display, d->egl_stream);
 
         for (auto *texture : d->textures)
-            p->deleteGLTextureWhenPossible(texture);
+            p->deleteGLTextureWhenPossible(texture, d->associatedGLContext);
     }
     delete d;
 }
-- 
2.25.1

