From 47a5c708bf4e555cb8febef583f32c99f7d8ea1e Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 24 Nov 2011 13:48:19 +0100 Subject: [PATCH] Add support for shared glyph cache Use a shared graphics cache to back the distance fields if it is available. Change-Id: Id5e6e7a28e38e349d787e66016b2d0faebc791d7 Reviewed-by: Jiang Jiang --- src/quick/items/qquicktext.cpp | 27 +- src/quick/items/qquicktext_p.h | 1 + src/quick/items/qquicktext_p_p.h | 7 + src/quick/items/qquicktextedit.cpp | 20 +- src/quick/items/qquicktextedit_p.h | 1 + src/quick/items/qquicktextedit_p_p.h | 9 +- src/quick/items/qquicktextinput.cpp | 30 +- src/quick/items/qquicktextinput_p.h | 1 + src/quick/items/qquicktextinput_p_p.h | 8 + src/quick/items/qquicktextnode.cpp | 5 +- src/quick/items/qquicktextnode_p.h | 3 +- src/quick/scenegraph/coreapi/qsgnodeupdater.cpp | 4 +- src/quick/scenegraph/qsgadaptationlayer.cpp | 15 + src/quick/scenegraph/qsgadaptationlayer_p.h | 11 + src/quick/scenegraph/qsgcontext.cpp | 36 ++ src/quick/scenegraph/qsgdistancefieldglyphnode.cpp | 14 +- src/quick/scenegraph/qsgdistancefieldglyphnode_p.h | 1 - .../qsgshareddistancefieldglyphcache.cpp | 621 ++++++++++++++++++++ .../qsgshareddistancefieldglyphcache_p.h | 113 ++++ src/quick/scenegraph/scenegraph.pri | 7 +- 20 files changed, 913 insertions(+), 21 deletions(-) create mode 100644 src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp create mode 100644 src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index d7b069a..82232ab 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -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(oldNode); diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index d204cf1..4619725 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -211,6 +211,7 @@ protected: private Q_SLOTS: void q_imagesLoaded(); + void triggerPreprocess(); private: Q_DISABLE_COPY(QQuickText) diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index efe9b23..dda24a0 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -161,6 +161,13 @@ public: }; NodeType nodeType; + enum UpdateType { + UpdateNone, + UpdatePreprocess, + UpdatePaintNode + }; + UpdateType updateType; + #if defined(Q_OS_MAC) QList linesRects; QThread *layoutThread; diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 9daead9..37c76c0 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -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(); } } diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 08729bf..8d268ea 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -290,6 +290,7 @@ private Q_SLOTS: void updateCursor(); void q_updateAlignment(); void updateSize(); + void triggerPreprocess(); private: void updateTotalLines(); diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 6605f7f..f8996c9 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -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 diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 16106be..237db35 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -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(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); diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index e2f7d9e..92d09c3 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -324,6 +324,7 @@ private Q_SLOTS: void updateCursorRectangle(); void q_canPasteChanged(); void q_updateAlignment(); + void triggerPreprocess(); private: Q_DECLARE_PRIVATE(QQuickTextInput) diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 980bf1d..1fc5565 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -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(); } diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 8811bb3..2f72b0c 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -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); diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h index 6d407f0..f64933b 100644 --- a/src/quick/items/qquicktextnode_p.h +++ b/src/quick/items/qquicktextnode_p.h @@ -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 m_textures; + QQuickItem *m_ownerElement; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp index f9e2a6a..1c50a4a 100644 --- a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp @@ -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(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; diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 4bb4066..574c121 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -267,6 +267,21 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList &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 &glyphs, const Texture &tex) { int i = m_cacheData->textures.indexOf(tex); diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 45826de..2d82ca3 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -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; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index cc87961..834f336 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -47,6 +47,8 @@ #include #include #include +#include + #include #include @@ -56,6 +58,11 @@ #include #include +#include +#include + +#include + #include #include @@ -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); } diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp index 8f681d2..eb1c1eb 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -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::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); diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h index 56f8038..a58e0b1 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -69,7 +69,6 @@ public: virtual void setStyleColor(const QColor &color); virtual void update(); - void preprocess(); void invalidateGlyphs(const QVector &glyphs); diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp new file mode 100644 index 0000000..841322e --- /dev/null +++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp @@ -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 +#include +#endif + +#include "qsgshareddistancefieldglyphcache_p.h" + +#include +#include +#include + +#include + +#include + +// #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG + +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(QVector) + +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 >(); + qRegisterMetaType >(); + + connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector)), + this, SLOT(reportItemsMissing(QByteArray,QVector)), + Qt::DirectConnection); + connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QSize,QVector,QVector)), + this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector,QVector)), + Qt::DirectConnection); + connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QSize,QVector,QVector)), + this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector,QVector)), + Qt::DirectConnection); + connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector)), + this, SLOT(reportItemsInvalidated(QByteArray,QVector)), + Qt::DirectConnection); +} + +QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache() +{ + { + QHash::const_iterator it = m_bufferForGlyph.constBegin(); + while (it != m_bufferForGlyph.constEnd()) { + m_sharedGraphicsCache->dereferenceBuffer(it.value()); + ++it; + } + } + + { + QHash::const_iterator it = m_pendingReadyGlyphs.constBegin(); + while (it != m_pendingReadyGlyphs.constEnd()) { + m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer); + ++it; + } + } +} + +void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet &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 glyphsVector; + glyphsVector.reserve(glyphs.size()); + + QSet::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, glyphsVector)); +} + +void QSGSharedDistanceFieldGlyphCache::waitForGlyphs() +{ + { + QMutexLocker locker(&m_pendingGlyphsMutex); + while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty()) + m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex); + } +} + +void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash &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 glyphIds(glyphCount); + QVector images(glyphCount); + QHash::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, glyphIds), + Q_ARG(QVector, images)); + } + + processPendingGlyphs(); +} + +void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet &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 &glyphs) +{ +#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) + qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)", + m_cacheId.constData(), glyphs.size()); +#endif + + QVector glyphsVector; + glyphsVector.reserve(glyphs.size()); + + QSet::const_iterator glyphsIt; + for (glyphsIt = glyphs.constBegin(); glyphsIt != glyphs.constEnd(); ++glyphsIt) { + QHash::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, 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 + +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(image.bits()); + for (int i=0; i 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 pendingMissingGlyphs; + pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size()); + + QSet::const_iterator it = m_pendingMissingGlyphs.constBegin(); + while (it != m_pendingMissingGlyphs.constEnd()) { + pendingMissingGlyphs.append(*it); + ++it; + } + + markGlyphsToRender(pendingMissingGlyphs); + } + + { + QVector filteredPendingInvalidatedGlyphs; + filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size()); + + QSet::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::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 glyphPositions; + + QHash textureContentForBuffer; + { + QHash::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::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 &itemIds, + const QVector &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= 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 &itemIds) +{ + { + QMutexLocker locker(&m_pendingGlyphsMutex); + if (m_cacheId != cacheId) + return; + + for (int i=0; i &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 +#include + +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 &glyphs); + void referenceGlyphs(const QSet &glyphs); + void storeGlyphs(const QHash &glyphs); + void releaseGlyphs(const QSet &glyphs); + +Q_SIGNALS: + void glyphsPending(); + +private Q_SLOTS: + void reportItemsMissing(const QByteArray &cacheId, const QVector &itemIds); + void reportItemsAvailable(const QByteArray &cacheId, + void *bufferId, const QSize &bufferSize, + const QVector &itemIds, const QVector &positions); + void reportItemsInvalidated(const QByteArray &cacheId, const QVector &itemIds); + +private: + void waitForGlyphs(); + void saveTexture(GLuint textureId, int width, int height); + + QSet m_requestedGlyphsThatHaveNotBeenReturned; + QWaitCondition m_pendingGlyphsCondition; + QByteArray m_cacheId; + QPlatformSharedGraphicsCache *m_sharedGraphicsCache; + QMutex m_pendingGlyphsMutex; + + QSet m_pendingInvalidatedGlyphs; + QSet m_pendingMissingGlyphs; + + struct PendingGlyph + { + PendingGlyph() : buffer(0) {} + + void *buffer; + QSize bufferSize; + QPoint position; + }; + + QHash m_pendingReadyGlyphs; + QHash m_bufferForGlyph; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGSHAREDDISTANCEFIELDGLYPHCACHE_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index db57b1e..9fc9222 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -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 -- 1.7.2.5