From 5f0013ee76605b9c7ceab168702b57e797b698e0 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 15 Feb 2012 11:32:02 +0100 Subject: Introduced QQuickCanvas::releaseResources(). This function can be used in certain situations to purge the texture cache and triggers more lazily to take down the scene graph and GL context Change-Id: Icd9360ff50fda0e721ba0f1b520cda678e457a35 Reviewed-by: Aaron Kennedy --- src/quick/items/qquickcanvas.cpp | 17 +++ src/quick/items/qquickcanvas.h | 3 + src/quick/items/qquickwindowmanager.cpp | 134 ++++++++++++++++----- src/quick/items/qquickwindowmanager_p.h | 2 + src/quick/util/qdeclarativepixmapcache.cpp | 12 ++ src/quick/util/qdeclarativepixmapcache_p.h | 2 + .../qtquick2/qquickcanvas/tst_qquickcanvas.cpp | 4 +- 7 files changed, 146 insertions(+), 28 deletions(-) diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp index 53ff4fd217..cbc5b8e00e 100644 --- a/src/quick/items/qquickcanvas.cpp +++ b/src/quick/items/qquickcanvas.cpp @@ -66,6 +66,8 @@ #include #include +#include + #include QT_BEGIN_NAMESPACE @@ -748,6 +750,21 @@ QQuickCanvas::~QQuickCanvas() delete d->rootItem; d->rootItem = 0; } + + +/*! + This function tries to release redundant resources currently held by the QML scene. + */ + +void QQuickCanvas::releaseResources() +{ + Q_D(QQuickCanvas); + d->windowManager->releaseResources(); + QDeclarativePixmap::purgeCache(); +} + + + /*! Returns the invisible root item of the scene. diff --git a/src/quick/items/qquickcanvas.h b/src/quick/items/qquickcanvas.h index 335cbf784d..3246d53a42 100644 --- a/src/quick/items/qquickcanvas.h +++ b/src/quick/items/qquickcanvas.h @@ -124,6 +124,9 @@ Q_SIGNALS: void afterRendering(); void clearColorChanged(const QColor &); +public slots: + void releaseResources(); + protected: QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent = 0); diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp index c28b77f63b..916615bdd8 100644 --- a/src/quick/items/qquickwindowmanager.cpp +++ b/src/quick/items/qquickwindowmanager.cpp @@ -164,6 +164,7 @@ public: , shouldExit(false) , hasExited(false) , isDeferredUpdatePosted(false) + , runToReleaseResources(false) , canvasToGrab(0) { sg->moveToThread(this); @@ -194,6 +195,8 @@ public: void sync(bool guiAlreadyLocked); void initialize(); + void releaseResources(); + void releaseResourcesInThread(); bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; } @@ -249,6 +252,7 @@ private: uint shouldExit : 1; uint hasExited : 1; uint isDeferredUpdatePosted : 1; + uint runToReleaseResources : 1; QQuickCanvas *canvasToGrab; QImage grabContent; @@ -287,6 +291,7 @@ public: void canvasDestroyed(QQuickCanvas *canvas); + void releaseResources(); void initializeGL(); void renderCanvas(QQuickCanvas *canvas); void paint(QQuickCanvas *canvas); @@ -298,6 +303,7 @@ public: bool *allowMainThreadProcessing(); QSGContext *sceneGraphContext() const; + QQuickCanvas *masterCanvas() const; bool event(QEvent *); @@ -548,10 +554,18 @@ void QQuickRenderThreadSingleContextWindowManager::run() #ifdef THREAD_DEBUG printf("QML Rendering Thread Started\n"); #endif - lock(); - Q_ASSERT(!gl); - initialize(); + + if (runToReleaseResources) { + releaseResourcesInThread(); + runToReleaseResources = false; + unlock(); + return; + } + + if (!gl) + initialize(); + // Wake GUI as it is waiting for the GL context to have appeared, as // an indication that the render thread is now running. wake(); @@ -748,12 +762,6 @@ void QQuickRenderThreadSingleContextWindowManager::run() m_removed_windows << m_rendered_windows.keys(); handleRemovedWindows(); - sg->invalidate(); - - gl->doneCurrent(); - delete gl; - gl = 0; - #ifdef THREAD_DEBUG printf(" RenderThread: render loop exited... Good Night!\n"); #endif @@ -772,6 +780,60 @@ void QQuickRenderThreadSingleContextWindowManager::run() #endif } +void QQuickRenderThreadSingleContextWindowManager::releaseResourcesInThread() +{ +#ifdef THREAD_DEBUG + printf(" RenderThread: releasing resources...\n"); +#endif + + QQuickCanvas *canvas = masterCanvas(); + QWindow *tmpSurface = 0; + + if (canvas) { + gl->makeCurrent(canvas); + } else { + tmpSurface = new QWindow(); + tmpSurface->setSurfaceType(QSurface::OpenGLSurface); + tmpSurface->resize(4, 4); + tmpSurface->create(); + gl->makeCurrent(tmpSurface); + } + + sg->invalidate(); + gl->doneCurrent(); + delete gl; + gl = 0; + + if (tmpSurface) + delete tmpSurface; + + wake(); +} + +void QQuickRenderThreadSingleContextWindowManager::releaseResources() +{ +#ifdef THREAD_DEBUG + printf("GUI: releasing resources\n"); +#endif + + lockInGui(); + if (!isRunning() && gl) { + runToReleaseResources = true; + start(); + + while (gl) { + wait(); + } + } +#ifdef THREAD_DEBUG + else { + printf("GUI: render thread running not releasing resources...\n"); + } +#endif + unlockInGui(); + +} + bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e) { Q_ASSERT(QThread::currentThread() == qApp->thread()); @@ -1003,7 +1065,6 @@ void QQuickRenderThreadSingleContextWindowManager::startRendering() animationTimer = -1; } - } @@ -1146,17 +1207,46 @@ void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas) m_windows.remove(canvas); QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas); cd->cleanupNodesOnShutdown(); +} + +void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas) +{ + hide(canvas); +} +void QQuickTrivialWindowManager::releaseResources() +{ if (m_windows.size() == 0) { + QQuickCanvas *canvas = masterCanvas(); + QWindow *tmpSurface = 0; + + if (canvas) { + gl->makeCurrent(canvas); + } else { + tmpSurface = new QWindow(); + tmpSurface->setSurfaceType(QSurface::OpenGLSurface); + tmpSurface->resize(4, 4); + tmpSurface->create(); + gl->makeCurrent(tmpSurface); + } + sg->invalidate(); delete gl; gl = 0; + + delete tmpSurface; } } -void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas) +QQuickCanvas *QQuickTrivialWindowManager::masterCanvas() const { - hide(canvas); + // Find a "proper surface" to bind... + for (QHash::const_iterator it = m_windows.constBegin(); + it != m_windows.constEnd(); ++it) { + if (it.key()->visible()) + return it.key(); + } + return 0; } void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas) @@ -1166,30 +1256,20 @@ void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas) CanvasData &data = const_cast(m_windows[canvas]); - QQuickCanvas *masterCanvas = 0; - if (!canvas->visible()) { - // Find a "proper surface" to bind... - for (QHash::const_iterator it = m_windows.constBegin(); - it != m_windows.constEnd() && !masterCanvas; ++it) { - if (it.key()->visible()) - masterCanvas = it.key(); - } - } else { - masterCanvas = canvas; - } + QQuickCanvas *window = canvas->visible() ? canvas : masterCanvas(); - if (!masterCanvas) + if (!window) return; if (!gl) { gl = new QOpenGLContext(); - gl->setFormat(masterCanvas->requestedFormat()); + gl->setFormat(window->requestedFormat()); gl->create(); - if (!gl->makeCurrent(masterCanvas)) + if (!gl->makeCurrent(window)) qWarning("QQuickCanvas: makeCurrent() failed..."); sg->initialize(gl); } else { - gl->makeCurrent(masterCanvas); + gl->makeCurrent(window); } bool alsoSwap = data.updatePending; diff --git a/src/quick/items/qquickwindowmanager_p.h b/src/quick/items/qquickwindowmanager_p.h index 6134204464..014b38132e 100644 --- a/src/quick/items/qquickwindowmanager_p.h +++ b/src/quick/items/qquickwindowmanager_p.h @@ -67,6 +67,8 @@ public: virtual QSGContext *sceneGraphContext() const = 0; + virtual void releaseResources() = 0; + // ### make this less of a singleton static QQuickWindowManager *instance(); }; diff --git a/src/quick/util/qdeclarativepixmapcache.cpp b/src/quick/util/qdeclarativepixmapcache.cpp index 43ce3346cb..95cbd361b8 100644 --- a/src/quick/util/qdeclarativepixmapcache.cpp +++ b/src/quick/util/qdeclarativepixmapcache.cpp @@ -695,6 +695,8 @@ public: void unreferencePixmap(QDeclarativePixmapData *); void referencePixmap(QDeclarativePixmapData *); + void purgeCache(); + protected: virtual void timerEvent(QTimerEvent *); @@ -827,6 +829,16 @@ void QDeclarativePixmapStore::timerEvent(QTimerEvent *) } } +void QDeclarativePixmapStore::purgeCache() +{ + shrinkCache(m_unreferencedCost); +} + +void QDeclarativePixmap::purgeCache() +{ + pixmapStore()->purgeCache(); +} + QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) : data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) { diff --git a/src/quick/util/qdeclarativepixmapcache_p.h b/src/quick/util/qdeclarativepixmapcache_p.h index 19c3a5264f..69cd84c465 100644 --- a/src/quick/util/qdeclarativepixmapcache_p.h +++ b/src/quick/util/qdeclarativepixmapcache_p.h @@ -126,6 +126,8 @@ public: bool connectDownloadProgress(QObject *, const char *); bool connectDownloadProgress(QObject *, int); + static void purgeCache(); + private: Q_DISABLE_COPY(QDeclarativePixmap) QDeclarativePixmapData *d; diff --git a/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp b/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp index eac0d851d5..8f0c4e6fce 100644 --- a/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp +++ b/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp @@ -685,7 +685,9 @@ void tst_qquickcanvas::headless() // Hide the canvas and verify signal emittion and GL context deletion canvas->hide(); - QCOMPARE(invalidated.size(), 1); + canvas->releaseResources(); + + QTRY_COMPARE(invalidated.size(), 1); QVERIFY(canvas->openglContext() == 0); // Destroy the native windowing system buffers -- cgit v1.2.3