1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #define EGL_EGLEXT_PROTOTYPES
43 #define GL_GLEXT_PROTOTYPES
44 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
45 #include <GLES2/gl2.h>
46 #include <GLES2/gl2ext.h>
49 #include "qsgshareddistancefieldglyphcache_p.h"
51 #include <QtCore/qhash.h>
52 #include <QtCore/qthread.h>
53 #include <QtCore/qcoreapplication.h>
55 #include <qpa/qplatformsharedgraphicscache.h>
57 #include <QtQuick/qquickwindow.h>
59 // #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG
65 class QSGInvokeEvent: public QEvent
68 QSGInvokeEvent(QPlatformSharedGraphicsCache *cache,
69 const QByteArray &cacheId = QByteArray(),
70 const QVector<quint32> &glyphIds = QVector<quint32>(),
71 bool inSceneGraphUpdate = false)
75 , m_glyphIds(glyphIds)
76 , m_inSceneGraphUpdate(inSceneGraphUpdate)
79 bool inSceneGraphUpdate() const { return m_inSceneGraphUpdate; }
80 QPlatformSharedGraphicsCache *cache() const { return m_cache; }
82 virtual void invoke() = 0;
84 QPlatformSharedGraphicsCache *m_cache;
86 QVector<quint32> m_glyphIds;
87 bool m_inSceneGraphUpdate;
90 class QSGReleaseItemsEvent: public QSGInvokeEvent
93 QSGReleaseItemsEvent(QPlatformSharedGraphicsCache *cache,
94 const QByteArray &cacheId,
95 const QVector<quint32> &glyphIds,
96 bool inSceneGraphUpdate)
97 : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
103 m_cache->releaseItems(m_cacheId, m_glyphIds);
107 class QSGRequestItemsEvent: public QSGInvokeEvent
110 QSGRequestItemsEvent(QPlatformSharedGraphicsCache *cache,
111 const QByteArray &cacheId,
112 const QVector<quint32> &glyphIds,
113 bool inSceneGraphUpdate)
114 : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
120 m_cache->requestItems(m_cacheId, m_glyphIds);
124 class QSGInsertItemsEvent: public QSGInvokeEvent
127 QSGInsertItemsEvent(QPlatformSharedGraphicsCache *cache,
128 const QByteArray &cacheId,
129 const QVector<quint32> &glyphIds,
130 const QVector<QImage> &images,
131 bool inSceneGraphUpdate)
132 : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
139 m_cache->insertItems(m_cacheId, m_glyphIds, m_images);
143 QVector<QImage> m_images;
146 class QSGEndRequestBatchEvent: public QSGInvokeEvent
149 QSGEndRequestBatchEvent(QPlatformSharedGraphicsCache *cache)
150 : QSGInvokeEvent(cache)
156 if (m_cache->requestBatchStarted())
157 m_cache->endRequestBatch();
161 class QSGMainThreadInvoker: public QObject
164 bool event(QEvent *e)
166 if (e->type() == QEvent::User) {
167 Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
169 QSGInvokeEvent *invokeEvent = static_cast<QSGInvokeEvent *>(e);
170 if (invokeEvent->inSceneGraphUpdate()) {
171 QPlatformSharedGraphicsCache *cache = invokeEvent->cache();
172 if (!cache->requestBatchStarted())
173 cache->beginRequestBatch();
176 static_cast<QSGInvokeEvent *>(e)->invoke();
179 return QObject::event(e);
182 static QSGMainThreadInvoker *instance()
184 if (m_invoker == 0) {
185 m_invoker = new QSGMainThreadInvoker;
186 m_invoker->moveToThread(QCoreApplication::instance()->thread());
193 static QSGMainThreadInvoker *m_invoker;
196 QSGMainThreadInvoker* QSGMainThreadInvoker::m_invoker = 0;
199 QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
200 QPlatformSharedGraphicsCache *sharedGraphicsCache,
201 QSGDistanceFieldGlyphCacheManager *man,
203 const QRawFont &font)
204 : QSGDistanceFieldGlyphCache(man, c, font)
206 , m_sharedGraphicsCache(sharedGraphicsCache)
207 , m_isInSceneGraphUpdate(false)
208 , m_hasPostedEvents(false)
210 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
211 qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p",
212 cacheId.constData(), QThread::currentThreadId());
215 Q_ASSERT(sizeof(glyph_t) == sizeof(quint32));
216 Q_ASSERT(sharedGraphicsCache != 0);
218 connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)),
219 this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)),
220 Qt::DirectConnection);
221 connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
222 this, SLOT(reportItemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
223 Qt::DirectConnection);
224 connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
225 this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
226 Qt::DirectConnection);
227 connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)),
228 this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
229 Qt::DirectConnection);
231 QQuickWindow *window = static_cast<QQuickWindow *>(c->surface());
232 Q_ASSERT(window != 0);
234 connect(window, SIGNAL(beforeSynchronizing()), this, SLOT(sceneGraphUpdateStarted()),
235 Qt::DirectConnection);
236 connect(window, SIGNAL(beforeRendering()), this, SLOT(sceneGraphUpdateDone()),
237 Qt::DirectConnection);
240 QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache()
243 QHash<glyph_t, void *>::const_iterator it = m_bufferForGlyph.constBegin();
244 while (it != m_bufferForGlyph.constEnd()) {
245 m_sharedGraphicsCache->dereferenceBuffer(it.value());
251 QHash<quint32, PendingGlyph>::const_iterator it = m_pendingReadyGlyphs.constBegin();
252 while (it != m_pendingReadyGlyphs.constEnd()) {
253 m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer);
259 void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
261 QMutexLocker locker(&m_pendingGlyphsMutex);
263 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
264 qDebug("QSGSharedDistanceFieldGlyphCache::requestGlyphs() called for %s (%d glyphs)",
265 m_cacheId.constData(), glyphs.size());
268 m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs);
269 m_requestedGlyphs.unite(glyphs);
271 QVector<quint32> glyphsVector;
272 glyphsVector.reserve(glyphs.size());
274 QSet<glyph_t>::const_iterator it;
275 for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
276 Q_ASSERT(!m_bufferForGlyph.contains(*it));
277 glyphsVector.append(*it);
280 m_hasPostedEvents = true;
281 QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
282 QCoreApplication::postEvent(invoker, new QSGRequestItemsEvent(m_sharedGraphicsCache,
285 m_isInSceneGraphUpdate));
288 void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
290 Q_ASSERT(!m_isInSceneGraphUpdate);
291 if (m_isInSceneGraphUpdate) {
292 qWarning("QSGSharedDistanceFieldGlyphCache::waitForGlyphs: Called from inside "
293 "scenegraph update. Will freeze.");
297 QMutexLocker locker(&m_pendingGlyphsMutex);
298 while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
299 m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex);
303 void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
306 QMutexLocker locker(&m_pendingGlyphsMutex);
307 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
308 qDebug("QSGSharedDistanceFieldGlyphCache::storeGlyphs() called for %s (%d glyphs)",
309 m_cacheId.constData(), glyphs.size());
312 int glyphCount = glyphs.size();
313 QVector<quint32> glyphIds(glyphCount);
314 QVector<QImage> images(glyphCount);
315 QHash<glyph_t, QImage>::const_iterator it = glyphs.constBegin();
317 while (it != glyphs.constEnd()) {
318 m_requestedGlyphsThatHaveNotBeenReturned.insert(it.key());
319 glyphIds[i] = it.key();
320 images[i] = it.value();
325 m_hasPostedEvents = true;
326 QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
327 QCoreApplication::postEvent(invoker, new QSGInsertItemsEvent(m_sharedGraphicsCache,
331 m_isInSceneGraphUpdate));
334 processPendingGlyphs();
337 void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
341 // Intentionally empty. Not required in this implementation, since the glyphs are reference
342 // counted outside and releaseGlyphs() will only be called when there are no more references.
345 void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
347 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
348 qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)",
349 m_cacheId.constData(), glyphs.size());
352 m_requestedGlyphs.subtract(glyphs);
354 QVector<quint32> glyphsVector;
355 glyphsVector.reserve(glyphs.size());
357 QSet<glyph_t>::const_iterator glyphsIt;
358 for (glyphsIt = glyphs.constBegin(); glyphsIt != glyphs.constEnd(); ++glyphsIt) {
359 QHash<glyph_t, void *>::iterator bufferIt = m_bufferForGlyph.find(*glyphsIt);
360 if (bufferIt != m_bufferForGlyph.end()) {
361 void *buffer = bufferIt.value();
362 removeGlyph(*glyphsIt);
363 m_bufferForGlyph.erase(bufferIt);
364 Q_ASSERT(!m_bufferForGlyph.contains(*glyphsIt));
366 if (!m_sharedGraphicsCache->dereferenceBuffer(buffer)) {
367 #if !defined(QT_NO_DEBUG)
368 bufferIt = m_bufferForGlyph.begin();
369 while (bufferIt != m_bufferForGlyph.end()) {
370 Q_ASSERT(bufferIt.value() != buffer);
377 glyphsVector.append(*glyphsIt);
380 m_hasPostedEvents = true;
381 QSGMainThreadInvoker *mainThreadInvoker = QSGMainThreadInvoker::instance();
382 QCoreApplication::postEvent(mainThreadInvoker, new QSGReleaseItemsEvent(m_sharedGraphicsCache,
385 m_isInSceneGraphUpdate));
388 void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
390 Owner &owner = m_registeredOwners[ownerElement];
391 if (owner.ref == 0) {
392 owner.item = ownerElement;
394 bool ok = connect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
395 Q_ASSERT_X(ok, Q_FUNC_INFO, "QML element that owns a glyph node must have triggerPreprocess() slot");
401 void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
403 QHash<QQuickItem *, Owner>::iterator it = m_registeredOwners.find(ownerElement);
404 if (it != m_registeredOwners.end() && --it->ref <= 0) {
406 disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
407 m_registeredOwners.erase(it);
411 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
412 # include <QtOpenGL/private/qglextensions_p.h>
414 void QSGSharedDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height)
417 glGenFramebuffers(1, &fboId);
419 GLuint tmpTexture = 0;
420 glGenTextures(1, &tmpTexture);
421 glBindTexture(GL_TEXTURE_2D, tmpTexture);
422 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
423 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
424 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
425 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
426 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
427 glBindTexture(GL_TEXTURE_2D, 0);
429 glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
430 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
433 glActiveTexture(GL_TEXTURE0);
434 glBindTexture(GL_TEXTURE_2D, textureId);
436 glDisable(GL_STENCIL_TEST);
437 glDisable(GL_DEPTH_TEST);
438 glDisable(GL_SCISSOR_TEST);
441 GLfloat textureCoordinateArray[8];
442 textureCoordinateArray[0] = 0.0f;
443 textureCoordinateArray[1] = 0.0f;
444 textureCoordinateArray[2] = 1.0f;
445 textureCoordinateArray[3] = 0.0f;
446 textureCoordinateArray[4] = 1.0f;
447 textureCoordinateArray[5] = 1.0f;
448 textureCoordinateArray[6] = 0.0f;
449 textureCoordinateArray[7] = 1.0f;
451 GLfloat vertexCoordinateArray[8];
452 vertexCoordinateArray[0] = -1.0f;
453 vertexCoordinateArray[1] = -1.0f;
454 vertexCoordinateArray[2] = 1.0f;
455 vertexCoordinateArray[3] = -1.0f;
456 vertexCoordinateArray[4] = 1.0f;
457 vertexCoordinateArray[5] = 1.0f;
458 vertexCoordinateArray[6] = -1.0f;
459 vertexCoordinateArray[7] = 1.0f;
461 glViewport(0, 0, width, height);
462 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
463 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
466 static const char *vertexShaderSource =
467 "attribute highp vec4 vertexCoordsArray; \n"
468 "attribute highp vec2 textureCoordArray; \n"
469 "varying highp vec2 textureCoords; \n"
472 " gl_Position = vertexCoordsArray; \n"
473 " textureCoords = textureCoordArray; \n"
476 static const char *fragmentShaderSource =
477 "varying highp vec2 textureCoords; \n"
478 "uniform sampler2D texture; \n"
481 " gl_FragColor = texture2D(texture, textureCoords); \n"
484 GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
485 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
487 if (vertexShader == 0 || fragmentShader == 0) {
488 GLenum error = glGetError();
489 qWarning("SharedGraphicsCacheServer::setupShaderPrograms: Failed to create shaders. (GL error: %x)",
494 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
495 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
496 glCompileShader(vertexShader);
499 glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
502 glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
503 if (qstrlen(infoLog) > 0) {
504 qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling vertex shader:\n %s",
509 glCompileShader(fragmentShader);
510 glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
511 if (qstrlen(infoLog) > 0) {
512 qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling fragent shader:\n %s",
517 GLuint shaderProgram = glCreateProgram();
518 glAttachShader(shaderProgram, vertexShader);
519 glAttachShader(shaderProgram, fragmentShader);
521 glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
522 glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
524 glLinkProgram(shaderProgram);
525 glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
526 if (qstrlen(infoLog) > 0) {
527 qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems linking shaders:\n %s",
532 glUseProgram(shaderProgram);
533 glEnableVertexAttribArray(0);
534 glEnableVertexAttribArray(1);
536 int textureUniformLocation = glGetUniformLocation(shaderProgram, "texture");
537 glUniform1i(textureUniformLocation, 0);
540 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
543 GLenum error = glGetError();
544 if (error != GL_NO_ERROR) {
545 qWarning("SharedGraphicsCacheServer::readBackBuffer: glDrawArrays reported error 0x%x",
550 uchar *data = new uchar[width * height * 4];
552 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
554 QImage image(width, height, QImage::Format_ARGB32);
555 quint32 *dest = reinterpret_cast<quint32 *>(image.bits());
556 for (int i=0; i<width*height; ++i)
557 dest[i] = qRgba(0xff, 0xff, 0xff, data[i]);
559 QByteArray fileName = m_cacheId + ' ' + QByteArray::number(textureId);
560 fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
561 image.save(QString::fromLocal8Bit(fileName));
564 GLenum error = glGetError();
565 if (error != GL_NO_ERROR) {
566 qWarning("SharedGraphicsCacheServer::readBackBuffer: glReadPixels reported error 0x%x",
571 glDisableVertexAttribArray(0);
572 glDisableVertexAttribArray(1);
574 glDeleteFramebuffers(1, &fboId);
575 glDeleteTextures(1, &tmpTexture);
582 struct TextureContent {
584 QVector<glyph_t> glyphs;
588 void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
590 Q_ASSERT(QThread::currentThread() == thread());
595 QMutexLocker locker(&m_pendingGlyphsMutex);
596 if (m_pendingMissingGlyphs.isEmpty()
597 && m_pendingReadyGlyphs.isEmpty()
598 && m_pendingInvalidatedGlyphs.isEmpty()) {
603 QVector<glyph_t> pendingMissingGlyphs;
604 pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size());
606 QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin();
607 while (it != m_pendingMissingGlyphs.constEnd()) {
608 pendingMissingGlyphs.append(*it);
612 markGlyphsToRender(pendingMissingGlyphs);
616 QVector<glyph_t> filteredPendingInvalidatedGlyphs;
617 filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size());
619 QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin();
620 while (it != m_pendingInvalidatedGlyphs.constEnd()) {
621 bool rerequestGlyph = false;
623 // The glyph was invalidated right after being posted as ready, we throw away
624 // the ready glyph and rerequest it to be certain
625 QHash<quint32, PendingGlyph>::iterator pendingGlyphIt = m_pendingReadyGlyphs.find(*it);
626 if (pendingGlyphIt != m_pendingReadyGlyphs.end()) {
627 m_sharedGraphicsCache->dereferenceBuffer(pendingGlyphIt.value().buffer);
628 pendingGlyphIt = m_pendingReadyGlyphs.erase(pendingGlyphIt);
629 rerequestGlyph = true;
632 void *bufferId = m_bufferForGlyph.value(*it, 0);
634 m_sharedGraphicsCache->dereferenceBuffer(bufferId);
635 m_bufferForGlyph.remove(*it);
636 rerequestGlyph = true;
640 filteredPendingInvalidatedGlyphs.append(*it);
645 // If this cache is still using the glyphs, reset the texture held by them, and mark them
646 // to be rendered again since they are still needed.
647 if (!filteredPendingInvalidatedGlyphs.isEmpty()) {
648 setGlyphsTexture(filteredPendingInvalidatedGlyphs, Texture());
649 markGlyphsToRender(filteredPendingInvalidatedGlyphs);
654 QList<GlyphPosition> glyphPositions;
656 QHash<void *, TextureContent> textureContentForBuffer;
658 QHash<quint32, PendingGlyph>::iterator it = m_pendingReadyGlyphs.begin();
659 while (it != m_pendingReadyGlyphs.end()) {
660 void *currentGlyphBuffer = m_bufferForGlyph.value(it.key(), 0);
661 if (currentGlyphBuffer != 0) {
662 if (!m_sharedGraphicsCache->dereferenceBuffer(currentGlyphBuffer)) {
663 Q_ASSERT(!textureContentForBuffer.contains(currentGlyphBuffer));
667 PendingGlyph &pendingGlyph = it.value();
669 // We don't ref or deref the buffer here, since it was already referenced when
670 // added to the pending ready glyphs
671 m_bufferForGlyph[it.key()] = pendingGlyph.buffer;
673 textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize;
674 textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key());
676 GlyphPosition glyphPosition;
677 glyphPosition.glyph = it.key();
678 glyphPosition.position = pendingGlyph.position;
680 glyphPositions.append(glyphPosition);
686 setGlyphsPosition(glyphPositions);
689 QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin();
690 while (it != textureContentForBuffer.constEnd()) {
692 texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
693 texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key());
695 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
696 saveTexture(texture.textureId, texture.size.width(), texture.size.height());
698 setGlyphsTexture(it.value().glyphs, texture);
705 m_pendingMissingGlyphs.clear();
706 m_pendingInvalidatedGlyphs.clear();
707 m_pendingReadyGlyphs.clear();
711 void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
713 const QVector<quint32> &itemIds,
714 const QVector<QPoint> &positions)
716 bool requestedItemsInList = false;
718 QMutexLocker locker(&m_pendingGlyphsMutex);
719 if (m_cacheId != cacheId)
722 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
723 qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)",
724 cacheId.constData(), itemIds.size());
727 for (int i=0; i<itemIds.size(); ++i) {
728 if (m_requestedGlyphsThatHaveNotBeenReturned.contains(itemIds.at(i))) {
729 requestedItemsInList = true;
735 if (requestedItemsInList)
736 reportItemsUpdated(cacheId, bufferId,itemIds, positions);
739 void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId,
741 const QVector<quint32> &itemIds,
742 const QVector<QPoint> &positions)
745 QMutexLocker locker(&m_pendingGlyphsMutex);
746 if (m_cacheId != cacheId)
749 Q_ASSERT(itemIds.size() == positions.size());
751 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
752 qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)",
753 cacheId.constData(), itemIds.size());
756 for (int i=0; i<itemIds.size(); ++i) {
757 if (m_requestedGlyphs.contains(itemIds.at(i))) {
758 PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
759 void *oldBuffer = pendingGlyph.buffer;
761 pendingGlyph.buffer = bufferId;
762 pendingGlyph.position = positions.at(i);
764 m_sharedGraphicsCache->referenceBuffer(bufferId);
766 m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
768 m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
773 m_pendingGlyphsCondition.wakeAll();
774 emit glyphsPending();
777 void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId,
778 const QVector<quint32> &itemIds)
781 QMutexLocker locker(&m_pendingGlyphsMutex);
782 if (m_cacheId != cacheId)
785 for (int i=0; i<itemIds.size(); ++i) {
786 if (m_requestedGlyphs.contains(itemIds.at(i)))
787 m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
791 emit glyphsPending();
795 void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId,
796 const QVector<quint32> &itemIds)
799 QMutexLocker locker(&m_pendingGlyphsMutex);
800 if (m_cacheId != cacheId)
803 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
804 qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)",
805 cacheId.constData(), itemIds.size());
808 for (int i=0; i<itemIds.size(); ++i) {
809 if (m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i)))
810 m_pendingMissingGlyphs.insert(itemIds.at(i));
814 m_pendingGlyphsCondition.wakeAll();
815 emit glyphsPending();
818 void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateStarted()
820 m_isInSceneGraphUpdate = true;
821 m_hasPostedEvents = false;
824 void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateDone()
826 m_isInSceneGraphUpdate = false;
828 if (m_hasPostedEvents) {
829 QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
830 QCoreApplication::postEvent(invoker, new QSGEndRequestBatchEvent(m_sharedGraphicsCache));
831 m_hasPostedEvents = false;