diff options
author | Robert Griebl <robert.griebl@pelagicore.com> | 2015-04-15 13:57:51 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@pelagicore.com> | 2015-04-23 15:00:46 +0000 |
commit | 92433623b31388e2e8c4d532033dad6189eaab24 (patch) | |
tree | 1a20685ae0f7e5d3137ca001c2c222ae16aaf241 /src | |
parent | 3fdec636980c23b14cfc6aa74bc48bbb960ba0b4 (diff) |
Fix a memory leak in the material shader cache.
There were multiple problems in the implementation of the shader cache:
1) it was not thread-safe
2) nothing was ever removed from the hash
3) since the keys into the hash are the actual shader source code, problem
#2 would lead to serious memory consumption over time
Change-Id: I20d1fb2074932e23f89edddba12e68ab8adcbff0
Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/items/qquickshadereffectnode.cpp | 28 | ||||
-rw-r--r-- | src/quick/items/qquickshadereffectnode_p.h | 4 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgrenderloop.cpp | 4 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgthreadedrenderloop.cpp | 4 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgwindowsrenderloop.cpp | 4 |
5 files changed, 39 insertions, 5 deletions
diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index 3542fbb27f..e593112ef5 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -38,6 +38,7 @@ #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgshadersourcebuilder_p.h> #include <QtQuick/private/qsgtexture_p.h> +#include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE @@ -348,7 +349,10 @@ uint qHash(const QQuickShaderEffectMaterialKey &key) } -QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QQuickShaderEffectMaterial::materialMap; +typedef QHash<QQuickShaderEffectMaterialKey, QWeakPointer<QSGMaterialType> > MaterialHash; + +Q_GLOBAL_STATIC(MaterialHash, materialHash) +Q_GLOBAL_STATIC(QMutex, materialHashMutex) QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node) : cullMode(NoCulling) @@ -403,12 +407,30 @@ int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source) { + QMutexLocker locker(materialHashMutex); + Q_UNUSED(locker); + m_source = source; m_emittedLogChanged = false; - m_type = materialMap.value(m_source); + QWeakPointer<QSGMaterialType> weakPtr = materialHash->value(m_source); + m_type = weakPtr.toStrongRef(); + if (m_type.isNull()) { m_type = QSharedPointer<QSGMaterialType>(new QSGMaterialType); - materialMap.insert(m_source, m_type); + materialHash->insert(m_source, m_type.toWeakRef()); + } +} + +void QQuickShaderEffectMaterial::cleanupMaterialCache() +{ + QMutexLocker locker(materialHashMutex); + Q_UNUSED(locker); + + for (MaterialHash::iterator it = materialHash->begin(); it != materialHash->end(); ) { + if (!it.value().toStrongRef()) + it = materialHash->erase(it); + else + ++it; } } diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index 7e3cf74276..28382c483c 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -101,6 +101,8 @@ public: void updateTextures() const; void invalidateTextureProvider(QSGTextureProvider *provider); + static void cleanupMaterialCache(); + protected: friend class QQuickCustomMaterialShader; @@ -115,8 +117,6 @@ protected: QQuickShaderEffectNode *m_node; bool m_emittedLogChanged; - - static QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > materialMap; }; diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index ce3bf7d61d..b7a6475c23 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -52,6 +52,8 @@ #include <QtQuick/private/qsgcontext_p.h> #include <private/qquickprofiler_p.h> +#include <private/qquickshadereffectnode_p.h> + #ifdef Q_OS_WIN # include <QtCore/qt_windows.h> #endif @@ -293,6 +295,8 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(!current)) qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; + QQuickShaderEffectMaterial::cleanupMaterialCache(); + d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { rc->invalidate(); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 7b1e24246b..26a0e78254 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -56,6 +56,8 @@ #include <private/qquickprofiler_p.h> #include <private/qqmldebugservice_p.h> +#include <private/qquickshadereffectnode_p.h> + /* Overall design: @@ -449,6 +451,8 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- cleanup without an OpenGL context"; } + QQuickShaderEffectMaterial::cleanupMaterialCache(); + // The canvas nodes must be cleaned up regardless if we are in the destructor.. if (wipeSG) { QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 0e22fa8b7b..309e877dae 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -48,6 +48,8 @@ #include <private/qquickprofiler_p.h> +#include <private/qquickshadereffectnode_p.h> + QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); @@ -232,6 +234,8 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(!current)) qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; + QQuickShaderEffectMaterial::cleanupMaterialCache(); + d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { d->context->invalidate(); |