#include <QtCore/qhash.h>
#include <QtCore/qthread.h>
-#include <QtGui/qplatformsharedgraphicscache_qpa.h>
+#include <QtCore/qcoreapplication.h>
-#include <QtQuick/qquickcanvas.h>
+#include <qpa/qplatformsharedgraphicscache.h>
-#include <QtOpenGL/qglframebufferobject.h>
+#include <QtQuick/qquickwindow.h>
// #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG
-Q_DECLARE_METATYPE(QVector<quint32>)
-Q_DECLARE_METATYPE(QVector<QImage>)
-
QT_BEGIN_NAMESPACE
+namespace {
+
+ class QSGInvokeEvent: public QEvent
+ {
+ public:
+ QSGInvokeEvent(QPlatformSharedGraphicsCache *cache,
+ const QByteArray &cacheId = QByteArray(),
+ const QVector<quint32> &glyphIds = QVector<quint32>(),
+ bool inSceneGraphUpdate = false)
+ : QEvent(User)
+ , m_cache(cache)
+ , m_cacheId(cacheId)
+ , m_glyphIds(glyphIds)
+ , m_inSceneGraphUpdate(inSceneGraphUpdate)
+ {}
+
+ bool inSceneGraphUpdate() const { return m_inSceneGraphUpdate; }
+ QPlatformSharedGraphicsCache *cache() const { return m_cache; }
+
+ virtual void invoke() = 0;
+ protected:
+ QPlatformSharedGraphicsCache *m_cache;
+ QByteArray m_cacheId;
+ QVector<quint32> m_glyphIds;
+ bool m_inSceneGraphUpdate;
+ };
+
+ class QSGReleaseItemsEvent: public QSGInvokeEvent
+ {
+ public:
+ QSGReleaseItemsEvent(QPlatformSharedGraphicsCache *cache,
+ const QByteArray &cacheId,
+ const QVector<quint32> &glyphIds,
+ bool inSceneGraphUpdate)
+ : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
+ {
+ }
+
+ void invoke()
+ {
+ m_cache->releaseItems(m_cacheId, m_glyphIds);
+ }
+ };
+
+ class QSGRequestItemsEvent: public QSGInvokeEvent
+ {
+ public:
+ QSGRequestItemsEvent(QPlatformSharedGraphicsCache *cache,
+ const QByteArray &cacheId,
+ const QVector<quint32> &glyphIds,
+ bool inSceneGraphUpdate)
+ : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
+ {
+ }
+
+ void invoke()
+ {
+ m_cache->requestItems(m_cacheId, m_glyphIds);
+ }
+ };
+
+ class QSGInsertItemsEvent: public QSGInvokeEvent
+ {
+ public:
+ QSGInsertItemsEvent(QPlatformSharedGraphicsCache *cache,
+ const QByteArray &cacheId,
+ const QVector<quint32> &glyphIds,
+ const QVector<QImage> &images,
+ bool inSceneGraphUpdate)
+ : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
+ , m_images(images)
+ {
+ }
+
+ void invoke()
+ {
+ m_cache->insertItems(m_cacheId, m_glyphIds, m_images);
+ }
+
+ private:
+ QVector<QImage> m_images;
+ };
+
+ class QSGEndRequestBatchEvent: public QSGInvokeEvent
+ {
+ public:
+ QSGEndRequestBatchEvent(QPlatformSharedGraphicsCache *cache)
+ : QSGInvokeEvent(cache)
+ {
+ }
+
+ void invoke()
+ {
+ if (m_cache->requestBatchStarted())
+ m_cache->endRequestBatch();
+ }
+ };
+
+ class QSGMainThreadInvoker: public QObject
+ {
+ public:
+ bool event(QEvent *e)
+ {
+ if (e->type() == QEvent::User) {
+ Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
+
+ QSGInvokeEvent *invokeEvent = static_cast<QSGInvokeEvent *>(e);
+ if (invokeEvent->inSceneGraphUpdate()) {
+ QPlatformSharedGraphicsCache *cache = invokeEvent->cache();
+ if (!cache->requestBatchStarted())
+ cache->beginRequestBatch();
+ }
+
+ static_cast<QSGInvokeEvent *>(e)->invoke();
+ return true;
+ }
+ return QObject::event(e);
+ }
+
+ static QSGMainThreadInvoker *instance()
+ {
+ if (m_invoker == 0) {
+ m_invoker = new QSGMainThreadInvoker;
+ m_invoker->moveToThread(QCoreApplication::instance()->thread());
+ }
+
+ return m_invoker;
+ }
+
+ private:
+ static QSGMainThreadInvoker *m_invoker;
+ };
+
+ QSGMainThreadInvoker* QSGMainThreadInvoker::m_invoker = 0;
+}
+
QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
QPlatformSharedGraphicsCache *sharedGraphicsCache,
QSGDistanceFieldGlyphCacheManager *man,
: QSGDistanceFieldGlyphCache(man, c, font)
, m_cacheId(cacheId)
, m_sharedGraphicsCache(sharedGraphicsCache)
+ , m_isInSceneGraphUpdate(false)
+ , m_hasPostedEvents(false)
{
#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p",
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>)),
+ connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
+ this, SLOT(reportItemsAvailable(QByteArray,void*,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>)),
+ connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
+ this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
Qt::DirectConnection);
connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)),
this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
Qt::DirectConnection);
+
+ QQuickWindow *window = static_cast<QQuickWindow *>(c->surface());
+ Q_ASSERT(window != 0);
+
+ connect(window, SIGNAL(beforeSynchronizing()), this, SLOT(sceneGraphUpdateStarted()),
+ Qt::DirectConnection);
+ connect(window, SIGNAL(beforeRendering()), this, SLOT(sceneGraphUpdateDone()),
+ Qt::DirectConnection);
}
QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache()
#endif
m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs);
+ m_requestedGlyphs.unite(glyphs);
QVector<quint32> glyphsVector;
glyphsVector.reserve(glyphs.size());
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));
+ m_hasPostedEvents = true;
+ QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
+ QCoreApplication::postEvent(invoker, new QSGRequestItemsEvent(m_sharedGraphicsCache,
+ m_cacheId,
+ glyphsVector,
+ m_isInSceneGraphUpdate));
}
void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
{
+ Q_ASSERT(!m_isInSceneGraphUpdate);
+ if (m_isInSceneGraphUpdate) {
+ qWarning("QSGSharedDistanceFieldGlyphCache::waitForGlyphs: Called from inside "
+ "scenegraph update. Will freeze.");
+ }
+
{
QMutexLocker locker(&m_pendingGlyphsMutex);
while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
++it; ++i;
}
- QMetaObject::invokeMethod(m_sharedGraphicsCache, "insertItems", Qt::QueuedConnection,
- Q_ARG(QByteArray, m_cacheId),
- Q_ARG(QVector<quint32>, glyphIds),
- Q_ARG(QVector<QImage>, images));
+ m_hasPostedEvents = true;
+ QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
+ QCoreApplication::postEvent(invoker, new QSGInsertItemsEvent(m_sharedGraphicsCache,
+ m_cacheId,
+ glyphIds,
+ images,
+ m_isInSceneGraphUpdate));
}
processPendingGlyphs();
m_cacheId.constData(), glyphs.size());
#endif
+ m_requestedGlyphs.subtract(glyphs);
+
QVector<quint32> glyphsVector;
glyphsVector.reserve(glyphs.size());
glyphsVector.append(*glyphsIt);
}
- QMetaObject::invokeMethod(m_sharedGraphicsCache, "releaseItems", Qt::QueuedConnection,
- Q_ARG(QByteArray, m_cacheId),
- Q_ARG(QVector<quint32>, glyphsVector));
+ m_hasPostedEvents = true;
+ QSGMainThreadInvoker *mainThreadInvoker = QSGMainThreadInvoker::instance();
+ QCoreApplication::postEvent(mainThreadInvoker, new QSGReleaseItemsEvent(m_sharedGraphicsCache,
+ m_cacheId,
+ glyphsVector,
+ m_isInSceneGraphUpdate));
}
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);
+ Owner &owner = m_registeredOwners[ownerElement];
+ if (owner.ref == 0) {
+ owner.item = 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);
+ }
+ ++owner.ref;
}
void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
{
- disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
+ QHash<QQuickItem *, Owner>::iterator it = m_registeredOwners.find(ownerElement);
+ if (it != m_registeredOwners.end() && --it->ref <= 0) {
+ if (it->item)
+ disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
+ m_registeredOwners.erase(it);
+ }
}
#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
for (int i=0; i<width*height; ++i)
dest[i] = qRgba(0xff, 0xff, 0xff, data[i]);
- QByteArray fileName = m_cacheId + " " + QByteArray::number(textureId);
+ QByteArray fileName = m_cacheId + ' ' + QByteArray::number(textureId);
fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
image.save(QString::fromLocal8Bit(fileName));
while (it != textureContentForBuffer.constEnd()) {
Texture texture;
texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
- texture.size = it.value().size;
+ texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key());
#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
saveTexture(texture.textureId, texture.size.width(), texture.size.height());
}
void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
- void *bufferId, const QSize &bufferSize,
- const QVector<quint32> &itemIds,
- const QVector<QPoint> &positions)
+ void *bufferId,
+ const QVector<quint32> &itemIds,
+ const QVector<QPoint> &positions)
+{
+ bool requestedItemsInList = false;
+ {
+ QMutexLocker locker(&m_pendingGlyphsMutex);
+ if (m_cacheId != cacheId)
+ return;
+
+#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
+ qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)",
+ cacheId.constData(), itemIds.size());
+#endif
+
+ for (int i=0; i<itemIds.size(); ++i) {
+ if (m_requestedGlyphsThatHaveNotBeenReturned.contains(itemIds.at(i))) {
+ requestedItemsInList = true;
+ break;
+ }
+ }
+ }
+
+ if (requestedItemsInList)
+ reportItemsUpdated(cacheId, bufferId,itemIds, positions);
+}
+
+void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId,
+ void *bufferId,
+ const QVector<quint32> &itemIds,
+ const QVector<QPoint> &positions)
{
{
QMutexLocker locker(&m_pendingGlyphsMutex);
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());
+ qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)",
+ cacheId.constData(), itemIds.size());
#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());
+ if (m_requestedGlyphs.contains(itemIds.at(i))) {
+ PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
+ void *oldBuffer = pendingGlyph.buffer;
- pendingGlyph.buffer = bufferId;
- pendingGlyph.position = positions.at(i);
- pendingGlyph.bufferSize = bufferSize;
+ pendingGlyph.buffer = bufferId;
+ pendingGlyph.position = positions.at(i);
- m_sharedGraphicsCache->referenceBuffer(bufferId);
- if (oldBuffer != 0)
- m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
+ m_sharedGraphicsCache->referenceBuffer(bufferId);
+ if (oldBuffer != 0)
+ m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
- m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
+ m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
+ }
}
}
if (m_cacheId != cacheId)
return;
- for (int i=0; i<itemIds.size(); ++i)
- m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
+ for (int i=0; i<itemIds.size(); ++i) {
+ if (m_requestedGlyphs.contains(itemIds.at(i)))
+ m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
+ }
}
emit glyphsPending();
#endif
for (int i=0; i<itemIds.size(); ++i) {
- m_pendingMissingGlyphs.insert(itemIds.at(i));
- m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
+ if (m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i)))
+ m_pendingMissingGlyphs.insert(itemIds.at(i));
}
}
emit glyphsPending();
}
+void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateStarted()
+{
+ m_isInSceneGraphUpdate = true;
+ m_hasPostedEvents = false;
+}
+
+void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateDone()
+{
+ m_isInSceneGraphUpdate = false;
+
+ if (m_hasPostedEvents) {
+ QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
+ QCoreApplication::postEvent(invoker, new QSGEndRequestBatchEvent(m_sharedGraphicsCache));
+ m_hasPostedEvents = false;
+ }
+}
+
QT_END_NAMESPACE