Do not leak textures from the backing stores

Neither the default nor the eglfs-specific backingstore release the OpenGL textures
that are in use when render-to-texture widgets are involved.

The result can be fatal on embedded devices that run out of GPU memory at after showing
and closing dialogs and popups a certain number of times.

Task-number: QTBUG-49363
Task-number: QTBUG-49399
Change-Id: Ia7471b037f147bcca0a4f1db5808ca348e230547
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
This commit is contained in:
Laszlo Agocs 2015-11-12 17:26:21 +01:00
parent fe4ab7edce
commit 5a48d1d164
4 changed files with 51 additions and 11 deletions

View File

@ -65,6 +65,10 @@ public:
~QPlatformBackingStorePrivate()
{
#ifndef QT_NO_OPENGL
QOpenGLContext *ctx = QOpenGLContext::currentContext();
Q_ASSERT(ctx);
if (textureId)
ctx->functions()->glDeleteTextures(1, &textureId);
if (blitter)
blitter->destroy();
delete blitter;

View File

@ -67,6 +67,7 @@ QOpenGLCompositorBackingStore::QOpenGLCompositorBackingStore(QWindow *window)
: QPlatformBackingStore(window),
m_window(window),
m_bsTexture(0),
m_bsTextureContext(0),
m_textures(new QPlatformTextureList),
m_lockedWidgetTextures(0)
{
@ -74,6 +75,14 @@ QOpenGLCompositorBackingStore::QOpenGLCompositorBackingStore(QWindow *window)
QOpenGLCompositorBackingStore::~QOpenGLCompositorBackingStore()
{
if (m_bsTexture) {
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx && m_bsTextureContext && ctx->shareGroup() == m_bsTextureContext->shareGroup())
glDeleteTextures(1, &m_bsTexture);
else
qWarning("QOpenGLCompositorBackingStore: Texture is not valid in the current context");
}
delete m_textures;
}
@ -85,6 +94,8 @@ QPaintDevice *QOpenGLCompositorBackingStore::paintDevice()
void QOpenGLCompositorBackingStore::updateTexture()
{
if (!m_bsTexture) {
m_bsTextureContext = QOpenGLContext::currentContext();
Q_ASSERT(m_bsTextureContext);
glGenTextures(1, &m_bsTexture);
glBindTexture(GL_TEXTURE_2D, m_bsTexture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

View File

@ -83,6 +83,7 @@ private:
QImage m_image;
QRegion m_dirty;
uint m_bsTexture;
QOpenGLContext *m_bsTextureContext;
QPlatformTextureList *m_textures;
QPlatformTextureList *m_lockedWidgetTextures;
};

View File

@ -72,6 +72,7 @@
#include <QtGui/qinputmethod.h>
#include <QtGui/qopenglcontext.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtGui/qoffscreensurface.h>
#include <private/qgraphicseffect_p.h>
#include <qbackingstore.h>
@ -1817,24 +1818,47 @@ void QWidgetPrivate::deleteSysExtra()
{
}
static void deleteBackingStore(QWidgetPrivate *d)
{
QTLWExtra *topData = d->topData();
// The context must be current when destroying the backing store as it may attempt to
// release resources like textures and shader programs. The window may not be suitable
// anymore as there will often not be a platform window underneath at this stage. Fall
// back to a QOffscreenSurface in this case.
QScopedPointer<QOffscreenSurface> tempSurface;
#ifndef QT_NO_OPENGL
if (d->textureChildSeen && topData->shareContext) {
if (topData->window->handle()) {
topData->shareContext->makeCurrent(topData->window);
} else {
tempSurface.reset(new QOffscreenSurface);
tempSurface->setFormat(topData->shareContext->format());
tempSurface->create();
topData->shareContext->makeCurrent(tempSurface.data());
}
}
#endif
delete topData->backingStore;
topData->backingStore = 0;
#ifndef QT_NO_OPENGL
if (d->textureChildSeen && topData->shareContext)
topData->shareContext->doneCurrent();
#endif
}
void QWidgetPrivate::deleteTLSysExtra()
{
if (extra && extra->topextra) {
//the qplatformbackingstore may hold a reference to the window, so the backingstore
//needs to be deleted first. If the backingstore holds GL resources, we need to
// make the context current here, since the platform bs does not have a reference
// to the widget.
// make the context current here. This is taken care of by deleteBackingStore().
#ifndef QT_NO_OPENGL
if (textureChildSeen && extra->topextra->shareContext)
extra->topextra->shareContext->makeCurrent(extra->topextra->window);
#endif
extra->topextra->backingStoreTracker.destroy();
delete extra->topextra->backingStore;
extra->topextra->backingStore = 0;
deleteBackingStore(this);
#ifndef QT_NO_OPENGL
if (textureChildSeen && extra->topextra->shareContext)
extra->topextra->shareContext->doneCurrent();
delete extra->topextra->shareContext;
extra->topextra->shareContext = 0;
#endif
@ -11980,7 +12004,7 @@ void QWidget::setBackingStore(QBackingStore *store)
return;
QBackingStore *oldStore = topData->backingStore;
delete topData->backingStore;
deleteBackingStore(d);
topData->backingStore = store;
QWidgetBackingStore *bs = d->maybeBackingStore();