aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2011-11-24 13:48:19 +0100
committerQt by Nokia <qt-info@nokia.com>2012-01-24 13:03:03 +0100
commit47a5c708bf4e555cb8febef583f32c99f7d8ea1e (patch)
tree722842be890fe711a353759b100ac50bef9c2648 /src
parent635d0a7cf6601b2e46e0eed21a648934bc471c6d (diff)
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 <jiang.jiang@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/quick/items/qquicktext.cpp27
-rw-r--r--src/quick/items/qquicktext_p.h1
-rw-r--r--src/quick/items/qquicktext_p_p.h7
-rw-r--r--src/quick/items/qquicktextedit.cpp20
-rw-r--r--src/quick/items/qquicktextedit_p.h1
-rw-r--r--src/quick/items/qquicktextedit_p_p.h9
-rw-r--r--src/quick/items/qquicktextinput.cpp30
-rw-r--r--src/quick/items/qquicktextinput_p.h1
-rw-r--r--src/quick/items/qquicktextinput_p_p.h8
-rw-r--r--src/quick/items/qquicktextnode.cpp5
-rw-r--r--src/quick/items/qquicktextnode_p.h3
-rw-r--r--src/quick/scenegraph/coreapi/qsgnodeupdater.cpp4
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp15
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h11
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp36
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp14
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h1
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp621
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h113
-rw-r--r--src/quick/scenegraph/scenegraph.pri7
20 files changed, 913 insertions, 21 deletions
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index d7b069a1fc..82232ab0ea 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<QQuickTextNode *>(oldNode);
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index d204cf1535..46197258c8 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 efe9b23d04..dda24a0a68 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<QRectF> linesRects;
QThread *layoutThread;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 9daead9ea7..37c76c09a3 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 08729bf5ef..8d268ea6f8 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 6605f7fc88..f8996c95ee 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 16106be43b..237db3537d 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<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);
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index e2f7d9eaeb..92d09c3efd 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 980bf1dea6..1fc55658e6 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 8811bb3d77..2f72b0c8fe 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 6d407f0767..f64933b09f 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<QSGTexture *> 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 f9e2a6ae56..1c50a4aa30 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<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;
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 4bb4066ab3..574c1218bb 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -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);
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 45826deae8..2d82ca30ba 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 cc879612ae..834f336394 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -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>
@@ -56,6 +58,11 @@
#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);
}
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index 8f681d2a0b..eb1c1eb22d 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<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);
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index 56f8038286..a58e0b1eb4 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<quint32> &glyphs);
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
new file mode 100644
index 0000000000..841322e58b
--- /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 <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
index 0000000000..6f5696eabb
--- /dev/null
+++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
@@ -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
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index db57b1e52b..9fc92222ae 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