From 9f72c297843459ab22eeb3af048baa9037aa9634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Wed, 17 Mar 2010 12:16:57 +0100 Subject: Make qt_gl_paint_engine() thread-safe. --- src/opengl/qgl.cpp | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 5c5d3d1..b875fd1 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -5031,25 +5031,54 @@ void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QM } #endif -#ifndef QT_OPENGL_ES_1 -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_2_engine) +#if !defined(QT_OPENGL_ES_1) +class QtGL2EngineStorage +{ +public: + QPaintEngine *engine() { + QPaintEngine *localEngine = storage.localData(); + if (!localEngine) { + localEngine = new QGL2PaintEngineEx; + storage.setLocalData(localEngine); + } + return localEngine; + } + +private: + QThreadStorage storage; +}; +Q_GLOBAL_STATIC(QtGL2EngineStorage, qt_gl_2_engine) #endif #ifndef QT_OPENGL_ES_2 -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine) +class QtGL1EngineStorage +{ +public: + QPaintEngine *engine() { + QPaintEngine *localEngine = storage.localData(); + if (!localEngine) { + localEngine = new QOpenGLPaintEngine; + storage.setLocalData(localEngine); + } + return localEngine; + } +private: + QThreadStorage storage; +}; +Q_GLOBAL_STATIC(QtGL1EngineStorage, 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 } -- cgit v0.12 From 38922774119817f4bf1595b9651f914e5c3d9f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 22 Jun 2010 12:51:00 +0200 Subject: More work on GL threading. Removed QObject inheritance from QGLEngineSharedShaders and made it thread-local, so that paintengines in different threads can use programs without clashing. Lifted some restrictions on QPixmap so that they may be used in threads when the GL2 engine is active. Made the QGLContextGroup a part of the pixmap and image cache keys. --- src/gui/image/qpixmap.cpp | 1 + src/gui/painting/qpainter.cpp | 11 ++- .../gl2paintengineex/qglengineshadermanager.cpp | 94 +++++++++++++++++----- .../gl2paintengineex/qglengineshadermanager_p.h | 13 ++- src/opengl/qgl.cpp | 12 ++- src/opengl/qgl_p.h | 3 + 6 files changed, 98 insertions(+), 36 deletions(-) diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 20e4b50..e630e80 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -94,6 +94,7 @@ Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap) static bool qt_pixmap_thread_test() { + return true; if (!qApp) { qFatal("QPixmap: Must construct a QApplication before a QPaintDevice"); return false; diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 312cc95..ec2aca4 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5252,7 +5252,8 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm) return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + if (d->engine->type() != QPaintEngine::OpenGL2) + qt_painter_thread_test(d->device->devType(), "drawPixmap()"); #endif if (d->extended) { @@ -5322,7 +5323,8 @@ 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()"); + if (d->engine->type() != QPaintEngine::OpenGL2) + qt_painter_thread_test(d->device->devType(), "drawPixmap()"); #endif qreal x = r.x(); @@ -5926,7 +5928,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()); @@ -6666,7 +6668,8 @@ void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPo return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()"); + if (d->engine->type() != QPaintEngine::OpenGL2) + qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()"); #endif qreal sw = pixmap.width(); diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 40b3641..5330706 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -51,19 +51,61 @@ QT_BEGIN_NAMESPACE static void qt_shared_shaders_free(void *data) { + qDebug() << "qt_shared_shaders_free() for thread:" << hex << QThread::currentThread(); delete reinterpret_cast(data); } -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shared_shaders, (qt_shared_shaders_free)) + +class QGLThreadLocalShaders { +public: + QGLThreadLocalShaders(QGLContextResource *resource) : m_resource(resource), m_shaders(0) {} + ~QGLThreadLocalShaders() { + if (m_shaders) { + qDebug() << "~QGLThreadLocalShaders() for thread:" << hex << QThread::currentThread(); + m_shaders->cleanupBeforeDestruction(); + } + } + QGLContextResource *m_resource; + QGLEngineSharedShaders *m_shaders; +}; + +class QGLThreadShaders +{ +public: + QGLContextResource *shadersForThread() { + QGLThreadLocalShaders *shaders = m_storage.localData(); + if (!shaders) { + // The QGLContextResource is needed because the shaders need to be cleaned up + // when the context is destroyed, not just when a thread exits + QGLContextResource *resource = new QGLContextResource(qt_shared_shaders_free); + qDebug() << "new local resource for thread:" << hex << QThread::currentThread() << resource; + shaders = new QGLThreadLocalShaders(resource); + m_storage.setLocalData(shaders); + } + return shaders->m_resource; + } + + void setShadersForThread(const QGLContext *context, QGLEngineSharedShaders *sharedShaders) { + shadersForThread()->insert(context, sharedShaders); + m_storage.localData()->m_shaders = sharedShaders; + qDebug() << " -> context:" << context; + } + +private: + QThreadStorage m_storage; +}; + +Q_GLOBAL_STATIC(QGLThreadShaders, qt_shared_shaders); QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context) { - QGLEngineSharedShaders *p = reinterpret_cast(qt_shared_shaders()->value(context)); - if (!p) { + QGLEngineSharedShaders *shaders = reinterpret_cast(qt_shared_shaders()->shadersForThread()->value(context)); + if (!shaders) { QGLShareContextScope scope(context); - qt_shared_shaders()->insert(context, p = new QGLEngineSharedShaders(context)); + shaders = new QGLEngineSharedShaders(context); + qt_shared_shaders()->setShadersForThread(context, shaders); } - return p; + return shaders; } const char* QGLEngineSharedShaders::qShaderSnippets[] = { @@ -170,18 +212,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 +242,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); @@ -224,9 +270,20 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) QGLEngineSharedShaders::~QGLEngineSharedShaders() { - QList::iterator itr; - for (itr = cachedPrograms.begin(); itr != cachedPrograms.end(); ++itr) - delete *itr; + cleanupBeforeDestruction(); +} + + +// This might be called both when a thread exits, or when the a +// context is destroyed + +void QGLEngineSharedShaders::cleanupBeforeDestruction() +{ + qDeleteAll(shaders); + shaders.clear(); + + qDeleteAll(cachedPrograms); + cachedPrograms.clear(); if (blitShaderProg) { delete blitShaderProg; @@ -276,7 +333,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 +360,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 +379,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 +472,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..8d3697b 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,16 @@ public: // full. void cleanupCustomStage(QGLCustomShaderStage* stage); -signals: - void shaderProgNeedsChanging(); + // this is needed so that threads can get a foot in and have a chance to + // clean up the shaders they've created before the thread exits + void cleanupBeforeDestruction(); private: QGLSharedResourceGuard ctxGuard; QGLShaderProgram *blitShaderProg; QGLShaderProgram *simpleShaderProg; QList cachedPrograms; + QList shaders; static const char* qShaderSnippets[TotalSnippetCount]; }; @@ -492,9 +494,6 @@ public: QGLEngineSharedShaders* sharedShaders; -private slots: - void shaderProgNeedsChangingSlot() { shaderProgNeedsChanging = true; } - private: QGLContext* ctx; bool shaderProgNeedsChanging; diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index b875fd1..fa6dd37 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2236,7 +2236,7 @@ QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_prem QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options) { - const qint64 key = image.cacheKey(); + const qint64 key = image.cacheKey() | (qint64) group; QGLTexture *texture = textureCacheLookup(key, target); if (texture) { glBindTexture(target, texture->id); @@ -2509,7 +2509,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, Q_UNUSED(q); #endif - const qint64 key = pixmap.cacheKey(); + const qint64 key = pixmap.cacheKey() | (qint64) group; QGLTexture *texture = textureCacheLookup(key, target); if (texture) { glBindTexture(target, texture->id); @@ -2532,6 +2532,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, } #endif + printf(" -> bindTexture key: %llx\n", key); if (!texture) { QImage image = pixmap.toImage(); // If the system depth is 16 and the pixmap doesn't have an alpha channel @@ -4061,11 +4062,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()) @@ -5373,6 +5370,7 @@ void *QGLContextResource::value(const QGLContext *key) void QGLContextResource::cleanup(const QGLContext *ctx, void *value) { + qDebug() << "QGLContextResource::cleanup() this:" << hex << this << "thread:" << QThread::currentThread(); QGLShareContextScope scope(ctx); free(value); active.deref(); diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 16c225f..9b862eb 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -473,6 +473,8 @@ private: QGLContext *m_ctx; }; +#include +#include class QGLTexture { public: QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D, @@ -489,6 +491,7 @@ public: ~QGLTexture() { if (options & QGLContext::MemoryManagedBindOption) { Q_ASSERT(context); + qDebug()<< "~QGLTexture: thread:" << hex <thread() << ")" << "context:" << context << "current context:" << QGLContext::currentContext(); QGLShareContextScope scope(context); #if defined(Q_WS_X11) // Although glXReleaseTexImage is a glX call, it must be called while there -- cgit v0.12 From 15ac2bc856735cdd81dde9f3d3dc2c47177ef814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 26 Mar 2010 10:05:16 +0100 Subject: Only use the texture_from_pixmap extension in the main thread. It's not possible to bind several texture ids to the same surface that wraps a native X11 Pixmap, which means we can't use it reliably from more than one thread at a time. For now we limit the usage to the main thread. --- src/opengl/qgl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index fa6dd37..995e753 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2521,7 +2521,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(&pixmap), key, options); if (texture) { -- cgit v0.12 From ac2818bef95f1347e80be8ce847f2557339b76e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Wed, 7 Apr 2010 14:57:24 +0200 Subject: Make it possible to call QWidget::metric() from a different thread. Under Windows. --- src/gui/kernel/qwidget_win.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 9d96fcb..60ba879 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -1633,8 +1633,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); @@ -1644,7 +1642,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: @@ -1695,6 +1693,7 @@ int QWidget::metric(PaintDeviceMetric m) const val = 0; qWarning("QWidget::metric: Invalid metric command"); } + ReleaseDC(0, gdc); } return val; } -- cgit v0.12 From 7e464e38009f2492a41e9972a75f868cf88d353a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 9 Apr 2010 17:52:38 +0200 Subject: Simplify how the QGLEngineSharedShaders object is cleaned up. There are two things that needs to be done when cleaning up those objects: 1) if the context is destroyed before the specific thread exits, the shaders that is allocated within that context needs to be cleanup before the context is destroyed. 2) if the thread exits before the context is destroyed, the thread needs to clean up the shaders and then delete the QGLEngineSharedShaders object. We can't use a QGLContextResouce for that because a) we don't know if our context ptrs are valid, b) QGLContextResource doesn't remove themselves from the QGLContextGroups internal list when destroyed, so we might get a free() call *after* the resource had been destroyed. Use the contextDestroyed() signal from QGLSignalProxy() instead, which is cleaner and actually works.. --- .../gl2paintengineex/qglengineshadermanager.cpp | 67 +++++++++++----------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 5330706..7d6d8a6 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -46,66 +46,61 @@ #include #endif - QT_BEGIN_NAMESPACE -static void qt_shared_shaders_free(void *data) +class QGLThreadLocalShaders : public QObject { - qDebug() << "qt_shared_shaders_free() for thread:" << hex << QThread::currentThread(); - delete reinterpret_cast(data); -} - - -class QGLThreadLocalShaders { + Q_OBJECT public: - QGLThreadLocalShaders(QGLContextResource *resource) : m_resource(resource), m_shaders(0) {} + QGLThreadLocalShaders(const QGLContext *context, QGLEngineSharedShaders *shaders) + : m_context(context), m_shaders(shaders) + { + connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)), + SLOT(contextDestroyed(const QGLContext*))); + } ~QGLThreadLocalShaders() { - if (m_shaders) { - qDebug() << "~QGLThreadLocalShaders() for thread:" << hex << QThread::currentThread(); + qDebug() << "~QGLThreadLocalShaders() for thread:" << hex << QThread::currentThread(); + qDebug() << " -> deleting shader cache:" << hex << m_shaders; + delete m_shaders; + } + const QGLContext *m_context; + QGLEngineSharedShaders *m_shaders; + +private slots: + void contextDestroyed(const QGLContext * context) { + if (context == m_context) { + qDebug() << "QGLThreadLocalShaders::contextDestroyed():"; + qDebug() << " -> cleaning up shader cache for thread:" << hex << QThread::currentThread(); + qDebug() << " -> shaders:" << hex << m_shaders; m_shaders->cleanupBeforeDestruction(); } } - QGLContextResource *m_resource; - QGLEngineSharedShaders *m_shaders; }; -class QGLThreadShaders +class QGLShaderStorage { public: - QGLContextResource *shadersForThread() { + QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { QGLThreadLocalShaders *shaders = m_storage.localData(); if (!shaders) { - // The QGLContextResource is needed because the shaders need to be cleaned up - // when the context is destroyed, not just when a thread exits - QGLContextResource *resource = new QGLContextResource(qt_shared_shaders_free); - qDebug() << "new local resource for thread:" << hex << QThread::currentThread() << resource; - shaders = new QGLThreadLocalShaders(resource); + qDebug() << "new shader cache for thread:" << hex << QThread::currentThread(); + QGLShareContextScope scope(context); + shaders = new QGLThreadLocalShaders(context, new QGLEngineSharedShaders(context)); + qDebug() << " -> context:" << context; m_storage.setLocalData(shaders); } - return shaders->m_resource; - } - - void setShadersForThread(const QGLContext *context, QGLEngineSharedShaders *sharedShaders) { - shadersForThread()->insert(context, sharedShaders); - m_storage.localData()->m_shaders = sharedShaders; - qDebug() << " -> context:" << context; + return shaders->m_shaders; } private: QThreadStorage m_storage; }; -Q_GLOBAL_STATIC(QGLThreadShaders, qt_shared_shaders); +Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage); QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context) { - QGLEngineSharedShaders *shaders = reinterpret_cast(qt_shared_shaders()->shadersForThread()->value(context)); - if (!shaders) { - QGLShareContextScope scope(context); - shaders = new QGLEngineSharedShaders(context); - qt_shared_shaders()->setShadersForThread(context, shaders); - } - return shaders; + return reinterpret_cast(qt_shader_storage()->shadersForThread(context)); } const char* QGLEngineSharedShaders::qShaderSnippets[] = { @@ -845,3 +840,5 @@ bool QGLEngineShaderManager::useCorrectShaderProg() } QT_END_NAMESPACE + +#include "qglengineshadermanager.moc" -- cgit v0.12 From 4c3ac765b26e56fbf08c9f70cf43aa00a6565cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 16 Apr 2010 11:38:20 +0200 Subject: Don't use the QGLSignalProxy to clean up the thread-local shaders. After much back and forth I think we can use this approach for cleaning up thread-local, shared resources. Main problem has been dealing with dangling pointers and references, but they should be solved now. --- .../gl2paintengineex/qglengineshadermanager.cpp | 49 ++++++++++++++-------- src/opengl/qgl.cpp | 9 ++++ src/opengl/qgl_p.h | 2 + 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 7d6d8a6..0ba324d 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -48,42 +48,59 @@ QT_BEGIN_NAMESPACE -class QGLThreadLocalShaders : public QObject + +// Handling the cleanup and creation of the thread-local shader cache +// differes from other QGLContextResources. The cache needs to be +// cleaned up when the thread exits, or when its group context is +// destroyed. + +static void qt_gl_free_shared_shaders(void *value); + +class QGLThreadLocalShaders { - Q_OBJECT public: QGLThreadLocalShaders(const QGLContext *context, QGLEngineSharedShaders *shaders) : m_context(context), m_shaders(shaders) { - connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)), - SLOT(contextDestroyed(const QGLContext*))); + m_resource = new QGLContextResource(qt_gl_free_shared_shaders); + m_resource->insert(context, this); } ~QGLThreadLocalShaders() { + qDebug() << "Thread exit:"; qDebug() << "~QGLThreadLocalShaders() for thread:" << hex << QThread::currentThread(); qDebug() << " -> deleting shader cache:" << hex << m_shaders; delete m_shaders; + // remove the resource from the group's resource list, so that + // the cleanup function is not called when the context is destroyed + m_resource->remove(m_context); + delete m_resource; } const QGLContext *m_context; QGLEngineSharedShaders *m_shaders; - -private slots: - void contextDestroyed(const QGLContext * context) { - if (context == m_context) { - qDebug() << "QGLThreadLocalShaders::contextDestroyed():"; - qDebug() << " -> cleaning up shader cache for thread:" << hex << QThread::currentThread(); - qDebug() << " -> shaders:" << hex << m_shaders; - m_shaders->cleanupBeforeDestruction(); - } - } + QGLContextResource *m_resource; }; +static void qt_gl_free_shared_shaders(void *value) +{ + QGLThreadLocalShaders *local = reinterpret_cast(value); + qDebug() << "Context destroyed:"; + qDebug() << " -> deleting shader cache" << hex << local->m_shaders; + delete local->m_shaders; + local->m_shaders = 0; + + // this function is called when the context group for the context + // we have ref'ed is deleted - that means our context ptr is no + // longer valid + local->m_context = 0; +} + class QGLShaderStorage { public: QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { QGLThreadLocalShaders *shaders = m_storage.localData(); if (!shaders) { - qDebug() << "new shader cache for thread:" << hex << QThread::currentThread(); + qDebug() << "New shader cache for thread:" << hex << QThread::currentThread(); QGLShareContextScope scope(context); shaders = new QGLThreadLocalShaders(context, new QGLEngineSharedShaders(context)); qDebug() << " -> context:" << context; @@ -840,5 +857,3 @@ bool QGLEngineShaderManager::useCorrectShaderProg() } QT_END_NAMESPACE - -#include "qglengineshadermanager.moc" diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 995e753..1223552 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -5377,6 +5377,15 @@ void QGLContextResource::cleanup(const QGLContext *ctx, void *value) active.deref(); } +void QGLContextResource::remove(const QGLContext *ctx) +{ + if (ctx) { + QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx); + group->m_resources.remove(this); + active.deref(); + } +} + void QGLContextGroup::cleanupResources(const QGLContext *ctx) { // If there are still shares, then no cleanup to be done yet. diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 9b862eb..d5083e9 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -624,6 +624,8 @@ public: void *value(const QGLContext *key); // Cleanup 'value' in response to a context group being destroyed. void cleanup(const QGLContext *ctx, void *value); + // Remove this resource from the group's resource list. + void remove(const QGLContext *ctx); private: FreeFunc free; QAtomicInt active; -- cgit v0.12 From 80a2ebd93346f885d904e4e1020e112cc635dbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 22 Jun 2010 13:02:54 +0200 Subject: Redesigned how GL resource management works. The usage of QGLContextResource has changed: You're now have to subclass QGLContextResource and reimplement the freeResource() function and add your cleanup code there instead of using a plain callback function. It's now also possible to delete a QGLContextResource *before* the QGLContextGroup it refers to is destroyed, as the resource will remove itself from the context groups it's a member of. The QGLTextureGlyphCache is no longer a QObject, and it no longer depends on the aboutToDestroyContext() signal. That concept doesn't work in a threaded environment, as it relies on an eventloop to dispatch the signal to the thread. It's common to *not* have an eventloop running in a thread, which means the signal might never be delivered. QGLTextureGlyphCache now inherits from QGLContextResource, and gets cleaned up correctly when the group context is destroyed. Note that up until now the glyph cache has never been shared among sharing contexts for the GL 2 engine. Made the gradient and pixmap blur caches use the new QGLContextResource scheme. Added a template that wraps the common init code for paintEngine() function implementations for QGLWidget, QGLPixelBuffer and QGLFramebufferObject. Fixed a bug in QFontCache where the font caches weren't cleared when a thread other than the main thread exited (backported to 4.6.3), which caused resource leaks. --- .../gl2paintengineex/qglengineshadermanager.cpp | 80 ++++++++-------------- .../gl2paintengineex/qglengineshadermanager_p.h | 4 -- src/opengl/gl2paintengineex/qglgradientcache.cpp | 12 ++-- .../gl2paintengineex/qpaintengineex_opengl2.cpp | 1 + .../gl2paintengineex/qtextureglyphcache_gl.cpp | 47 +++---------- .../gl2paintengineex/qtextureglyphcache_gl_p.h | 31 +++------ src/opengl/qgl.cpp | 61 +++++------------ src/opengl/qgl_p.h | 32 +++++++-- src/opengl/qglframebufferobject.cpp | 8 +-- src/opengl/qglpixelbuffer.cpp | 13 ++-- src/opengl/qglpixmapfilter.cpp | 12 ++-- 11 files changed, 116 insertions(+), 185 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 0ba324d..888bac9 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -48,65 +48,54 @@ QT_BEGIN_NAMESPACE +class QGLSharedShaderResource : public QGLContextResource +{ +public: + ~QGLSharedShaderResource() { + qDebug() << "~QGLSharedShaderResource cleaned up" << hex << this; + } -// Handling the cleanup and creation of the thread-local shader cache -// differes from other QGLContextResources. The cache needs to be -// cleaned up when the thread exits, or when its group context is -// destroyed. - -static void qt_gl_free_shared_shaders(void *value); + void freeResource(void *value) + { + qDebug() << "Context destroyed:"; + qDebug() << " -> deleting shader cache" << hex << value; + delete reinterpret_cast(value); + } +}; class QGLThreadLocalShaders { public: - QGLThreadLocalShaders(const QGLContext *context, QGLEngineSharedShaders *shaders) - : m_context(context), m_shaders(shaders) - { - m_resource = new QGLContextResource(qt_gl_free_shared_shaders); - m_resource->insert(context, this); + QGLEngineSharedShaders *shadersForContext(const QGLContext *context) { + QGLEngineSharedShaders *shaders = reinterpret_cast(m_resource.value(context)); + if (!shaders) { + QGLShareContextScope scope(context); + shaders = new QGLEngineSharedShaders(context); + qDebug() << " -> new shader cache for context:" << hex << context << "cache:" << shaders; + m_resource.insert(context, shaders); + } + return shaders; } + ~QGLThreadLocalShaders() { qDebug() << "Thread exit:"; qDebug() << "~QGLThreadLocalShaders() for thread:" << hex << QThread::currentThread(); - qDebug() << " -> deleting shader cache:" << hex << m_shaders; - delete m_shaders; - // remove the resource from the group's resource list, so that - // the cleanup function is not called when the context is destroyed - m_resource->remove(m_context); - delete m_resource; } - const QGLContext *m_context; - QGLEngineSharedShaders *m_shaders; - QGLContextResource *m_resource; -}; -static void qt_gl_free_shared_shaders(void *value) -{ - QGLThreadLocalShaders *local = reinterpret_cast(value); - qDebug() << "Context destroyed:"; - qDebug() << " -> deleting shader cache" << hex << local->m_shaders; - delete local->m_shaders; - local->m_shaders = 0; - - // this function is called when the context group for the context - // we have ref'ed is deleted - that means our context ptr is no - // longer valid - local->m_context = 0; -} +private: + QGLSharedShaderResource m_resource; +}; class QGLShaderStorage { public: QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { - QGLThreadLocalShaders *shaders = m_storage.localData(); + QGLThreadLocalShaders *&shaders = m_storage.localData(); if (!shaders) { - qDebug() << "New shader cache for thread:" << hex << QThread::currentThread(); - QGLShareContextScope scope(context); - shaders = new QGLThreadLocalShaders(context, new QGLEngineSharedShaders(context)); - qDebug() << " -> context:" << context; - m_storage.setLocalData(shaders); + qDebug() << "New thread storage for:" << hex << QThread::currentThread(); + shaders = new QGLThreadLocalShaders; } - return shaders->m_shaders; + return shaders->shadersForContext(context); } private: @@ -282,15 +271,6 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) QGLEngineSharedShaders::~QGLEngineSharedShaders() { - cleanupBeforeDestruction(); -} - - -// This might be called both when a thread exits, or when the a -// context is destroyed - -void QGLEngineSharedShaders::cleanupBeforeDestruction() -{ qDeleteAll(shaders); shaders.clear(); diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 8d3697b..e5ababf 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -364,10 +364,6 @@ public: // full. void cleanupCustomStage(QGLCustomShaderStage* stage); - // this is needed so that threads can get a foot in and have a chance to - // clean up the shaders they've created before the thread exits - void cleanupBeforeDestruction(); - private: QGLSharedResourceGuard ctxGuard; QGLShaderProgram *blitShaderProg; diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp index a1495dd..a0a3f8f 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp @@ -46,12 +46,16 @@ QT_BEGIN_NAMESPACE -static void QGL2GradientCache_free(void *ptr) +class QGLGradientCacheResource : public QGLContextResource { - delete reinterpret_cast(ptr); -} +public: + void freeResource(void *value) + { + delete reinterpret_cast(value); + } +}; -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_gradient_caches, (QGL2GradientCache_free)) +Q_GLOBAL_STATIC(QGLGradientCacheResource, qt_gradient_caches) QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) { diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 634b315..260e797 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1457,6 +1457,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp (QGLTextureGlyphCache *) staticTextItem->fontEngine->glyphCache(ctx, glyphType, QTransform()); if (!cache || cache->cacheType() != glyphType) { cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform()); + cache->insert(ctx, cache); staticTextItem->fontEngine->setGlyphCache(ctx, cache); } diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index faf4563..001a09e 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -54,45 +54,10 @@ extern Q_GUI_EXPORT bool qt_cleartype_enabled; QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) : QImageTextureGlyphCache(type, matrix) - , ctx(0) - , pex(0) + , ctx(context) , m_width(0) , m_height(0) { - if (context != 0) - 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; - } -} - -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. @@ -101,8 +66,12 @@ void QGLTextureGlyphCache::setContext(const QGLContext *context) if (!ctx->d_ptr->workaround_brokenFBOReadBack && pex != 0) glGenFramebuffers(1, &m_fbo); - connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)), - SLOT(contextDestroyed(const QGLContext*))); + fprintf(stderr, "## QGLTextureGlyphCache(): ctx: %p - this: %p\n", ctx, this); +} + +QGLTextureGlyphCache::~QGLTextureGlyphCache() +{ + fprintf(stderr, "## ~QGLTextureGlyphCache(): context: %p - this: %p\n", ctx, this); } void QGLTextureGlyphCache::createTextureData(int width, int height) @@ -161,7 +130,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) GLuint oldTexture = m_texture; createTextureData(width, height); - + if (pex == 0 || ctx->d_ptr->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::resizeTextureData(width, height); Q_ASSERT(image().depth() == 8); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index d291ac3..710a12d 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -62,9 +62,8 @@ QT_BEGIN_NAMESPACE class QGL2PaintEngineExPrivate; -class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QObject, public QImageTextureGlyphCache +class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QGLContextResource, public QImageTextureGlyphCache { - Q_OBJECT public: QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); ~QGLTextureGlyphCache(); @@ -82,33 +81,21 @@ public: inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } - void setContext(const QGLContext *context); inline const QGLContext *context() const { return ctx; } + void freeResource(void *) { + qDebug() << "QGLTextureGlyphCache::freeResource():" << this << "ctx:" << ctx; + // At this point, the context group is made current, so it's safe to + // release resources without a makeCurrent() call + if (!ctx->d_ptr->workaround_brokenFBOReadBack) + glDeleteFramebuffers(1, &m_fbo); -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(nextCtx); - } - } + if (m_width || m_height) + glDeleteTextures(1, &m_texture); } private: diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 1223552..753a984 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2028,6 +2028,7 @@ QGLContext::~QGLContext() // remove any textures cached in this context QGLTextureCache::instance()->removeContextTextures(this); + qDebug() << "~QGLContext(): about to clean up" << hex << this; d_ptr->group->cleanupResources(this); QGLSignalProxy::instance()->emitAboutToDestroyContext(this); @@ -2533,7 +2534,6 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, } #endif - printf(" -> bindTexture key: %llx\n", key); if (!texture) { QImage image = pixmap.toImage(); // If the system depth is 16 and the pixmap doesn't have an alpha channel @@ -5029,41 +5029,12 @@ void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QM } #endif -#if !defined(QT_OPENGL_ES_1) -class QtGL2EngineStorage -{ -public: - QPaintEngine *engine() { - QPaintEngine *localEngine = storage.localData(); - if (!localEngine) { - localEngine = new QGL2PaintEngineEx; - storage.setLocalData(localEngine); - } - return localEngine; - } - -private: - QThreadStorage storage; -}; -Q_GLOBAL_STATIC(QtGL2EngineStorage, qt_gl_2_engine) +#ifndef QT_OPENGL_ES_1 +Q_GLOBAL_STATIC(QGLEngineThreadStorage, qt_gl_2_engine) #endif #ifndef QT_OPENGL_ES_2 -class QtGL1EngineStorage -{ -public: - QPaintEngine *engine() { - QPaintEngine *localEngine = storage.localData(); - if (!localEngine) { - localEngine = new QOpenGLPaintEngine; - storage.setLocalData(localEngine); - } - return localEngine; - } -private: - QThreadStorage storage; -}; -Q_GLOBAL_STATIC(QtGL1EngineStorage, qt_gl_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage, qt_gl_engine) #endif Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine() @@ -5339,13 +5310,17 @@ void QGLContextGroup::removeShare(const QGLContext *context) { group->m_shares.clear(); } -QGLContextResource::QGLContextResource(FreeFunc f) - : free(f), active(0) +QGLContextResource::QGLContextResource() + : active(0) { } QGLContextResource::~QGLContextResource() { + 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" @@ -5360,6 +5335,7 @@ void QGLContextResource::insert(const QGLContext *key, void *value) QGLContextGroup *group = QGLContextPrivate::contextGroup(key); Q_ASSERT(!group->m_resources.contains(this)); group->m_resources.insert(this, value); + m_groups.append(group); active.ref(); } @@ -5371,23 +5347,18 @@ void *QGLContextResource::value(const QGLContext *key) void QGLContextResource::cleanup(const QGLContext *ctx, void *value) { - qDebug() << "QGLContextResource::cleanup() this:" << hex << this << "thread:" << QThread::currentThread(); + qDebug() << "QGLContextResource::cleanup() this:" << hex << this << "ctx:" << ctx << "thread:" << (void *)QThread::currentThread(); QGLShareContextScope scope(ctx); - free(value); + freeResource(value); active.deref(); -} -void QGLContextResource::remove(const QGLContext *ctx) -{ - if (ctx) { - QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx); - group->m_resources.remove(this); - active.deref(); - } + QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx); + m_groups.removeOne(group); } void QGLContextGroup::cleanupResources(const QGLContext *ctx) { + qDebug() << "QGLContextGroup::cleanupResources() for ctx:" << hex << ctx << "shares:" << m_shares.size() << "res:" << m_resources.size(); // If there are still shares, then no cleanup to be done yet. if (m_shares.size() > 1) return; diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index d5083e9..1399359 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -229,6 +229,7 @@ public: static void addShare(const QGLContext *context, const QGLContext *share); static void removeShare(const QGLContext *context); + private: QGLContextGroup(const QGLContext *context); @@ -491,7 +492,6 @@ public: ~QGLTexture() { if (options & QGLContext::MemoryManagedBindOption) { Q_ASSERT(context); - qDebug()<< "~QGLTexture: thread:" << hex <thread() << ")" << "context:" << context << "current context:" << QGLContext::currentContext(); QGLShareContextScope scope(context); #if defined(Q_WS_X11) // Although glXReleaseTexImage is a glX call, it must be called while there @@ -615,19 +615,17 @@ inline GLenum qt_gl_preferredTextureTarget() class Q_OPENGL_EXPORT QGLContextResource { public: - typedef void (*FreeFunc)(void *); - QGLContextResource(FreeFunc f); - ~QGLContextResource(); + QGLContextResource(); + virtual ~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); - // Remove this resource from the group's resource list. - void remove(const QGLContext *ctx); + virtual void freeResource(void *value) = 0; private: - FreeFunc free; + QList m_groups; QAtomicInt active; }; @@ -720,6 +718,26 @@ 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 QGLEngineThreadStorage +{ +public: + QPaintEngine *engine() { + QPaintEngine *localEngine = storage.localData(); + if (!localEngine) { + localEngine = new T; + storage.setLocalData(localEngine); + } + return localEngine; + } + +private: + QThreadStorage 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, qt_buffer_2_engine) #endif #ifndef QT_OPENGL_ES_2 -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage, 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..e5aed44 100644 --- a/src/opengl/qglpixelbuffer.cpp +++ b/src/opengl/qglpixelbuffer.cpp @@ -388,25 +388,26 @@ bool QGLPixelBuffer::isValid() const } #if !defined(QT_OPENGL_ES_1) -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage, qt_buffer_2_engine) #endif #ifndef QT_OPENGL_ES_2 -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) +Q_GLOBAL_STATIC(QGLEngineThreadStorage, qt_buffer_engine) #endif /*! \reimp */ QPaintEngine *QGLPixelBuffer::paintEngine() const { + return qt_qgl_paint_engine(); #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/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index bfa5ef1..011869e 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -337,12 +337,16 @@ private: QList QGLBlurTextureCache::blurTextureCaches; -static void QGLBlurTextureCache_free(void *ptr) +class QGLBlurCacheResource : public QGLContextResource { - delete reinterpret_cast(ptr); -} +public: + void freeResource(void *value) + { + delete reinterpret_cast(value); + } +}; -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_blur_texture_caches, (QGLBlurTextureCache_free)) +Q_GLOBAL_STATIC(QGLBlurCacheResource, qt_blur_texture_caches) QGLBlurTextureCache::QGLBlurTextureCache() : timerId(0) -- cgit v0.12 From 83abd5b9c8cf2b069c6b065ad37c93afe59d5d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 22 Jun 2010 13:12:13 +0200 Subject: Make the GL texture cache share textures between shared contexts. Up until now the GL 2 engine texture cache did not share glyph textures between sharing contexts, which is bad. Fix this by using the context group's context as the cache key. --- src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 260e797..fe0d6c9 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1453,12 +1453,13 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp QOpenGL2PaintEngineState *s = q->state(); + void *cacheKey = const_cast(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()); cache->insert(ctx, cache); - staticTextItem->fontEngine->setGlyphCache(ctx, cache); + staticTextItem->fontEngine->setGlyphCache(cacheKey, cache); } cache->setPaintEnginePrivate(this); -- cgit v0.12 From 0441db10ddf2bd9141d7801e1e53c9cbdde2ebc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Wed, 28 Apr 2010 14:36:25 +0200 Subject: Remove the DDS texture cache. It has no practical use and is a leftover from when pixmap/image textures and dds textures used the same cache. --- src/opengl/qgl.cpp | 26 -------------------------- src/opengl/qgl_p.h | 3 --- 2 files changed, 29 deletions(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 753a984..3e556e0 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2089,20 +2089,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; } @@ -2728,24 +2718,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 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); } diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 1399359..9b1856e 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -209,8 +209,6 @@ public: class QGLContextResource; class QGLSharedResourceGuard; -typedef QHash QGLDDSCache; - // QGLContextPrivate has the responsibility of creating context groups. // QGLContextPrivate maintains the reference counter and destroys // context groups when needed. @@ -239,7 +237,6 @@ private: QHash m_resources; QGLSharedResourceGuard *m_guards; // double-linked list of active guards. QAtomicInt m_refs; - QGLDDSCache m_dds_cache; void cleanupResources(const QGLContext *ctx); -- cgit v0.12 From 6451874792f515e5e30597e815a21a3a6cc325bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 30 Apr 2010 13:03:12 +0200 Subject: QGLPixelBuffer should manage its own paint engines. --- src/opengl/qgl.cpp | 2 -- src/opengl/qglpixelbuffer.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 3e556e0..74efb34 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -4035,8 +4035,6 @@ 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::ParentChange) { // if we've reparented a window that has the current context // bound, we need to rebind that context to the new window id diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp index e5aed44..3d9cd21 100644 --- a/src/opengl/qglpixelbuffer.cpp +++ b/src/opengl/qglpixelbuffer.cpp @@ -398,7 +398,6 @@ Q_GLOBAL_STATIC(QGLEngineThreadStorage, qt_buffer_engine) /*! \reimp */ QPaintEngine *QGLPixelBuffer::paintEngine() const { - return qt_qgl_paint_engine(); #if defined(QT_OPENGL_ES_1) return qt_buffer_engine()->engine(); #elif defined(QT_OPENGL_ES_2) -- cgit v0.12 From decda7ea196b610ab1d70345cb89a4a67bfd1e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 30 Apr 2010 13:17:14 +0200 Subject: Unnecessary alloc + remove unused headers. --- src/opengl/qgl_p.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 9b1856e..acd799a 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -471,8 +471,6 @@ private: QGLContext *m_ctx; }; -#include -#include class QGLTexture { public: QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D, @@ -724,11 +722,9 @@ class QGLEngineThreadStorage { public: QPaintEngine *engine() { - QPaintEngine *localEngine = storage.localData(); - if (!localEngine) { + QPaintEngine *&localEngine = storage.localData(); + if (!localEngine) localEngine = new T; - storage.setLocalData(localEngine); - } return localEngine; } -- cgit v0.12 From 778ee2e9a2db3f3a1bc9d282356798baa77f6efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 30 Apr 2010 15:33:26 +0200 Subject: Add an application attribute to control XInitThreads(). In order to make Xlib thread-safe XInitThreads() needs to be the first Xlib call issued. This attribute controls this behavior. Having Xlib thread-safe will most likely incur some overhead, which is why this is not done by default. --- src/corelib/global/qnamespace.h | 1 + src/corelib/global/qnamespace.qdoc | 4 ++++ src/gui/image/qpixmap.cpp | 3 +-- src/gui/kernel/qapplication_x11.cpp | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index a9c56f6..84ab567 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 7eae3a5..ce1176d 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -170,6 +170,10 @@ 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. + + \omitvalue AA_AttributeCount */ diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index e630e80..a2b0b95 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -94,13 +94,12 @@ Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap) static bool qt_pixmap_thread_test() { - return true; if (!qApp) { qFatal("QPixmap: Must construct a QApplication before a QPaintDevice"); 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/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]) { -- cgit v0.12 From 76a14c8254f4f8beb16de897f31bab13dc7609a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 30 Apr 2010 15:53:06 +0200 Subject: Check Qt::AA_X11InitThreads to determine pixmap usage in threads. --- src/gui/painting/qpainter.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index ec2aca4..8ac40ee 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; @@ -5252,8 +5256,7 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm) return; #ifndef QT_NO_DEBUG - if (d->engine->type() != QPaintEngine::OpenGL2) - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawPixmap()"); #endif if (d->extended) { @@ -5323,8 +5326,7 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) if (!d->engine || pm.isNull()) return; #ifndef QT_NO_DEBUG - if (d->engine->type() != QPaintEngine::OpenGL2) - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawPixmap()"); #endif qreal x = r.x(); @@ -6668,8 +6670,7 @@ void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPo return; #ifndef QT_NO_DEBUG - if (d->engine->type() != QPaintEngine::OpenGL2) - qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()"); #endif qreal sw = pixmap.width(); -- cgit v0.12 From b2de72a51124333b639fdfbda829f54088dff838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 17 Jun 2010 14:46:27 +0200 Subject: Rework the internal GL resource system yet again. Instead of having to sub-class QGLContextResource, we now have two template classes: 1. 'QGLContextResource' to handle context specific resources that are *not* shared in a group, even though the context itself is in a sharing group 2. 'QGLContextGroupResource' to handle resources shared in a context group. Classes used as the template class type must have a constructor with the following signature: T(const QGLContext *); The resources that the templates wrap are freed up when the context or context group it is tied to is destroyed. The resource is also freed when the QGLContextResource object is destroyed, which might lead to context switches, depending on where and in how many non-sharing contexts the resource is used. The templates wrap some boiler plate code that were common for all the use cases. --- .../gl2paintengineex/qglengineshadermanager.cpp | 49 +------- src/opengl/gl2paintengineex/qglgradientcache.cpp | 19 +-- src/opengl/gl2paintengineex/qglgradientcache_p.h | 4 +- .../gl2paintengineex/qtextureglyphcache_gl_p.h | 2 +- src/opengl/qgl.cpp | 34 +++-- src/opengl/qgl.h | 1 + src/opengl/qgl_p.h | 137 ++++++++++++++++++--- src/opengl/qglpixmapfilter.cpp | 23 +--- 8 files changed, 161 insertions(+), 108 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 888bac9..c06bb3c 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -48,65 +48,27 @@ QT_BEGIN_NAMESPACE -class QGLSharedShaderResource : public QGLContextResource -{ -public: - ~QGLSharedShaderResource() { - qDebug() << "~QGLSharedShaderResource cleaned up" << hex << this; - } - - void freeResource(void *value) - { - qDebug() << "Context destroyed:"; - qDebug() << " -> deleting shader cache" << hex << value; - delete reinterpret_cast(value); - } -}; - -class QGLThreadLocalShaders -{ -public: - QGLEngineSharedShaders *shadersForContext(const QGLContext *context) { - QGLEngineSharedShaders *shaders = reinterpret_cast(m_resource.value(context)); - if (!shaders) { - QGLShareContextScope scope(context); - shaders = new QGLEngineSharedShaders(context); - qDebug() << " -> new shader cache for context:" << hex << context << "cache:" << shaders; - m_resource.insert(context, shaders); - } - return shaders; - } - - ~QGLThreadLocalShaders() { - qDebug() << "Thread exit:"; - qDebug() << "~QGLThreadLocalShaders() for thread:" << hex << QThread::currentThread(); - } - -private: - QGLSharedShaderResource m_resource; -}; - class QGLShaderStorage { public: QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { - QGLThreadLocalShaders *&shaders = m_storage.localData(); + QGLContextGroupResource *&shaders = m_storage.localData(); if (!shaders) { qDebug() << "New thread storage for:" << hex << QThread::currentThread(); - shaders = new QGLThreadLocalShaders; + shaders = new QGLContextGroupResource(); } - return shaders->shadersForContext(context); + return shaders->value(context); } private: - QThreadStorage m_storage; + QThreadStorage *> m_storage; }; Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage); QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context) { - return reinterpret_cast(qt_shader_storage()->shadersForThread(context)); + return qt_shader_storage()->shadersForThread(context); } const char* QGLEngineSharedShaders::qShaderSnippets[] = { @@ -271,6 +233,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) QGLEngineSharedShaders::~QGLEngineSharedShaders() { + qDebug() << "####### ~QGLEngineSharedShaders() ##########"; qDeleteAll(shaders); shaders.clear(); diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp index a0a3f8f..f1b095d 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp @@ -46,26 +46,11 @@ QT_BEGIN_NAMESPACE -class QGLGradientCacheResource : public QGLContextResource -{ -public: - void freeResource(void *value) - { - delete reinterpret_cast(value); - } -}; - -Q_GLOBAL_STATIC(QGLGradientCacheResource, qt_gradient_caches) +Q_GLOBAL_STATIC(QGLContextGroupResource, qt_gradient_caches) QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) { - QGL2GradientCache *p = reinterpret_cast(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/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index 710a12d..1a78eed 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE class QGL2PaintEngineExPrivate; -class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QGLContextResource, public QImageTextureGlyphCache +class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QGLContextGroupResourceBase, public QImageTextureGlyphCache { public: QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 74efb34..6755d93 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2029,6 +2029,9 @@ QGLContext::~QGLContext() QGLTextureCache::instance()->removeContextTextures(this); qDebug() << "~QGLContext(): about to clean up" << hex << 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); @@ -2037,6 +2040,10 @@ QGLContext::~QGLContext() void QGLContextPrivate::cleanup() { + QHash::ConstIterator it; + for (it = m_resources.begin(); it != m_resources.end(); ++it) + it.key()->freeResource(it.value()); + m_resources.clear(); } #define ctx q_ptr @@ -5282,15 +5289,17 @@ void QGLContextGroup::removeShare(const QGLContext *context) { group->m_shares.clear(); } -QGLContextResource::QGLContextResource() +QGLContextGroupResourceBase::QGLContextGroupResourceBase() : active(0) { } -QGLContextResource::~QGLContextResource() +QGLContextGroupResourceBase::~QGLContextGroupResourceBase() { + qDebug() << "~QGLContextGroupResourceBase()" << hex << this << "group size:" << m_groups.size(); for (int i = 0; i < m_groups.size(); ++i) { m_groups.at(i)->m_resources.remove(this); + qDebug() << " ->removing from group resource list:" << hex << this; active.deref(); } #ifndef QT_NO_DEBUG @@ -5302,24 +5311,24 @@ QGLContextResource::~QGLContextResource() #endif } -void QGLContextResource::insert(const QGLContext *key, void *value) +void QGLContextGroupResourceBase::insert(const QGLContext *context, void *value) { - QGLContextGroup *group = QGLContextPrivate::contextGroup(key); + 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) { - qDebug() << "QGLContextResource::cleanup() this:" << hex << this << "ctx:" << ctx << "thread:" << (void *)QThread::currentThread(); + qDebug() << "QGLContextGroupResourceBase::cleanup() this:" << hex << this << "ctx:" << ctx << "thread:" << (void *)QThread::currentThread(); QGLShareContextScope scope(ctx); freeResource(value); active.deref(); @@ -5328,17 +5337,18 @@ void QGLContextResource::cleanup(const QGLContext *ctx, void *value) m_groups.removeOne(group); } -void QGLContextGroup::cleanupResources(const QGLContext *ctx) +void QGLContextGroup::cleanupResources(const QGLContext *context) { - qDebug() << "QGLContextGroup::cleanupResources() for ctx:" << hex << ctx << "shares:" << m_shares.size() << "res:" << m_resources.size(); + qDebug() << "QGLContextGroup::cleanupResources() for ctx:" << hex << context + << "shares:" << m_shares.size() << "res:" << m_resources.size(); // 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::ConstIterator it; + QHash::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 f0b36f7..d354f92 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -436,6 +436,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 acd799a..3be30de 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -206,7 +206,7 @@ public: #endif }; -class QGLContextResource; +class QGLContextGroupResourceBase; class QGLSharedResourceGuard; // QGLContextPrivate has the responsibility of creating context groups. @@ -234,7 +234,7 @@ private: QGLExtensionFuncs m_extensionFuncs; const QGLContext *m_context; // context group's representative QList m_shares; - QHash m_resources; + QHash m_resources; QGLSharedResourceGuard *m_guards; // double-linked list of active guards. QAtomicInt m_refs; @@ -242,7 +242,7 @@ private: friend class QGLContext; friend class QGLContextPrivate; - friend class QGLContextResource; + friend class QGLContextGroupResourceBase; }; // Get the context that resources for "ctx" will transfer to once @@ -307,6 +307,8 @@ class QGLTexture; // all the GL2 engine uses: #define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3 +class QGLContextResourceBase; + class QGLContextPrivate { Q_DECLARE_PUBLIC(QGLContext) @@ -398,6 +400,7 @@ public: GLuint current_fbo; GLuint default_fbo; QPaintEngine *active_engine; + QHash m_resources; bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT]; @@ -606,24 +609,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 QGLContextGroupResourceBase { public: - QGLContextResource(); - virtual ~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; -private: + +protected: QList 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 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(QGLContextGroupResourceBase::value(context)); + if (resource) { + QGLShareContextScope scope(context); + delete resource; + } + } + } + + T *value(const QGLContext *context) { + T *resource = reinterpret_cast(QGLContextGroupResourceBase::value(context)); + if (!resource) { + resource = new T(context); + insert(context, resource); + } + return resource; + } + +protected: + void freeResource(void *resource) { + delete reinterpret_cast(resource); + } +}; + +/* + Base for resources that are context specific. +*/ +class 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 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 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(QGLContextResourceBase::value(context)); + if (resource) { + QGLShareContextScope scope(context); + delete resource; + } + } + } + + T *value(const QGLContext *context) { + T *resource = reinterpret_cast(QGLContextResourceBase::value(context)); + if (!resource) { + resource = new T(context); + insert(context, resource); + } + return resource; + } + +protected: + void freeResource(void *resource) { + delete reinterpret_cast(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 diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 011869e..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,19 +336,9 @@ private: }; QList QGLBlurTextureCache::blurTextureCaches; +Q_GLOBAL_STATIC(QGLContextGroupResource, qt_blur_texture_caches) -class QGLBlurCacheResource : public QGLContextResource -{ -public: - void freeResource(void *value) - { - delete reinterpret_cast(value); - } -}; - -Q_GLOBAL_STATIC(QGLBlurCacheResource, qt_blur_texture_caches) - -QGLBlurTextureCache::QGLBlurTextureCache() +QGLBlurTextureCache::QGLBlurTextureCache(const QGLContext *) : timerId(0) { cache.setMaxCost(4 * 1024 * 1024); @@ -370,12 +360,7 @@ void QGLBlurTextureCache::timerEvent(QTimerEvent *) QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context) { - QGLBlurTextureCache *p = reinterpret_cast(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) -- cgit v0.12 From 7348a762379bbc615194281806949e7fbac503a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 17 Jun 2010 15:57:03 +0200 Subject: Clarification regarding the WA_X11InitThreads flag. --- src/corelib/global/qnamespace.qdoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index ce1176d..19742e7 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -171,7 +171,8 @@ constructed. \value AA_X11InitThreads Calls XInitThreads() as part of the QApplication - construction in order to make Xlib calls thread-safe. + construction in order to make Xlib calls thread-safe. This + attribute must be set before QApplication is constructed. \omitvalue AA_AttributeCount -- cgit v0.12 From 35f0729a36f1562abc4b3bb12e35d86afdd19a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 22 Jun 2010 13:15:49 +0200 Subject: Clean up debug output. --- .../gl2paintengineex/qglengineshadermanager.cpp | 13 +++++++++---- .../gl2paintengineex/qtextureglyphcache_gl.cpp | 8 ++++++-- .../gl2paintengineex/qtextureglyphcache_gl_p.h | 5 ++++- src/opengl/qgl.cpp | 20 ++++++++++++++------ 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index c06bb3c..4b5f53e 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -46,6 +46,8 @@ #include #endif +// #define QT_GL_SHARED_SHADER_DEBUG + QT_BEGIN_NAMESPACE class QGLShaderStorage @@ -53,10 +55,8 @@ class QGLShaderStorage public: QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { QGLContextGroupResource *&shaders = m_storage.localData(); - if (!shaders) { - qDebug() << "New thread storage for:" << hex << QThread::currentThread(); + if (!shaders) shaders = new QGLContextGroupResource(); - } return shaders->value(context); } @@ -229,11 +229,16 @@ 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() { - qDebug() << "####### ~QGLEngineSharedShaders() ##########"; +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> ~QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif qDeleteAll(shaders); shaders.clear(); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index 001a09e..290c57e 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -66,12 +66,16 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngin if (!ctx->d_ptr->workaround_brokenFBOReadBack && pex != 0) glGenFramebuffers(1, &m_fbo); - fprintf(stderr, "## QGLTextureGlyphCache(): ctx: %p - this: %p\n", ctx, this); +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx); +#endif } QGLTextureGlyphCache::~QGLTextureGlyphCache() { - fprintf(stderr, "## ~QGLTextureGlyphCache(): context: %p - this: %p\n", ctx, this); +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> ~QGLTextureGlyphCache() %p.", this); +#endif } void QGLTextureGlyphCache::createTextureData(int width, int height) diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index 1a78eed..04d1354 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -57,6 +57,7 @@ #include #include +// #define QT_GL_TEXTURE_GLYPH_CACHE_DEBUG QT_BEGIN_NAMESPACE @@ -87,7 +88,9 @@ public: } void freeResource(void *) { - qDebug() << "QGLTextureGlyphCache::freeResource():" << this << "ctx:" << ctx; +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug("Freeing glyph cache resource %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 diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 6755d93..19ab51d 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -94,6 +94,8 @@ #include +// #define QT_GL_CONTEXT_RESOURCE_DEBUG + QT_BEGIN_NAMESPACE #if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) @@ -2028,7 +2030,6 @@ QGLContext::~QGLContext() // remove any textures cached in this context QGLTextureCache::instance()->removeContextTextures(this); - qDebug() << "~QGLContext(): about to clean up" << hex << this; // clean up resources specific to this context d_ptr->cleanup(); // clean up resources belonging to this context's group @@ -5292,14 +5293,18 @@ void QGLContextGroup::removeShare(const QGLContext *context) { QGLContextGroupResourceBase::QGLContextGroupResourceBase() : active(0) { +#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG + qDebug("Creating context group resource object %p.", this); +#endif } QGLContextGroupResourceBase::~QGLContextGroupResourceBase() { - qDebug() << "~QGLContextGroupResourceBase()" << hex << this << "group size:" << m_groups.size(); +#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); - qDebug() << " ->removing from group resource list:" << hex << this; active.deref(); } #ifndef QT_NO_DEBUG @@ -5313,6 +5318,9 @@ QGLContextGroupResourceBase::~QGLContextGroupResourceBase() 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); @@ -5328,7 +5336,9 @@ void *QGLContextGroupResourceBase::value(const QGLContext *context) void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx, void *value) { - qDebug() << "QGLContextGroupResourceBase::cleanup() this:" << hex << this << "ctx:" << ctx << "thread:" << (void *)QThread::currentThread(); +#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(); @@ -5339,8 +5349,6 @@ void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx, void *value) void QGLContextGroup::cleanupResources(const QGLContext *context) { - qDebug() << "QGLContextGroup::cleanupResources() for ctx:" << hex << context - << "shares:" << m_shares.size() << "res:" << m_resources.size(); // If there are still shares, then no cleanup to be done yet. if (m_shares.size() > 1) return; -- cgit v0.12 From 48ecc50d1d5cb698749df97e4ecee0f096904618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Mon, 21 Jun 2010 17:04:09 +0200 Subject: Make qt_pixmap_serial thread-safe. --- src/gui/image/qpixmap_x11.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index 6bebefc..d61f21d 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(); @@ -1950,7 +1950,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) { @@ -2201,7 +2201,7 @@ void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect) const QX11PixmapData *x11Data = static_cast(data); - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); flags &= ~Uninitialized; xinfo = x11Data->xinfo; @@ -2321,7 +2321,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; -- cgit v0.12 From de7a1e3780c145b8721f42bb9477224b5a8ae93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Mon, 21 Jun 2010 17:04:43 +0200 Subject: Enable glyph uploads from a thread in the X11 font engine. Note: this doesn't necessarily mean it's thread-safe yet. --- src/gui/text/qfontengine_x11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { -- cgit v0.12 From 10ac24c98ea7f2738662be91fda7feec7f322dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 22 Jun 2010 12:34:07 +0200 Subject: The group pointer should no longer be a part of the 64 bit int. The group is now a proper part of the QGLTextureCacheKey, and it's no longer necessary with this hack. --- src/opengl/qgl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 19ab51d..1f2a886 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2235,7 +2235,7 @@ QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_prem QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options) { - const qint64 key = image.cacheKey() | (qint64) group; + const qint64 key = image.cacheKey(); QGLTexture *texture = textureCacheLookup(key, target); if (texture) { glBindTexture(target, texture->id); @@ -2508,7 +2508,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, Q_UNUSED(q); #endif - const qint64 key = pixmap.cacheKey() | (qint64) group; + const qint64 key = pixmap.cacheKey(); QGLTexture *texture = textureCacheLookup(key, target); if (texture) { glBindTexture(target, texture->id); -- cgit v0.12 From e11af338a0d0e96a8100d0e78cc42e67e1447864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Wed, 23 Jun 2010 16:04:28 +0200 Subject: Rework how QGLTextureGlyphCache makes use of the resource system. Separate out the GL objects (the texture and the fbo) that needs to be tracked into a QGLGlyphTexture structure and make that accessible via a QGLContextGroupResource object. --- .../gl2paintengineex/qpaintengineex_opengl2.cpp | 1 - .../gl2paintengineex/qtextureglyphcache_gl.cpp | 45 ++++++----- .../gl2paintengineex/qtextureglyphcache_gl_p.h | 86 ++++++++++++++-------- src/opengl/qgl.h | 1 + 4 files changed, 77 insertions(+), 56 deletions(-) diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index fe0d6c9..756180f 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1458,7 +1458,6 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp (QGLTextureGlyphCache *) staticTextItem->fontEngine->glyphCache(cacheKey, glyphType, QTransform()); if (!cache || cache->cacheType() != glyphType) { cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform()); - cache->insert(ctx, cache); staticTextItem->fontEngine->setGlyphCache(cacheKey, cache); } diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index 290c57e..b4dc4fa 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -54,21 +54,12 @@ extern Q_GUI_EXPORT bool qt_cleartype_enabled; QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) : QImageTextureGlyphCache(type, matrix) - , ctx(context) - , m_width(0) - , m_height(0) + , ctx(0) { - // 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); - #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx); #endif + setContext(context); } QGLTextureGlyphCache::~QGLTextureGlyphCache() @@ -78,6 +69,12 @@ QGLTextureGlyphCache::~QGLTextureGlyphCache() #endif } +void QGLTextureGlyphCache::setContext(const QGLContext *context) +{ + ctx = context; + m_h = 0; +} + void QGLTextureGlyphCache::createTextureData(int width, int height) { if (ctx == 0) { @@ -97,11 +94,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 data(width * height); for (int i = 0; i < data.size(); ++i) @@ -122,9 +120,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) @@ -132,7 +131,7 @@ 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) { @@ -146,7 +145,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); @@ -201,7 +200,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); @@ -223,10 +222,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; @@ -234,7 +234,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; } @@ -264,7 +263,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 04d1354..66aed91 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -63,7 +63,42 @@ QT_BEGIN_NAMESPACE class QGL2PaintEngineExPrivate; -class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QGLContextGroupResourceBase, 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 { public: QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix); @@ -75,46 +110,33 @@ public: virtual int glyphMargin() const; virtual int glyphPadding() const; - inline GLuint texture() const { return m_texture; } - - inline int width() const { return m_width; } - inline int height() const { return m_height; } - - inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } - - inline const QGLContext *context() const - { - return ctx; + inline GLuint texture() const { + QGLTextureGlyphCache *that = const_cast(this); + QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); + return glyphTexture ? glyphTexture->m_texture : 0; } - void freeResource(void *) { -#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG - qDebug("Freeing glyph cache resource %p for context %p.", this, ctx); -#endif + inline int width() const { + QGLTextureGlyphCache *that = const_cast(this); + QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); + return glyphTexture ? glyphTexture->m_width : 0; + } + inline int height() const { + QGLTextureGlyphCache *that = const_cast(this); + QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx); + return glyphTexture ? glyphTexture->m_height : 0; + } - // At this point, the context group is made current, so it's safe to - // release resources without a makeCurrent() call - if (!ctx->d_ptr->workaround_brokenFBOReadBack) - glDeleteFramebuffers(1, &m_fbo); + inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } - if (m_width || m_height) - glDeleteTextures(1, &m_texture); - } + void setContext(const QGLContext *context); + inline const QGLContext *context() const { return ctx; } private: - void cleanUpContext(); + QGLContextGroupResource 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.h b/src/opengl/qgl.h index d354f92..1bef33d 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -417,6 +417,7 @@ private: friend class QGLPixmapData; friend class QGLPixmapFilterBase; friend class QGLTextureGlyphCache; + friend class QGLGlyphTexture; friend class QGLContextGroup; friend class QGLSharedResourceGuard; friend class QGLPixmapBlurFilter; -- cgit v0.12 From 449cbdfd1b8523e721d8b86e49647495b67005e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 2 Jul 2010 15:38:07 +0200 Subject: Fix the tests for the internal GL resource system. --- src/opengl/qgl_p.h | 4 ++-- tests/auto/qgl/tst_qgl.cpp | 26 +++++++++----------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 3be30de..fd6b7f0 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -612,7 +612,7 @@ inline GLenum qt_gl_preferredTextureTarget() /* Base for resources that are shared in a context group. */ -class QGLContextGroupResourceBase +class Q_OPENGL_EXPORT QGLContextGroupResourceBase { public: QGLContextGroupResourceBase(); @@ -673,7 +673,7 @@ protected: /* Base for resources that are context specific. */ -class QGLContextResourceBase +class Q_OPENGL_EXPORT QGLContextResourceBase { public: virtual ~QGLContextResourceBase() { 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(data); -} - -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shared_test, (qt_shared_test_free)) +Q_GLOBAL_STATIC(QGLContextGroupResource, 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); -- cgit v0.12 From 6f0597a198912628c52f22ece63cb5d169dcaed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 2 Jul 2010 16:35:44 +0200 Subject: Remove warning. --- src/opengl/qgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 1bef33d..b6a95e7 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -417,7 +417,7 @@ private: friend class QGLPixmapData; friend class QGLPixmapFilterBase; friend class QGLTextureGlyphCache; - friend class QGLGlyphTexture; + friend struct QGLGlyphTexture; friend class QGLContextGroup; friend class QGLSharedResourceGuard; friend class QGLPixmapBlurFilter; -- cgit v0.12 From 21158f7b31b6053b4c704f434f3f480328a0ba65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Fri, 2 Jul 2010 17:55:49 +0200 Subject: Initialize pex. This got lost in a conflict resolving session. --- src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index b4dc4fa..9ce5d55 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -55,6 +55,7 @@ extern Q_GUI_EXPORT bool qt_cleartype_enabled; QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) : QImageTextureGlyphCache(type, matrix) , ctx(0) + , pex(0) { #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx); -- cgit v0.12 From 40c8aba524831dd0d2f8dfcdf07797f5c2c47f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Mon, 5 Jul 2010 14:15:57 +0200 Subject: QGLPixelBuffer should not rely on temporary QGLWidgets. Use QGLTemporaryContext instead. Also, to re-use already registered window classes for the temporary native window, make qt_reg_wndclass() register a QWidget default class if it's passed a 0 pointer. Reviewed-by: Gunnar --- src/gui/kernel/qapplication_win.cpp | 25 +++++++++++++++++-------- src/gui/painting/qpainter.cpp | 10 +++++----- src/opengl/qglpixelbuffer_win.cpp | 8 ++------ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index e10a6b0..c5340c0 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -944,26 +944,33 @@ bool qt_nograb() // application no-grab option typedef QHash 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; 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 @@ -978,7 +985,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 @@ -1049,7 +1056,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 @@ -1071,8 +1081,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/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 8ac40ee..0911d4f 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5256,7 +5256,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) { @@ -5326,7 +5326,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(); @@ -5733,7 +5733,7 @@ void QPainter::drawGlyphs(const QPointF &position, const QGlyphs &glyphs) int count = qMin(glyphIndexes.size(), glyphPositions.size()); QVarLengthArray fixedPointPositions(count); for (int i=0; idrawGlyphs(glyphIndexes.data(), fixedPointPositions.data(), count); @@ -5742,7 +5742,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 positions(glyphCount); for (int i=0; idevice->devType(), "drawTiledPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()", true); #endif qreal sw = pixmap.width(); 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); -- cgit v0.12 From 7629184953898416c5ffa302b761e1ee3f65d9ee Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 6 Jul 2010 13:28:30 +0200 Subject: Make QScriptAnalysis fit in two bytes again When the size of the "flags" variable was increased, the bit field was expanded to be three bytes. Since the "script" variable does not use its full eight bits, we reduce it by one to compensate. Reviewed-by: Simon Hausmann --- src/gui/text/qtextengine_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 908a0ec..1ec3791 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -128,7 +128,7 @@ struct Q_AUTOTEST_EXPORT QScriptAnalysis TabOrObject = Tab, Object = 7 }; - unsigned short script : 8; + unsigned short script : 7; unsigned short bidiLevel : 6; // Unicode Bidi algorithm embedding level (0-61) unsigned short flags : 3; inline bool operator == (const QScriptAnalysis &other) const { -- cgit v0.12 From 591e2a1e096618979250f062648b731367e60710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 6 Jul 2010 16:18:02 +0200 Subject: Don't call GetDC() nedlessly. It seems that the CS_OWNDC class style flag only returns the same DC when GetDC() is called in the same thread that created the window. This in turn generates a bad DC resource leak if you have a rendering thread with one or more GL context switches, so just call GetDC() when you actually need to. Reviewed-by: Kim --- src/opengl/qgl_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opengl/qgl_win.cpp b/src/opengl/qgl_win.cpp index 5ab944a..52988b6 100644 --- a/src/opengl/qgl_win.cpp +++ b/src/opengl/qgl_win.cpp @@ -1283,7 +1283,7 @@ void QGLContext::makeCurrent() if (d->rc == wglGetCurrentContext() || !d->valid) // already current return; - if (d->win) { + if (d->win && !d->dc) { d->dc = GetDC(d->win); if (!d->dc) { qwglError("QGLContext::makeCurrent()", "GetDC()"); -- cgit v0.12 From 6d337a3dd715597aa8ec797f6aeb8f42db129a00 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 14 Jun 2010 13:23:15 +0200 Subject: Add QItemSelectionRange::operator<() Reviewed-by: Olivier Goffart Merge-Request: 688 --- src/gui/itemviews/qitemselectionmodel.h | 24 ++++ .../tst_qitemselectionmodel.cpp | 159 +++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/src/gui/itemviews/qitemselectionmodel.h b/src/gui/itemviews/qitemselectionmodel.h index 436514f..e2bd06b 100644 --- a/src/gui/itemviews/qitemselectionmodel.h +++ b/src/gui/itemviews/qitemselectionmodel.h @@ -101,6 +101,30 @@ public: { return (tl == other.tl && br == other.br); } inline bool operator!=(const QItemSelectionRange &other) const { return !operator==(other); } + inline bool operator<(const QItemSelectionRange &other) const + { + // Comparing parents will compare the models, but if two equivalent ranges + // in two different models have invalid parents, they would appear the same + if (other.tl.model() == tl.model()) { + // parent has to be calculated, so we only do so once. + const QModelIndex topLeftParent = tl.parent(); + const QModelIndex otherTopLeftParent = other.tl.parent(); + if (topLeftParent == otherTopLeftParent) { + if (other.tl.row() == tl.row()) { + if (other.tl.column() == tl.column()) { + if (other.br.row() == br.row()) { + return br.column() < other.br.column(); + } + return br.row() < other.br.row(); + } + return tl.column() < other.tl.column(); + } + return tl.row() < other.tl.row(); + } + return topLeftParent < otherTopLeftParent; + } + return tl.model() < other.tl.model(); + } inline bool isValid() const { diff --git a/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp b/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp index bfe306d..46bd4a2 100644 --- a/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp +++ b/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp @@ -95,6 +95,8 @@ private slots: void QTBUG5671_layoutChangedWithAllSelected(); void QTBUG2804_layoutChangedTreeSelection(); void deselectRemovedMiddleRange(); + void rangeOperatorLessThan_data(); + void rangeOperatorLessThan(); private: QAbstractItemModel *model; @@ -2401,6 +2403,163 @@ void tst_QItemSelectionModel::deselectRemovedMiddleRange() QVERIFY(spy.size() == 1); } +static QStandardItemModel* getModel(QObject *parent) +{ + QStandardItemModel *model = new QStandardItemModel(parent); + + for (int i = 0; i < 4; ++i) { + QStandardItem *parentItem = model->invisibleRootItem(); + QList list; + for (int j = 0; j < 4; ++j) { + list.append(new QStandardItem(QString("item %1, %2").arg(i).arg(j))); + } + parentItem->appendRow(list); + parentItem = list.first(); + for (int j = 0; j < 4; ++j) { + QList list; + for (int k = 0; k < 4; ++k) { + list.append(new QStandardItem(QString("item %1, %2").arg(i).arg(j))); + } + parentItem->appendRow(list); + } + } + return model; +} + +enum Result { + LessThan, + NotLessThan, + NotEqual +}; + +Q_DECLARE_METATYPE(Result); + +void tst_QItemSelectionModel::rangeOperatorLessThan_data() +{ + QTest::addColumn("parent1"); + QTest::addColumn("top1"); + QTest::addColumn("left1"); + QTest::addColumn("bottom1"); + QTest::addColumn("right1"); + QTest::addColumn("parent2"); + QTest::addColumn("top2"); + QTest::addColumn("left2"); + QTest::addColumn("bottom2"); + QTest::addColumn("right2"); + QTest::addColumn("result"); + + QTest::newRow("lt01") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 3 << 3 << NotLessThan; + + QTest::newRow("lt02") << -1 << 0 << 0 << 2 << 3 + << -1 << 0 << 0 << 3 << 3 << LessThan; + QTest::newRow("lt03") << -1 << 0 << 0 << 3 << 2 + << -1 << 0 << 0 << 3 << 3 << LessThan; + QTest::newRow("lt04") << -1 << 0 << 0 << 2 << 2 + << -1 << 0 << 0 << 3 << 3 << LessThan; + + QTest::newRow("lt05") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 2 << 3 << NotLessThan; + QTest::newRow("lt06") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 3 << 2 << NotLessThan; + QTest::newRow("lt07") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 2 << 2 << NotLessThan; + + QTest::newRow("lt08") << -1 << 0 << 0 << 3 << 3 + << 0 << 0 << 0 << 3 << 3 << NotEqual; + QTest::newRow("lt09") << 1 << 0 << 0 << 3 << 3 + << 0 << 0 << 0 << 3 << 3 << NotEqual; + QTest::newRow("lt10") << 1 << 0 << 0 << 1 << 1 + << 0 << 2 << 2 << 3 << 3 << NotEqual; + QTest::newRow("lt11") << 1 << 2 << 2 << 3 << 3 + << 0 << 0 << 0 << 1 << 1 << NotEqual; + + QTest::newRow("lt12") << -1 << 0 << 0 << 1 << 1 + << -1 << 2 << 2 << 3 << 3 << LessThan; + QTest::newRow("lt13") << -1 << 2 << 2 << 3 << 3 + << -1 << 0 << 0 << 1 << 1 << NotLessThan; + QTest::newRow("lt14") << 1 << 0 << 0 << 1 << 1 + << 1 << 2 << 2 << 3 << 3 << LessThan; + QTest::newRow("lt15") << 1 << 2 << 2 << 3 << 3 + << 1 << 0 << 0 << 1 << 1 << NotLessThan; + + QTest::newRow("lt16") << -1 << 0 << 0 << 2 << 2 + << -1 << 1 << 1 << 3 << 3 << LessThan; + QTest::newRow("lt17") << -1 << 1 << 1 << 3 << 3 + << -1 << 0 << 0 << 2 << 2 << NotLessThan; + QTest::newRow("lt18") << 1 << 0 << 0 << 2 << 2 + << 1 << 1 << 1 << 3 << 3 << LessThan; + QTest::newRow("lt19") << 1 << 1 << 1 << 3 << 3 + << 1 << 0 << 0 << 2 << 2 << NotLessThan; +} + +void tst_QItemSelectionModel::rangeOperatorLessThan() +{ + QStandardItemModel *model1 = getModel(this); + QStandardItemModel *model2 = getModel(this); + + QFETCH(int, parent1); + QFETCH(int, top1); + QFETCH(int, left1); + QFETCH(int, bottom1); + QFETCH(int, right1); + QFETCH(int, parent2); + QFETCH(int, top2); + QFETCH(int, left2); + QFETCH(int, bottom2); + QFETCH(int, right2); + QFETCH(Result, result); + + QModelIndex p1 = model1->index(parent1, 0); + + QModelIndex tl1 = model1->index(top1, left1, p1); + QModelIndex br1 = model1->index(bottom1, right1, p1); + + QItemSelectionRange r1(tl1, br1); + + QModelIndex p2 = model1->index(parent2, 0); + + QModelIndex tl2 = model1->index(top2, left2, p2); + QModelIndex br2 = model1->index(bottom2, right2, p2); + + QItemSelectionRange r2(tl2, br2); + + if (result == LessThan) + QVERIFY(r1 < r2); + else if (result == NotLessThan) + QVERIFY(!(r1 < r2)); + else if (result == NotEqual) + if (!(r1 < r2)) + QVERIFY(r2 < r1); + + // Ranges in different models are always non-equal + + QModelIndex p3 = model2->index(parent1, 0); + + QModelIndex tl3 = model2->index(top1, left1, p3); + QModelIndex br3 = model2->index(bottom1, right1, p3); + + QItemSelectionRange r3(tl3, br3); + + if (!(r1 < r3)) + QVERIFY(r3 < r1); + + if (!(r2 < r3)) + QVERIFY(r3 < r2); + + QModelIndex p4 = model2->index(parent2, 0); + + QModelIndex tl4 = model2->index(top2, left2, p4); + QModelIndex br4 = model2->index(bottom2, right2, p4); + + QItemSelectionRange r4(tl4, br4); + + if (!(r1 < r4)) + QVERIFY(r4 < r1); + + if (!(r2 < r4)) + QVERIFY(r4 < r2); +} QTEST_MAIN(tst_QItemSelectionModel) #include "tst_qitemselectionmodel.moc" -- cgit v0.12 From f2077762cffba8c66cc3880c486161201421e074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Wed, 7 Jul 2010 15:10:13 +0200 Subject: Made QGL2GradientCache thread safe. This can be improved by using a QReadWriteLocker, but it means restructuring the find/insert code. Use a QMutex for now. --- src/opengl/gl2paintengineex/qglgradientcache.cpp | 28 +++++++++++++++++++----- src/opengl/gl2paintengineex/qglgradientcache_p.h | 5 +++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp index f1b095d..cbd5eb8 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp @@ -42,18 +42,33 @@ #include "qglgradientcache_p.h" #include #include - +#include QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QGLContextGroupResource, qt_gradient_caches) +class QGL2GradientCacheWrapper +{ +public: + QGL2GradientCache *cacheForContext(const QGLContext *context) { + QMutexLocker lock(&m_mutex); + return m_resource.value(context); + } + +private: + QGLContextGroupResource m_resource; + QMutex m_mutex; +}; + +Q_GLOBAL_STATIC(QGL2GradientCacheWrapper, qt_gradient_caches) QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) { - return qt_gradient_caches()->value(context); + return qt_gradient_caches()->cacheForContext(context); } -void QGL2GradientCache::cleanCache() { +void QGL2GradientCache::cleanCache() +{ + QMutexLocker lock(&m_mutex); QGLGradientColorTableHash::const_iterator it = cache.constBegin(); for (; it != cache.constEnd(); ++it) { const CacheInfo &cache_info = it.value(); @@ -64,6 +79,7 @@ void QGL2GradientCache::cleanCache() { GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity) { + QMutexLocker lock(&m_mutex); quint64 hash_val = 0; QGradientStops stops = gradient.stops(); @@ -77,7 +93,9 @@ GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity) else { do { const CacheInfo &cache_info = it.value(); - if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode()) { + if (cache_info.stops == stops && cache_info.opacity == opacity + && cache_info.interpolationMode == gradient.interpolationMode()) + { return cache_info.texId; } ++it; diff --git a/src/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/gl2paintengineex/qglgradientcache_p.h index 8ecb34e..7e93d87 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache_p.h +++ b/src/opengl/gl2paintengineex/qglgradientcache_p.h @@ -54,6 +54,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -81,16 +82,16 @@ public: GLuint getBuffer(const QGradient &gradient, qreal opacity); inline int paletteSize() const { return 1024; } -protected: +private: inline int maxCacheSize() const { return 60; } inline void generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const; GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity); - void cleanCache(); QGLGradientColorTableHash cache; + QMutex m_mutex; }; QT_END_NAMESPACE -- cgit v0.12 From 96507cedbc6007bd8db81d82c051da86da5b0e4b Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Wed, 7 Jul 2010 17:11:39 +0200 Subject: Fixes the double spinbox not updating geometry when suffix is changed patch was found in the task and looks good. Task-number: QTBUG-9530 --- src/gui/widgets/qspinbox.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/widgets/qspinbox.cpp b/src/gui/widgets/qspinbox.cpp index 2d871d0..10ce144 100644 --- a/src/gui/widgets/qspinbox.cpp +++ b/src/gui/widgets/qspinbox.cpp @@ -697,6 +697,9 @@ void QDoubleSpinBox::setSuffix(const QString &suffix) d->suffix = suffix; d->updateEdit(); + + d->cachedSizeHint = QSize(); + updateGeometry(); } /*! -- cgit v0.12 From c4179a0e29dab5034c1980622df88a05724706e4 Mon Sep 17 00:00:00 2001 From: James Perrett Date: Thu, 8 Jul 2010 15:06:10 +0200 Subject: Add eglnullws QScreen driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This QScreen driver can be used for embedded Qt applications that draw everything within a single full-screen QGLWidget. It uses underlying support for a null window system (NullWS) such as is present in PowerVR systems. It offers performance and stability improvements over the powervr QScreen driver for applications that do not need full QWS windowing support. Reviewed-by: Jørgen Lind --- src/plugins/gfxdrivers/eglnullws/README | 48 +++++++ src/plugins/gfxdrivers/eglnullws/eglnullws.pro | 18 +++ .../gfxdrivers/eglnullws/eglnullwsscreen.cpp | 141 +++++++++++++++++++++ src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h | 28 ++++ .../gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp | 25 ++++ .../gfxdrivers/eglnullws/eglnullwsscreenplugin.h | 6 + .../eglnullws/eglnullwswindowsurface.cpp | 43 +++++++ .../gfxdrivers/eglnullws/eglnullwswindowsurface.h | 22 ++++ src/plugins/gfxdrivers/gfxdrivers.pro | 1 + 9 files changed, 332 insertions(+) create mode 100644 src/plugins/gfxdrivers/eglnullws/README create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullws.pro create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp create mode 100644 src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h diff --git a/src/plugins/gfxdrivers/eglnullws/README b/src/plugins/gfxdrivers/eglnullws/README new file mode 100644 index 0000000..80b88c7 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/README @@ -0,0 +1,48 @@ +EGL NullWS QScreen Driver +========================= + +If your application draws everything within a single full-screen QGLWidget then +you may wish to use this QScreen plugin driver. This driver simply returns 0 +(as a EGLNativeWindowType value) when asked by the QtOpenGl module to create a +native window. Some OpenGL ES implementations (including PowerVR) interpret this +to mean that a full-screen OpenGL context is desired without any windowing +support (NullWS). + +To tell a Qt/Embedded application to use this driver use the -display command +line option or the QWS_DISPLAY environment variable. The following driver +options are supported: + +size=WIDTHxHEIGHT Screen size reported by the driver +format=FORMAT Screen format + +Run with '-display eglnullws:help' to get a full list of options (including a +list of supported format strings). + +If you choose a screen format that is not supported by the hardware then the +QtOpenGl module will write out a list of supported EGL configurations. Use +one of the supported screen formats from this list. + +Using this driver with PowerVR hardware +--------------------------------------- + +Using this plugin with PowerVR hardware should give a significant speedup +compared to running with the Qt powervr driver (with a full-screen QGLWidget). +This is because sacrificing the window system allows less work to be done in +order to get graphics on the screen. Using this driver also avoids the memory +fragmentation issues present in the powervr driver and avoids any direct +dependencies on the deprecated PVR2D API from Imagination Technologies. + +To use this driver ensure you have /etc/powervr.ini with contents similar to +this: + +[default] +WindowSystem=libpvrPVR2D_FLIPWSEGL.so + +This driver will also function with libpvrPVR2D_FRONTWSEGL.so, but that draws +straight into the framebuffer and will therefore cause flickering (it can be +useful for performance testing though). The flip plugin uses triple buffering, +so you will need to set the virtual vertical resolution of your framebuffer to +be three times the physical vertical resolution of your screen. This can be +done with 'fbset -vyres'. Failure to do this can cause system crashes. You +should also ensure that the plugin you choose in powervr.ini is in your library +path (it may just silently default to the flip plugin if not). diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullws.pro b/src/plugins/gfxdrivers/eglnullws/eglnullws.pro new file mode 100644 index 0000000..242ab07 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullws.pro @@ -0,0 +1,18 @@ +TARGET = qeglnullws +include(../../qpluginbase.pri) + +CONFIG += warn_on +QT += opengl + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/gfxdrivers + +target.path = $$[QT_INSTALL_PLUGINS]/gfxdrivers +INSTALLS += target + +HEADERS = eglnullwsscreen.h \ + eglnullwsscreenplugin.h \ + eglnullwswindowsurface.h + +SOURCES = eglnullwsscreen.cpp \ + eglnullwsscreenplugin.cpp \ + eglnullwswindowsurface.cpp diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp new file mode 100644 index 0000000..9a21447 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp @@ -0,0 +1,141 @@ +#include "eglnullwsscreen.h" +#include "eglnullwswindowsurface.h" +#include "eglnullwsscreenplugin.h" + +#include +#include + +namespace +{ + class EGLNullWSScreenSurfaceFunctions : public QGLScreenSurfaceFunctions + { + public: + virtual bool createNativeWindow(QWidget *, EGLNativeWindowType *native) + { *native = 0; return true; } + }; +} + +EGLNullWSScreen::EGLNullWSScreen(int displayId) : QGLScreen(displayId) {} + +EGLNullWSScreen::~EGLNullWSScreen() {} + +bool EGLNullWSScreen::initDevice() +{ + setSurfaceFunctions(new EGLNullWSScreenSurfaceFunctions); + return true; +} + +static const QHash & formatDictionary() +{ + static QHash dictionary; + if (dictionary.isEmpty()) { + dictionary["rgb32"] = QImage::Format_RGB32; + dictionary["argb32"] = QImage::Format_ARGB32; + dictionary["rgb16"] = QImage::Format_RGB16; + dictionary["rgb666"] = QImage::Format_RGB666; + dictionary["rgb555"] = QImage::Format_RGB555; + dictionary["rgb888"] = QImage::Format_RGB888; + dictionary["rgb444"] = QImage::Format_RGB444; + } + return dictionary; +} + +static int depthForFormat(QImage::Format format) +{ + switch (format) { + case QImage::Format_RGB32: return 32; + case QImage::Format_ARGB32: return 32; + case QImage::Format_RGB16: return 16; + case QImage::Format_RGB666: return 24; + case QImage::Format_RGB555: return 16; + case QImage::Format_RGB888: return 24; + case QImage::Format_RGB444: return 16; + default: + Q_ASSERT(!"Unknown format"); + return -1; + } +} + +static void printHelp() +{ + QByteArray formatsBuf; + QTextStream(&formatsBuf) << QStringList(formatDictionary().keys()).join(", "); + qWarning( + "%s: Valid options are:\n" + "size=WIDTHxHEIGHT Screen size reported by this driver\n" + "format=FORMAT Screen format, where FORMAT is one of the following:\n" + " %s\n", + PluginName, + formatsBuf.constData()); +} + +bool EGLNullWSScreen::connect(const QString &displaySpec) +{ + const QStringList args = displaySpec.section(':', 1).split(':', QString::SkipEmptyParts); + Q_FOREACH(const QString arg, args) { + const QString optionName = arg.section('=', 0, 0); + const QString optionArg = arg.section('=', 1); + if (optionName == QLatin1String("size")) { + w = optionArg.section('x', 0, 0).toInt(); + h = optionArg.section('x', 1, 1).toInt(); + } else if (optionName == QLatin1String("format")) { + if (formatDictionary().contains(optionArg)) + setPixelFormat(formatDictionary().value(optionArg)); + else + printHelp(); + } else { + printHelp(); + } + } + + if (w == 0 || h == 0) { + w = 640; + h = 480; + qWarning("%s: Using default screen size %dx%d", PluginName, w, h); + } + dw = w; + dh = h; + + if (pixelFormat() == QImage::Format_Invalid) { + qWarning("%s: Using default screen format argb32", PluginName); + setPixelFormat(QImage::Format_ARGB32); + } + d = depthForFormat(pixelFormat()); + + static const int Dpi = 120; + static const qreal ScalingFactor = static_cast(25.4) / Dpi; + physWidth = qRound(dw * ScalingFactor); + physHeight = qRound(dh * ScalingFactor); + + return true; +} + +void EGLNullWSScreen::disconnect() {} + +void EGLNullWSScreen::shutdownDevice() {} + +void EGLNullWSScreen::setMode(int /*width*/, int /*height*/, int /*depth*/) {} + +void EGLNullWSScreen::blank(bool /*on*/) {} + +void EGLNullWSScreen::exposeRegion(QRegion /*r*/, int /*changing*/) {} + +QWSWindowSurface* EGLNullWSScreen::createSurface(QWidget *widget) const +{ + if (qobject_cast(widget)) { + return new EGLNullWSWindowSurface(widget); + } else { + qWarning("%s: Creating non-GL surface", PluginName); + return QScreen::createSurface(widget); + } +} + +QWSWindowSurface* EGLNullWSScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("eglnullws")) { + return new EGLNullWSWindowSurface; + } else { + qWarning("%s: Creating non-GL surface", PluginName); + return QScreen::createSurface(key); + } +} diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h new file mode 100644 index 0000000..9b620a4 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h @@ -0,0 +1,28 @@ +#ifndef EGLNULLWSSCREEN +#define EGLNULLWSSCREEN + +#include + +class EGLNullWSScreen : public QGLScreen +{ +public: + EGLNullWSScreen(int displayId); + virtual ~EGLNullWSScreen(); + + virtual bool initDevice(); + virtual bool connect(const QString &displaySpec); + virtual void disconnect(); + virtual void shutdownDevice(); + + virtual void setMode(int width, int height, int depth); + virtual void blank(bool on); + + virtual void exposeRegion(QRegion r, int changing); + + virtual QWSWindowSurface* createSurface(QWidget *widget) const; + virtual QWSWindowSurface* createSurface(const QString &key) const; + + virtual bool hasOpenGL() { return true; } +}; + +#endif // EGLNULLWSSCREEN diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp new file mode 100644 index 0000000..354ddef --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp @@ -0,0 +1,25 @@ +#include "eglnullwsscreenplugin.h" +#include "eglnullwsscreen.h" + +#include +#include + +class EGLNullWSScreenPlugin : public QScreenDriverPlugin +{ +public: + virtual QStringList keys() const; + virtual QScreen *create(const QString& driver, int displayId); +}; + +QStringList EGLNullWSScreenPlugin::keys() const +{ + return QStringList() << QLatin1String(PluginName); +} + +QScreen *EGLNullWSScreenPlugin::create(const QString& driver, int displayId) +{ + return (driver.toLower() == QLatin1String(PluginName) ? + new EGLNullWSScreen(displayId) : 0); +} + +Q_EXPORT_PLUGIN2(qeglnullws, EGLNullWSScreenPlugin) diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h new file mode 100644 index 0000000..14e2a82 --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h @@ -0,0 +1,6 @@ +#ifndef EGLNULLWSSCREENPLUGIN_H +#define EGLNULLWSSCREENPLUGIN_H + +const char *const PluginName = "eglnullws"; + +#endif // EGLNULLWSSCREENPLUGIN_H diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp new file mode 100644 index 0000000..016748e --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp @@ -0,0 +1,43 @@ +#include "eglnullwswindowsurface.h" +#include "eglnullwsscreenplugin.h" + +#include + +static const QWSWindowSurface::SurfaceFlags Flags + = QWSWindowSurface::RegionReserved | QWSWindowSurface::RegionReserved; + +EGLNullWSWindowSurface::EGLNullWSWindowSurface(QWidget *w) + : + QWSGLWindowSurface(w), + widget(w) +{ + setSurfaceFlags(Flags); +} + +EGLNullWSWindowSurface::EGLNullWSWindowSurface() + : widget(0) +{ + setSurfaceFlags(Flags); +} + +EGLNullWSWindowSurface::~EGLNullWSWindowSurface() {} + +QString EGLNullWSWindowSurface::key() const +{ + return QLatin1String(PluginName); +} + +QPaintDevice *EGLNullWSWindowSurface::paintDevice() +{ + return widget; +} + +bool EGLNullWSWindowSurface::isValid() const +{ + return qobject_cast(window()); +} + +QImage EGLNullWSWindowSurface::image() const +{ + return QImage(); +} diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h new file mode 100644 index 0000000..7e5d2fa --- /dev/null +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h @@ -0,0 +1,22 @@ +#ifndef EGLNULLWSWINDOWSURFACE_H +#define EGLNULLWSWINDOWSURFACE_H + +#include + +class EGLNullWSWindowSurface : public QWSGLWindowSurface +{ +public: + EGLNullWSWindowSurface(QWidget *widget); + EGLNullWSWindowSurface(); + virtual ~EGLNullWSWindowSurface(); + + virtual QString key() const; + virtual QPaintDevice *paintDevice(); + virtual bool isValid() const; + virtual QImage image() const; + +private: + QWidget *widget; +}; + +#endif // EGLNULLWSWINDOWSURFACE_H diff --git a/src/plugins/gfxdrivers/gfxdrivers.pro b/src/plugins/gfxdrivers/gfxdrivers.pro index d1ee3f2..1f38942 100644 --- a/src/plugins/gfxdrivers/gfxdrivers.pro +++ b/src/plugins/gfxdrivers/gfxdrivers.pro @@ -7,3 +7,4 @@ contains(gfx-plugins, vnc) :SUBDIRS += vnc contains(gfx-plugins, transformed) :SUBDIRS += transformed contains(gfx-plugins, svgalib) :SUBDIRS += svgalib contains(gfx-plugins, powervr) :SUBDIRS += powervr +contains(gfx-plugins, eglnullws) :SUBDIRS += eglnullws -- cgit v0.12 From eb7c795a9172bf3ee643e72cbd67db062c9883a3 Mon Sep 17 00:00:00 2001 From: James Perrett Date: Thu, 8 Jul 2010 15:06:12 +0200 Subject: Use Q_ASSERT_X() rather than Q_ASSERT(!"message") idiom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids warnings on some compilers. Reviewed-by: Jørgen Lind --- src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp index 9a21447..341caff 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp @@ -51,7 +51,7 @@ static int depthForFormat(QImage::Format format) case QImage::Format_RGB888: return 24; case QImage::Format_RGB444: return 16; default: - Q_ASSERT(!"Unknown format"); + Q_ASSERT_X(false, "EGLNullWSScreen", "Unknown format"); return -1; } } -- cgit v0.12 From 9b0ab2254f09d7c465e7f92cb2a41eddce83a84b Mon Sep 17 00:00:00 2001 From: James Perrett Date: Thu, 8 Jul 2010 15:06:13 +0200 Subject: Remove explicit 'virtual' specifiers to conform to Qt coding standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Jørgen Lind --- src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h index 9b620a4..a9cb64c 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h @@ -7,22 +7,22 @@ class EGLNullWSScreen : public QGLScreen { public: EGLNullWSScreen(int displayId); - virtual ~EGLNullWSScreen(); + ~EGLNullWSScreen(); - virtual bool initDevice(); - virtual bool connect(const QString &displaySpec); - virtual void disconnect(); - virtual void shutdownDevice(); + bool initDevice(); + bool connect(const QString &displaySpec); + void disconnect(); + void shutdownDevice(); - virtual void setMode(int width, int height, int depth); - virtual void blank(bool on); + void setMode(int width, int height, int depth); + void blank(bool on); - virtual void exposeRegion(QRegion r, int changing); + void exposeRegion(QRegion r, int changing); - virtual QWSWindowSurface* createSurface(QWidget *widget) const; - virtual QWSWindowSurface* createSurface(const QString &key) const; + QWSWindowSurface* createSurface(QWidget *widget) const; + QWSWindowSurface* createSurface(const QString &key) const; - virtual bool hasOpenGL() { return true; } + bool hasOpenGL() { return true; } }; #endif // EGLNULLWSSCREEN -- cgit v0.12 From 0bad059e4120b6e6c25427a19e3da942c1ff3151 Mon Sep 17 00:00:00 2001 From: James Perrett Date: Thu, 8 Jul 2010 15:06:15 +0200 Subject: Avoid static local QHash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change formatDictionary() to return a QHash by-value rather than a const reference to a static local QHash. This avoids memory being permanently allocated on the heap for a dictionary that is only needed during startup. Reviewed-by: Jørgen Lind --- .../gfxdrivers/eglnullws/eglnullwsscreen.cpp | 33 +++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp index 341caff..4213097 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp @@ -25,18 +25,16 @@ bool EGLNullWSScreen::initDevice() return true; } -static const QHash & formatDictionary() +static const QHash formatDictionary() { - static QHash dictionary; - if (dictionary.isEmpty()) { - dictionary["rgb32"] = QImage::Format_RGB32; - dictionary["argb32"] = QImage::Format_ARGB32; - dictionary["rgb16"] = QImage::Format_RGB16; - dictionary["rgb666"] = QImage::Format_RGB666; - dictionary["rgb555"] = QImage::Format_RGB555; - dictionary["rgb888"] = QImage::Format_RGB888; - dictionary["rgb444"] = QImage::Format_RGB444; - } + QHash dictionary; + dictionary["rgb32"] = QImage::Format_RGB32; + dictionary["argb32"] = QImage::Format_ARGB32; + dictionary["rgb16"] = QImage::Format_RGB16; + dictionary["rgb666"] = QImage::Format_RGB666; + dictionary["rgb555"] = QImage::Format_RGB555; + dictionary["rgb888"] = QImage::Format_RGB888; + dictionary["rgb444"] = QImage::Format_RGB444; return dictionary; } @@ -56,10 +54,10 @@ static int depthForFormat(QImage::Format format) } } -static void printHelp() +static void printHelp(const QHash &formatDictionary) { QByteArray formatsBuf; - QTextStream(&formatsBuf) << QStringList(formatDictionary().keys()).join(", "); + QTextStream(&formatsBuf) << QStringList(formatDictionary.keys()).join(", "); qWarning( "%s: Valid options are:\n" "size=WIDTHxHEIGHT Screen size reported by this driver\n" @@ -72,6 +70,7 @@ static void printHelp() bool EGLNullWSScreen::connect(const QString &displaySpec) { const QStringList args = displaySpec.section(':', 1).split(':', QString::SkipEmptyParts); + const QHash formatDict = formatDictionary(); Q_FOREACH(const QString arg, args) { const QString optionName = arg.section('=', 0, 0); const QString optionArg = arg.section('=', 1); @@ -79,12 +78,12 @@ bool EGLNullWSScreen::connect(const QString &displaySpec) w = optionArg.section('x', 0, 0).toInt(); h = optionArg.section('x', 1, 1).toInt(); } else if (optionName == QLatin1String("format")) { - if (formatDictionary().contains(optionArg)) - setPixelFormat(formatDictionary().value(optionArg)); + if (formatDict.contains(optionArg)) + setPixelFormat(formatDict.value(optionArg)); else - printHelp(); + printHelp(formatDict); } else { - printHelp(); + printHelp(formatDict); } } -- cgit v0.12 From 1441c74b89fa6b8b1368888413d5763f81cf733b Mon Sep 17 00:00:00 2001 From: James Perrett Date: Thu, 8 Jul 2010 15:06:16 +0200 Subject: Add copyright header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Jørgen Lind --- .../gfxdrivers/eglnullws/eglnullwsscreen.cpp | 41 ++++++++++++++++++++++ src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h | 41 ++++++++++++++++++++++ .../gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp | 41 ++++++++++++++++++++++ .../gfxdrivers/eglnullws/eglnullwsscreenplugin.h | 41 ++++++++++++++++++++++ .../eglnullws/eglnullwswindowsurface.cpp | 41 ++++++++++++++++++++++ .../gfxdrivers/eglnullws/eglnullwswindowsurface.h | 41 ++++++++++++++++++++++ 6 files changed, 246 insertions(+) diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp index 4213097..b8ea5d5 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "eglnullwsscreen.h" #include "eglnullwswindowsurface.h" #include "eglnullwsscreenplugin.h" diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h index a9cb64c..7f794bc 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreen.h @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #ifndef EGLNULLWSSCREEN #define EGLNULLWSSCREEN diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp index 354ddef..67b3f56 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "eglnullwsscreenplugin.h" #include "eglnullwsscreen.h" diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h index 14e2a82..84f0699 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwsscreenplugin.h @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #ifndef EGLNULLWSSCREENPLUGIN_H #define EGLNULLWSSCREENPLUGIN_H diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp index 016748e..da4b728 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "eglnullwswindowsurface.h" #include "eglnullwsscreenplugin.h" diff --git a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h index 7e5d2fa..b730415 100644 --- a/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h +++ b/src/plugins/gfxdrivers/eglnullws/eglnullwswindowsurface.h @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #ifndef EGLNULLWSWINDOWSURFACE_H #define EGLNULLWSWINDOWSURFACE_H -- cgit v0.12 From e02d97c50cee8bdc662bb1183ed01aa0dac53f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 8 Jul 2010 17:31:16 +0200 Subject: Track the GL DCs we use across different threads under Windows. A DC returned by GetDC() is only valid within the context of the calling thread, so we need to track which tread a DC belongs to. Reviewed-by: Kim --- src/opengl/qgl.cpp | 1 + src/opengl/qgl_p.h | 1 + src/opengl/qgl_win.cpp | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 3f2af9d..05dae60 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -1655,6 +1655,7 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format) #if defined(Q_WS_WIN) dc = 0; win = 0; + threadId = 0; pixelFormatId = 0; cmap = 0; hbitmap = 0; diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index eca4a29..dfbc926 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -347,6 +347,7 @@ public: QGLCmap* cmap; HBITMAP hbitmap; HDC hbitmap_hdc; + Qt::HANDLE threadId; #endif #ifndef QT_NO_EGL uint ownsEglContext : 1; diff --git a/src/opengl/qgl_win.cpp b/src/opengl/qgl_win.cpp index 52988b6..3f13ac4 100644 --- a/src/opengl/qgl_win.cpp +++ b/src/opengl/qgl_win.cpp @@ -1261,6 +1261,7 @@ void QGLContext::reset() } d->dc = 0; d->win = 0; + d->threadId = 0; d->pixelFormatId = 0; d->sharing = false; d->valid = false; @@ -1283,8 +1284,9 @@ void QGLContext::makeCurrent() if (d->rc == wglGetCurrentContext() || !d->valid) // already current return; - if (d->win && !d->dc) { + if (d->win && (!d->dc || d->threadId != QThread::currentThreadId())) { d->dc = GetDC(d->win); + d->threadId = QThread::currentThreadId(); if (!d->dc) { qwglError("QGLContext::makeCurrent()", "GetDC()"); return; @@ -1322,6 +1324,7 @@ void QGLContext::doneCurrent() if (d->win && d->dc) { ReleaseDC(d->win, d->dc); d->dc = 0; + d->threadId = 0; } } -- cgit v0.12 From 1e9a92cd6e8670a9dc90fed2044e7de38a3b13e6 Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Fri, 9 Jul 2010 10:47:46 +0200 Subject: Incorrect assertion in item views we were checking if the parent of main items was QModelIndex instead if checking if it is just invalid (it might have a pointer to its model and still be invalid). Task-number: QTBUG-12090 Reviewed-by: Ian Walters --- src/gui/itemviews/qabstractitemview.cpp | 2 +- src/gui/kernel/qapplication.cpp | 2 ++ src/gui/kernel/qwidget.cpp | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 15e59ba..302aa20 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -653,7 +653,7 @@ void QAbstractItemView::setModel(QAbstractItemModel *model) "QAbstractItemView::setModel", "A model should return the exact same index " "(including its internal id/pointer) when asked for it twice in a row."); - Q_ASSERT_X(d->model->index(0,0).parent() == QModelIndex(), + Q_ASSERT_X(!d->model->index(0,0).parent().isValid(), "QAbstractItemView::setModel", "The parent of a top level index should be invalid"); diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 4f557d6..6a67369 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -5780,6 +5780,8 @@ Q_GUI_EXPORT void qt_translateRawTouchEvent(QWidget *window, QGestureManager* QGestureManager::instance() { QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); + if (!qAppPriv) + return 0; if (!qAppPriv->gestureManager) qAppPriv->gestureManager = new QGestureManager(qApp); return qAppPriv->gestureManager; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 2627960..2e980c1 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -12053,8 +12053,8 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) { Q_D(QWidget); if (d->gestureContext.remove(gesture)) { - QGestureManager *manager = QGestureManager::instance(); - manager->cleanupCachedGestures(this, gesture); + if (QGestureManager *manager = QGestureManager::instance()) + manager->cleanupCachedGestures(this, gesture); } } #endif // QT_NO_GESTURES -- cgit v0.12 From 2b358140c6b75ee29135175535190d415f5af10f Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Fri, 9 Jul 2010 11:28:26 +0200 Subject: Small fix in iteviews for sizeHintForColumn/Row We were trying to check the column/row count but always from the root of the model insteal of the root that can be defined in the view. Task-number: QTBUG-5773 --- src/gui/itemviews/qabstractitemview.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 302aa20..ea24328 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -2944,7 +2944,7 @@ int QAbstractItemView::sizeHintForRow(int row) const { Q_D(const QAbstractItemView); - if (row < 0 || row >= d->model->rowCount() || !model()) + if (row < 0 || row >= d->model->rowCount(d->root)) return -1; ensurePolished(); @@ -2956,7 +2956,7 @@ int QAbstractItemView::sizeHintForRow(int row) const for (int c = 0; c < colCount; ++c) { index = d->model->index(row, c, d->root); if (QWidget *editor = d->editorForIndex(index).editor) - height = qMax(height, editor->size().height()); + height = qMax(height, editor->height()); int hint = d->delegateForIndex(index)->sizeHint(option, index).height(); height = qMax(height, hint); } @@ -2975,7 +2975,7 @@ int QAbstractItemView::sizeHintForColumn(int column) const { Q_D(const QAbstractItemView); - if (column < 0 || column >= d->model->columnCount() || !model()) + if (column < 0 || column >= d->model->columnCount(d->root)) return -1; ensurePolished(); -- cgit v0.12 From d193933b31088f83358a657f0399c6eeae2467e2 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 12 Jul 2010 09:54:40 +0200 Subject: Make QFontEngineGlyphCache explicitly shared QFontEngine will delete its glyph cache (and if it's a GL engine, its texture) when the font engine dies. The font engine can die when the QFontCache decides to clean it up, e.g. based on the trigger of a timer. To make it possible to use the glyph cache elsewhere, without interference by the font cache, it has been made explicitly shared. Reviewed-by: Kim --- src/gui/text/qfontengine.cpp | 12 +++++------- src/gui/text/qfontengine_p.h | 2 +- src/gui/text/qfontengineglyphcache_p.h | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 2d95bae..3f6b409 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -185,10 +185,6 @@ QFontEngine::QFontEngine() QFontEngine::~QFontEngine() { - for (QLinkedList::const_iterator it = m_glyphCaches.constBegin(), - end = m_glyphCaches.constEnd(); it != end; ++it) { - delete it->cache; - } m_glyphCaches.clear(); qHBFreeFace(hbFace); } @@ -734,14 +730,16 @@ void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data) { Q_ASSERT(data); - GlyphCacheEntry entry = { key, data }; + GlyphCacheEntry entry; + entry.context = key; + entry.cache = data; if (m_glyphCaches.contains(entry)) return; // Limit the glyph caches to 4. This covers all 90 degree rotations and limits // memory use when there is continous or random rotation if (m_glyphCaches.size() == 4) - delete m_glyphCaches.takeLast().cache; + m_glyphCaches.removeLast(); m_glyphCaches.push_front(entry); @@ -750,7 +748,7 @@ void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data) QFontEngineGlyphCache *QFontEngine::glyphCache(void *key, QFontEngineGlyphCache::Type type, const QTransform &transform) const { for (QLinkedList::const_iterator it = m_glyphCaches.constBegin(), end = m_glyphCaches.constEnd(); it != end; ++it) { - QFontEngineGlyphCache *c = it->cache; + QFontEngineGlyphCache *c = it->cache.data(); if (key == it->context && type == c->cacheType() && qtransform_equals_no_translate(c->m_transform, transform)) { diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index d29ef45..4e518d6 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -262,7 +262,7 @@ protected: private: struct GlyphCacheEntry { void *context; - QFontEngineGlyphCache *cache; + QExplicitlySharedDataPointer cache; bool operator==(const GlyphCacheEntry &other) { return context == other.context && cache == other.cache; } }; diff --git a/src/gui/text/qfontengineglyphcache_p.h b/src/gui/text/qfontengineglyphcache_p.h index 7b82b46..eba16f7 100644 --- a/src/gui/text/qfontengineglyphcache_p.h +++ b/src/gui/text/qfontengineglyphcache_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE -class QFontEngineGlyphCache +class QFontEngineGlyphCache: public QSharedData { public: enum Type { -- cgit v0.12 From 8c662477a3596fd5fdf8388ed8266e52aa8b7069 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Jul 2010 19:58:08 +0200 Subject: Skipped tst_QGL::graphicsViewClipping on mac. As it prevent integration See QTBUG-12138 --- tests/auto/qgl/tst_qgl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index c05f730..ffd7622 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -838,6 +838,9 @@ public: void tst_QGL::graphicsViewClipping() { +#ifdef Q_WS_MAC + QSKIP("Prevent integration, see QTBUG-12138", SkipAll); +#endif const int size = 64; UnclippedWidget *widget = new UnclippedWidget; widget->setFixedSize(size, size); -- cgit v0.12 From 5652cb1c49b4350f9a80eafaae4276e173d845bc Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 13 Jul 2010 09:49:56 +0200 Subject: Skipped tst_QGL::glFBOUseInGLWidget on mac. As it prevent integration See QTBUG-12138 --- tests/auto/qgl/tst_qgl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index ffd7622..e76de02 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -1286,6 +1286,9 @@ protected: void tst_QGL::glFBOUseInGLWidget() { +#ifdef Q_WS_MAC + QSKIP("Prevent integration, see QTBUG-12138", SkipAll); +#endif if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); -- cgit v0.12 From dac4ff9ea66bb4e09c220069ff7a55e3f87c9f53 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 13 Jul 2010 09:49:56 +0200 Subject: Skipped tst_QGL::glPBufferRendering on mac. As it prevent integration See QTBUG-12138 --- tests/auto/qgl/tst_qgl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index e76de02..34f64f0 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -943,6 +943,10 @@ void tst_QGL::partialGLWidgetUpdates() // This tests that rendering to a QGLPBuffer using QPainter works. void tst_QGL::glPBufferRendering() { +#ifdef Q_WS_MAC + QSKIP("Prevent integration, see QTBUG-12138", SkipAll); +#endif + if (!QGLPixelBuffer::hasOpenGLPbuffers()) QSKIP("QGLPixelBuffer not supported on this platform", SkipSingle); -- cgit v0.12 From 11c669e2a56b6b4de0a40393167887d53f3134a6 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 13 Jul 2010 09:49:56 +0200 Subject: Skipped tst_QGL::replaceClipping on mac. As it prevent integration See QTBUG-12138 --- tests/auto/qgl/tst_qgl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index 34f64f0..c71824a 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -1689,6 +1689,10 @@ protected: void tst_QGL::replaceClipping() { +#ifdef Q_WS_MAC + QSKIP("Prevent integration, see QTBUG-12138", SkipAll); +#endif + ReplaceClippingGLWidget glw; #ifdef Q_WS_QWS glw.setWindowFlags(Qt::FramelessWindowHint); -- cgit v0.12 From 5bcb42d8f47f7206e9c0776b67a4c990ac93aa34 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 14 Jul 2010 10:31:32 +0200 Subject: tst_QGL: Skip all QGL test on mac as they crash and prevent integration See QTBUG-12138 --- tests/auto/qgl/tst_qgl.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index c71824a..d6a6e9c 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -72,7 +72,9 @@ public: tst_QGL(); virtual ~tst_QGL(); +#ifndef Q_WS_MAC //All tests are disabled on mac as they crash and prevent integration, see QTBUG-12138 private slots: +#endif void getSetCheck(); void openGLVersionCheck(); void graphicsViewClipping(); @@ -838,9 +840,6 @@ public: void tst_QGL::graphicsViewClipping() { -#ifdef Q_WS_MAC - QSKIP("Prevent integration, see QTBUG-12138", SkipAll); -#endif const int size = 64; UnclippedWidget *widget = new UnclippedWidget; widget->setFixedSize(size, size); @@ -943,10 +942,6 @@ void tst_QGL::partialGLWidgetUpdates() // This tests that rendering to a QGLPBuffer using QPainter works. void tst_QGL::glPBufferRendering() { -#ifdef Q_WS_MAC - QSKIP("Prevent integration, see QTBUG-12138", SkipAll); -#endif - if (!QGLPixelBuffer::hasOpenGLPbuffers()) QSKIP("QGLPixelBuffer not supported on this platform", SkipSingle); @@ -1290,9 +1285,6 @@ protected: void tst_QGL::glFBOUseInGLWidget() { -#ifdef Q_WS_MAC - QSKIP("Prevent integration, see QTBUG-12138", SkipAll); -#endif if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); @@ -1689,10 +1681,6 @@ protected: void tst_QGL::replaceClipping() { -#ifdef Q_WS_MAC - QSKIP("Prevent integration, see QTBUG-12138", SkipAll); -#endif - ReplaceClippingGLWidget glw; #ifdef Q_WS_QWS glw.setWindowFlags(Qt::FramelessWindowHint); -- cgit v0.12