diff options
author | Samuel Rødal <samuel.rodal@nokia.com> | 2011-08-16 09:29:44 +0200 |
---|---|---|
committer | Samuel Rødal <samuel.rodal@nokia.com> | 2011-08-29 10:24:55 +0200 |
commit | aaa4a26f82f99fa8724841eba91bad029306e0ce (patch) | |
tree | 3d24e874bff00efc0e73af610bc80e645d14f789 /src/gui/kernel | |
parent | 00fd783a39d55d2365ddead4fab2cc06091c119f (diff) |
Move GL resource handling enablers to QtGui.
Made resource handling more robust by attempting to free GL resources in
the correct thread, and not forcing a context to become current to free
resources.
Change-Id: Ie81d4005b608972375755571d9b50ce82080709b
Reviewed-on: http://codereview.qt.nokia.com/3258
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
Diffstat (limited to 'src/gui/kernel')
-rw-r--r-- | src/gui/kernel/kernel.pri | 1 | ||||
-rw-r--r-- | src/gui/kernel/qguiglcontext_qpa.cpp | 229 | ||||
-rw-r--r-- | src/gui/kernel/qguiglcontext_qpa.h | 33 | ||||
-rw-r--r-- | src/gui/kernel/qguiglcontext_qpa_p.h | 181 | ||||
-rw-r--r-- | src/gui/kernel/qplatformglcontext_qpa.cpp | 2 |
5 files changed, 409 insertions, 37 deletions
diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 58291f5237..44d039512f 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -51,6 +51,7 @@ qpa { kernel/qplatformwindow_qpa.h \ kernel/qplatformglcontext_qpa.h \ kernel/qguiglcontext_qpa.h \ + kernel/qguiglcontext_qpa_p.h \ kernel/qplatformcursor_qpa.h \ kernel/qplatformclipboard_qpa.h \ kernel/qplatformnativeinterface_qpa.h \ diff --git a/src/gui/kernel/qguiglcontext_qpa.cpp b/src/gui/kernel/qguiglcontext_qpa.cpp index 2c43befbc9..71c830174e 100644 --- a/src/gui/kernel/qguiglcontext_qpa.cpp +++ b/src/gui/kernel/qguiglcontext_qpa.cpp @@ -41,6 +41,7 @@ #include "qplatformglcontext_qpa.h" #include "qguiglcontext_qpa.h" +#include "qguiglcontext_qpa_p.h" #include "qwindow.h" #include <QtCore/QThreadStorage> @@ -63,35 +64,6 @@ public: static QThreadStorage<QGuiGLThreadContext *> qwindow_context_storage; -class QGuiGLContextPrivate -{ -public: - QGuiGLContextPrivate() - : qGLContextHandle(0) - , platformGLContext(0) - , shareContext(0) - , screen(0) - , surface(0) - { - } - - virtual ~QGuiGLContextPrivate() - { - //do not delete the QGLContext handle here as it is deleted in - //QWidgetPrivate::deleteTLSysExtra() - } - void *qGLContextHandle; - void (*qGLContextDeleteFunction)(void *handle); - - QSurfaceFormat requestedFormat; - QPlatformGLContext *platformGLContext; - QGuiGLContext *shareContext; - QScreen *screen; - QSurface *surface; - - static void setCurrentContext(QGuiGLContext *context); -}; - void QGuiGLContextPrivate::setCurrentContext(QGuiGLContext *context) { QGuiGLThreadContext *threadContext = qwindow_context_storage.localData(); @@ -118,6 +90,11 @@ QGuiGLContext* QGuiGLContext::currentContext() return 0; } +bool QGuiGLContext::areSharing(QGuiGLContext *first, QGuiGLContext *second) +{ + return first->shareGroup() == second->shareGroup(); +} + QPlatformGLContext *QGuiGLContext::handle() const { Q_D(const QGuiGLContext); @@ -136,7 +113,7 @@ QPlatformGLContext *QGuiGLContext::shareHandle() const Creates a new GL context instance, you need to call create() before it can be used. */ QGuiGLContext::QGuiGLContext() - : d_ptr(new QGuiGLContextPrivate()) + : QObject(*new QGuiGLContextPrivate()) { Q_D(QGuiGLContext); d->screen = QGuiApplication::primaryScreen(); @@ -174,7 +151,7 @@ void QGuiGLContext::setScreen(QScreen *screen) /*! Attempts to create the GL context with the desired parameters. - Returns true if the native context was successfully created and is ready to be used.d + Returns true if the native context was successfully created and is ready to be used. */ bool QGuiGLContext::create() { @@ -183,6 +160,8 @@ bool QGuiGLContext::create() Q_D(QGuiGLContext); d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformGLContext(this); d->platformGLContext->setContext(this); + d->shareGroup = d->shareContext ? d->shareContext->shareGroup() : new QGuiGLContextGroup; + d->shareGroup->d_func()->addContext(this); return d->platformGLContext; } @@ -191,6 +170,9 @@ void QGuiGLContext::destroy() Q_D(QGuiGLContext); if (QGuiGLContext::currentContext() == this) doneCurrent(); + if (d->shareGroup) + d->shareGroup->d_func()->removeContext(this); + d->shareGroup = 0; delete d->platformGLContext; d->platformGLContext = 0; } @@ -232,6 +214,9 @@ bool QGuiGLContext::makeCurrent(QSurface *surface) if (d->platformGLContext->makeCurrent(surface->surfaceHandle())) { QGuiGLContextPrivate::setCurrentContext(this); d->surface = surface; + + d->shareGroup->d_func()->deletePendingResources(this); + return true; } @@ -247,6 +232,9 @@ void QGuiGLContext::doneCurrent() if (!d->platformGLContext) return; + if (QGuiGLContext::currentContext() == this) + d->shareGroup->d_func()->deletePendingResources(this); + d->platformGLContext->doneCurrent(); QGuiGLContextPrivate::setCurrentContext(0); @@ -293,6 +281,12 @@ QSurfaceFormat QGuiGLContext::format() const return d->platformGLContext->format(); } +QGuiGLContextGroup *QGuiGLContext::shareGroup() const +{ + Q_D(const QGuiGLContext); + return d->shareGroup; +} + QGuiGLContext *QGuiGLContext::shareContext() const { Q_D(const QGuiGLContext); @@ -331,3 +325,176 @@ void QGuiGLContext::deleteQGLContext() d->qGLContextHandle = 0; } } + +QGuiGLContextGroup::QGuiGLContextGroup() + : QObject(*new QGuiGLContextGroupPrivate()) +{ +} + +QGuiGLContextGroup::~QGuiGLContextGroup() +{ + Q_D(QGuiGLContextGroup); + + QList<QGLSharedResource *>::iterator it = d->m_sharedResources.begin(); + QList<QGLSharedResource *>::iterator end = d->m_sharedResources.end(); + + while (it != end) { + (*it)->invalidateResource(); + (*it)->m_group = 0; + ++it; + } + + qDeleteAll(d->m_pendingDeletion.begin(), d->m_pendingDeletion.end()); +} + +QList<QGuiGLContext *> QGuiGLContextGroup::shares() const +{ + Q_D(const QGuiGLContextGroup); + return d->m_shares; +} + +QGuiGLContextGroup *QGuiGLContextGroup::currentContextGroup() +{ + QGuiGLContext *current = QGuiGLContext::currentContext(); + return current ? current->shareGroup() : 0; +} + +void QGuiGLContextGroupPrivate::addContext(QGuiGLContext *ctx) +{ + QMutexLocker locker(&m_mutex); + m_refs.ref(); + m_shares << ctx; +} + +void QGuiGLContextGroupPrivate::removeContext(QGuiGLContext *ctx) +{ + Q_Q(QGuiGLContextGroup); + + QMutexLocker locker(&m_mutex); + m_shares.removeOne(ctx); + + if (ctx == m_context && !m_shares.isEmpty()) + m_context = m_shares.first(); + + if (!m_refs.deref()) + q->deleteLater(); +} + +void QGuiGLContextGroupPrivate::deletePendingResources(QGuiGLContext *ctx) +{ + QMutexLocker locker(&m_mutex); + + QList<QGLSharedResource *>::iterator it = m_pendingDeletion.begin(); + QList<QGLSharedResource *>::iterator end = m_pendingDeletion.end(); + while (it != end) { + (*it)->freeResource(ctx); + delete *it; + ++it; + } + m_pendingDeletion.clear(); +} + +QGLSharedResource::QGLSharedResource(QGuiGLContextGroup *group) + : m_group(group) +{ + QMutexLocker locker(&m_group->d_func()->m_mutex); + m_group->d_func()->m_sharedResources << this; +} + +QGLSharedResource::~QGLSharedResource() +{ +} + +// schedule the resource for deletion at an appropriate time +void QGLSharedResource::free() +{ + if (!m_group) { + delete this; + return; + } + + QMutexLocker locker(&m_group->d_func()->m_mutex); + m_group->d_func()->m_sharedResources.removeOne(this); + m_group->d_func()->m_pendingDeletion << this; + + // can we delete right away? + QGuiGLContext *current = QGuiGLContext::currentContext(); + if (current && current->shareGroup() == m_group) { + m_group->d_func()->deletePendingResources(current); + } +} + +QGLMultiGroupSharedResource::QGLMultiGroupSharedResource() + : active(0) +{ +#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG + qDebug("Creating context group resource object %p.", this); +#endif +} + +QGLMultiGroupSharedResource::~QGLMultiGroupSharedResource() +{ +#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG + qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size()); +#endif + for (int i = 0; i < m_groups.size(); ++i) { + QGuiGLContext *context = m_groups.at(i)->shares().first(); + QGLSharedResource *resource = value(context); + if (resource) + resource->free(); + m_groups.at(i)->d_func()->m_resources.remove(this); + active.deref(); + } +#ifndef QT_NO_DEBUG + if (active != 0) { + qWarning("QtOpenGL: Resources are still available at program shutdown.\n" + " This is possibly caused by a leaked QGLWidget, \n" + " QGLFramebufferObject or QGLPixelBuffer."); + } +#endif +} + +void QGLMultiGroupSharedResource::insert(QGuiGLContext *context, QGLSharedResource *value) +{ +#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG + qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this); +#endif + QGuiGLContextGroup *group = context->shareGroup(); + Q_ASSERT(!group->d_func()->m_resources.contains(this)); + group->d_func()->m_resources.insert(this, value); + m_groups.append(group); + active.ref(); +} + +QGLSharedResource *QGLMultiGroupSharedResource::value(QGuiGLContext *context) +{ + QGuiGLContextGroup *group = context->shareGroup(); + return group->d_func()->m_resources.value(this, 0); +} + +void QGLMultiGroupSharedResource::cleanup(QGuiGLContext *ctx) +{ + QGLSharedResource *resource = value(ctx); + + if (resource != 0) { + resource->free(); + + QGuiGLContextGroup *group = ctx->shareGroup(); + group->d_func()->m_resources.remove(this); + m_groups.removeOne(group); + active.deref(); + } +} + +void QGLMultiGroupSharedResource::cleanup(QGuiGLContext *ctx, QGLSharedResource *value) +{ +#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG + qDebug("Cleaning up context group resource %p, for context %p in thread %p.", this, ctx, QThread::currentThread()); +#endif + value->free(); + active.deref(); + + QGuiGLContextGroup *group = ctx->shareGroup(); + m_groups.removeOne(group); +} + diff --git a/src/gui/kernel/qguiglcontext_qpa.h b/src/gui/kernel/qguiglcontext_qpa.h index 11a7e16a82..a234bd3cb6 100644 --- a/src/gui/kernel/qguiglcontext_qpa.h +++ b/src/gui/kernel/qguiglcontext_qpa.h @@ -54,12 +54,34 @@ QT_BEGIN_NAMESPACE QT_MODULE(Gui) class QGuiGLContextPrivate; +class QGuiGLContextGroupPrivate; class QPlatformGLContext; class QSurface; -class Q_GUI_EXPORT QGuiGLContext +class Q_GUI_EXPORT QGuiGLContextGroup : public QObject { -Q_DECLARE_PRIVATE(QGuiGLContext); + Q_OBJECT + Q_DECLARE_PRIVATE(QGuiGLContextGroup) +public: + ~QGuiGLContextGroup(); + + QList<QGuiGLContext *> shares() const; + + static QGuiGLContextGroup *currentContextGroup(); + +private: + QGuiGLContextGroup(); + + friend class QGuiGLContext; + friend class QGLContextGroupResourceBase; + friend class QGLSharedResource; + friend class QGLMultiGroupSharedResource; +}; + +class Q_GUI_EXPORT QGuiGLContext : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGuiGLContext); public: QGuiGLContext(); ~QGuiGLContext(); @@ -73,6 +95,7 @@ public: QSurfaceFormat format() const; QGuiGLContext *shareContext() const; + QGuiGLContextGroup *shareGroup() const; QScreen *screen() const; bool makeCurrent(QSurface *surface); @@ -84,15 +107,15 @@ public: QSurface *surface() const; static QGuiGLContext *currentContext(); + static bool areSharing(QGuiGLContext *first, QGuiGLContext *second); QPlatformGLContext *handle() const; QPlatformGLContext *shareHandle() const; private: - QScopedPointer<QGuiGLContextPrivate> d_ptr; - //hack to make it work with QGLContext::CurrentContext friend class QGLContext; + friend class QGLContextResourceBase; friend class QWidgetPrivate; void *qGLContextHandle() const; @@ -100,8 +123,6 @@ private: void deleteQGLContext(); void destroy(); - - Q_DISABLE_COPY(QGuiGLContext); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qguiglcontext_qpa_p.h b/src/gui/kernel/qguiglcontext_qpa_p.h new file mode 100644 index 0000000000..a9c8f5c75e --- /dev/null +++ b/src/gui/kernel/qguiglcontext_qpa_p.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGUIGLCONTEXT_P_H +#define QGUIGLCONTEXT_P_H + +#include "qguiglcontext_qpa.h" +#include <private/qobject_p.h> +#include <qmutex.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGuiGLContext; +class QGLMultiGroupSharedResource; + +class Q_GUI_EXPORT QGLSharedResource +{ +public: + QGLSharedResource(QGuiGLContextGroup *group); + virtual ~QGLSharedResource() = 0; + + QGuiGLContextGroup *group() const { return m_group; } + + // schedule the resource for deletion at an appropriate time + void free(); + +protected: + // the resource's share group no longer exists, invalidate the resource + virtual void invalidateResource() = 0; + + // a valid context in the group is current, free the resource + virtual void freeResource(QGuiGLContext *context) = 0; + +private: + QGuiGLContextGroup *m_group; + + friend class QGuiGLContextGroup; + friend class QGuiGLContextGroupPrivate; + + Q_DISABLE_COPY(QGLSharedResource); +}; + +class Q_GUI_EXPORT QGuiGLContextGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGuiGLContextGroup); +public: + QGuiGLContextGroupPrivate() + : m_context(0) + , m_mutex(QMutex::Recursive) + , m_refs(0) + { + } + + void addContext(QGuiGLContext *ctx); + void removeContext(QGuiGLContext *ctx); + + void deletePendingResources(QGuiGLContext *ctx); + + QGuiGLContext *m_context; + + QList<QGuiGLContext *> m_shares; + QMutex m_mutex; + + QHash<QGLMultiGroupSharedResource *, QGLSharedResource *> m_resources; + QAtomicInt m_refs; + + QList<QGLSharedResource *> m_sharedResources; + QList<QGLSharedResource *> m_pendingDeletion; + + void cleanupResources(QGuiGLContext *ctx); +}; + +class Q_GUI_EXPORT QGLMultiGroupSharedResource +{ +public: + QGLMultiGroupSharedResource(); + ~QGLMultiGroupSharedResource(); + + void insert(QGuiGLContext *context, QGLSharedResource *value); + void cleanup(QGuiGLContext *context); + void cleanup(QGuiGLContext *context, QGLSharedResource *value); + + QGLSharedResource *value(QGuiGLContext *context); + + template <typename T> + T *value(QGuiGLContext *context) { + QGuiGLContextGroup *group = context->shareGroup(); + T *resource = static_cast<T *>(group->d_func()->m_resources.value(this, 0)); + if (!resource) { + resource = new T(context); + insert(context, resource); + } + return resource; + } + +private: + QAtomicInt active; + QList<QGuiGLContextGroup *> m_groups; +}; + +class Q_GUI_EXPORT QGuiGLContextPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGuiGLContext); +public: + QGuiGLContextPrivate() + : qGLContextHandle(0) + , platformGLContext(0) + , shareContext(0) + , shareGroup(0) + , screen(0) + , surface(0) + { + } + + virtual ~QGuiGLContextPrivate() + { + //do not delete the QGLContext handle here as it is deleted in + //QWidgetPrivate::deleteTLSysExtra() + } + void *qGLContextHandle; + void (*qGLContextDeleteFunction)(void *handle); + + QSurfaceFormat requestedFormat; + QPlatformGLContext *platformGLContext; + QGuiGLContext *shareContext; + QGuiGLContextGroup *shareGroup; + QScreen *screen; + QSurface *surface; + + QHash<QGLMultiGroupSharedResource *, void *> m_resources; + + static void setCurrentContext(QGuiGLContext *context); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGUIGLCONTEXT_P_H diff --git a/src/gui/kernel/qplatformglcontext_qpa.cpp b/src/gui/kernel/qplatformglcontext_qpa.cpp index 81030b8cfe..5ce7db09e2 100644 --- a/src/gui/kernel/qplatformglcontext_qpa.cpp +++ b/src/gui/kernel/qplatformglcontext_qpa.cpp @@ -65,6 +65,8 @@ /*! \fn void QPlatformGLContext::swapBuffers() Reimplement in subclass to native swap buffers calls + + The implementation must support being called in a thread different than the gui-thread. */ /*! \fn void *QPlatformGLContext::getProcAddress(const QString &procName) |