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>
This commit is contained in:
Samuel Rødal 2011-08-16 09:29:44 +02:00
parent 00fd783a39
commit aaa4a26f82
20 changed files with 775 additions and 488 deletions

View File

@ -51,6 +51,7 @@ qpa {
kernel/qplatformwindow_qpa.h \ kernel/qplatformwindow_qpa.h \
kernel/qplatformglcontext_qpa.h \ kernel/qplatformglcontext_qpa.h \
kernel/qguiglcontext_qpa.h \ kernel/qguiglcontext_qpa.h \
kernel/qguiglcontext_qpa_p.h \
kernel/qplatformcursor_qpa.h \ kernel/qplatformcursor_qpa.h \
kernel/qplatformclipboard_qpa.h \ kernel/qplatformclipboard_qpa.h \
kernel/qplatformnativeinterface_qpa.h \ kernel/qplatformnativeinterface_qpa.h \

View File

@ -41,6 +41,7 @@
#include "qplatformglcontext_qpa.h" #include "qplatformglcontext_qpa.h"
#include "qguiglcontext_qpa.h" #include "qguiglcontext_qpa.h"
#include "qguiglcontext_qpa_p.h"
#include "qwindow.h" #include "qwindow.h"
#include <QtCore/QThreadStorage> #include <QtCore/QThreadStorage>
@ -63,35 +64,6 @@ public:
static QThreadStorage<QGuiGLThreadContext *> qwindow_context_storage; 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) void QGuiGLContextPrivate::setCurrentContext(QGuiGLContext *context)
{ {
QGuiGLThreadContext *threadContext = qwindow_context_storage.localData(); QGuiGLThreadContext *threadContext = qwindow_context_storage.localData();
@ -118,6 +90,11 @@ QGuiGLContext* QGuiGLContext::currentContext()
return 0; return 0;
} }
bool QGuiGLContext::areSharing(QGuiGLContext *first, QGuiGLContext *second)
{
return first->shareGroup() == second->shareGroup();
}
QPlatformGLContext *QGuiGLContext::handle() const QPlatformGLContext *QGuiGLContext::handle() const
{ {
Q_D(const QGuiGLContext); 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. Creates a new GL context instance, you need to call create() before it can be used.
*/ */
QGuiGLContext::QGuiGLContext() QGuiGLContext::QGuiGLContext()
: d_ptr(new QGuiGLContextPrivate()) : QObject(*new QGuiGLContextPrivate())
{ {
Q_D(QGuiGLContext); Q_D(QGuiGLContext);
d->screen = QGuiApplication::primaryScreen(); d->screen = QGuiApplication::primaryScreen();
@ -174,7 +151,7 @@ void QGuiGLContext::setScreen(QScreen *screen)
/*! /*!
Attempts to create the GL context with the desired parameters. 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() bool QGuiGLContext::create()
{ {
@ -183,6 +160,8 @@ bool QGuiGLContext::create()
Q_D(QGuiGLContext); Q_D(QGuiGLContext);
d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformGLContext(this); d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformGLContext(this);
d->platformGLContext->setContext(this); d->platformGLContext->setContext(this);
d->shareGroup = d->shareContext ? d->shareContext->shareGroup() : new QGuiGLContextGroup;
d->shareGroup->d_func()->addContext(this);
return d->platformGLContext; return d->platformGLContext;
} }
@ -191,6 +170,9 @@ void QGuiGLContext::destroy()
Q_D(QGuiGLContext); Q_D(QGuiGLContext);
if (QGuiGLContext::currentContext() == this) if (QGuiGLContext::currentContext() == this)
doneCurrent(); doneCurrent();
if (d->shareGroup)
d->shareGroup->d_func()->removeContext(this);
d->shareGroup = 0;
delete d->platformGLContext; delete d->platformGLContext;
d->platformGLContext = 0; d->platformGLContext = 0;
} }
@ -232,6 +214,9 @@ bool QGuiGLContext::makeCurrent(QSurface *surface)
if (d->platformGLContext->makeCurrent(surface->surfaceHandle())) { if (d->platformGLContext->makeCurrent(surface->surfaceHandle())) {
QGuiGLContextPrivate::setCurrentContext(this); QGuiGLContextPrivate::setCurrentContext(this);
d->surface = surface; d->surface = surface;
d->shareGroup->d_func()->deletePendingResources(this);
return true; return true;
} }
@ -247,6 +232,9 @@ void QGuiGLContext::doneCurrent()
if (!d->platformGLContext) if (!d->platformGLContext)
return; return;
if (QGuiGLContext::currentContext() == this)
d->shareGroup->d_func()->deletePendingResources(this);
d->platformGLContext->doneCurrent(); d->platformGLContext->doneCurrent();
QGuiGLContextPrivate::setCurrentContext(0); QGuiGLContextPrivate::setCurrentContext(0);
@ -293,6 +281,12 @@ QSurfaceFormat QGuiGLContext::format() const
return d->platformGLContext->format(); return d->platformGLContext->format();
} }
QGuiGLContextGroup *QGuiGLContext::shareGroup() const
{
Q_D(const QGuiGLContext);
return d->shareGroup;
}
QGuiGLContext *QGuiGLContext::shareContext() const QGuiGLContext *QGuiGLContext::shareContext() const
{ {
Q_D(const QGuiGLContext); Q_D(const QGuiGLContext);
@ -331,3 +325,176 @@ void QGuiGLContext::deleteQGLContext()
d->qGLContextHandle = 0; 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);
}

View File

@ -54,12 +54,34 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Gui) QT_MODULE(Gui)
class QGuiGLContextPrivate; class QGuiGLContextPrivate;
class QGuiGLContextGroupPrivate;
class QPlatformGLContext; class QPlatformGLContext;
class QSurface; 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: public:
QGuiGLContext(); QGuiGLContext();
~QGuiGLContext(); ~QGuiGLContext();
@ -73,6 +95,7 @@ public:
QSurfaceFormat format() const; QSurfaceFormat format() const;
QGuiGLContext *shareContext() const; QGuiGLContext *shareContext() const;
QGuiGLContextGroup *shareGroup() const;
QScreen *screen() const; QScreen *screen() const;
bool makeCurrent(QSurface *surface); bool makeCurrent(QSurface *surface);
@ -84,15 +107,15 @@ public:
QSurface *surface() const; QSurface *surface() const;
static QGuiGLContext *currentContext(); static QGuiGLContext *currentContext();
static bool areSharing(QGuiGLContext *first, QGuiGLContext *second);
QPlatformGLContext *handle() const; QPlatformGLContext *handle() const;
QPlatformGLContext *shareHandle() const; QPlatformGLContext *shareHandle() const;
private: private:
QScopedPointer<QGuiGLContextPrivate> d_ptr;
//hack to make it work with QGLContext::CurrentContext //hack to make it work with QGLContext::CurrentContext
friend class QGLContext; friend class QGLContext;
friend class QGLContextResourceBase;
friend class QWidgetPrivate; friend class QWidgetPrivate;
void *qGLContextHandle() const; void *qGLContextHandle() const;
@ -100,8 +123,6 @@ private:
void deleteQGLContext(); void deleteQGLContext();
void destroy(); void destroy();
Q_DISABLE_COPY(QGuiGLContext);
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -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

View File

@ -65,6 +65,8 @@
/*! \fn void QPlatformGLContext::swapBuffers() /*! \fn void QPlatformGLContext::swapBuffers()
Reimplement in subclass to native swap buffers calls 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) /*! \fn void *QPlatformGLContext::getProcAddress(const QString &procName)

View File

@ -44,6 +44,8 @@
#include "qpaintengineex_opengl2_p.h" #include "qpaintengineex_opengl2_p.h"
#include "qglshadercache_p.h" #include "qglshadercache_p.h"
#include <QtGui/private/qguiglcontext_qpa_p.h>
#if defined(QT_DEBUG) #if defined(QT_DEBUG)
#include <QMetaEnum> #include <QMetaEnum>
#endif #endif
@ -52,18 +54,50 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QGLEngineSharedShadersResource : public QGLSharedResource
{
public:
QGLEngineSharedShadersResource(QGuiGLContext *ctx)
: QGLSharedResource(ctx->shareGroup())
, m_shaders(new QGLEngineSharedShaders(QGLContext::fromGuiGLContext(ctx)))
{
}
~QGLEngineSharedShadersResource()
{
delete m_shaders;
}
void invalidateResource()
{
delete m_shaders;
m_shaders = 0;
}
void freeResource(QGuiGLContext *)
{
}
QGLEngineSharedShaders *shaders() const { return m_shaders; }
private:
QGLEngineSharedShaders *m_shaders;
};
class QGLShaderStorage class QGLShaderStorage
{ {
public: public:
QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { QGLEngineSharedShaders *shadersForThread(const QGLContext *context) {
QGLContextGroupResource<QGLEngineSharedShaders> *&shaders = m_storage.localData(); QGLMultiGroupSharedResource *&shaders = m_storage.localData();
if (!shaders) if (!shaders)
shaders = new QGLContextGroupResource<QGLEngineSharedShaders>(); shaders = new QGLMultiGroupSharedResource;
return shaders->value(context); QGLEngineSharedShadersResource *resource =
shaders->value<QGLEngineSharedShadersResource>(context->contextHandle());
return resource ? resource->shaders() : 0;
} }
private: private:
QThreadStorage<QGLContextGroupResource<QGLEngineSharedShaders> *> m_storage; QThreadStorage<QGLMultiGroupSharedResource *> m_storage;
}; };
Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage); Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage);
@ -81,8 +115,7 @@ const char* QGLEngineSharedShaders::qShaderSnippets[] = {
}; };
QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
: ctxGuard(context) : blitShaderProg(0)
, blitShaderProg(0)
, simpleShaderProg(0) , simpleShaderProg(0)
{ {
@ -327,14 +360,14 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
vertexSource.append(qShaderSnippets[prog.mainVertexShader]); vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
vertexSource.append(qShaderSnippets[prog.positionVertexShader]); vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram(ctxGuard.context(), 0)); QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram);
CachedShader shaderCache(fragSource, vertexSource); CachedShader shaderCache(fragSource, vertexSource);
bool inCache = shaderCache.load(shaderProgram.data(), ctxGuard.context()); bool inCache = shaderCache.load(shaderProgram.data(), QGLContext::currentContext());
if (!inCache) { if (!inCache) {
QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0)); QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment));
QByteArray description; QByteArray description;
#if defined(QT_DEBUG) #if defined(QT_DEBUG)
// Name the shader for easier debugging // Name the shader for easier debugging
@ -357,7 +390,7 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
break; break;
} }
QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0)); QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex));
#if defined(QT_DEBUG) #if defined(QT_DEBUG)
// Name the shader for easier debugging // Name the shader for easier debugging
description.clear(); description.clear();
@ -396,7 +429,7 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
newProg->program->link(); newProg->program->link();
if (newProg->program->isLinked()) { if (newProg->program->isLinked()) {
if (!inCache) if (!inCache)
shaderCache.store(newProg->program, ctxGuard.context()); shaderCache.store(newProg->program, QGLContext::currentContext());
} else { } else {
QLatin1String none("none"); QLatin1String none("none");
QLatin1String br("\n"); QLatin1String br("\n");

View File

@ -365,7 +365,6 @@ public:
void cleanupCustomStage(QGLCustomShaderStage* stage); void cleanupCustomStage(QGLCustomShaderStage* stage);
private: private:
QGLSharedResourceGuard ctxGuard;
QGLShaderProgram *blitShaderProg; QGLShaderProgram *blitShaderProg;
QGLShaderProgram *simpleShaderProg; QGLShaderProgram *simpleShaderProg;
QList<QGLEngineShaderProg*> cachedPrograms; QList<QGLEngineShaderProg*> cachedPrograms;

View File

@ -51,21 +51,42 @@ class QGL2GradientCacheWrapper
public: public:
QGL2GradientCache *cacheForContext(const QGLContext *context) { QGL2GradientCache *cacheForContext(const QGLContext *context) {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
return m_resource.value(context); return m_resource.value<QGL2GradientCache>(context->contextHandle());
} }
private: private:
QGLContextGroupResource<QGL2GradientCache> m_resource; QGLMultiGroupSharedResource m_resource;
QMutex m_mutex; QMutex m_mutex;
}; };
Q_GLOBAL_STATIC(QGL2GradientCacheWrapper, qt_gradient_caches) Q_GLOBAL_STATIC(QGL2GradientCacheWrapper, qt_gradient_caches)
QGL2GradientCache::QGL2GradientCache(QGuiGLContext *ctx)
: QGLSharedResource(ctx->shareGroup())
{
}
QGL2GradientCache::~QGL2GradientCache()
{
cache.clear();
}
QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context)
{ {
return qt_gradient_caches()->cacheForContext(context); return qt_gradient_caches()->cacheForContext(context);
} }
void QGL2GradientCache::invalidateResource()
{
QMutexLocker lock(&m_mutex);
cache.clear();
}
void QGL2GradientCache::freeResource(QGuiGLContext *)
{
cleanCache();
}
void QGL2GradientCache::cleanCache() void QGL2GradientCache::cleanCache()
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);

View File

@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QGL2GradientCache class QGL2GradientCache : public QGLSharedResource
{ {
struct CacheInfo struct CacheInfo
{ {
@ -76,12 +76,15 @@ class QGL2GradientCache
public: public:
static QGL2GradientCache *cacheForContext(const QGLContext *context); static QGL2GradientCache *cacheForContext(const QGLContext *context);
QGL2GradientCache(const QGLContext *) {} QGL2GradientCache(QGuiGLContext *);
~QGL2GradientCache() { cleanCache(); } ~QGL2GradientCache();
GLuint getBuffer(const QGradient &gradient, qreal opacity); GLuint getBuffer(const QGradient &gradient, qreal opacity);
inline int paletteSize() const { return 1024; } inline int paletteSize() const { return 1024; }
void invalidateResource();
void freeResource(QGuiGLContext *ctx);
private: private:
inline int maxCacheSize() const { return 60; } inline int maxCacheSize() const { return 60; }
inline void generateGradientColorTable(const QGradient& gradient, inline void generateGradientColorTable(const QGradient& gradient,

View File

@ -1580,10 +1580,9 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp
QGLTextureGlyphCache *cache = QGLTextureGlyphCache *cache =
(QGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(cacheKey, glyphType, QTransform()); (QGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(cacheKey, glyphType, QTransform());
if (!cache || cache->cacheType() != glyphType || cache->context() == 0) { if (!cache || cache->cacheType() != glyphType || cache->contextGroup() == 0) {
cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform()); cache = new QGLTextureGlyphCache(glyphType, QTransform());
staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache); staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache);
cache->insert(ctx, cache);
recreateVertexArrays = true; recreateVertexArrays = true;
} }

View File

@ -51,19 +51,16 @@ extern Q_GUI_EXPORT bool qt_cleartype_enabled;
QBasicAtomicInt qgltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); QBasicAtomicInt qgltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1);
QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) QGLTextureGlyphCache::QGLTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix)
: QImageTextureGlyphCache(type, matrix), QGLContextGroupResourceBase() : QImageTextureGlyphCache(type, matrix)
, ctx(0)
, pex(0) , pex(0)
, m_blitProgram(0) , m_blitProgram(0)
, m_filterMode(Nearest) , m_filterMode(Nearest)
, m_serialNumber(qgltextureglyphcache_serial_number.fetchAndAddRelaxed(1)) , m_serialNumber(qgltextureglyphcache_serial_number.fetchAndAddRelaxed(1))
{ {
#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx); qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, QGuiGLContext::currentContext());
#endif #endif
setContext(context);
m_vertexCoordinateArray[0] = -1.0f; m_vertexCoordinateArray[0] = -1.0f;
m_vertexCoordinateArray[1] = -1.0f; m_vertexCoordinateArray[1] = -1.0f;
m_vertexCoordinateArray[2] = 1.0f; m_vertexCoordinateArray[2] = 1.0f;
@ -91,14 +88,9 @@ QGLTextureGlyphCache::~QGLTextureGlyphCache()
delete m_blitProgram; delete m_blitProgram;
} }
void QGLTextureGlyphCache::setContext(const QGLContext *context)
{
ctx = context;
m_h = 0;
}
void QGLTextureGlyphCache::createTextureData(int width, int height) void QGLTextureGlyphCache::createTextureData(int width, int height)
{ {
QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
if (ctx == 0) { if (ctx == 0) {
qWarning("QGLTextureGlyphCache::createTextureData: Called with no context"); qWarning("QGLTextureGlyphCache::createTextureData: Called with no context");
return; return;
@ -116,12 +108,17 @@ void QGLTextureGlyphCache::createTextureData(int width, int height)
if (height < 16) if (height < 16)
height = 16; height = 16;
QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx); if (m_textureResource && !m_textureResource->m_texture)
glGenTextures(1, &glyphTexture->m_texture); delete m_textureResource;
glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture);
glyphTexture->m_width = width; if (!m_textureResource)
glyphTexture->m_height = height; m_textureResource = new QGLGlyphTexture(ctx);
glGenTextures(1, &m_textureResource->m_texture);
glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
m_textureResource->m_width = width;
m_textureResource->m_height = height;
if (m_type == QFontEngineGlyphCache::Raster_RGBMask) { if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
QVarLengthArray<uchar> data(width * height * 4); QVarLengthArray<uchar> data(width * height * 4);
@ -144,14 +141,14 @@ void QGLTextureGlyphCache::createTextureData(int width, int height)
void QGLTextureGlyphCache::resizeTextureData(int width, int height) void QGLTextureGlyphCache::resizeTextureData(int width, int height)
{ {
QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
if (ctx == 0) { if (ctx == 0) {
qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context"); qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context");
return; return;
} }
QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx);
int oldWidth = glyphTexture->m_width; int oldWidth = m_textureResource->m_width;
int oldHeight = glyphTexture->m_height; int oldHeight = m_textureResource->m_height;
// Make the lower glyph texture size 16 x 16. // Make the lower glyph texture size 16 x 16.
if (width < 16) if (width < 16)
@ -159,7 +156,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
if (height < 16) if (height < 16)
height = 16; height = 16;
GLuint oldTexture = glyphTexture->m_texture; GLuint oldTexture = m_textureResource->m_texture;
createTextureData(width, height); createTextureData(width, height);
if (ctx->d_ptr->workaround_brokenFBOReadBack) { if (ctx->d_ptr->workaround_brokenFBOReadBack) {
@ -173,7 +170,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
// ### the QTextureGlyphCache API needs to be reworked to allow // ### the QTextureGlyphCache API needs to be reworked to allow
// ### resizeTextureData to fail // ### resizeTextureData to fail
glBindFramebuffer(GL_FRAMEBUFFER_EXT, glyphTexture->m_fbo); glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_textureResource->m_fbo);
GLuint tmp_texture; GLuint tmp_texture;
glGenTextures(1, &tmp_texture); glGenTextures(1, &tmp_texture);
@ -257,7 +254,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
@ -276,16 +273,16 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
{ {
QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
if (ctx == 0) { if (ctx == 0) {
qWarning("QGLTextureGlyphCache::fillTexture: Called with no context"); qWarning("QGLTextureGlyphCache::fillTexture: Called with no context");
return; return;
} }
QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx);
if (ctx->d_ptr->workaround_brokenFBOReadBack) { if (ctx->d_ptr->workaround_brokenFBOReadBack) {
QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
const QImage &texture = image(); const QImage &texture = image();
const uchar *bits = texture.constBits(); const uchar *bits = texture.constBits();
bits += c.y * texture.bytesPerLine() + c.x; bits += c.y * texture.bytesPerLine() + c.x;
@ -322,7 +319,7 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed sub
} }
} }
glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
if (mask.format() == QImage::Format_RGB32) { if (mask.format() == QImage::Format_RGB32) {
glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits()); glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits());
} else { } else {
@ -358,6 +355,7 @@ int QGLTextureGlyphCache::glyphPadding() const
int QGLTextureGlyphCache::maxTextureWidth() const int QGLTextureGlyphCache::maxTextureWidth() const
{ {
QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
if (ctx == 0) if (ctx == 0)
return QImageTextureGlyphCache::maxTextureWidth(); return QImageTextureGlyphCache::maxTextureWidth();
else else
@ -366,6 +364,7 @@ int QGLTextureGlyphCache::maxTextureWidth() const
int QGLTextureGlyphCache::maxTextureHeight() const int QGLTextureGlyphCache::maxTextureHeight() const
{ {
QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
if (ctx == 0) if (ctx == 0)
return QImageTextureGlyphCache::maxTextureHeight(); return QImageTextureGlyphCache::maxTextureHeight();
@ -377,16 +376,15 @@ int QGLTextureGlyphCache::maxTextureHeight() const
void QGLTextureGlyphCache::clear() void QGLTextureGlyphCache::clear()
{ {
if (ctx != 0) { m_textureResource->free();
m_textureResource.cleanup(ctx); m_textureResource = 0;
m_w = 0; m_w = 0;
m_h = 0; m_h = 0;
m_cx = 0; m_cx = 0;
m_cy = 0; m_cy = 0;
m_currentRowHeight = 0; m_currentRowHeight = 0;
coords.clear(); coords.clear();
}
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -63,10 +63,11 @@ QT_BEGIN_NAMESPACE
class QGL2PaintEngineExPrivate; class QGL2PaintEngineExPrivate;
struct QGLGlyphTexture struct QGLGlyphTexture : public QGLSharedResource
{ {
QGLGlyphTexture(const QGLContext *ctx) QGLGlyphTexture(const QGLContext *ctx)
: m_width(0) : QGLSharedResource(ctx->contextHandle()->shareGroup())
, m_width(0)
, m_height(0) , m_height(0)
{ {
if (ctx && !ctx->d_ptr->workaround_brokenFBOReadBack) if (ctx && !ctx->d_ptr->workaround_brokenFBOReadBack)
@ -77,19 +78,24 @@ struct QGLGlyphTexture
#endif #endif
} }
~QGLGlyphTexture() { void freeResource(QGuiGLContext *context)
const QGLContext *ctx = QGLContext::currentContext(); {
const QGLContext *ctx = QGLContext::fromGuiGLContext(context);
#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
qDebug("~QGLGlyphTexture() %p for context %p.", this, ctx); qDebug("~QGLGlyphTexture() %p for context %p.", this, ctx);
#endif #endif
// At this point, the context group is made current, so it's safe to if (!ctx->d_ptr->workaround_brokenFBOReadBack)
// release resources without a makeCurrent() call glDeleteFramebuffers(1, &m_fbo);
if (ctx) { if (m_width || m_height)
if (!ctx->d_ptr->workaround_brokenFBOReadBack) glDeleteTextures(1, &m_texture);
glDeleteFramebuffers(1, &m_fbo); }
if (m_width || m_height)
glDeleteTextures(1, &m_texture); void invalidateResource()
} {
m_texture = 0;
m_fbo = 0;
m_width = 0;
m_height = 0;
} }
GLuint m_texture; GLuint m_texture;
@ -98,10 +104,10 @@ struct QGLGlyphTexture
int m_height; int m_height;
}; };
class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QImageTextureGlyphCache, public QGLContextGroupResourceBase class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QImageTextureGlyphCache
{ {
public: public:
QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); QGLTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix);
~QGLTextureGlyphCache(); ~QGLTextureGlyphCache();
virtual void createTextureData(int width, int height); virtual void createTextureData(int width, int height);
@ -113,25 +119,24 @@ public:
inline GLuint texture() const { inline GLuint texture() const {
QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this);
QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); QGLGlyphTexture *glyphTexture = that->m_textureResource;
return glyphTexture ? glyphTexture->m_texture : 0; return glyphTexture ? glyphTexture->m_texture : 0;
} }
inline int width() const { inline int width() const {
QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this);
QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); QGLGlyphTexture *glyphTexture = that->m_textureResource;
return glyphTexture ? glyphTexture->m_width : 0; return glyphTexture ? glyphTexture->m_width : 0;
} }
inline int height() const { inline int height() const {
QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this);
QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); QGLGlyphTexture *glyphTexture = that->m_textureResource;
return glyphTexture ? glyphTexture->m_height : 0; return glyphTexture ? glyphTexture->m_height : 0;
} }
inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; }
void setContext(const QGLContext *context); inline const QGuiGLContextGroup *contextGroup() const { return m_textureResource ? m_textureResource->group() : 0; }
inline const QGLContext *context() const { return ctx; }
inline int serialNumber() const { return m_serialNumber; } inline int serialNumber() const { return m_serialNumber; }
@ -144,12 +149,9 @@ public:
void clear(); void clear();
void freeResource(void *) { ctx = 0; }
private: private:
QGLContextGroupResource<QGLGlyphTexture> m_textureResource; QGLGlyphTexture *m_textureResource;
const QGLContext *ctx;
QGL2PaintEngineExPrivate *pex; QGL2PaintEngineExPrivate *pex;
QGLShaderProgram *m_blitProgram; QGLShaderProgram *m_blitProgram;
FilterMode m_filterMode; FilterMode m_filterMode;

View File

@ -1476,42 +1476,16 @@ Q_GLOBAL_STATIC(QGLContextGroupList, qt_context_groups)
*****************************************************************************/ *****************************************************************************/
QGLContextGroup::QGLContextGroup(const QGLContext *context) QGLContextGroup::QGLContextGroup(const QGLContext *context)
: m_context(context), m_guards(0), m_refs(1) : m_context(context), m_refs(1)
{ {
qt_context_groups()->append(this); qt_context_groups()->append(this);
} }
QGLContextGroup::~QGLContextGroup() QGLContextGroup::~QGLContextGroup()
{ {
// Clear any remaining QGLSharedResourceGuard objects on the group.
QGLSharedResourceGuard *guard = m_guards;
while (guard != 0) {
guard->m_group = 0;
guard->m_id = 0;
guard = guard->m_next;
}
qt_context_groups()->remove(this); qt_context_groups()->remove(this);
} }
void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard)
{
if (m_guards)
m_guards->m_prev = guard;
guard->m_next = m_guards;
guard->m_prev = 0;
m_guards = guard;
}
void QGLContextGroup::removeGuard(QGLSharedResourceGuard *guard)
{
if (guard->m_next)
guard->m_next->m_prev = guard->m_prev;
if (guard->m_prev)
guard->m_prev->m_next = guard->m_next;
else
m_guards = guard->m_next;
}
const QGLContext *qt_gl_transfer_context(const QGLContext *ctx) const QGLContext *qt_gl_transfer_context(const QGLContext *ctx)
{ {
if (!ctx) if (!ctx)
@ -1934,8 +1908,6 @@ QGLContext::~QGLContext()
// clean up resources specific to this context // clean up resources specific to this context
d_ptr->cleanup(); d_ptr->cleanup();
// clean up resources belonging to this context's group
d_ptr->group->cleanupResources(this);
QGLSignalProxy::instance()->emitAboutToDestroyContext(this); QGLSignalProxy::instance()->emitAboutToDestroyContext(this);
reset(); reset();
@ -5017,108 +4989,6 @@ void QGLContextGroup::removeShare(const QGLContext *context) {
group->m_shares.clear(); group->m_shares.clear();
} }
QGLContextGroupResourceBase::QGLContextGroupResourceBase()
: active(0)
{
#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
qDebug("Creating context group resource object %p.", this);
#endif
}
QGLContextGroupResourceBase::~QGLContextGroupResourceBase()
{
#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) {
m_groups.at(i)->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 QGLContextGroupResourceBase::insert(const QGLContext *context, void *value)
{
#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
#endif
QGLContextGroup *group = QGLContextPrivate::contextGroup(context);
Q_ASSERT(!group->m_resources.contains(this));
group->m_resources.insert(this, value);
m_groups.append(group);
active.ref();
}
void *QGLContextGroupResourceBase::value(const QGLContext *context)
{
QGLContextGroup *group = QGLContextPrivate::contextGroup(context);
return group->m_resources.value(this, 0);
}
void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx)
{
void *resource = value(ctx);
if (resource != 0) {
QGLShareContextScope scope(ctx);
freeResource(resource);
QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx);
group->m_resources.remove(this);
m_groups.removeOne(group);
active.deref();
}
}
void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx, void *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
QGLShareContextScope scope(ctx);
freeResource(value);
active.deref();
QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx);
m_groups.removeOne(group);
}
void QGLContextGroup::cleanupResources(const QGLContext *context)
{
// If there are still shares, then no cleanup to be done yet.
if (m_shares.size() > 1)
return;
// Iterate over all resources and free each in turn.
QHash<QGLContextGroupResourceBase *, void *>::ConstIterator it;
for (it = m_resources.begin(); it != m_resources.end(); ++it)
it.key()->cleanup(context, it.value());
}
QGLSharedResourceGuard::~QGLSharedResourceGuard()
{
if (m_group)
m_group->removeGuard(this);
}
void QGLSharedResourceGuard::setContext(const QGLContext *context)
{
if (m_group)
m_group->removeGuard(this);
if (context) {
m_group = QGLContextPrivate::contextGroup(context);
m_group->addGuard(this);
} else {
m_group = 0;
}
}
QSize QGLTexture::bindCompressedTexture QSize QGLTexture::bindCompressedTexture
(const QString& fileName, const char *format) (const QString& fileName, const char *format)
{ {

View File

@ -374,7 +374,6 @@ private:
friend class QGLTextureGlyphCache; friend class QGLTextureGlyphCache;
friend struct QGLGlyphTexture; friend struct QGLGlyphTexture;
friend class QGLContextGroup; friend class QGLContextGroup;
friend class QGLSharedResourceGuard;
friend class QGLPixmapBlurFilter; friend class QGLPixmapBlurFilter;
friend class QGLExtensions; friend class QGLExtensions;
friend class QGLTexture; friend class QGLTexture;

View File

@ -61,6 +61,7 @@
#include "QtCore/qhash.h" #include "QtCore/qhash.h"
#include "QtCore/qatomic.h" #include "QtCore/qatomic.h"
#include "QtWidgets/private/qwidget_p.h" #include "QtWidgets/private/qwidget_p.h"
#include "QtGui/private/qguiglcontext_qpa_p.h"
#include "qcache.h" #include "qcache.h"
#include "qglpaintdevice_p.h" #include "qglpaintdevice_p.h"
@ -177,9 +178,6 @@ public:
bool disable_clear_on_painter_begin; bool disable_clear_on_painter_begin;
}; };
class QGLContextGroupResourceBase;
class QGLSharedResourceGuard;
// QGLContextPrivate has the responsibility of creating context groups. // QGLContextPrivate has the responsibility of creating context groups.
// QGLContextPrivate maintains the reference counter and destroys // QGLContextPrivate maintains the reference counter and destroys
// context groups when needed. // context groups when needed.
@ -193,9 +191,6 @@ public:
bool isSharing() const { return m_shares.size() >= 2; } bool isSharing() const { return m_shares.size() >= 2; }
QList<const QGLContext *> shares() const { return m_shares; } QList<const QGLContext *> shares() const { return m_shares; }
void addGuard(QGLSharedResourceGuard *guard);
void removeGuard(QGLSharedResourceGuard *guard);
static void addShare(const QGLContext *context, const QGLContext *share); static void addShare(const QGLContext *context, const QGLContext *share);
static void removeShare(const QGLContext *context); static void removeShare(const QGLContext *context);
@ -205,12 +200,8 @@ private:
QGLExtensionFuncs m_extensionFuncs; QGLExtensionFuncs m_extensionFuncs;
const QGLContext *m_context; // context group's representative const QGLContext *m_context; // context group's representative
QList<const QGLContext *> m_shares; QList<const QGLContext *> m_shares;
QHash<QGLContextGroupResourceBase *, void *> m_resources;
QGLSharedResourceGuard *m_guards; // double-linked list of active guards.
QAtomicInt m_refs; QAtomicInt m_refs;
void cleanupResources(const QGLContext *ctx);
friend class QGLContext; friend class QGLContext;
friend class QGLContextPrivate; friend class QGLContextPrivate;
friend class QGLContextGroupResourceBase; friend class QGLContextGroupResourceBase;
@ -539,114 +530,69 @@ QGLTexture* QGLTextureCache::getTexture(QGLContext *ctx, qint64 key)
extern Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine(); extern Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine();
/*
Base for resources that are shared in a context group.
*/
class Q_OPENGL_EXPORT QGLContextGroupResourceBase
{
public:
QGLContextGroupResourceBase();
virtual ~QGLContextGroupResourceBase();
void insert(const QGLContext *context, void *value);
void *value(const QGLContext *context);
void cleanup(const QGLContext *context);
void cleanup(const QGLContext *context, void *value);
virtual void freeResource(void *value) = 0;
protected:
QList<QGLContextGroup *> m_groups;
private:
QAtomicInt active;
};
/*
The QGLContextGroupResource template is used to manage a resource
for a group of sharing GL contexts. When the last context in the
group is destroyed, or when the QGLContextGroupResource object
itself is destroyed (implies potential context switches), the
resource will be freed.
The class used as the template class type needs to have a
constructor with the following signature:
T(const QGLContext *);
*/
template <class T>
class QGLContextGroupResource : public QGLContextGroupResourceBase
{
public:
~QGLContextGroupResource() {
for (int i = 0; i < m_groups.size(); ++i) {
const QGLContext *context = m_groups.at(i)->context();
T *resource = reinterpret_cast<T *>(QGLContextGroupResourceBase::value(context));
if (resource) {
QGLShareContextScope scope(context);
delete resource;
}
}
}
T *value(const QGLContext *context) {
T *resource = reinterpret_cast<T *>(QGLContextGroupResourceBase::value(context));
if (!resource) {
resource = new T(context);
insert(context, resource);
}
return resource;
}
protected:
void freeResource(void *resource) {
delete reinterpret_cast<T *>(resource);
}
};
// Put a guard around a GL object identifier and its context. // Put a guard around a GL object identifier and its context.
// When the context goes away, a shared context will be used // When the context goes away, a shared context will be used
// in its place. If there are no more shared contexts, then // in its place. If there are no more shared contexts, then
// the identifier is returned as zero - it is assumed that the // the identifier is returned as zero - it is assumed that the
// context destruction cleaned up the identifier in this case. // context destruction cleaned up the identifier in this case.
class Q_OPENGL_EXPORT QGLSharedResourceGuard class Q_OPENGL_EXPORT QGLSharedResourceGuardBase : public QGLSharedResource
{ {
public: public:
QGLSharedResourceGuard(const QGLContext *context) QGLSharedResourceGuardBase(QGLContext *context, GLuint id)
: m_group(0), m_id(0), m_next(0), m_prev(0) : QGLSharedResource(context->contextHandle()->shareGroup())
, m_id(id)
{ {
setContext(context);
} }
QGLSharedResourceGuard(const QGLContext *context, GLuint id)
: m_group(0), m_id(id), m_next(0), m_prev(0)
{
setContext(context);
}
~QGLSharedResourceGuard();
const QGLContext *context() const
{
return m_group ? m_group->context() : 0;
}
void setContext(const QGLContext *context);
GLuint id() const GLuint id() const
{ {
return m_id; return m_id;
} }
void setId(GLuint id) protected:
void invalidateResource()
{ {
m_id = id; m_id = 0;
}
void freeResource(QGuiGLContext *context)
{
if (m_id) {
freeResource(QGLContext::fromGuiGLContext(context), m_id);
}
}
virtual void freeResource(QGLContext *ctx, GLuint id) = 0;
private:
GLuint m_id;
};
template <typename Func>
class QGLSharedResourceGuard : public QGLSharedResourceGuardBase
{
public:
QGLSharedResourceGuard(QGLContext *context, GLuint id, Func func)
: QGLSharedResourceGuardBase(context, id)
, m_func(func)
{
}
protected:
void freeResource(QGLContext *ctx, GLuint id)
{
m_func(ctx, id);
} }
private: private:
QGLContextGroup *m_group; Func m_func;
GLuint m_id;
QGLSharedResourceGuard *m_next;
QGLSharedResourceGuard *m_prev;
friend class QGLContextGroup;
}; };
template <typename Func>
QGLSharedResourceGuardBase *createSharedResourceGuard(QGLContext *context, GLuint id, Func cleanupFunc)
{
return new QGLSharedResourceGuard<Func>(context, id, cleanupFunc);
}
class QGLExtensionMatcher class QGLExtensionMatcher
{ {

View File

@ -141,7 +141,7 @@ public:
QAtomicInt ref; QAtomicInt ref;
QGLBuffer::Type type; QGLBuffer::Type type;
QGLSharedResourceGuard guard; QGLSharedResourceGuardBase *guard;
QGLBuffer::UsagePattern usagePattern; QGLBuffer::UsagePattern usagePattern;
QGLBuffer::UsagePattern actualUsagePattern; QGLBuffer::UsagePattern actualUsagePattern;
}; };
@ -184,7 +184,7 @@ QGLBuffer::QGLBuffer(const QGLBuffer &other)
d_ptr->ref.ref(); d_ptr->ref.ref();
} }
#define ctx d->guard.context() #define ctx QGLContext::currentContext();
/*! /*!
Destroys this buffer object, including the storage being Destroys this buffer object, including the storage being
@ -250,6 +250,14 @@ void QGLBuffer::setUsagePattern(QGLBuffer::UsagePattern value)
#undef ctx #undef ctx
namespace {
void freeBufferFunc(QGLContext *ctx, GLuint id)
{
Q_UNUSED(ctx);
glDeleteBuffers(1, &id);
}
}
/*! /*!
Creates the buffer object in the GL server. Returns true if Creates the buffer object in the GL server. Returns true if
the object was created; false otherwise. the object was created; false otherwise.
@ -266,24 +274,26 @@ void QGLBuffer::setUsagePattern(QGLBuffer::UsagePattern value)
bool QGLBuffer::create() bool QGLBuffer::create()
{ {
Q_D(QGLBuffer); Q_D(QGLBuffer);
if (d->guard.id()) if (d->guard && d->guard->id())
return true; return true;
const QGLContext *ctx = QGLContext::currentContext(); QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
if (ctx) { if (ctx) {
if (!qt_resolve_buffer_extensions(const_cast<QGLContext *>(ctx))) if (!qt_resolve_buffer_extensions(ctx))
return false; return false;
GLuint bufferId = 0; GLuint bufferId = 0;
glGenBuffers(1, &bufferId); glGenBuffers(1, &bufferId);
if (bufferId) { if (bufferId) {
d->guard.setContext(ctx); if (d->guard)
d->guard.setId(bufferId); d->guard->free();
d->guard = createSharedResourceGuard(ctx, bufferId, freeBufferFunc);
return true; return true;
} }
} }
return false; return false;
} }
#define ctx d->guard.context() #define ctx QGLContext::currentContext()
/*! /*!
Returns true if this buffer has been created; false otherwise. Returns true if this buffer has been created; false otherwise.
@ -293,7 +303,7 @@ bool QGLBuffer::create()
bool QGLBuffer::isCreated() const bool QGLBuffer::isCreated() const
{ {
Q_D(const QGLBuffer); Q_D(const QGLBuffer);
return d->guard.id() != 0; return d->guard && d->guard->id();
} }
/*! /*!
@ -304,14 +314,10 @@ bool QGLBuffer::isCreated() const
void QGLBuffer::destroy() void QGLBuffer::destroy()
{ {
Q_D(QGLBuffer); Q_D(QGLBuffer);
GLuint bufferId = d->guard.id(); if (d->guard) {
if (bufferId) { d->guard->free();
// Switch to the original creating context to destroy it. d->guard = 0;
QGLShareContextScope scope(d->guard.context());
glDeleteBuffers(1, &bufferId);
} }
d->guard.setId(0);
d->guard.setContext(0);
} }
/*! /*!
@ -358,7 +364,7 @@ void QGLBuffer::write(int offset, const void *data, int count)
qWarning("QGLBuffer::allocate(): buffer not created"); qWarning("QGLBuffer::allocate(): buffer not created");
#endif #endif
Q_D(QGLBuffer); Q_D(QGLBuffer);
if (d->guard.id()) if (d->guard && d->guard->id())
glBufferSubData(d->type, offset, count, data); glBufferSubData(d->type, offset, count, data);
} }
@ -378,7 +384,7 @@ void QGLBuffer::allocate(const void *data, int count)
qWarning("QGLBuffer::allocate(): buffer not created"); qWarning("QGLBuffer::allocate(): buffer not created");
#endif #endif
Q_D(QGLBuffer); Q_D(QGLBuffer);
if (d->guard.id()) if (d->guard && d->guard->id())
glBufferData(d->type, count, data, d->actualUsagePattern); glBufferData(d->type, count, data, d->actualUsagePattern);
} }
@ -413,10 +419,9 @@ bool QGLBuffer::bind()
qWarning("QGLBuffer::bind(): buffer not created"); qWarning("QGLBuffer::bind(): buffer not created");
#endif #endif
Q_D(const QGLBuffer); Q_D(const QGLBuffer);
GLuint bufferId = d->guard.id(); GLuint bufferId = d->guard ? d->guard->id() : 0;
if (bufferId) { if (bufferId) {
if (!QGLContext::areSharing(QGLContext::currentContext(), if (d->guard->group() != QGuiGLContextGroup::currentContextGroup()) {
d->guard.context())) {
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
qWarning("QGLBuffer::bind: buffer is not valid in the current context"); qWarning("QGLBuffer::bind: buffer is not valid in the current context");
#endif #endif
@ -445,7 +450,7 @@ void QGLBuffer::release()
qWarning("QGLBuffer::release(): buffer not created"); qWarning("QGLBuffer::release(): buffer not created");
#endif #endif
Q_D(const QGLBuffer); Q_D(const QGLBuffer);
if (d->guard.id()) if (d->guard && d->guard->id())
glBindBuffer(d->type, 0); glBindBuffer(d->type, 0);
} }
@ -471,7 +476,7 @@ void QGLBuffer::release(QGLBuffer::Type type)
glBindBuffer(GLenum(type), 0); glBindBuffer(GLenum(type), 0);
} }
#define ctx d->guard.context() #define ctx QGLContext::currentContext()
/*! /*!
Returns the GL identifier associated with this buffer; zero if Returns the GL identifier associated with this buffer; zero if
@ -482,7 +487,7 @@ void QGLBuffer::release(QGLBuffer::Type type)
GLuint QGLBuffer::bufferId() const GLuint QGLBuffer::bufferId() const
{ {
Q_D(const QGLBuffer); Q_D(const QGLBuffer);
return d->guard.id(); return d->guard ? d->guard->id() : 0;
} }
#ifndef GL_BUFFER_SIZE #ifndef GL_BUFFER_SIZE
@ -501,7 +506,7 @@ GLuint QGLBuffer::bufferId() const
int QGLBuffer::size() const int QGLBuffer::size() const
{ {
Q_D(const QGLBuffer); Q_D(const QGLBuffer);
if (!d->guard.id()) if (!d->guard || !d->guard->id())
return -1; return -1;
GLint value = -1; GLint value = -1;
glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value); glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value);
@ -529,7 +534,7 @@ void *QGLBuffer::map(QGLBuffer::Access access)
if (!isCreated()) if (!isCreated())
qWarning("QGLBuffer::map(): buffer not created"); qWarning("QGLBuffer::map(): buffer not created");
#endif #endif
if (!d->guard.id()) if (!d->guard || !d->guard->id())
return 0; return 0;
if (!glMapBufferARB) if (!glMapBufferARB)
return 0; return 0;
@ -556,7 +561,7 @@ bool QGLBuffer::unmap()
if (!isCreated()) if (!isCreated())
qWarning("QGLBuffer::unmap(): buffer not created"); qWarning("QGLBuffer::unmap(): buffer not created");
#endif #endif
if (!d->guard.id()) if (!d->guard || !d->guard->id())
return false; return false;
if (!glUnmapBufferARB) if (!glUnmapBufferARB)
return false; return false;

View File

@ -55,8 +55,8 @@ QT_BEGIN_NAMESPACE
extern Q_OPENGL_EXPORT QImage qt_gl_read_framebuffer(const QSize&, bool, bool); extern Q_OPENGL_EXPORT QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
#define QGL_FUNC_CONTEXT const QGLContext *ctx = d_ptr->fbo_guard.context(); #define QGL_FUNC_CONTEXT const QGLContext *ctx = QGLContext::currentContext();
#define QGL_FUNCP_CONTEXT const QGLContext *ctx = fbo_guard.context(); #define QGL_FUNCP_CONTEXT const QGLContext *ctx = QGLContext::currentContext();
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
#define QT_RESET_GLERROR() \ #define QT_RESET_GLERROR() \
@ -351,13 +351,7 @@ void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f,
QGLContext *QGLFBOGLPaintDevice::context() const QGLContext *QGLFBOGLPaintDevice::context() const
{ {
QGLContext *fboContext = const_cast<QGLContext *>(fbo->d_ptr->fbo_guard.context()); return const_cast<QGLContext *>(QGLContext::currentContext());
QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
if (QGLContextPrivate::contextGroup(fboContext) == QGLContextPrivate::contextGroup(currentContext))
return currentContext;
else
return fboContext;
} }
bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const
@ -407,13 +401,33 @@ bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const
return false; return false;
} }
namespace
{
void freeFramebufferFunc(QGLContext *ctx, GLuint id)
{
Q_UNUSED(ctx);
glDeleteFramebuffers(1, &id);
}
void freeRenderbufferFunc(QGLContext *ctx, GLuint id)
{
Q_UNUSED(ctx);
glDeleteRenderbuffers(1, &id);
}
void freeTextureFunc(QGLContext *ctx, GLuint id)
{
Q_UNUSED(ctx);
glDeleteTextures(1, &id);
}
}
void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
QGLFramebufferObject::Attachment attachment, QGLFramebufferObject::Attachment attachment,
GLenum texture_target, GLenum internal_format, GLenum texture_target, GLenum internal_format,
GLint samples, bool mipmap) GLint samples, bool mipmap)
{ {
QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
fbo_guard.setContext(ctx);
bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx))) if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
@ -427,9 +441,11 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
GLuint fbo = 0; GLuint fbo = 0;
glGenFramebuffers(1, &fbo); glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo); glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
fbo_guard.setId(fbo);
glDevice.setFBO(q, attachment); GLuint texture = 0;
GLuint color_buffer = 0;
GLuint depth_buffer = 0;
GLuint stencil_buffer = 0;
QT_CHECK_GLERROR(); QT_CHECK_GLERROR();
// init texture // init texture
@ -603,7 +619,21 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
} }
glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
if (!valid) { if (valid) {
fbo_guard = createSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
if (color_buffer)
color_buffer_guard = createSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
else
texture_guard = createSharedResourceGuard(ctx, texture, freeTextureFunc);
if (depth_buffer)
depth_buffer_guard = createSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
if (stencil_buffer) {
if (stencil_buffer == depth_buffer)
stencil_buffer_guard = depth_buffer_guard;
else
stencil_buffer_guard = createSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
}
} else {
if (color_buffer) if (color_buffer)
glDeleteRenderbuffers(1, &color_buffer); glDeleteRenderbuffers(1, &color_buffer);
else else
@ -613,7 +643,6 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
if (stencil_buffer && depth_buffer != stencil_buffer) if (stencil_buffer && depth_buffer != stencil_buffer)
glDeleteRenderbuffers(1, &stencil_buffer); glDeleteRenderbuffers(1, &stencil_buffer);
glDeleteFramebuffers(1, &fbo); glDeleteFramebuffers(1, &fbo);
fbo_guard.setId(0);
} }
QT_CHECK_GLERROR(); QT_CHECK_GLERROR();
@ -622,6 +651,8 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
format.setAttachment(fbo_attachment); format.setAttachment(fbo_attachment);
format.setInternalTextureFormat(internal_format); format.setInternalTextureFormat(internal_format);
format.setMipmap(mipmap); format.setMipmap(mipmap);
glDevice.setFBO(q, attachment);
} }
/*! /*!
@ -849,23 +880,19 @@ QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachm
QGLFramebufferObject::~QGLFramebufferObject() QGLFramebufferObject::~QGLFramebufferObject()
{ {
Q_D(QGLFramebufferObject); Q_D(QGLFramebufferObject);
QGL_FUNC_CONTEXT;
delete d->engine; delete d->engine;
if (isValid() && ctx) { if (d->texture_guard)
QGLShareContextScope scope(ctx); d->texture_guard->free();
if (d->texture) if (d->color_buffer_guard)
glDeleteTextures(1, &d->texture); d->color_buffer_guard->free();
if (d->color_buffer) if (d->depth_buffer_guard)
glDeleteRenderbuffers(1, &d->color_buffer); d->depth_buffer_guard->free();
if (d->depth_buffer) if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
glDeleteRenderbuffers(1, &d->depth_buffer); d->stencil_buffer_guard->free();
if (d->stencil_buffer && d->stencil_buffer != d->depth_buffer) if (d->fbo_guard)
glDeleteRenderbuffers(1, &d->stencil_buffer); d->fbo_guard->free();
GLuint fbo = d->fbo();
glDeleteFramebuffers(1, &fbo);
}
} }
/*! /*!
@ -889,7 +916,7 @@ QGLFramebufferObject::~QGLFramebufferObject()
bool QGLFramebufferObject::isValid() const bool QGLFramebufferObject::isValid() const
{ {
Q_D(const QGLFramebufferObject); Q_D(const QGLFramebufferObject);
return d->valid && d->fbo_guard.context(); return d->valid && d->fbo_guard && d->fbo_guard->id();
} }
/*! /*!
@ -972,7 +999,7 @@ bool QGLFramebufferObject::release()
GLuint QGLFramebufferObject::texture() const GLuint QGLFramebufferObject::texture() const
{ {
Q_D(const QGLFramebufferObject); Q_D(const QGLFramebufferObject);
return d->texture; return d->texture_guard ? d->texture_guard->id() : 0;
} }
/*! /*!

View File

@ -130,8 +130,9 @@ private:
class QGLFramebufferObjectPrivate class QGLFramebufferObjectPrivate
{ {
public: public:
QGLFramebufferObjectPrivate() : fbo_guard(0), texture(0), depth_buffer(0), stencil_buffer(0) QGLFramebufferObjectPrivate() : fbo_guard(0), texture_guard(0), depth_buffer_guard(0)
, color_buffer(0), valid(false), engine(0) {} , stencil_buffer_guard(0), color_buffer_guard(0)
, valid(false), engine(0) {}
~QGLFramebufferObjectPrivate() {} ~QGLFramebufferObjectPrivate() {}
void init(QGLFramebufferObject *q, const QSize& sz, void init(QGLFramebufferObject *q, const QSize& sz,
@ -139,11 +140,11 @@ public:
GLenum internal_format, GLenum texture_target, GLenum internal_format, GLenum texture_target,
GLint samples = 0, bool mipmap = false); GLint samples = 0, bool mipmap = false);
bool checkFramebufferStatus() const; bool checkFramebufferStatus() const;
QGLSharedResourceGuard fbo_guard; QGLSharedResourceGuardBase *fbo_guard;
GLuint texture; QGLSharedResourceGuardBase *texture_guard;
GLuint depth_buffer; QGLSharedResourceGuardBase *depth_buffer_guard;
GLuint stencil_buffer; QGLSharedResourceGuardBase *stencil_buffer_guard;
GLuint color_buffer; QGLSharedResourceGuardBase *color_buffer_guard;
GLenum target; GLenum target;
QSize size; QSize size;
QGLFramebufferObjectFormat format; QGLFramebufferObjectFormat format;
@ -152,7 +153,7 @@ public:
mutable QPaintEngine *engine; mutable QPaintEngine *engine;
QGLFBOGLPaintDevice glDevice; QGLFBOGLPaintDevice glDevice;
inline GLuint fbo() const { return fbo_guard.id(); } inline GLuint fbo() const { return fbo_guard ? fbo_guard->id() : 0; }
}; };

View File

@ -41,6 +41,7 @@
#include "qglfunctions.h" #include "qglfunctions.h"
#include "qgl_p.h" #include "qgl_p.h"
#include "QtGui/private/qguiglcontext_qpa_p.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -139,16 +140,27 @@ QT_BEGIN_NAMESPACE
*/ */
// Hidden private fields for additional extension data. // Hidden private fields for additional extension data.
struct QGLFunctionsPrivateEx : public QGLFunctionsPrivate struct QGLFunctionsPrivateEx : public QGLFunctionsPrivate, public QGLSharedResource
{ {
QGLFunctionsPrivateEx(const QGLContext *context = 0) QGLFunctionsPrivateEx(QGuiGLContext *context)
: QGLFunctionsPrivate(context) : QGLFunctionsPrivate(QGLContext::fromGuiGLContext(context))
, QGLSharedResource(context->shareGroup())
, m_features(-1) {} , m_features(-1) {}
void invalidateResource()
{
m_features = -1;
}
void freeResource(QGuiGLContext *)
{
// no gl resources to free
}
int m_features; int m_features;
}; };
Q_GLOBAL_STATIC(QGLContextGroupResource<QGLFunctionsPrivateEx>, qt_gl_functions_resource) Q_GLOBAL_STATIC(QGLMultiGroupSharedResource, qt_gl_functions_resource)
static QGLFunctionsPrivateEx *qt_gl_functions(const QGLContext *context = 0) static QGLFunctionsPrivateEx *qt_gl_functions(const QGLContext *context = 0)
{ {
@ -157,13 +169,7 @@ static QGLFunctionsPrivateEx *qt_gl_functions(const QGLContext *context = 0)
Q_ASSERT(context); Q_ASSERT(context);
QGLFunctionsPrivateEx *funcs = QGLFunctionsPrivateEx *funcs =
reinterpret_cast<QGLFunctionsPrivateEx *> reinterpret_cast<QGLFunctionsPrivateEx *>
(qt_gl_functions_resource()->value(context)); (qt_gl_functions_resource()->value<QGLFunctionsPrivateEx>(context->contextHandle()));
#if QT_VERSION < 0x040800
if (!funcs) {
funcs = new QGLFunctionsPrivateEx();
qt_gl_functions_resource()->insert(context, funcs);
}
#endif
return funcs; return funcs;
} }

View File

@ -189,15 +189,15 @@ class QGLShaderPrivate : public QObjectPrivate
{ {
Q_DECLARE_PUBLIC(QGLShader) Q_DECLARE_PUBLIC(QGLShader)
public: public:
QGLShaderPrivate(const QGLContext *context, QGLShader::ShaderType type) QGLShaderPrivate(const QGLContext *, QGLShader::ShaderType type)
: shaderGuard(context) : shaderGuard(0)
, shaderType(type) , shaderType(type)
, compiled(false) , compiled(false)
{ {
} }
~QGLShaderPrivate(); ~QGLShaderPrivate();
QGLSharedResourceGuard shaderGuard; QGLSharedResourceGuardBase *shaderGuard;
QGLShader::ShaderType shaderType; QGLShader::ShaderType shaderType;
bool compiled; bool compiled;
QString log; QString log;
@ -207,22 +207,28 @@ public:
void deleteShader(); void deleteShader();
}; };
#define ctx shaderGuard.context() namespace {
void freeShaderFunc(QGLContext *ctx, GLuint id)
{
Q_UNUSED(ctx);
glDeleteShader(id);
}
}
#define ctx QGLContext::currentContext()
QGLShaderPrivate::~QGLShaderPrivate() QGLShaderPrivate::~QGLShaderPrivate()
{ {
if (shaderGuard.id()) { if (shaderGuard)
QGLShareContextScope scope(shaderGuard.context()); shaderGuard->free();
glDeleteShader(shaderGuard.id());
}
} }
bool QGLShaderPrivate::create() bool QGLShaderPrivate::create()
{ {
const QGLContext *context = shaderGuard.context(); QGLContext *context = const_cast<QGLContext *>(QGLContext::currentContext());
if (!context) if (!context)
return false; return false;
if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) { if (qt_resolve_glsl_extensions(context)) {
GLuint shader; GLuint shader;
if (shaderType == QGLShader::Vertex) if (shaderType == QGLShader::Vertex)
shader = glCreateShader(GL_VERTEX_SHADER); shader = glCreateShader(GL_VERTEX_SHADER);
@ -234,7 +240,7 @@ bool QGLShaderPrivate::create()
qWarning() << "QGLShader: could not create shader"; qWarning() << "QGLShader: could not create shader";
return false; return false;
} }
shaderGuard.setId(shader); shaderGuard = createSharedResourceGuard(context, shader, freeShaderFunc);
return true; return true;
} else { } else {
return false; return false;
@ -243,7 +249,7 @@ bool QGLShaderPrivate::create()
bool QGLShaderPrivate::compile(QGLShader *q) bool QGLShaderPrivate::compile(QGLShader *q)
{ {
GLuint shader = shaderGuard.id(); GLuint shader = shaderGuard ? shaderGuard->id() : 0;
if (!shader) if (!shader)
return false; return false;
glCompileShader(shader); glCompileShader(shader);
@ -286,15 +292,12 @@ bool QGLShaderPrivate::compile(QGLShader *q)
void QGLShaderPrivate::deleteShader() void QGLShaderPrivate::deleteShader()
{ {
if (shaderGuard.id()) { if (shaderGuard) {
glDeleteShader(shaderGuard.id()); shaderGuard->free();
shaderGuard.setId(0); shaderGuard = 0;
} }
} }
#undef ctx
#define ctx d->shaderGuard.context()
/*! /*!
Constructs a new QGLShader object of the specified \a type Constructs a new QGLShader object of the specified \a type
and attaches it to \a parent. If shader programs are not supported, and attaches it to \a parent. If shader programs are not supported,
@ -387,7 +390,7 @@ static const char redefineHighp[] =
bool QGLShader::compileSourceCode(const char *source) bool QGLShader::compileSourceCode(const char *source)
{ {
Q_D(QGLShader); Q_D(QGLShader);
if (d->shaderGuard.id()) { if (d->shaderGuard && d->shaderGuard->id()) {
QVarLengthArray<const char *, 4> src; QVarLengthArray<const char *, 4> src;
QVarLengthArray<GLint, 4> srclen; QVarLengthArray<GLint, 4> srclen;
int headerLen = 0; int headerLen = 0;
@ -420,7 +423,7 @@ bool QGLShader::compileSourceCode(const char *source)
#endif #endif
src.append(source + headerLen); src.append(source + headerLen);
srclen.append(GLint(qstrlen(source + headerLen))); srclen.append(GLint(qstrlen(source + headerLen)));
glShaderSource(d->shaderGuard.id(), src.size(), src.data(), srclen.data()); glShaderSource(d->shaderGuard->id(), src.size(), src.data(), srclen.data());
return d->compile(this); return d->compile(this);
} else { } else {
return false; return false;
@ -480,7 +483,7 @@ bool QGLShader::compileSourceFile(const QString& fileName)
QByteArray QGLShader::sourceCode() const QByteArray QGLShader::sourceCode() const
{ {
Q_D(const QGLShader); Q_D(const QGLShader);
GLuint shader = d->shaderGuard.id(); GLuint shader = d->shaderGuard ? d->shaderGuard->id() : 0;
if (!shader) if (!shader)
return QByteArray(); return QByteArray();
GLint size = 0; GLint size = 0;
@ -525,22 +528,17 @@ QString QGLShader::log() const
GLuint QGLShader::shaderId() const GLuint QGLShader::shaderId() const
{ {
Q_D(const QGLShader); Q_D(const QGLShader);
return d->shaderGuard.id(); return d->shaderGuard ? d->shaderGuard->id() : 0;
} }
#undef ctx #undef ctx
#define ctx programGuard.context()
class QGLShaderProgramPrivate : public QObjectPrivate class QGLShaderProgramPrivate : public QObjectPrivate
{ {
Q_DECLARE_PUBLIC(QGLShaderProgram) Q_DECLARE_PUBLIC(QGLShaderProgram)
public: public:
QGLShaderProgramPrivate(const QGLContext *context) QGLShaderProgramPrivate(const QGLContext *)
: programGuard(context) : programGuard(0)
, linked(false) , linked(false)
, inited(false) , inited(false)
, removingShaders(false) , removingShaders(false)
@ -551,7 +549,7 @@ public:
} }
~QGLShaderProgramPrivate(); ~QGLShaderProgramPrivate();
QGLSharedResourceGuard programGuard; QGLSharedResourceGuardBase *programGuard;
bool linked; bool linked;
bool inited; bool inited;
bool removingShaders; bool removingShaders;
@ -567,12 +565,19 @@ public:
bool hasShader(QGLShader::ShaderType type) const; bool hasShader(QGLShader::ShaderType type) const;
}; };
namespace {
void freeProgramFunc(QGLContext *ctx, GLuint id)
{
Q_UNUSED(ctx);
glDeleteProgram(id);
}
}
QGLShaderProgramPrivate::~QGLShaderProgramPrivate() QGLShaderProgramPrivate::~QGLShaderProgramPrivate()
{ {
if (programGuard.id()) { if (programGuard)
QGLShareContextScope scope(programGuard.context()); programGuard->free();
glDeleteProgram(programGuard.id());
}
} }
bool QGLShaderProgramPrivate::hasShader(QGLShader::ShaderType type) const bool QGLShaderProgramPrivate::hasShader(QGLShader::ShaderType type) const
@ -584,8 +589,7 @@ bool QGLShaderProgramPrivate::hasShader(QGLShader::ShaderType type) const
return false; return false;
} }
#undef ctx #define ctx QGLContext::currentContext()
#define ctx d->programGuard.context()
/*! /*!
Constructs a new shader program and attaches it to \a parent. Constructs a new shader program and attaches it to \a parent.
@ -623,24 +627,21 @@ QGLShaderProgram::~QGLShaderProgram()
bool QGLShaderProgram::init() bool QGLShaderProgram::init()
{ {
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
if (d->programGuard.id() || d->inited) if ((d->programGuard && d->programGuard->id()) || d->inited)
return true; return true;
d->inited = true; d->inited = true;
const QGLContext *context = d->programGuard.context(); QGLContext *context = const_cast<QGLContext *>(QGLContext::currentContext());
if (!context) {
context = QGLContext::currentContext();
d->programGuard.setContext(context);
}
if (!context) if (!context)
return false; return false;
if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) { if (qt_resolve_glsl_extensions(context)) {
GLuint program = glCreateProgram(); GLuint program = glCreateProgram();
if (!program) { if (!program) {
qWarning() << "QGLShaderProgram: could not create shader program"; qWarning() << "QGLShaderProgram: could not create shader program";
return false; return false;
} }
d->programGuard.setId(program); if (d->programGuard)
delete d->programGuard;
d->programGuard = createSharedResourceGuard(context, program, freeProgramFunc);
return true; return true;
} else { } else {
qWarning() << "QGLShaderProgram: shader programs are not supported"; qWarning() << "QGLShaderProgram: shader programs are not supported";
@ -667,15 +668,14 @@ bool QGLShaderProgram::addShader(QGLShader *shader)
return false; return false;
if (d->shaders.contains(shader)) if (d->shaders.contains(shader))
return true; // Already added to this shader program. return true; // Already added to this shader program.
if (d->programGuard.id() && shader) { if (d->programGuard && d->programGuard->id() && shader) {
if (!QGLContext::areSharing(shader->d_func()->shaderGuard.context(), if (!shader->d_func()->shaderGuard || !shader->d_func()->shaderGuard->id())
d->programGuard.context())) { return false;
if (d->programGuard->group() != shader->d_func()->shaderGuard->group()) {
qWarning("QGLShaderProgram::addShader: Program and shader are not associated with same context."); qWarning("QGLShaderProgram::addShader: Program and shader are not associated with same context.");
return false; return false;
} }
if (!shader->d_func()->shaderGuard.id()) glAttachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id());
return false;
glAttachShader(d->programGuard.id(), shader->d_func()->shaderGuard.id());
d->linked = false; // Program needs to be relinked. d->linked = false; // Program needs to be relinked.
d->shaders.append(shader); d->shaders.append(shader);
connect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); connect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed()));
@ -784,14 +784,17 @@ bool QGLShaderProgram::addShaderFromSourceFile
/*! /*!
Removes \a shader from this shader program. The object is not deleted. Removes \a shader from this shader program. The object is not deleted.
The shader program must be valid in the current QGLContext.
\sa addShader(), link(), removeAllShaders() \sa addShader(), link(), removeAllShaders()
*/ */
void QGLShaderProgram::removeShader(QGLShader *shader) void QGLShaderProgram::removeShader(QGLShader *shader)
{ {
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
if (d->programGuard.id() && shader && shader->d_func()->shaderGuard.id()) { if (d->programGuard && d->programGuard->id()
QGLShareContextScope scope(d->programGuard.context()); && shader && shader->d_func()->shaderGuard)
glDetachShader(d->programGuard.id(), shader->d_func()->shaderGuard.id()); {
glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id());
} }
d->linked = false; // Program needs to be relinked. d->linked = false; // Program needs to be relinked.
if (shader) { if (shader) {
@ -826,8 +829,11 @@ void QGLShaderProgram::removeAllShaders()
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
d->removingShaders = true; d->removingShaders = true;
foreach (QGLShader *shader, d->shaders) { foreach (QGLShader *shader, d->shaders) {
if (d->programGuard.id() && shader && shader->d_func()->shaderGuard.id()) if (d->programGuard && d->programGuard->id()
glDetachShader(d->programGuard.id(), shader->d_func()->shaderGuard.id()); && shader && shader->d_func()->shaderGuard)
{
glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id());
}
} }
foreach (QGLShader *shader, d->anonShaders) { foreach (QGLShader *shader, d->anonShaders) {
// Delete shader objects that were created anonymously. // Delete shader objects that were created anonymously.
@ -856,7 +862,7 @@ void QGLShaderProgram::removeAllShaders()
bool QGLShaderProgram::link() bool QGLShaderProgram::link()
{ {
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
GLuint program = d->programGuard.id(); GLuint program = d->programGuard ? d->programGuard->id() : 0;
if (!program) if (!program)
return false; return false;
@ -946,13 +952,13 @@ QString QGLShaderProgram::log() const
bool QGLShaderProgram::bind() bool QGLShaderProgram::bind()
{ {
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
GLuint program = d->programGuard.id(); GLuint program = d->programGuard ? d->programGuard->id() : 0;
if (!program) if (!program)
return false; return false;
if (!d->linked && !link()) if (!d->linked && !link())
return false; return false;
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
if (!QGLContext::areSharing(d->programGuard.context(), QGLContext::currentContext())) { if (d->programGuard->group() != QGuiGLContextGroup::currentContextGroup()) {
qWarning("QGLShaderProgram::bind: program is not valid in the current context."); qWarning("QGLShaderProgram::bind: program is not valid in the current context.");
return false; return false;
} }
@ -974,7 +980,7 @@ void QGLShaderProgram::release()
{ {
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
if (!QGLContext::areSharing(d->programGuard.context(), QGLContext::currentContext())) if (d->programGuard->group() != QGuiGLContextGroup::currentContextGroup())
qWarning("QGLShaderProgram::release: program is not valid in the current context."); qWarning("QGLShaderProgram::release: program is not valid in the current context.");
#endif #endif
#if defined(QT_OPENGL_ES_2) #if defined(QT_OPENGL_ES_2)
@ -996,7 +1002,7 @@ void QGLShaderProgram::release()
GLuint QGLShaderProgram::programId() const GLuint QGLShaderProgram::programId() const
{ {
Q_D(const QGLShaderProgram); Q_D(const QGLShaderProgram);
GLuint id = d->programGuard.id(); GLuint id = d->programGuard ? d->programGuard->id() : 0;
if (id) if (id)
return id; return id;
@ -1005,7 +1011,7 @@ GLuint QGLShaderProgram::programId() const
// themselves, particularly those using program binaries. // themselves, particularly those using program binaries.
if (!const_cast<QGLShaderProgram *>(this)->init()) if (!const_cast<QGLShaderProgram *>(this)->init())
return 0; return 0;
return d->programGuard.id(); return d->programGuard ? d->programGuard->id() : 0;
} }
/*! /*!
@ -1022,9 +1028,9 @@ GLuint QGLShaderProgram::programId() const
void QGLShaderProgram::bindAttributeLocation(const char *name, int location) void QGLShaderProgram::bindAttributeLocation(const char *name, int location)
{ {
Q_D(QGLShaderProgram); Q_D(QGLShaderProgram);
if (!init()) if (!init() || !d->programGuard || !d->programGuard->id())
return; return;
glBindAttribLocation(d->programGuard.id(), location, name); glBindAttribLocation(d->programGuard->id(), location, name);
d->linked = false; // Program needs to be relinked. d->linked = false; // Program needs to be relinked.
} }
@ -1074,8 +1080,8 @@ void QGLShaderProgram::bindAttributeLocation(const QString& name, int location)
int QGLShaderProgram::attributeLocation(const char *name) const int QGLShaderProgram::attributeLocation(const char *name) const
{ {
Q_D(const QGLShaderProgram); Q_D(const QGLShaderProgram);
if (d->linked) { if (d->linked && d->programGuard && d->programGuard->id()) {
return glGetAttribLocation(d->programGuard.id(), name); return glGetAttribLocation(d->programGuard->id(), name);
} else { } else {
qWarning() << "QGLShaderProgram::attributeLocation(" << name qWarning() << "QGLShaderProgram::attributeLocation(" << name
<< "): shader program is not linked"; << "): shader program is not linked";
@ -1752,8 +1758,8 @@ int QGLShaderProgram::uniformLocation(const char *name) const
{ {
Q_D(const QGLShaderProgram); Q_D(const QGLShaderProgram);
Q_UNUSED(d); Q_UNUSED(d);
if (d->linked) { if (d->linked && d->programGuard && d->programGuard->id()) {
return glGetUniformLocation(d->programGuard.id(), name); return glGetUniformLocation(d->programGuard->id(), name);
} else { } else {
qWarning() << "QGLShaderProgram::uniformLocation(" << name qWarning() << "QGLShaderProgram::uniformLocation(" << name
<< "): shader program is not linked"; << "): shader program is not linked";