Add support for shared glyph cache
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>
Thu, 24 Nov 2011 12:48:19 +0000 (13:48 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 24 Jan 2012 12:03:03 +0000 (13:03 +0100)
Use a shared graphics cache to back the distance fields if
it is available.

Change-Id: Id5e6e7a28e38e349d787e66016b2d0faebc791d7
Reviewed-by: Jiang Jiang <jiang.jiang@nokia.com>
20 files changed:
src/quick/items/qquicktext.cpp
src/quick/items/qquicktext_p.h
src/quick/items/qquicktext_p_p.h
src/quick/items/qquicktextedit.cpp
src/quick/items/qquicktextedit_p.h
src/quick/items/qquicktextedit_p_p.h
src/quick/items/qquicktextinput.cpp
src/quick/items/qquicktextinput_p.h
src/quick/items/qquicktextinput_p_p.h
src/quick/items/qquicktextnode.cpp
src/quick/items/qquicktextnode_p.h
src/quick/scenegraph/coreapi/qsgnodeupdater.cpp
src/quick/scenegraph/qsgadaptationlayer.cpp
src/quick/scenegraph/qsgadaptationlayer_p.h
src/quick/scenegraph/qsgcontext.cpp
src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp [new file with mode: 0644]
src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h [new file with mode: 0644]
src/quick/scenegraph/scenegraph.pri

index d7b069a..82232ab 100644 (file)
@@ -85,7 +85,7 @@ QQuickTextPrivate::QQuickTextPrivate()
   disableDistanceField(false), internalWidthUpdate(false),
   requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
   layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true),
-  naturalWidth(0), doc(0), elipsisLayout(0), textLine(0), nodeType(NodeIsNull)
+  naturalWidth(0), doc(0), elipsisLayout(0), textLine(0), nodeType(NodeIsNull), updateType(UpdatePaintNode)
 
 #if defined(Q_OS_MAC)
 , layoutThread(0), paintingThread(0)
@@ -371,6 +371,7 @@ void QQuickTextPrivate::updateSize()
         q->setImplicitSize(0, fontHeight);
         paintedSize = QSize(0, fontHeight);
         emit q->paintedSizeChanged();
+        updateType = UpdatePaintNode;
         q->update();
         return;
     }
@@ -445,6 +446,7 @@ void QQuickTextPrivate::updateSize()
         paintedSize = size;
         emit q->paintedSizeChanged();
     }
+    updateType = UpdatePaintNode;
     q->update();
 }
 
@@ -898,6 +900,7 @@ void QQuickTextPrivate::checkImageCache()
 
     imageCacheDirty = false;
     textureImageCacheDirty = true;
+    updateType = UpdatePaintNode;
     q->update();
 }
 
@@ -1325,8 +1328,10 @@ void QQuickText::setStyle(QQuickText::TextStyle style)
         return;
 
     // changing to/from Normal requires the boundingRect() to change
-    if (isComponentComplete() && (d->style == Normal || style == Normal))
+    if (isComponentComplete() && (d->style == Normal || style == Normal)) {
+        d->updateType = QQuickTextPrivate::UpdatePaintNode;
         update();
+    }
     d->style = style;
     d->markDirty();
     emit styleChanged(d->style);
@@ -1842,6 +1847,14 @@ geomChangeDone:
     QQuickItem::geometryChanged(newGeometry, oldGeometry);
 }
 
+void QQuickText::triggerPreprocess()
+{
+    Q_D(QQuickText);
+    if (d->updateType == QQuickTextPrivate::UpdateNone)
+        d->updateType = QQuickTextPrivate::UpdatePreprocess;
+    update();
+}
+
 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
 {
     Q_UNUSED(data);
@@ -1852,6 +1865,14 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
         return 0;
     }
 
+    if (!d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
+        // Update done in preprocess() in the nodes
+        d->updateType = QQuickTextPrivate::UpdateNone;
+        return oldNode;
+    }
+
+    d->updateType = QQuickTextPrivate::UpdateNone;
+
     QRectF bounds = boundingRect();
 
     // We need to make sure the layout is done in the current thread
@@ -1902,7 +1923,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
         QQuickTextNode *node = 0;
         if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) {
             delete oldNode;
-            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
+            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
             d->nodeType = QQuickTextPrivate::NodeIsText;
         } else {
             node = static_cast<QQuickTextNode *>(oldNode);
index d204cf1..4619725 100644 (file)
@@ -211,6 +211,7 @@ protected:
 
 private Q_SLOTS:
     void q_imagesLoaded();
+    void triggerPreprocess();
 
 private:
     Q_DISABLE_COPY(QQuickText)
index efe9b23..dda24a0 100644 (file)
@@ -161,6 +161,13 @@ public:
     };
     NodeType nodeType;
 
+    enum UpdateType {
+        UpdateNone,
+        UpdatePreprocess,
+        UpdatePaintNode
+    };
+    UpdateType updateType;
+
 #if defined(Q_OS_MAC)
     QList<QRectF> linesRects;
     QThread *layoutThread;
index 9daead9..37c76c0 100644 (file)
@@ -1609,11 +1609,27 @@ void QQuickTextEdit::updateImageCache(const QRectF &)
 
 }
 
+void QQuickTextEdit::triggerPreprocess()
+{
+    Q_D(QQuickTextEdit);
+    if (d->updateType == QQuickTextEditPrivate::UpdateNone)
+        d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
+    update();
+}
+
 QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
 {
     Q_UNUSED(updatePaintNodeData);
     Q_D(QQuickTextEdit);
 
+    if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode && oldNode != 0) {
+        // Update done in preprocess() in the nodes
+        d->updateType = QQuickTextEditPrivate::UpdateNone;
+        return oldNode;
+    }
+
+    d->updateType = QQuickTextEditPrivate::UpdateNone;
+
     QSGNode *currentNode = oldNode;
     if (d->richText && d->useImageFallback) {
         QSGImageNode *node = 0;
@@ -1651,7 +1667,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
         QQuickTextNode *node = 0;
         if (oldNode == 0 || d->nodeType != QQuickTextEditPrivate::NodeIsText) {
             delete oldNode;
-            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
+            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
             d->nodeType = QQuickTextEditPrivate::NodeIsText;
             currentNode = node;
         } else {
@@ -1962,6 +1978,7 @@ void QQuickTextEdit::updateDocument()
 
     if (isComponentComplete()) {
         updateImageCache();
+        d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
         update();
     }
 }
@@ -1971,6 +1988,7 @@ void QQuickTextEdit::updateCursor()
     Q_D(QQuickTextEdit);
     if (isComponentComplete()) {
         updateImageCache(d->control->cursorRect());
+        d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
         update();
     }
 }
index 08729bf..8d268ea 100644 (file)
@@ -290,6 +290,7 @@ private Q_SLOTS:
     void updateCursor();
     void q_updateAlignment();
     void updateSize();
+    void triggerPreprocess();
 
 private:
     void updateTotalLines();
index 6605f7f..f8996c9 100644 (file)
@@ -78,7 +78,7 @@ public:
       textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), cursorComponent(0), cursor(0),
       format(QQuickTextEdit::PlainText), document(0), wrapMode(QQuickTextEdit::NoWrap),
       mouseSelectionMode(QQuickTextEdit::SelectCharacters),
-      lineCount(0), yoff(0), nodeType(NodeIsNull), texture(0)
+      lineCount(0), yoff(0), nodeType(NodeIsNull), texture(0), updateType(UpdatePaintNode)
     {
     }
 
@@ -143,6 +143,13 @@ public:
     NodeType nodeType;
     QSGTexture *texture;
     QPixmap pixmapCache;
+
+    enum UpdateType {
+        UpdateNone,
+        UpdateOnlyPreprocess,
+        UpdatePaintNode
+    };
+    UpdateType updateType;
 };
 
 QT_END_NAMESPACE
index 16106be..237db35 100644 (file)
@@ -328,6 +328,7 @@ void QQuickTextInput::setColor(const QColor &c)
     if (c != d->color) {
         d->color = c;
         d->textLayoutDirty = true;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
         emit colorChanged(c);
     }
@@ -355,6 +356,7 @@ void QQuickTextInput::setSelectionColor(const QColor &color)
     d->m_palette.setColor(QPalette::Highlight, d->selectionColor);
     if (d->hasSelectedText()) {
         d->textLayoutDirty = true;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
     }
     emit selectionColorChanged(color);
@@ -380,6 +382,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
     d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor);
     if (d->hasSelectedText()) {
         d->textLayoutDirty = true;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
     }
     emit selectedTextColorChanged(color);
@@ -642,6 +645,7 @@ void QQuickTextInput::setCursorVisible(bool on)
         return;
     d->cursorVisible = on;
     d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
+    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
     update();
     emit cursorVisibleChanged(d->cursorVisible);
 }
@@ -1621,14 +1625,30 @@ void QQuickTextInputPrivate::updateVerticalScroll()
         textLayoutDirty = true;
 }
 
+void QQuickTextInput::triggerPreprocess()
+{
+    Q_D(QQuickTextInput);
+    if (d->updateType == QQuickTextInputPrivate::UpdateNone)
+        d->updateType = QQuickTextInputPrivate::UpdateOnlyPreprocess;
+    update();
+}
+
 QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
 {
     Q_UNUSED(data);
     Q_D(QQuickTextInput);
 
+    if (d->updateType != QQuickTextInputPrivate::UpdatePaintNode && oldNode != 0) {
+        // Update done in preprocess() in the nodes
+        d->updateType = QQuickTextInputPrivate::UpdateNone;
+        return oldNode;
+    }
+
+    d->updateType = QQuickTextInputPrivate::UpdateNone;
+
     QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
     if (node == 0)
-        node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
+        node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
     d->textNode = node;
 
     if (!d->textLayoutDirty) {
@@ -2408,6 +2428,7 @@ void QQuickTextInput::updateCursorRectangle()
 
     d->updateHorizontalScroll();
     d->updateVerticalScroll();
+    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
     update();
     emit cursorRectangleChanged();
     if (d->cursorItem) {
@@ -2421,6 +2442,7 @@ void QQuickTextInput::selectionChanged()
 {
     Q_D(QQuickTextInput);
     d->textLayoutDirty = true; //TODO: Only update rect in selection
+    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
     update();
     emit selectedTextChanged();
 
@@ -2584,6 +2606,7 @@ void QQuickTextInputPrivate::updateLayout()
     m_ascent = qRound(firstLine.ascent());
     textLayoutDirty = true;
 
+    updateType = UpdatePaintNode;
     q->update();
     q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
 
@@ -3788,8 +3811,10 @@ void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec)
         m_blinkStatus = 1;
     } else {
         m_blinkTimer = 0;
-        if (m_blinkStatus == 1)
+        if (m_blinkStatus == 1) {
+            updateType = UpdatePaintNode;
             q->update();
+        }
     }
     m_blinkPeriod = msec;
 }
@@ -3809,6 +3834,7 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
     Q_D(QQuickTextInput);
     if (event->timerId() == d->m_blinkTimer) {
         d->m_blinkStatus = !d->m_blinkStatus;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
     } else if (event->timerId() == d->m_deleteAllTimer) {
         killTimer(d->m_deleteAllTimer);
index e2f7d9e..92d09c3 100644 (file)
@@ -324,6 +324,7 @@ private Q_SLOTS:
     void updateCursorRectangle();
     void q_canPasteChanged();
     void q_updateAlignment();
+    void triggerPreprocess();
 
 private:
     Q_DECLARE_PRIVATE(QQuickTextInput)
index 980bf1d..1fc5565 100644 (file)
@@ -128,6 +128,7 @@ public:
         , m_acceptableInput(1)
         , m_blinkStatus(0)
         , m_passwordEchoEditing(false)
+        , updateType(UpdatePaintNode)
     {
     }
 
@@ -256,6 +257,13 @@ public:
     uint m_blinkStatus : 1;
     uint m_passwordEchoEditing;
 
+    enum UpdateType {
+        UpdateNone,
+        UpdateOnlyPreprocess,
+        UpdatePaintNode
+    };
+    UpdateType updateType;
+
     static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
         return t->d_func();
     }
index 8811bb3..2f72b0c 100644 (file)
@@ -69,8 +69,8 @@ QT_BEGIN_NAMESPACE
 /*!
   Creates an empty QQuickTextNode
 */
-QQuickTextNode::QQuickTextNode(QSGContext *context)
-    : m_context(context), m_cursorNode(0)
+QQuickTextNode::QQuickTextNode(QSGContext *context, QQuickItem *ownerElement)
+    : m_context(context), m_cursorNode(0), m_ownerElement(ownerElement)
 {
 #if defined(QML_RUNTIME_TESTING)
     description = QLatin1String("text");
@@ -131,6 +131,7 @@ QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun
                                      QSGNode *parentNode)
 {
     QSGGlyphNode *node = m_context->createGlyphNode();
+    node->setOwnerElement(m_ownerElement);
     node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
     node->setStyle(style);
     node->setStyleColor(styleColor);
index 6d407f0..f64933b 100644 (file)
@@ -74,7 +74,7 @@ public:
     };
     Q_DECLARE_FLAGS(Decorations, Decoration)
 
-    QQuickTextNode(QSGContext *);
+    QQuickTextNode(QSGContext *, QQuickItem *ownerElement);
     ~QQuickTextNode();
 
     static bool isComplexRichText(QTextDocument *);
@@ -103,6 +103,7 @@ private:
     QSGContext *m_context;
     QSGSimpleRectNode *m_cursorNode;
     QList<QSGTexture *> m_textures;
+    QQuickItem *m_ownerElement;
 };
 
 QT_END_NAMESPACE
index f9e2a6a..1c50a4a 100644 (file)
@@ -97,15 +97,13 @@ void QSGNodeUpdater::updateStates(QSGNode *n)
 bool QSGNodeUpdater::isNodeBlocked(QSGNode *node, QSGNode *root) const
 {
     qreal opacity = 1;
-    while (node != root) {
+    while (node != root && node != 0) {
         if (node->type() == QSGNode::OpacityNodeType) {
             opacity *= static_cast<QSGOpacityNode *>(node)->opacity();
             if (opacity < 0.001)
                 return true;
         }
         node = node->parent();
-
-        Q_ASSERT_X(node, "QSGNodeUpdater::isNodeBlocked", "node is not in the subtree of root");
     }
 
     return false;
index 4bb4066..574c121 100644 (file)
@@ -267,6 +267,21 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &g
     }
 }
 
+void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
+{
+    Q_UNUSED(ownerElement);
+}
+
+void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
+{
+    Q_UNUSED(ownerElement);
+}
+
+void QSGDistanceFieldGlyphCache::processPendingGlyphs()
+{
+    /* Intentionally empty */
+}
+
 void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
 {
     int i = m_cacheData->textures.indexOf(tex);
index 45826de..2d82ca3 100644 (file)
@@ -110,6 +110,8 @@ public:
         HighQualitySubPixelAntialiasing
     };
 
+    QSGGlyphNode() : m_ownerElement(0) {}
+
     virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) = 0;
     virtual void setColor(const QColor &color) = 0;
     virtual void setStyle(QQuickText::TextStyle style) = 0;
@@ -123,8 +125,12 @@ public:
 
     virtual void update() = 0;
 
+    void setOwnerElement(QQuickItem *ownerElement) { m_ownerElement = ownerElement; }
+    QQuickItem *ownerElement() const { return m_ownerElement; }
+
 protected:
     QRectF m_bounding_rect;
+    QQuickItem *m_ownerElement;
 };
 
 class Q_QUICK_EXPORT QSGDistanceFieldGlyphCache
@@ -185,6 +191,10 @@ public:
     void registerGlyphNode(QSGDistanceFieldGlyphNode *node);
     void unregisterGlyphNode(QSGDistanceFieldGlyphNode *node);
 
+    virtual void registerOwnerElement(QQuickItem *ownerElement);
+    virtual void unregisterOwnerElement(QQuickItem *ownerElement);
+    virtual void processPendingGlyphs();
+
 protected:
     struct GlyphPosition {
         glyph_t glyph;
@@ -204,6 +214,7 @@ protected:
     void updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize);
 
     bool containsGlyph(glyph_t glyph) const;
+    GLuint textureIdForGlyph(glyph_t glyph) const;
 
     QOpenGLContext *ctx;
 
index cc87961..834f336 100644 (file)
@@ -47,6 +47,8 @@
 #include <QtQuick/private/qsgdefaultimagenode_p.h>
 #include <QtQuick/private/qsgdefaultglyphnode_p.h>
 #include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
+#include <QtQuick/private/qsgshareddistancefieldglyphcache_p.h>
+
 #include <QtQuick/private/qsgtexture_p.h>
 #include <QtQuick/private/qdeclarativepixmapcache_p.h>
 
 #include <QDeclarativeImageProvider>
 #include <private/qdeclarativeglobal_p.h>
 
+#include <QtQuick/private/qsgtexture_p.h>
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <QtGui/qplatformsharedgraphicscache_qpa.h>
+
 #include <private/qobject_p.h>
 #include <qmutex.h>
 
@@ -247,6 +254,35 @@ QSGImageNode *QSGContext::createImageNode()
 QSGDistanceFieldGlyphCache *QSGContext::createDistanceFieldGlyphCache(const QRawFont &font)
 {
     Q_D(QSGContext);
+
+    QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+    if (platformIntegration != 0
+        && platformIntegration->hasCapability(QPlatformIntegration::SharedGraphicsCache)) {
+        QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
+        if (!fe->faceId().filename.isEmpty()) {
+            QByteArray keyName = fe->faceId().filename;
+            if (font.style() != QFont::StyleNormal)
+                keyName += QByteArray(" I");
+            if (font.weight() != QFont::Normal)
+                keyName += " " + QByteArray::number(font.weight());
+            keyName += QByteArray(" DF");
+            QPlatformSharedGraphicsCache *sharedGraphicsCache =
+                    platformIntegration->createPlatformSharedGraphicsCache(keyName);
+
+            if (sharedGraphicsCache != 0) {
+                sharedGraphicsCache->ensureCacheInitialized(keyName,
+                                                            QPlatformSharedGraphicsCache::OpenGLTexture,
+                                                            QPlatformSharedGraphicsCache::Alpha8);
+
+                return new QSGSharedDistanceFieldGlyphCache(keyName,
+                                                            sharedGraphicsCache,
+                                                            d->distanceFieldCacheManager,
+                                                            glContext(),
+                                                            font);
+            }
+        }
+    }
+
     return new QSGDefaultDistanceFieldGlyphCache(d->distanceFieldCacheManager, glContext(), font);
 }
 
index 8f681d2..eb1c1eb 100644 (file)
@@ -57,10 +57,10 @@ QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheM
     , m_dirtyGeometry(false)
     , m_dirtyMaterial(false)
 {
-    setFlag(UsePreprocess);
     m_geometry.setDrawingMode(GL_TRIANGLES);
     setGeometry(&m_geometry);
     setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode());
+    setFlag(UsePreprocess);
 #ifdef QML_RUNTIME_TESTING
     description = QLatin1String("glyphs");
 #endif
@@ -112,9 +112,13 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR
     QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
     m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont());
     if (m_glyph_cache != oldCache) {
-        if (oldCache)
+        Q_ASSERT(ownerElement() != 0);
+        if (oldCache) {
             oldCache->unregisterGlyphNode(this);
+            oldCache->unregisterOwnerElement(ownerElement());
+        }
         m_glyph_cache->registerGlyphNode(this);
+        m_glyph_cache->registerOwnerElement(ownerElement());
     }
     m_glyph_cache->populate(glyphs.glyphIndexes());
 
@@ -158,12 +162,13 @@ void QSGDistanceFieldGlyphNode::preprocess()
 {
     Q_ASSERT(m_glyph_cache);
 
-    m_glyph_cache->update();
-
     for (int i = 0; i < m_nodesToDelete.count(); ++i)
         delete m_nodesToDelete.at(i);
     m_nodesToDelete.clear();
 
+    m_glyph_cache->processPendingGlyphs();
+    m_glyph_cache->update();
+
     if (m_dirtyGeometry)
         updateGeometry();
 }
@@ -285,6 +290,7 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
         QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator subIt = m_subNodes.find(ite.key());
         if (subIt == m_subNodes.end()) {
             QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_glyph_cacheManager);
+            subNode->setOwnerElement(m_ownerElement);
             subNode->setColor(m_color);
             subNode->setStyle(m_style);
             subNode->setStyleColor(m_styleColor);
index 56f8038..a58e0b1 100644 (file)
@@ -69,7 +69,6 @@ public:
     virtual void setStyleColor(const QColor &color);
 
     virtual void update();
-
     void preprocess();
 
     void invalidateGlyphs(const QVector<quint32> &glyphs);
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
new file mode 100644 (file)
index 0000000..841322e
--- /dev/null
@@ -0,0 +1,621 @@
+/****************************************************************************
+**
+** 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 QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#endif
+
+#include "qsgshareddistancefieldglyphcache_p.h"
+
+#include <QtCore/qhash.h>
+#include <QtCore/qthread.h>
+#include <QtGui/qplatformsharedgraphicscache_qpa.h>
+
+#include <QtQuick/qquickcanvas.h>
+
+#include <QtOpenGL/qglframebufferobject.h>
+
+// #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG
+
+Q_DECLARE_METATYPE(QVector<quint32>)
+Q_DECLARE_METATYPE(QVector<QImage>)
+
+QT_BEGIN_NAMESPACE
+
+QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
+                                                                   QPlatformSharedGraphicsCache *sharedGraphicsCache,
+                                                                   QSGDistanceFieldGlyphCacheManager *man,
+                                                                   QOpenGLContext *c,
+                                                                   const QRawFont &font)
+    : QSGDistanceFieldGlyphCache(man, c, font)
+    , m_cacheId(cacheId)
+    , m_sharedGraphicsCache(sharedGraphicsCache)
+{
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+    qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p",
+           cacheId.constData(), QThread::currentThreadId());
+#endif
+
+    Q_ASSERT(sizeof(glyph_t) == sizeof(quint32));
+    Q_ASSERT(sharedGraphicsCache != 0);
+
+    qRegisterMetaType<QVector<quint32> >();
+    qRegisterMetaType<QVector<QImage> >();
+
+    connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)),
+            this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)),
+            Qt::DirectConnection);
+    connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
+            this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
+            Qt::DirectConnection);
+    connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
+            this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
+            Qt::DirectConnection);
+    connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)),
+            this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
+            Qt::DirectConnection);
+}
+
+QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache()
+{
+    {
+        QHash<glyph_t, void *>::const_iterator it = m_bufferForGlyph.constBegin();
+        while (it != m_bufferForGlyph.constEnd()) {
+            m_sharedGraphicsCache->dereferenceBuffer(it.value());
+            ++it;
+        }
+    }
+
+    {
+        QHash<quint32, PendingGlyph>::const_iterator it = m_pendingReadyGlyphs.constBegin();
+        while (it != m_pendingReadyGlyphs.constEnd()) {
+            m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer);
+            ++it;
+        }
+    }
+}
+
+void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
+{
+    QMutexLocker locker(&m_pendingGlyphsMutex);
+
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+    qDebug("QSGSharedDistanceFieldGlyphCache::requestGlyphs() called for %s (%d glyphs)",
+           m_cacheId.constData(), glyphs.size());
+#endif
+
+    m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs);
+
+    QVector<quint32> glyphsVector;
+    glyphsVector.reserve(glyphs.size());
+
+    QSet<glyph_t>::const_iterator it;
+    for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
+        Q_ASSERT(!m_bufferForGlyph.contains(*it));
+        glyphsVector.append(*it);
+    }
+
+    // Invoke method on queued connection to make sure it's called asynchronously on the
+    // correct thread (requestGlyphs() is called from the rendering thread.)
+    QMetaObject::invokeMethod(m_sharedGraphicsCache, "requestItems", Qt::QueuedConnection,
+                              Q_ARG(QByteArray, m_cacheId),
+                              Q_ARG(QVector<quint32>, glyphsVector));
+}
+
+void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
+{
+    {
+        QMutexLocker locker(&m_pendingGlyphsMutex);
+        while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
+            m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex);
+    }
+}
+
+void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
+{
+    {
+        QMutexLocker locker(&m_pendingGlyphsMutex);
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+        qDebug("QSGSharedDistanceFieldGlyphCache::storeGlyphs() called for %s (%d glyphs)",
+               m_cacheId.constData(), glyphs.size());
+#endif
+
+        int glyphCount = glyphs.size();
+        QVector<quint32> glyphIds(glyphCount);
+        QVector<QImage> images(glyphCount);
+        QHash<glyph_t, QImage>::const_iterator it = glyphs.constBegin();
+        int i=0;
+        while (it != glyphs.constEnd()) {
+            m_requestedGlyphsThatHaveNotBeenReturned.insert(it.key());
+            glyphIds[i] = it.key();
+            images[i] = it.value();
+
+            ++it; ++i;
+        }
+
+        QMetaObject::invokeMethod(m_sharedGraphicsCache, "insertItems", Qt::QueuedConnection,
+                                  Q_ARG(QByteArray, m_cacheId),
+                                  Q_ARG(QVector<quint32>, glyphIds),
+                                  Q_ARG(QVector<QImage>, images));
+    }
+
+    processPendingGlyphs();
+}
+
+void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
+{
+    Q_UNUSED(glyphs);
+
+    // Intentionally empty. Not required in this implementation, since the glyphs are reference
+    // counted outside and releaseGlyphs() will only be called when there are no more references.
+}
+
+void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
+{
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+    qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)",
+           m_cacheId.constData(), glyphs.size());
+#endif
+
+    QVector<quint32> glyphsVector;
+    glyphsVector.reserve(glyphs.size());
+
+    QSet<glyph_t>::const_iterator glyphsIt;
+    for (glyphsIt = glyphs.constBegin(); glyphsIt != glyphs.constEnd(); ++glyphsIt) {
+        QHash<glyph_t, void *>::iterator bufferIt = m_bufferForGlyph.find(*glyphsIt);
+        if (bufferIt != m_bufferForGlyph.end()) {
+            void *buffer = bufferIt.value();
+            removeGlyph(*glyphsIt);
+            m_bufferForGlyph.erase(bufferIt);
+            Q_ASSERT(!m_bufferForGlyph.contains(*glyphsIt));
+
+            if (!m_sharedGraphicsCache->dereferenceBuffer(buffer)) {
+#if !defined(QT_NO_DEBUG)
+                bufferIt = m_bufferForGlyph.begin();
+                while (bufferIt != m_bufferForGlyph.end()) {
+                    Q_ASSERT(bufferIt.value() != buffer);
+                    ++bufferIt;
+                }
+#endif
+            }
+        }
+
+        glyphsVector.append(*glyphsIt);
+    }
+
+    QMetaObject::invokeMethod(m_sharedGraphicsCache, "releaseItems", Qt::QueuedConnection,
+                              Q_ARG(QByteArray, m_cacheId),
+                              Q_ARG(QVector<quint32>, glyphsVector));
+}
+
+void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
+{
+    bool ok = connect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
+    Q_ASSERT_X(ok, Q_FUNC_INFO, "QML element that owns a glyph node must have triggerPreprocess() slot");
+    Q_UNUSED(ok);
+}
+
+void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
+{
+    disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
+}
+
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
+#  include <QtOpenGL/private/qglextensions_p.h>
+
+void QSGSharedDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height)
+{
+    GLuint fboId;
+    glGenFramebuffers(1, &fboId);
+
+    GLuint tmpTexture = 0;
+    glGenTextures(1, &tmpTexture);
+    glBindTexture(GL_TEXTURE_2D, tmpTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
+    glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
+                           tmpTexture, 0);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, textureId);
+
+    glDisable(GL_STENCIL_TEST);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_SCISSOR_TEST);
+    glDisable(GL_BLEND);
+
+    GLfloat textureCoordinateArray[8];
+    textureCoordinateArray[0] = 0.0f;
+    textureCoordinateArray[1] = 0.0f;
+    textureCoordinateArray[2] = 1.0f;
+    textureCoordinateArray[3] = 0.0f;
+    textureCoordinateArray[4] = 1.0f;
+    textureCoordinateArray[5] = 1.0f;
+    textureCoordinateArray[6] = 0.0f;
+    textureCoordinateArray[7] = 1.0f;
+
+    GLfloat vertexCoordinateArray[8];
+    vertexCoordinateArray[0] = -1.0f;
+    vertexCoordinateArray[1] = -1.0f;
+    vertexCoordinateArray[2] =  1.0f;
+    vertexCoordinateArray[3] = -1.0f;
+    vertexCoordinateArray[4] =  1.0f;
+    vertexCoordinateArray[5] =  1.0f;
+    vertexCoordinateArray[6] = -1.0f;
+    vertexCoordinateArray[7] =  1.0f;
+
+    glViewport(0, 0, width, height);
+    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
+    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
+
+    {
+        static const char *vertexShaderSource =
+                "attribute highp   vec4      vertexCoordsArray; \n"
+                "attribute highp   vec2      textureCoordArray; \n"
+                "varying   highp   vec2      textureCoords;     \n"
+                "void main(void) \n"
+                "{ \n"
+                "    gl_Position = vertexCoordsArray;   \n"
+                "    textureCoords = textureCoordArray; \n"
+                "} \n";
+
+        static const char *fragmentShaderSource =
+                "varying   highp   vec2      textureCoords; \n"
+                "uniform   sampler2D         texture;       \n"
+                "void main() \n"
+                "{ \n"
+                "    gl_FragColor = texture2D(texture, textureCoords); \n"
+                "} \n";
+
+        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
+        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+        if (vertexShader == 0 || fragmentShader == 0) {
+            GLenum error = glGetError();
+            qWarning("SharedGraphicsCacheServer::setupShaderPrograms: Failed to create shaders. (GL error: %x)",
+                     error);
+            return;
+        }
+
+        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
+        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
+        glCompileShader(vertexShader);
+
+        GLint len = 1;
+        glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
+
+        char infoLog[2048];
+        glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
+        if (qstrlen(infoLog) > 0) {
+            qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling vertex shader:\n %s",
+                     infoLog);
+            //return;
+        }
+
+        glCompileShader(fragmentShader);
+        glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
+        if (qstrlen(infoLog) > 0) {
+            qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling fragent shader:\n %s",
+                     infoLog);
+            //return;
+        }
+
+        GLuint shaderProgram = glCreateProgram();
+        glAttachShader(shaderProgram, vertexShader);
+        glAttachShader(shaderProgram, fragmentShader);
+
+        glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
+        glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
+
+        glLinkProgram(shaderProgram);
+        glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
+        if (qstrlen(infoLog) > 0) {
+            qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems linking shaders:\n %s",
+                     infoLog);
+            //return;
+        }
+
+        glUseProgram(shaderProgram);
+        glEnableVertexAttribArray(0);
+        glEnableVertexAttribArray(1);
+
+        int textureUniformLocation = glGetUniformLocation(shaderProgram, "texture");
+        glUniform1i(textureUniformLocation, 0);
+    }
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+    {
+        GLenum error = glGetError();
+        if (error != GL_NO_ERROR) {
+            qWarning("SharedGraphicsCacheServer::readBackBuffer: glDrawArrays reported error 0x%x",
+                     error);
+        }
+    }
+
+    uchar *data = new uchar[width * height * 4];
+
+    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+    QImage image(width, height, QImage::Format_ARGB32);
+    quint32 *dest = reinterpret_cast<quint32 *>(image.bits());
+    for (int i=0; i<width*height; ++i)
+        dest[i] = qRgba(0xff, 0xff, 0xff, data[i]);
+
+    QByteArray fileName = m_cacheId + " " + QByteArray::number(textureId);
+    fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
+    image.save(QString::fromLocal8Bit(fileName));
+
+    {
+        GLenum error = glGetError();
+        if (error != GL_NO_ERROR) {
+            qWarning("SharedGraphicsCacheServer::readBackBuffer: glReadPixels reported error 0x%x",
+                     error);
+        }
+    }
+
+    glDisableVertexAttribArray(0);
+    glDisableVertexAttribArray(1);
+
+    glDeleteFramebuffers(1, &fboId);
+    glDeleteTextures(1, &tmpTexture);
+
+    delete[] data;
+}
+#endif
+
+namespace {
+    struct TextureContent {
+        QSize size;
+        QVector<glyph_t> glyphs;
+    };
+}
+
+void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
+{
+    Q_ASSERT(QThread::currentThread() == thread());
+
+    waitForGlyphs();
+
+    {
+        QMutexLocker locker(&m_pendingGlyphsMutex);
+        if (m_pendingMissingGlyphs.isEmpty()
+            && m_pendingReadyGlyphs.isEmpty()
+            && m_pendingInvalidatedGlyphs.isEmpty()) {
+            return;
+        }
+
+        {
+            QVector<glyph_t> pendingMissingGlyphs;
+            pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size());
+
+            QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin();
+            while (it != m_pendingMissingGlyphs.constEnd()) {
+                pendingMissingGlyphs.append(*it);
+                ++it;
+            }
+
+            markGlyphsToRender(pendingMissingGlyphs);
+        }
+
+        {
+            QVector<glyph_t> filteredPendingInvalidatedGlyphs;
+            filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size());
+
+            QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin();
+            while (it != m_pendingInvalidatedGlyphs.constEnd()) {
+                bool rerequestGlyph = false;
+
+                // The glyph was invalidated right after being posted as ready, we throw away
+                // the ready glyph and rerequest it to be certain
+                QHash<quint32, PendingGlyph>::iterator pendingGlyphIt = m_pendingReadyGlyphs.find(*it);
+                if (pendingGlyphIt != m_pendingReadyGlyphs.end()) {
+                    m_sharedGraphicsCache->dereferenceBuffer(pendingGlyphIt.value().buffer);
+                    pendingGlyphIt = m_pendingReadyGlyphs.erase(pendingGlyphIt);
+                    rerequestGlyph = true;
+                }
+
+                void *bufferId = m_bufferForGlyph.value(*it, 0);
+                if (bufferId != 0) {
+                    m_sharedGraphicsCache->dereferenceBuffer(bufferId);
+                    m_bufferForGlyph.remove(*it);
+                    rerequestGlyph = true;
+                }
+
+                if (rerequestGlyph)
+                    filteredPendingInvalidatedGlyphs.append(*it);
+
+                ++it;
+            }
+
+            // If this cache is still using the glyphs, reset the texture held by them, and mark them
+            // to be rendered again since they are still needed.
+            if (!filteredPendingInvalidatedGlyphs.isEmpty()) {
+                setGlyphsTexture(filteredPendingInvalidatedGlyphs, Texture());
+                markGlyphsToRender(filteredPendingInvalidatedGlyphs);
+            }
+        }
+
+        {
+            QList<GlyphPosition> glyphPositions;
+
+            QHash<void *, TextureContent> textureContentForBuffer;
+            {
+                QHash<quint32, PendingGlyph>::iterator it = m_pendingReadyGlyphs.begin();
+                while (it != m_pendingReadyGlyphs.end()) {
+                    void *currentGlyphBuffer = m_bufferForGlyph.value(it.key(), 0);
+                    if (currentGlyphBuffer != 0) {
+                        if (!m_sharedGraphicsCache->dereferenceBuffer(currentGlyphBuffer)) {
+                            Q_ASSERT(!textureContentForBuffer.contains(currentGlyphBuffer));
+                        }
+                    }
+
+                    PendingGlyph &pendingGlyph  = it.value();
+
+                    // We don't ref or deref the buffer here, since it was already referenced when
+                    // added to the pending ready glyphs
+                    m_bufferForGlyph[it.key()] = pendingGlyph.buffer;
+
+                    textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize;
+                    textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key());
+
+                    GlyphPosition glyphPosition;
+                    glyphPosition.glyph = it.key();
+                    glyphPosition.position = pendingGlyph.position;
+
+                    glyphPositions.append(glyphPosition);
+
+                    ++it;
+                }
+            }
+
+            setGlyphsPosition(glyphPositions);
+
+            {
+                QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin();
+                while (it != textureContentForBuffer.constEnd()) {
+                    Texture texture;
+                    texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
+                    texture.size = it.value().size;
+
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
+                    saveTexture(texture.textureId, texture.size.width(), texture.size.height());
+#endif
+                    setGlyphsTexture(it.value().glyphs, texture);
+
+                    ++it;
+                }
+            }
+        }
+
+        m_pendingMissingGlyphs.clear();
+        m_pendingInvalidatedGlyphs.clear();
+        m_pendingReadyGlyphs.clear();
+    }
+}
+
+void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
+                                                        void *bufferId, const QSize &bufferSize,
+                                                        const QVector<quint32> &itemIds,
+                                                        const QVector<QPoint> &positions)
+{
+    {
+        QMutexLocker locker(&m_pendingGlyphsMutex);
+        if (m_cacheId != cacheId)
+            return;
+
+        Q_ASSERT(itemIds.size() == positions.size());
+
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+        qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs, bufferSize: %dx%d)",
+               cacheId.constData(), itemIds.size(), bufferSize.width(), bufferSize.height());
+#endif
+
+        for (int i=0; i<itemIds.size(); ++i) {
+            PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
+            void *oldBuffer = pendingGlyph.buffer;
+            Q_ASSERT(bufferSize.height() >= pendingGlyph.bufferSize.height());
+
+            pendingGlyph.buffer = bufferId;
+            pendingGlyph.position = positions.at(i);
+            pendingGlyph.bufferSize = bufferSize;
+
+            m_sharedGraphicsCache->referenceBuffer(bufferId);
+            if (oldBuffer != 0)
+                m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
+
+            m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
+        }
+    }
+
+    m_pendingGlyphsCondition.wakeAll();
+    emit glyphsPending();
+}
+
+void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId,
+                                                              const QVector<quint32> &itemIds)
+{
+    {
+        QMutexLocker locker(&m_pendingGlyphsMutex);
+        if (m_cacheId != cacheId)
+            return;
+
+        for (int i=0; i<itemIds.size(); ++i)
+            m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
+    }
+
+    emit glyphsPending();
+}
+
+
+void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId,
+                                                          const QVector<quint32> &itemIds)
+{
+    {
+        QMutexLocker locker(&m_pendingGlyphsMutex);
+        if (m_cacheId != cacheId)
+            return;
+
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+        qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)",
+               cacheId.constData(), itemIds.size());
+#endif
+
+        for (int i=0; i<itemIds.size(); ++i) {
+            m_pendingMissingGlyphs.insert(itemIds.at(i));
+            m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
+        }
+    }
+
+    m_pendingGlyphsCondition.wakeAll();
+    emit glyphsPending();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
new file mode 100644 (file)
index 0000000..6f5696e
--- /dev/null
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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 QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSHAREDDISTANCEFIELDGLYPHCACHE_H
+#define QSGSHAREDDISTANCEFIELDGLYPHCACHE_H
+
+#include <QtCore/qwaitcondition.h>
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformSharedGraphicsCache;
+class QSGSharedDistanceFieldGlyphCache : public QObject, public QSGDistanceFieldGlyphCache
+{
+    Q_OBJECT
+public:
+    explicit QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
+                                              QPlatformSharedGraphicsCache *sharedGraphicsCache,
+                                              QSGDistanceFieldGlyphCacheManager *man,
+                                              QOpenGLContext *c,
+                                              const QRawFont &font);
+    ~QSGSharedDistanceFieldGlyphCache();
+
+    void registerOwnerElement(QQuickItem *ownerElement);
+    void unregisterOwnerElement(QQuickItem *ownerElement);
+    void processPendingGlyphs();
+
+    void requestGlyphs(const QSet<glyph_t> &glyphs);
+    void referenceGlyphs(const QSet<glyph_t> &glyphs);
+    void storeGlyphs(const QHash<glyph_t, QImage> &glyphs);
+    void releaseGlyphs(const QSet<glyph_t> &glyphs);
+
+Q_SIGNALS:
+    void glyphsPending();
+
+private Q_SLOTS:
+    void reportItemsMissing(const QByteArray &cacheId, const QVector<quint32> &itemIds);
+    void reportItemsAvailable(const QByteArray &cacheId,
+                          void *bufferId, const QSize &bufferSize,
+                          const QVector<quint32> &itemIds, const QVector<QPoint> &positions);
+    void reportItemsInvalidated(const QByteArray &cacheId, const QVector<quint32> &itemIds);
+
+private:
+    void waitForGlyphs();
+    void saveTexture(GLuint textureId, int width, int height);
+
+    QSet<quint32> m_requestedGlyphsThatHaveNotBeenReturned;
+    QWaitCondition m_pendingGlyphsCondition;
+    QByteArray m_cacheId;
+    QPlatformSharedGraphicsCache *m_sharedGraphicsCache;
+    QMutex m_pendingGlyphsMutex;
+
+    QSet<glyph_t> m_pendingInvalidatedGlyphs;
+    QSet<glyph_t> m_pendingMissingGlyphs;
+
+    struct PendingGlyph
+    {
+        PendingGlyph() : buffer(0) {}
+
+        void *buffer;
+        QSize bufferSize;
+        QPoint position;
+    };
+
+    QHash<quint32, PendingGlyph> m_pendingReadyGlyphs;
+    QHash<glyph_t, void *> m_bufferForGlyph;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSGSHAREDDISTANCEFIELDGLYPHCACHE_H
index db57b1e..9fc9222 100644 (file)
@@ -18,7 +18,6 @@ SOURCES += \
     $$PWD/coreapi/qsgnodeupdater.cpp \
     $$PWD/coreapi/qsgrenderer.cpp
 
-
 # Util API
 HEADERS += \
     $$PWD/util/qsgareaallocator_p.h \
@@ -63,7 +62,8 @@ HEADERS += \
     $$PWD/qsgdefaultimagenode_p.h \
     $$PWD/qsgdefaultrectanglenode_p.h \
     $$PWD/qsgflashnode_p.h \
-    $$PWD/qsgpathsimplifier_p.h
+    $$PWD/qsgpathsimplifier_p.h \
+    $$PWD/qsgshareddistancefieldglyphcache_p.h
 
 SOURCES += \
     $$PWD/qsgadaptationlayer.cpp \
@@ -77,7 +77,8 @@ SOURCES += \
     $$PWD/qsgdefaultimagenode.cpp \
     $$PWD/qsgdefaultrectanglenode.cpp \
     $$PWD/qsgflashnode.cpp \
-    $$PWD/qsgpathsimplifier.cpp
+    $$PWD/qsgpathsimplifier.cpp \
+    $$PWD/qsgshareddistancefieldglyphcache.cpp