Skip to content

Commit 29529c7

Browse files
committed
Fix texture deletion without a GL context (issue #229)
Texture objects are usually destroyed (e.g. when a source is removed) while no OpenGL context is current, so calling glDeleteTextures() in ~Texture() was unsafe: depending on the driver it leaked the texture or could delete an unrelated one in whatever context happened to be current. Defer the deletion instead. ~Texture() now queues the id, and the renderer frees all queued ids via Texture::deleteOrphanedTextures() at the start of painting, when the shared GL context is guaranteed to be current. Affects all texture sources (Video, Image, Syphon).
1 parent 01ccd18 commit 29529c7

3 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/core/Source.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ namespace mmp {
3232

3333
UidAllocator Source::allocator;
3434

35+
QVector<GLuint> Texture::_orphanedTextures;
36+
QMutex Texture::_orphanedTexturesMutex;
37+
38+
void Texture::orphanTexture(GLuint id)
39+
{
40+
QMutexLocker locker(&_orphanedTexturesMutex);
41+
_orphanedTextures.append(id);
42+
}
43+
44+
void Texture::deleteOrphanedTextures()
45+
{
46+
QMutexLocker locker(&_orphanedTexturesMutex);
47+
if (_orphanedTextures.isEmpty())
48+
return;
49+
glDeleteTextures((GLsizei) _orphanedTextures.size(), _orphanedTextures.constData());
50+
_orphanedTextures.clear();
51+
}
52+
3553
void Texture::update()
3654
{
3755
if (textureId == 0)

src/core/Source.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <QColor>
2929
#include <QElapsedTimer>
3030
#include <QMutex>
31+
#include <QVector>
3132

3233
#if __APPLE__
3334
#include <OpenGL/gl.h>
@@ -169,12 +170,22 @@ class Texture : public Source
169170

170171
public:
171172
virtual ~Texture() {
172-
// TODO: this needs to be fixed: it will not work unless it is executed from within a GL context
173-
// see issue #229
173+
// A Texture is usually destroyed (e.g. when its source is removed) when no
174+
// GL context is current, so we cannot call glDeleteTextures here (issue
175+
// #229). Instead the id is queued and freed by deleteOrphanedTextures(),
176+
// which the renderer calls while a context is current.
174177
if (textureId != 0)
175-
glDeleteTextures(1, &textureId);
178+
orphanTexture(textureId);
176179
}
177180

181+
/// Frees textures queued by destroyed Texture objects. MUST be called with a
182+
/// current GL context (the renderer calls it at the start of painting).
183+
static void deleteOrphanedTextures();
184+
185+
protected:
186+
/// Queues a texture id for deferred deletion (see issue #229).
187+
static void orphanTexture(GLuint id);
188+
178189
public:
179190
virtual void update();
180191

@@ -220,6 +231,12 @@ class Texture : public Source
220231
protected:
221232
// Lists QProperties that should NOT be parsed automatically.
222233
virtual QList<QString> _propertiesSpecial() const { return Source::_propertiesSpecial() << "x" << "y"; }
234+
235+
private:
236+
// Texture ids whose Texture objects were destroyed without a current GL
237+
// context; freed later by deleteOrphanedTextures(). See issue #229.
238+
static QVector<GLuint> _orphanedTextures;
239+
static QMutex _orphanedTexturesMutex;
223240
};
224241

225242
/**

src/gui/ShapeGraphicsItem.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ void TextureGraphicsItem::_prePaint(QPainter* painter,
264264
Q_UNUSED(option);
265265
painter->beginNativePainting();
266266

267+
// Free any textures orphaned by destroyed sources now that a GL context is
268+
// current (see issue #229).
269+
Texture::deleteOrphanedTextures();
270+
267271
// Project source texture and sent it to destination.
268272
texture->update();
269273

0 commit comments

Comments
 (0)