diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2010-07-06 10:35:02 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2010-07-06 10:35:02 (GMT) |
commit | ebb539d3852658a104e6d59804ea9ad813f9e82d (patch) | |
tree | 463880587981051da456cc7cf8ca3e1bcbc0634c | |
parent | 32d6c5adb9d75ad7e3b2cef818f6b17d77d0f929 (diff) | |
parent | 40c8aba524831dd0d2f8dfcdf07797f5c2c47f5b (diff) | |
download | Qt-ebb539d3852658a104e6d59804ea9ad813f9e82d.zip Qt-ebb539d3852658a104e6d59804ea9ad813f9e82d.tar.gz Qt-ebb539d3852658a104e6d59804ea9ad813f9e82d.tar.bz2 |
Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into master-integration
* 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2: (24 commits)
QGLPixelBuffer should not rely on temporary QGLWidgets.
Initialize pex.
Remove warning.
Fix the tests for the internal GL resource system.
Rework how QGLTextureGlyphCache makes use of the resource system.
The group pointer should no longer be a part of the 64 bit int.
Enable glyph uploads from a thread in the X11 font engine.
Make qt_pixmap_serial thread-safe.
Clean up debug output.
Clarification regarding the WA_X11InitThreads flag.
Rework the internal GL resource system yet again.
Check Qt::AA_X11InitThreads to determine pixmap usage in threads.
Add an application attribute to control XInitThreads().
Unnecessary alloc + remove unused headers.
QGLPixelBuffer should manage its own paint engines.
Remove the DDS texture cache.
Make the GL texture cache share textures between shared contexts.
Redesigned how GL resource management works.
Don't use the QGLSignalProxy to clean up the thread-local shaders.
Simplify how the QGLEngineSharedShaders object is cleaned up.
...
24 files changed, 394 insertions, 283 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index a12e121..9ac6d68 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -530,6 +530,7 @@ public: AA_DontUseNativeMenuBar = 6, AA_MacDontSwapCtrlAndMeta = 7, AA_S60DontConstructApplicationPanes = 8, + AA_X11InitThreads = 9, // Add new attributes before this line AA_AttributeCount diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index d419759..564164b 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -156,6 +156,11 @@ whole lifetime. This attribute must be set before QApplication is constructed. + \value AA_X11InitThreads Calls XInitThreads() as part of the QApplication + construction in order to make Xlib calls thread-safe. This + attribute must be set before QApplication is constructed. + + \omitvalue AA_AttributeCount */ diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index fd2c139..7e41e91 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -99,7 +99,7 @@ static bool qt_pixmap_thread_test() return false; } #ifndef Q_WS_WIN - if (qApp->thread() != QThread::currentThread()) { + if (!QApplication::testAttribute(Qt::AA_X11InitThreads) && qApp->thread() != QThread::currentThread()) { qWarning("QPixmap: It is not safe to use pixmaps outside the GUI thread"); return false; } diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index e8dc5ae..afbfa35 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -310,7 +310,7 @@ static int defaultScreen = -1; QPixmap member functions *****************************************************************************/ -static int qt_pixmap_serial = 0; +QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0); int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; QX11PixmapData::QX11PixmapData(PixelType type) @@ -327,7 +327,7 @@ QPixmapData *QX11PixmapData::createCompatiblePixmapData() const void QX11PixmapData::resize(int width, int height) { - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); w = width; h = height; @@ -410,7 +410,7 @@ struct QX11AlphaDetector void QX11PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags) { - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); w = img.width(); h = img.height(); @@ -1948,7 +1948,7 @@ QPixmap QX11PixmapData::transformed(const QTransform &transform, x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), w, h, d); - x11Data->setSerialNumber(++qt_pixmap_serial); + x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); #ifndef QT_NO_XRENDER if (X11->use_xrender) { @@ -2199,7 +2199,7 @@ void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect) const QX11PixmapData *x11Data = static_cast<const QX11PixmapData*>(data); - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); flags &= ~Uninitialized; xinfo = x11Data->xinfo; @@ -2319,7 +2319,7 @@ QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode) } QX11PixmapData *data = new QX11PixmapData(depth == 1 ? QPixmapData::BitmapType : QPixmapData::PixmapType); - data->setSerialNumber(++qt_pixmap_serial); + data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); data->flags = QX11PixmapData::Readonly; data->share_mode = mode; data->w = width; diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 1af45ba..6f5aa11 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -942,29 +942,36 @@ bool qt_nograb() // application no-grab option typedef QHash<QString, int> WinClassNameHash; Q_GLOBAL_STATIC(WinClassNameHash, winclassNames) +// +// If 0 is passed as the widget pointer, register a window class +// for QWidget as default. This is used in QGLTemporaryContext +// during GL initialization, where we don't want to use temporary +// QWidgets or QGLWidgets, neither do we want to have separate code +// to register window classes. +// const QString qt_reg_winclass(QWidget *w) // register window class { - int flags = w->windowFlags(); + int flags = w ? w->windowFlags() : 0; int type = flags & Qt::WindowType_Mask; uint style; bool icon; QString cname; - if (qt_widget_private(w)->isGLWidget) { + if (w && qt_widget_private(w)->isGLWidget) { cname = QLatin1String("QGLWidget"); style = CS_DBLCLKS; #ifndef Q_WS_WINCE style |= CS_OWNDC; #endif icon = true; - } else if (flags & Qt::MSWindowsOwnDC) { + } else if (w && (flags & Qt::MSWindowsOwnDC)) { cname = QLatin1String("QWidgetOwnDC"); style = CS_DBLCLKS; #ifndef Q_WS_WINCE style |= CS_OWNDC; #endif icon = true; - } else if (type == Qt::Tool || type == Qt::ToolTip){ + } else if (w && (type == Qt::Tool || type == Qt::ToolTip)) { style = CS_DBLCLKS; if (w->inherits("QTipLabel") || w->inherits("QAlphaWidget")) { if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP @@ -979,7 +986,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class style |= CS_SAVEBITS; #endif icon = false; - } else if (type == Qt::Popup) { + } else if (w && (type == Qt::Popup)) { cname = QLatin1String("QPopup"); style = CS_DBLCLKS; #ifndef Q_WS_WINCE @@ -1050,7 +1057,10 @@ const QString qt_reg_winclass(QWidget *w) // register window class } wc.hCursor = 0; #ifndef Q_WS_WINCE - wc.hbrBackground = qt_widget_private(w)->isGLWidget ? 0 : (HBRUSH)GetSysColorBrush(COLOR_WINDOW); + HBRUSH brush = 0; + if (w && !qt_widget_private(w)->isGLWidget) + brush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); + wc.hbrBackground = brush; #else wc.hbrBackground = 0; #endif @@ -1072,8 +1082,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class Q_GUI_EXPORT const QString qt_getRegisteredWndClass() { - QWidget w; - return qt_reg_winclass(&w); + return qt_reg_winclass(0); } static void unregWinClasses() diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 9d5a7c0..5e212ca 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -1710,6 +1710,9 @@ void qt_init(QApplicationPrivate *priv, int, } else { // Qt controls everything (default) + if (QApplication::testAttribute(Qt::AA_X11InitThreads)) + XInitThreads(); + // Set application name and class char *app_class = 0; if (argv && argv[0]) { diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 0f05c6b..a2024e0 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -1634,8 +1634,6 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) } } -extern Q_GUI_EXPORT HDC qt_win_display_dc(); - int QWidget::metric(PaintDeviceMetric m) const { Q_D(const QWidget); @@ -1645,7 +1643,7 @@ int QWidget::metric(PaintDeviceMetric m) const } else if (m == PdmHeight) { val = data->crect.height(); } else { - HDC gdc = qt_win_display_dc(); + HDC gdc = GetDC(0); switch (m) { case PdmDpiX: case PdmPhysicalDpiX: @@ -1696,6 +1694,7 @@ int QWidget::metric(PaintDeviceMetric m) const val = 0; qWarning("QWidget::metric: Invalid metric command"); } + ReleaseDC(0, gdc); } return val; } diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 1bf90e3..a845842 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -155,6 +155,10 @@ static bool qt_painter_thread_test(int devType, const char *what, bool extraCond #endif break; default: +#ifdef Q_WS_X11 + if (QApplication::testAttribute(Qt::AA_X11InitThreads)) + return true; +#endif if (!extraCondition && QThread::currentThread() != qApp->thread()) { qWarning("QPainter: It is not safe to use %s outside the GUI thread", what); return false; @@ -5249,7 +5253,7 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm) return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawPixmap()", true); #endif if (d->extended) { @@ -5319,7 +5323,7 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) if (!d->engine || pm.isNull()) return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawPixmap()", true); #endif qreal x = r.x(); @@ -5726,7 +5730,7 @@ void QPainter::drawGlyphs(const QPointF &position, const QGlyphs &glyphs) int count = qMin(glyphIndexes.size(), glyphPositions.size()); QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count); for (int i=0; i<count; ++i) - fixedPointPositions[i] = QFixedPoint::fromPointF(position + glyphPositions.at(i)); + fixedPointPositions[i] = QFixedPoint::fromPointF(position + glyphPositions.at(i)); d->drawGlyphs(glyphIndexes.data(), fixedPointPositions.data(), count); @@ -5735,7 +5739,7 @@ void QPainter::drawGlyphs(const QPointF &position, const QGlyphs &glyphs) void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, const QPointF *positionArray, int glyphCount) -{ +{ QVarLengthArray<QFixedPoint, 128> positions(glyphCount); for (int i=0; i<glyphCount; ++i) positions[i] = QFixedPoint::fromPointF(positionArray[i]); @@ -5923,7 +5927,7 @@ void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText // Recreate the layout of the static text because the matrix or font has changed if (staticTextNeedsReinit) - staticText_d->init(); + staticText_d->init(); if (transformedPosition != staticText_d->position) { // Translate to actual position QFixed fx = QFixed::fromReal(transformedPosition.x()); @@ -6663,7 +6667,7 @@ void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPo return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()", true); #endif qreal sw = pixmap.width(); diff --git a/src/gui/text/qfontengine_x11.cpp b/src/gui/text/qfontengine_x11.cpp index b7e4be2..aee21f6 100644 --- a/src/gui/text/qfontengine_x11.cpp +++ b/src/gui/text/qfontengine_x11.cpp @@ -992,7 +992,7 @@ QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int s face_id.filename = file_name; face_id.index = face_index; - canUploadGlyphsToServer = qApp->thread() == QThread::currentThread(); + canUploadGlyphsToServer = QApplication::testAttribute(Qt::AA_X11InitThreads) || (qApp->thread() == QThread::currentThread()); subpixelType = Subpixel_None; if (antialias) { diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 40b3641..4b5f53e 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -46,24 +46,29 @@ #include <QMetaEnum> #endif +// #define QT_GL_SHARED_SHADER_DEBUG QT_BEGIN_NAMESPACE -static void qt_shared_shaders_free(void *data) +class QGLShaderStorage { - delete reinterpret_cast<QGLEngineSharedShaders *>(data); -} +public: + QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { + QGLContextGroupResource<QGLEngineSharedShaders> *&shaders = m_storage.localData(); + if (!shaders) + shaders = new QGLContextGroupResource<QGLEngineSharedShaders>(); + return shaders->value(context); + } + +private: + QThreadStorage<QGLContextGroupResource<QGLEngineSharedShaders> *> m_storage; +}; -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shared_shaders, (qt_shared_shaders_free)) +Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage); QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context) { - QGLEngineSharedShaders *p = reinterpret_cast<QGLEngineSharedShaders *>(qt_shared_shaders()->value(context)); - if (!p) { - QGLShareContextScope scope(context); - qt_shared_shaders()->insert(context, p = new QGLEngineSharedShaders(context)); - } - return p; + return qt_shader_storage()->shadersForThread(context); } const char* QGLEngineSharedShaders::qShaderSnippets[] = { @@ -170,18 +175,20 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) source.clear(); source.append(qShaderSnippets[MainVertexShader]); source.append(qShaderSnippets[PositionOnlyVertexShader]); - vertexShader = new QGLShader(QGLShader::Vertex, context, this); + vertexShader = new QGLShader(QGLShader::Vertex, context, 0); + shaders.append(vertexShader); if (!vertexShader->compileSourceCode(source)) qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); source.clear(); source.append(qShaderSnippets[MainFragmentShader]); source.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); - fragShader = new QGLShader(QGLShader::Fragment, context, this); + fragShader = new QGLShader(QGLShader::Fragment, context, 0); + shaders.append(fragShader); if (!fragShader->compileSourceCode(source)) qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); - simpleShaderProg = new QGLShaderProgram(context, this); + simpleShaderProg = new QGLShaderProgram(context, 0); simpleShaderProg->addShader(vertexShader); simpleShaderProg->addShader(fragShader); simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); @@ -198,18 +205,20 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) source.clear(); source.append(qShaderSnippets[MainWithTexCoordsVertexShader]); source.append(qShaderSnippets[UntransformedPositionVertexShader]); - vertexShader = new QGLShader(QGLShader::Vertex, context, this); + vertexShader = new QGLShader(QGLShader::Vertex, context, 0); + shaders.append(vertexShader); if (!vertexShader->compileSourceCode(source)) qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); source.clear(); source.append(qShaderSnippets[MainFragmentShader]); source.append(qShaderSnippets[ImageSrcFragmentShader]); - fragShader = new QGLShader(QGLShader::Fragment, context, this); + fragShader = new QGLShader(QGLShader::Fragment, context, 0); + shaders.append(fragShader); if (!fragShader->compileSourceCode(source)) qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); - blitShaderProg = new QGLShaderProgram(context, this); + blitShaderProg = new QGLShaderProgram(context, 0); blitShaderProg->addShader(vertexShader); blitShaderProg->addShader(fragShader); blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); @@ -220,13 +229,21 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) << simpleShaderProg->log(); } +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif } QGLEngineSharedShaders::~QGLEngineSharedShaders() { - QList<QGLEngineShaderProg*>::iterator itr; - for (itr = cachedPrograms.begin(); itr != cachedPrograms.end(); ++itr) - delete *itr; +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> ~QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif + qDeleteAll(shaders); + shaders.clear(); + + qDeleteAll(cachedPrograms); + cachedPrograms.clear(); if (blitShaderProg) { delete blitShaderProg; @@ -276,7 +293,8 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS source.append(qShaderSnippets[prog.compositionFragShader]); if (prog.maskFragShader) source.append(qShaderSnippets[prog.maskFragShader]); - fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), this); + fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0); + shaders.append(fragShader); QByteArray description; #if defined(QT_DEBUG) // Name the shader for easier debugging @@ -302,7 +320,8 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS source.clear(); source.append(qShaderSnippets[prog.mainVertexShader]); source.append(qShaderSnippets[prog.positionVertexShader]); - vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), this); + vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0); + shaders.append(vertexShader); #if defined(QT_DEBUG) // Name the shader for easier debugging description.clear(); @@ -320,7 +339,7 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS newProg = new QGLEngineShaderProg(prog); // If the shader program's not found in the cache, create it now. - newProg->program = new QGLShaderProgram(ctxGuard.context(), this); + newProg->program = new QGLShaderProgram(ctxGuard.context(), 0); newProg->program->addShader(vertexShader); newProg->program->addShader(fragShader); @@ -413,7 +432,6 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) currentShaderProg(0) { sharedShaders = QGLEngineSharedShaders::shadersForContext(context); - connect(sharedShaders, SIGNAL(shaderProgNeedsChanging()), this, SLOT(shaderProgNeedsChangingSlot())); } QGLEngineShaderManager::~QGLEngineShaderManager() diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 06b96ae..e5ababf 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -259,9 +259,9 @@ static const GLuint QT_PMV_MATRIX_3_ATTR = 5; class QGLEngineShaderProg; -class QGLEngineSharedShaders : public QObject +class QGLEngineSharedShaders { - Q_OBJECT + Q_GADGET public: enum SnippetName { @@ -364,14 +364,12 @@ public: // full. void cleanupCustomStage(QGLCustomShaderStage* stage); -signals: - void shaderProgNeedsChanging(); - private: QGLSharedResourceGuard ctxGuard; QGLShaderProgram *blitShaderProg; QGLShaderProgram *simpleShaderProg; QList<QGLEngineShaderProg*> cachedPrograms; + QList<QGLShader *> shaders; static const char* qShaderSnippets[TotalSnippetCount]; }; @@ -492,9 +490,6 @@ public: QGLEngineSharedShaders* sharedShaders; -private slots: - void shaderProgNeedsChangingSlot() { shaderProgNeedsChanging = true; } - private: QGLContext* ctx; bool shaderProgNeedsChanging; diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp index a1495dd..f1b095d 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp @@ -46,22 +46,11 @@ QT_BEGIN_NAMESPACE -static void QGL2GradientCache_free(void *ptr) -{ - delete reinterpret_cast<QGL2GradientCache *>(ptr); -} - -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_gradient_caches, (QGL2GradientCache_free)) +Q_GLOBAL_STATIC(QGLContextGroupResource<QGL2GradientCache>, qt_gradient_caches) QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) { - QGL2GradientCache *p = reinterpret_cast<QGL2GradientCache *>(qt_gradient_caches()->value(context)); - if (!p) { - QGLShareContextScope scope(context); - p = new QGL2GradientCache; - qt_gradient_caches()->insert(context, p); - } - return p; + return qt_gradient_caches()->value(context); } void QGL2GradientCache::cleanCache() { diff --git a/src/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/gl2paintengineex/qglgradientcache_p.h index 0a5f846..8ecb34e 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache_p.h +++ b/src/opengl/gl2paintengineex/qglgradientcache_p.h @@ -75,8 +75,8 @@ class QGL2GradientCache public: static QGL2GradientCache *cacheForContext(const QGLContext *context); - QGL2GradientCache() { } - ~QGL2GradientCache() {cleanCache();} + QGL2GradientCache(const QGLContext *) {} + ~QGL2GradientCache() { cleanCache(); } GLuint getBuffer(const QGradient &gradient, qreal opacity); inline int paletteSize() const { return 1024; } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 634b315..756180f 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1453,11 +1453,12 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp QOpenGL2PaintEngineState *s = q->state(); + void *cacheKey = const_cast<QGLContext *>(QGLContextPrivate::contextGroup(ctx)->context()); QGLTextureGlyphCache *cache = - (QGLTextureGlyphCache *) staticTextItem->fontEngine->glyphCache(ctx, glyphType, QTransform()); + (QGLTextureGlyphCache *) staticTextItem->fontEngine->glyphCache(cacheKey, glyphType, QTransform()); if (!cache || cache->cacheType() != glyphType) { cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform()); - staticTextItem->fontEngine->setGlyphCache(ctx, cache); + staticTextItem->fontEngine->setGlyphCache(cacheKey, cache); } cache->setPaintEnginePrivate(this); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index faf4563..9ce5d55 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -56,53 +56,24 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngin : QImageTextureGlyphCache(type, matrix) , ctx(0) , pex(0) - , m_width(0) - , m_height(0) { - if (context != 0) - setContext(context); +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx); +#endif + setContext(context); } QGLTextureGlyphCache::~QGLTextureGlyphCache() { - cleanUpContext(); -} - -void QGLTextureGlyphCache::cleanUpContext() -{ - if (ctx) { - QGLShareContextScope scope(ctx); - - if (!ctx->d_ptr->workaround_brokenFBOReadBack && pex != 0) - glDeleteFramebuffers(1, &m_fbo); - - if (m_width || m_height) { - glDeleteTextures(1, &m_texture); - m_width = 0; - m_height = 0; - m_h = 0; - } - - ctx = 0; - } +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> ~QGLTextureGlyphCache() %p.", this); +#endif } void QGLTextureGlyphCache::setContext(const QGLContext *context) { - cleanUpContext(); - ctx = context; - - // broken FBO readback is a bug in the SGX 1.3 and 1.4 drivers for the N900 where - // copying between FBO's is broken if the texture is either GL_ALPHA or POT. The - // workaround is to use a system-memory copy of the glyph cache for this device. - // Switching to NPOT and GL_RGBA would both cost a lot more graphics memory and - // be slower, so that is not desireable. - if (!ctx->d_ptr->workaround_brokenFBOReadBack && pex != 0) - glGenFramebuffers(1, &m_fbo); - - connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)), - SLOT(contextDestroyed(const QGLContext*))); + m_h = 0; } void QGLTextureGlyphCache::createTextureData(int width, int height) @@ -124,11 +95,12 @@ void QGLTextureGlyphCache::createTextureData(int width, int height) if (height < 16) height = 16; - glGenTextures(1, &m_texture); - glBindTexture(GL_TEXTURE_2D, m_texture); + QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx); + glGenTextures(1, &glyphTexture->m_texture); + glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); - m_width = width; - m_height = height; + glyphTexture->m_width = width; + glyphTexture->m_height = height; QVarLengthArray<uchar> data(width * height); for (int i = 0; i < data.size(); ++i) @@ -149,9 +121,10 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context"); return; } + QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx); - int oldWidth = m_width; - int oldHeight = m_height; + int oldWidth = glyphTexture->m_width; + int oldHeight = glyphTexture->m_height; // Make the lower glyph texture size 16 x 16. if (width < 16) @@ -159,9 +132,9 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) if (height < 16) height = 16; - GLuint oldTexture = m_texture; + GLuint oldTexture = glyphTexture->m_texture; createTextureData(width, height); - + if (pex == 0 || ctx->d_ptr->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::resizeTextureData(width, height); Q_ASSERT(image().depth() == 8); @@ -173,7 +146,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) // ### the QTextureGlyphCache API needs to be reworked to allow // ### resizeTextureData to fail - glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, glyphTexture->m_fbo); GLuint tmp_texture; glGenTextures(1, &tmp_texture); @@ -228,7 +201,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindTexture(GL_TEXTURE_2D, m_texture); + glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); @@ -250,10 +223,11 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) return; } + QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx); if (pex == 0 || ctx->d_ptr->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::fillTexture(c, glyph); - glBindTexture(GL_TEXTURE_2D, m_texture); + glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); const QImage &texture = image(); const uchar *bits = texture.constBits(); bits += c.y * texture.bytesPerLine() + c.x; @@ -261,7 +235,6 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits); bits += texture.bytesPerLine(); } - return; } @@ -291,7 +264,7 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) } } - glBindTexture(GL_TEXTURE_2D, m_texture); + glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); if (mask.format() == QImage::Format_RGB32) { glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits()); } else { diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index d291ac3..66aed91 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -57,14 +57,49 @@ #include <private/qgl_p.h> #include <qglshaderprogram.h> +// #define QT_GL_TEXTURE_GLYPH_CACHE_DEBUG QT_BEGIN_NAMESPACE class QGL2PaintEngineExPrivate; -class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QObject, public QImageTextureGlyphCache +struct QGLGlyphTexture +{ + QGLGlyphTexture(const QGLContext *ctx) + : m_width(0) + , m_height(0) + { + if (ctx && !ctx->d_ptr->workaround_brokenFBOReadBack) + glGenFramebuffers(1, &m_fbo); + +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> QGLGlyphTexture() %p for context %p.", this, ctx); +#endif + } + + ~QGLGlyphTexture() { + const QGLContext *ctx = QGLContext::currentContext(); +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug("~QGLGlyphTexture() %p for context %p.", this, ctx); +#endif + // At this point, the context group is made current, so it's safe to + // release resources without a makeCurrent() call + if (ctx) { + if (!ctx->d_ptr->workaround_brokenFBOReadBack) + glDeleteFramebuffers(1, &m_fbo); + if (m_width || m_height) + glDeleteTextures(1, &m_texture); + } + } + + GLuint m_texture; + GLuint m_fbo; + int m_width; + int m_height; +}; + +class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QImageTextureGlyphCache { - Q_OBJECT public: QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); ~QGLTextureGlyphCache(); @@ -75,56 +110,33 @@ public: virtual int glyphMargin() const; virtual int glyphPadding() const; - inline GLuint texture() const { return m_texture; } + inline GLuint texture() const { + QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); + QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); + return glyphTexture ? glyphTexture->m_texture : 0; + } - inline int width() const { return m_width; } - inline int height() const { return m_height; } + inline int width() const { + QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); + QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); + return glyphTexture ? glyphTexture->m_width : 0; + } + inline int height() const { + QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); + QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); + return glyphTexture ? glyphTexture->m_height : 0; + } inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } void setContext(const QGLContext *context); - inline const QGLContext *context() const - { - return ctx; - } - - - -public Q_SLOTS: - void contextDestroyed(const QGLContext *context) { - if (context == ctx) { - const QGLContext *nextCtx = qt_gl_transfer_context(ctx); - if (!nextCtx) { - // the context may not be current, so we cannot directly - // destroy the fbo and texture here, but since the context - // is about to be destroyed, the GL server will do the - // clean up for us anyway - m_fbo = 0; - m_texture = 0; - ctx = 0; - } else { - // since the context holding the texture is shared, and - // about to be destroyed, we have to transfer ownership - // of the texture to one of the share contexts - ctx = const_cast<QGLContext *>(nextCtx); - } - } - } + inline const QGLContext *context() const { return ctx; } private: - void cleanUpContext(); + QGLContextGroupResource<QGLGlyphTexture> m_textureResource; const QGLContext *ctx; - QGL2PaintEngineExPrivate *pex; - - GLuint m_texture; - GLuint m_fbo; - - int m_width; - int m_height; - - QGLShaderProgram *m_program; }; QT_END_NAMESPACE diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 9effb34..3f2af9d 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -94,6 +94,8 @@ #include <qmutex.h> +// #define QT_GL_CONTEXT_RESOURCE_DEBUG + QT_BEGIN_NAMESPACE #if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) @@ -2054,6 +2056,9 @@ QGLContext::~QGLContext() // remove any textures cached in this context QGLTextureCache::instance()->removeContextTextures(this); + // clean up resources specific to this context + d_ptr->cleanup(); + // clean up resources belonging to this context's group d_ptr->group->cleanupResources(this); QGLSignalProxy::instance()->emitAboutToDestroyContext(this); @@ -2062,6 +2067,10 @@ QGLContext::~QGLContext() void QGLContextPrivate::cleanup() { + QHash<QGLContextResourceBase *, void *>::ConstIterator it; + for (it = m_resources.begin(); it != m_resources.end(); ++it) + it.key()->freeResource(it.value()); + m_resources.clear(); } #define ctx q_ptr @@ -2114,20 +2123,10 @@ void QGLContextPrivate::syncGlState() GLuint QGLContext::bindTexture(const QString &fileName) { - Q_D(QGLContext); - QGLDDSCache *dds_cache = &(d->group->m_dds_cache); - QGLDDSCache::const_iterator it = dds_cache->constFind(fileName); - if (it != dds_cache->constEnd()) { - glBindTexture(GL_TEXTURE_2D, it.value()); - return it.value(); - } - QGLTexture texture(this); QSize size = texture.bindCompressedTexture(fileName); if (!size.isValid()) return 0; - - dds_cache->insert(fileName, texture.id); return texture.id; } @@ -2547,7 +2546,8 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, const QX11Info *xinfo = qt_x11Info(paintDevice); if (pd->classId() == QPixmapData::X11Class && pd->pixelType() == QPixmapData::PixmapType && xinfo && xinfo->screen() == pixmap.x11Info().screen() - && target == GL_TEXTURE_2D) + && target == GL_TEXTURE_2D + && QApplication::instance()->thread() == QThread::currentThread()) { texture = bindTextureFromNativePixmap(const_cast<QPixmap*>(&pixmap), key, options); if (texture) { @@ -2752,24 +2752,8 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, Q */ void QGLContext::deleteTexture(GLuint id) { - Q_D(QGLContext); - if (QGLTextureCache::instance()->remove(this, id)) return; - - // check the DDS cache if the texture wasn't found in the pixmap/image - // cache - QGLDDSCache *dds_cache = &(d->group->m_dds_cache); - QList<QString> ddsKeys = dds_cache->keys(); - for (int i = 0; i < ddsKeys.size(); ++i) { - GLuint texture = dds_cache->value(ddsKeys.at(i)); - if (id == texture) { - dds_cache->remove(ddsKeys.at(i)); - break; - } - } - - // Finally, actually delete the texture ID glDeleteTextures(1, &id); } @@ -4087,13 +4071,7 @@ bool QGLWidget::event(QEvent *e) } #if defined(Q_WS_X11) - // prevents X errors on some systems, where we get a flush to a - // hidden widget - if (e->type() == QEvent::Hide) { - makeCurrent(); - glFinish(); - doneCurrent(); - } else if (e->type() == QEvent::ParentChange) { + if (e->type() == QEvent::ParentChange) { // if we've reparented a window that has the current context // bound, we need to rebind that context to the new window id if (d->glcx == QGLContext::currentContext()) @@ -5060,24 +5038,24 @@ void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QM #endif #ifndef QT_OPENGL_ES_1 -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_2_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_gl_2_engine) #endif #ifndef QT_OPENGL_ES_2 -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_gl_engine) #endif Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine() { #if defined(QT_OPENGL_ES_1) - return qt_gl_engine(); + return qt_gl_engine()->engine(); #elif defined(QT_OPENGL_ES_2) - return qt_gl_2_engine(); + return qt_gl_2_engine()->engine(); #else if (qt_gl_preferGL2Engine()) - return qt_gl_2_engine(); + return qt_gl_2_engine()->engine(); else - return qt_gl_engine(); + return qt_gl_engine()->engine(); #endif } @@ -5340,13 +5318,23 @@ void QGLContextGroup::removeShare(const QGLContext *context) { group->m_shares.clear(); } -QGLContextResource::QGLContextResource(FreeFunc f) - : free(f), active(0) +QGLContextGroupResourceBase::QGLContextGroupResourceBase() + : active(0) { +#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG + qDebug("Creating context group resource object %p.", this); +#endif } -QGLContextResource::~QGLContextResource() +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" @@ -5356,37 +5344,47 @@ QGLContextResource::~QGLContextResource() #endif } -void QGLContextResource::insert(const QGLContext *key, void *value) +void QGLContextGroupResourceBase::insert(const QGLContext *context, void *value) { - QGLContextGroup *group = QGLContextPrivate::contextGroup(key); +#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 *QGLContextResource::value(const QGLContext *key) +void *QGLContextGroupResourceBase::value(const QGLContext *context) { - QGLContextGroup *group = QGLContextPrivate::contextGroup(key); + QGLContextGroup *group = QGLContextPrivate::contextGroup(context); return group->m_resources.value(this, 0); } -void QGLContextResource::cleanup(const QGLContext *ctx, void *value) +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); - free(value); + freeResource(value); active.deref(); + + QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx); + m_groups.removeOne(group); } -void QGLContextGroup::cleanupResources(const QGLContext *ctx) +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<QGLContextResource *, void *>::ConstIterator it; + QHash<QGLContextGroupResourceBase *, void *>::ConstIterator it; for (it = m_resources.begin(); it != m_resources.end(); ++it) - it.key()->cleanup(ctx, it.value()); + it.key()->cleanup(context, it.value()); } QGLSharedResourceGuard::~QGLSharedResourceGuard() diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index f85cad5..9315ac4 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -424,6 +424,7 @@ private: friend class QGLPixmapData; friend class QGLPixmapFilterBase; friend class QGLTextureGlyphCache; + friend struct QGLGlyphTexture; friend class QGLContextGroup; friend class QGLSharedResourceGuard; friend class QGLPixmapBlurFilter; @@ -443,6 +444,7 @@ private: friend class QGLWidgetGLPaintDevice; friend class QX11GLPixmapData; friend class QX11GLSharedContexts; + friend class QGLContextResourceBase; private: Q_DISABLE_COPY(QGLContext) }; diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index cd5dbbe..eca4a29 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -210,11 +210,9 @@ public: #endif }; -class QGLContextResource; +class QGLContextGroupResourceBase; class QGLSharedResourceGuard; -typedef QHash<QString, GLuint> QGLDDSCache; - // QGLContextPrivate has the responsibility of creating context groups. // QGLContextPrivate maintains the reference counter and destroys // context groups when needed. @@ -233,22 +231,22 @@ public: static void addShare(const QGLContext *context, const QGLContext *share); static void removeShare(const QGLContext *context); + private: QGLContextGroup(const QGLContext *context); QGLExtensionFuncs m_extensionFuncs; const QGLContext *m_context; // context group's representative QList<const QGLContext *> m_shares; - QHash<QGLContextResource *, void *> m_resources; + QHash<QGLContextGroupResourceBase *, void *> m_resources; QGLSharedResourceGuard *m_guards; // double-linked list of active guards. QAtomicInt m_refs; - QGLDDSCache m_dds_cache; void cleanupResources(const QGLContext *ctx); friend class QGLContext; friend class QGLContextPrivate; - friend class QGLContextResource; + friend class QGLContextGroupResourceBase; }; // Get the context that resources for "ctx" will transfer to once @@ -313,6 +311,8 @@ class QGLTexture; // all the GL2 engine uses: #define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3 +class QGLContextResourceBase; + class QGLContextPrivate { Q_DECLARE_PUBLIC(QGLContext) @@ -404,6 +404,7 @@ public: GLuint current_fbo; GLuint default_fbo; QPaintEngine *active_engine; + QHash<QGLContextResourceBase *, void *> m_resources; bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT]; @@ -612,24 +613,130 @@ inline GLenum qt_gl_preferredTextureTarget() #endif } -// One resource per group of shared contexts. -class Q_OPENGL_EXPORT QGLContextResource +/* + Base for resources that are shared in a context group. +*/ +class Q_OPENGL_EXPORT QGLContextGroupResourceBase { public: - typedef void (*FreeFunc)(void *); - QGLContextResource(FreeFunc f); - ~QGLContextResource(); - // Set resource 'value' for 'key' and all its shared contexts. - void insert(const QGLContext *key, void *value); - // Return resource for 'key' or a shared context. - void *value(const QGLContext *key); - // Cleanup 'value' in response to a context group being destroyed. - void cleanup(const QGLContext *ctx, void *value); + QGLContextGroupResourceBase(); + virtual ~QGLContextGroupResourceBase(); + void insert(const QGLContext *context, void *value); + void *value(const QGLContext *context); + void cleanup(const QGLContext *context, void *value); + virtual void freeResource(void *value) = 0; + +protected: + QList<QGLContextGroup *> m_groups; + private: - FreeFunc free; 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); + } +}; + +/* + Base for resources that are context specific. +*/ +class Q_OPENGL_EXPORT QGLContextResourceBase +{ +public: + virtual ~QGLContextResourceBase() { + for (int i = 0; i < m_contexts.size(); ++i) + m_contexts.at(i)->d_ptr->m_resources.remove(this); + } + + void insert(const QGLContext *context, void *value) { + context->d_ptr->m_resources.insert(this, value); + } + + void *value(const QGLContext *context) { + return context->d_ptr->m_resources.value(this, 0); + } + virtual void freeResource(void *value) = 0; + +protected: + QList<const QGLContext *> m_contexts; +}; + +/* + The QGLContextResource template is used to manage a resource for a + single GL context. Just before the context is destroyed (while it's + still the current context), or when the QGLContextResource 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 QGLContextResource : public QGLContextResourceBase +{ +public: + ~QGLContextResource() { + for (int i = 0; i < m_contexts.size(); ++i) { + const QGLContext *context = m_contexts.at(i); + T *resource = reinterpret_cast<T *>(QGLContextResourceBase::value(context)); + if (resource) { + QGLShareContextScope scope(context); + delete resource; + } + } + } + + T *value(const QGLContext *context) { + T *resource = reinterpret_cast<T *>(QGLContextResourceBase::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. // When the context goes away, a shared context will be used // in its place. If there are no more shared contexts, then @@ -719,6 +826,24 @@ private: int gl_extensions_length; }; + +// this is a class that wraps a QThreadStorage object for storing +// thread local instances of the GL 1 and GL 2 paint engines + +template <class T> +class QGLEngineThreadStorage +{ +public: + QPaintEngine *engine() { + QPaintEngine *&localEngine = storage.localData(); + if (!localEngine) + localEngine = new T; + return localEngine; + } + +private: + QThreadStorage<QPaintEngine *> storage; +}; QT_END_NAMESPACE #endif // QGL_P_H diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index deffc20..fe60e83 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -984,11 +984,11 @@ QImage QGLFramebufferObject::toImage() const } #if !defined(QT_OPENGL_ES_1) -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine) #endif #ifndef QT_OPENGL_ES_2 -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_buffer_engine) #endif /*! \reimp */ @@ -1002,7 +1002,7 @@ QPaintEngine *QGLFramebufferObject::paintEngine() const #if !defined (QT_OPENGL_ES_2) if (qt_gl_preferGL2Engine()) { #endif - QPaintEngine *engine = qt_buffer_2_engine(); + QPaintEngine *engine = qt_buffer_2_engine()->engine(); if (engine->isActive() && engine->paintDevice() != this) { d->engine = new QGL2PaintEngineEx; return d->engine; @@ -1014,7 +1014,7 @@ QPaintEngine *QGLFramebufferObject::paintEngine() const #endif #if !defined(QT_OPENGL_ES_2) - QPaintEngine *engine = qt_buffer_engine(); + QPaintEngine *engine = qt_buffer_engine()->engine(); if (engine->isActive() && engine->paintDevice() != this) { d->engine = new QOpenGLPaintEngine; return d->engine; diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp index 9a8b243..3d9cd21 100644 --- a/src/opengl/qglpixelbuffer.cpp +++ b/src/opengl/qglpixelbuffer.cpp @@ -388,25 +388,25 @@ bool QGLPixelBuffer::isValid() const } #if !defined(QT_OPENGL_ES_1) -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine) #endif #ifndef QT_OPENGL_ES_2 -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_buffer_engine) #endif /*! \reimp */ QPaintEngine *QGLPixelBuffer::paintEngine() const { #if defined(QT_OPENGL_ES_1) - return qt_buffer_engine(); + return qt_buffer_engine()->engine(); #elif defined(QT_OPENGL_ES_2) - return qt_buffer_2_engine(); + return qt_buffer_2_engine()->engine(); #else if (qt_gl_preferGL2Engine()) - return qt_buffer_2_engine(); + return qt_buffer_2_engine()->engine(); else - return qt_buffer_engine(); + return qt_buffer_engine()->engine(); #endif } diff --git a/src/opengl/qglpixelbuffer_win.cpp b/src/opengl/qglpixelbuffer_win.cpp index 8d0d105..df83566 100644 --- a/src/opengl/qglpixelbuffer_win.cpp +++ b/src/opengl/qglpixelbuffer_win.cpp @@ -239,8 +239,7 @@ static void qt_format_to_attrib_list(bool has_render_texture, const QGLFormat &f bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget) { - QGLWidget dmy; - dmy.makeCurrent(); // needed for wglGetProcAddress() to succeed + QGLTemporaryContext tempContext; PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC) wglGetProcAddress("wglCreatePbufferARB"); @@ -254,7 +253,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge if (!wglCreatePbufferARB) // assumes that if one can be resolved, all of them can return false; - dc = GetDC(dmy.winId()); + dc = wglGetCurrentDC(); Q_ASSERT(dc); PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = @@ -284,7 +283,6 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge if (num_formats == 0) { qWarning("QGLPixelBuffer: Unable to find a pixel format with pbuffer - giving up."); - ReleaseDC(dmy.winId(), dc); return false; } format = pfiToQGLFormat(dc, pixel_format); @@ -303,12 +301,10 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge has_render_texture = false; if (!pbuf) { qWarning("QGLPixelBuffer: Unable to create pbuffer [w=%d, h=%d] - giving up.", size.width(), size.height()); - ReleaseDC(dmy.winId(), dc); return false; } } - ReleaseDC(dmy.winId(), dc); dc = wglGetPbufferDCARB(pbuf); ctx = wglCreateContext(dc); diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index bfa5ef1..68586c1 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -315,7 +315,7 @@ class QGLBlurTextureCache : public QObject public: static QGLBlurTextureCache *cacheForContext(const QGLContext *context); - QGLBlurTextureCache(); + QGLBlurTextureCache(const QGLContext *); ~QGLBlurTextureCache(); QGLBlurTextureInfo *takeBlurTextureInfo(const QPixmap &pixmap); @@ -336,15 +336,9 @@ private: }; QList<QGLBlurTextureCache *> QGLBlurTextureCache::blurTextureCaches; +Q_GLOBAL_STATIC(QGLContextGroupResource<QGLBlurTextureCache>, qt_blur_texture_caches) -static void QGLBlurTextureCache_free(void *ptr) -{ - delete reinterpret_cast<QGLBlurTextureCache *>(ptr); -} - -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_blur_texture_caches, (QGLBlurTextureCache_free)) - -QGLBlurTextureCache::QGLBlurTextureCache() +QGLBlurTextureCache::QGLBlurTextureCache(const QGLContext *) : timerId(0) { cache.setMaxCost(4 * 1024 * 1024); @@ -366,12 +360,7 @@ void QGLBlurTextureCache::timerEvent(QTimerEvent *) QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context) { - QGLBlurTextureCache *p = reinterpret_cast<QGLBlurTextureCache *>(qt_blur_texture_caches()->value(context)); - if (!p) { - p = new QGLBlurTextureCache; - qt_blur_texture_caches()->insert(context, p); - } - return p; + return qt_blur_texture_caches()->value(context); } QGLBlurTextureInfo *QGLBlurTextureCache::takeBlurTextureInfo(const QPixmap &pixmap) diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index 8ee494f..c05f730 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -1871,11 +1871,10 @@ void tst_QGL::destroyFBOAfterContext() #ifdef QT_BUILD_INTERNAL -class tst_QGLResource : public QObject +class tst_QGLResource { - Q_OBJECT public: - tst_QGLResource(QObject *parent = 0) : QObject(parent) {} + tst_QGLResource(const QGLContext * = 0) {} ~tst_QGLResource() { ++deletions; } static int deletions; @@ -1883,12 +1882,7 @@ public: int tst_QGLResource::deletions = 0; -static void qt_shared_test_free(void *data) -{ - delete reinterpret_cast<tst_QGLResource *>(data); -} - -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shared_test, (qt_shared_test_free)) +Q_GLOBAL_STATIC(QGLContextGroupResource<tst_QGLResource>, qt_shared_test) #endif @@ -1908,10 +1902,9 @@ void tst_QGL::shareRegister() guard.setId(3); QVERIFY(guard.id() == 3); - // Add a resource to the first context. - tst_QGLResource *res1 = new tst_QGLResource(); - QVERIFY(!qt_shared_test()->value(glw1->context())); - qt_shared_test()->insert(glw1->context(), res1); + // Request a tst_QGLResource object for the first context. + tst_QGLResource *res1 = qt_shared_test()->value(glw1->context()); + QVERIFY(res1); QVERIFY(qt_shared_test()->value(glw1->context()) == res1); // Create another context that shares with the first. @@ -1950,10 +1943,9 @@ void tst_QGL::shareRegister() QGLSharedResourceGuard guard3(glw3->context()); guard3.setId(5); - // Add a resource to the third context. - tst_QGLResource *res3 = new tst_QGLResource(); - QVERIFY(!qt_shared_test()->value(glw3->context())); - qt_shared_test()->insert(glw3->context(), res3); + // Request a resource to the third context. + tst_QGLResource *res3 = qt_shared_test()->value(glw3->context()); + QVERIFY(res3); QVERIFY(qt_shared_test()->value(glw1->context()) == res1); QVERIFY(qt_shared_test()->value(glw2->context()) == res1); QVERIFY(qt_shared_test()->value(glw3->context()) == res3); |