Merge remote-tracking branch 'origin/master' into api_changes
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgshareddistancefieldglyphcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
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>
47 #endif
48
49 #include "qsgshareddistancefieldglyphcache_p.h"
50
51 #include <QtCore/qhash.h>
52 #include <QtCore/qthread.h>
53 #include <QtGui/qplatformsharedgraphicscache_qpa.h>
54
55 #include <QtQuick/qquickcanvas.h>
56
57 #include <QtOpenGL/qglframebufferobject.h>
58
59 // #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG
60
61 Q_DECLARE_METATYPE(QVector<quint32>)
62 Q_DECLARE_METATYPE(QVector<QImage>)
63
64 QT_BEGIN_NAMESPACE
65
66 QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
67                                                                    QPlatformSharedGraphicsCache *sharedGraphicsCache,
68                                                                    QSGDistanceFieldGlyphCacheManager *man,
69                                                                    QOpenGLContext *c,
70                                                                    const QRawFont &font)
71     : QSGDistanceFieldGlyphCache(man, c, font)
72     , m_cacheId(cacheId)
73     , m_sharedGraphicsCache(sharedGraphicsCache)
74 {
75 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
76     qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p",
77            cacheId.constData(), QThread::currentThreadId());
78 #endif
79
80     Q_ASSERT(sizeof(glyph_t) == sizeof(quint32));
81     Q_ASSERT(sharedGraphicsCache != 0);
82
83     qRegisterMetaType<QVector<quint32> >();
84     qRegisterMetaType<QVector<QImage> >();
85
86     connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)),
87             this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)),
88             Qt::DirectConnection);
89     connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
90             this, SLOT(reportItemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
91             Qt::DirectConnection);
92     connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
93             this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
94             Qt::DirectConnection);
95     connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)),
96             this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
97             Qt::DirectConnection);
98 }
99
100 QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache()
101 {
102     {
103         QHash<glyph_t, void *>::const_iterator it = m_bufferForGlyph.constBegin();
104         while (it != m_bufferForGlyph.constEnd()) {
105             m_sharedGraphicsCache->dereferenceBuffer(it.value());
106             ++it;
107         }
108     }
109
110     {
111         QHash<quint32, PendingGlyph>::const_iterator it = m_pendingReadyGlyphs.constBegin();
112         while (it != m_pendingReadyGlyphs.constEnd()) {
113             m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer);
114             ++it;
115         }
116     }
117 }
118
119 void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
120 {
121     QMutexLocker locker(&m_pendingGlyphsMutex);
122
123 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
124     qDebug("QSGSharedDistanceFieldGlyphCache::requestGlyphs() called for %s (%d glyphs)",
125            m_cacheId.constData(), glyphs.size());
126 #endif
127
128     m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs);
129     m_requestedGlyphs.unite(glyphs);
130
131     QVector<quint32> glyphsVector;
132     glyphsVector.reserve(glyphs.size());
133
134     QSet<glyph_t>::const_iterator it;
135     for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
136         Q_ASSERT(!m_bufferForGlyph.contains(*it));
137         glyphsVector.append(*it);
138     }
139
140     // Invoke method on queued connection to make sure it's called asynchronously on the
141     // correct thread (requestGlyphs() is called from the rendering thread.)
142     QMetaObject::invokeMethod(m_sharedGraphicsCache, "requestItems", Qt::QueuedConnection,
143                               Q_ARG(QByteArray, m_cacheId),
144                               Q_ARG(QVector<quint32>, glyphsVector));
145 }
146
147 void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
148 {
149     {
150         QMutexLocker locker(&m_pendingGlyphsMutex);
151         while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
152             m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex);
153     }
154 }
155
156 void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
157 {
158     {
159         QMutexLocker locker(&m_pendingGlyphsMutex);
160 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
161         qDebug("QSGSharedDistanceFieldGlyphCache::storeGlyphs() called for %s (%d glyphs)",
162                m_cacheId.constData(), glyphs.size());
163 #endif
164
165         int glyphCount = glyphs.size();
166         QVector<quint32> glyphIds(glyphCount);
167         QVector<QImage> images(glyphCount);
168         QHash<glyph_t, QImage>::const_iterator it = glyphs.constBegin();
169         int i=0;
170         while (it != glyphs.constEnd()) {
171             m_requestedGlyphsThatHaveNotBeenReturned.insert(it.key());
172             glyphIds[i] = it.key();
173             images[i] = it.value();
174
175             ++it; ++i;
176         }
177
178         QMetaObject::invokeMethod(m_sharedGraphicsCache, "insertItems", Qt::QueuedConnection,
179                                   Q_ARG(QByteArray, m_cacheId),
180                                   Q_ARG(QVector<quint32>, glyphIds),
181                                   Q_ARG(QVector<QImage>, images));
182     }
183
184     processPendingGlyphs();
185 }
186
187 void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
188 {
189     Q_UNUSED(glyphs);
190
191     // Intentionally empty. Not required in this implementation, since the glyphs are reference
192     // counted outside and releaseGlyphs() will only be called when there are no more references.
193 }
194
195 void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
196 {
197 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
198     qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)",
199            m_cacheId.constData(), glyphs.size());
200 #endif
201
202     m_requestedGlyphs.subtract(glyphs);
203
204     QVector<quint32> glyphsVector;
205     glyphsVector.reserve(glyphs.size());
206
207     QSet<glyph_t>::const_iterator glyphsIt;
208     for (glyphsIt = glyphs.constBegin(); glyphsIt != glyphs.constEnd(); ++glyphsIt) {
209         QHash<glyph_t, void *>::iterator bufferIt = m_bufferForGlyph.find(*glyphsIt);
210         if (bufferIt != m_bufferForGlyph.end()) {
211             void *buffer = bufferIt.value();
212             removeGlyph(*glyphsIt);
213             m_bufferForGlyph.erase(bufferIt);
214             Q_ASSERT(!m_bufferForGlyph.contains(*glyphsIt));
215
216             if (!m_sharedGraphicsCache->dereferenceBuffer(buffer)) {
217 #if !defined(QT_NO_DEBUG)
218                 bufferIt = m_bufferForGlyph.begin();
219                 while (bufferIt != m_bufferForGlyph.end()) {
220                     Q_ASSERT(bufferIt.value() != buffer);
221                     ++bufferIt;
222                 }
223 #endif
224             }
225         }
226
227         glyphsVector.append(*glyphsIt);
228     }
229
230     QMetaObject::invokeMethod(m_sharedGraphicsCache, "releaseItems", Qt::QueuedConnection,
231                               Q_ARG(QByteArray, m_cacheId),
232                               Q_ARG(QVector<quint32>, glyphsVector));
233 }
234
235 void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
236 {
237     Owner &owner = m_registeredOwners[ownerElement];
238     if (owner.ref == 0) {
239         owner.item = ownerElement;
240
241         bool ok = connect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
242         Q_ASSERT_X(ok, Q_FUNC_INFO, "QML element that owns a glyph node must have triggerPreprocess() slot");
243         Q_UNUSED(ok);
244     }
245     ++owner.ref;
246 }
247
248 void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
249 {
250     QHash<QQuickItem *, Owner>::iterator it = m_registeredOwners.find(ownerElement);
251     if (it != m_registeredOwners.end() && --it->ref <= 0) {
252         if (it->item)
253             disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
254         m_registeredOwners.erase(it);
255     }
256 }
257
258 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
259 #  include <QtOpenGL/private/qglextensions_p.h>
260
261 void QSGSharedDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height)
262 {
263     GLuint fboId;
264     glGenFramebuffers(1, &fboId);
265
266     GLuint tmpTexture = 0;
267     glGenTextures(1, &tmpTexture);
268     glBindTexture(GL_TEXTURE_2D, tmpTexture);
269     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
270     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
271     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
272     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
273     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
274     glBindTexture(GL_TEXTURE_2D, 0);
275
276     glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
277     glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
278                            tmpTexture, 0);
279
280     glActiveTexture(GL_TEXTURE0);
281     glBindTexture(GL_TEXTURE_2D, textureId);
282
283     glDisable(GL_STENCIL_TEST);
284     glDisable(GL_DEPTH_TEST);
285     glDisable(GL_SCISSOR_TEST);
286     glDisable(GL_BLEND);
287
288     GLfloat textureCoordinateArray[8];
289     textureCoordinateArray[0] = 0.0f;
290     textureCoordinateArray[1] = 0.0f;
291     textureCoordinateArray[2] = 1.0f;
292     textureCoordinateArray[3] = 0.0f;
293     textureCoordinateArray[4] = 1.0f;
294     textureCoordinateArray[5] = 1.0f;
295     textureCoordinateArray[6] = 0.0f;
296     textureCoordinateArray[7] = 1.0f;
297
298     GLfloat vertexCoordinateArray[8];
299     vertexCoordinateArray[0] = -1.0f;
300     vertexCoordinateArray[1] = -1.0f;
301     vertexCoordinateArray[2] =  1.0f;
302     vertexCoordinateArray[3] = -1.0f;
303     vertexCoordinateArray[4] =  1.0f;
304     vertexCoordinateArray[5] =  1.0f;
305     vertexCoordinateArray[6] = -1.0f;
306     vertexCoordinateArray[7] =  1.0f;
307
308     glViewport(0, 0, width, height);
309     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
310     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
311
312     {
313         static const char *vertexShaderSource =
314                 "attribute highp   vec4      vertexCoordsArray; \n"
315                 "attribute highp   vec2      textureCoordArray; \n"
316                 "varying   highp   vec2      textureCoords;     \n"
317                 "void main(void) \n"
318                 "{ \n"
319                 "    gl_Position = vertexCoordsArray;   \n"
320                 "    textureCoords = textureCoordArray; \n"
321                 "} \n";
322
323         static const char *fragmentShaderSource =
324                 "varying   highp   vec2      textureCoords; \n"
325                 "uniform   sampler2D         texture;       \n"
326                 "void main() \n"
327                 "{ \n"
328                 "    gl_FragColor = texture2D(texture, textureCoords); \n"
329                 "} \n";
330
331         GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
332         GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
333
334         if (vertexShader == 0 || fragmentShader == 0) {
335             GLenum error = glGetError();
336             qWarning("SharedGraphicsCacheServer::setupShaderPrograms: Failed to create shaders. (GL error: %x)",
337                      error);
338             return;
339         }
340
341         glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
342         glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
343         glCompileShader(vertexShader);
344
345         GLint len = 1;
346         glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
347
348         char infoLog[2048];
349         glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
350         if (qstrlen(infoLog) > 0) {
351             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling vertex shader:\n %s",
352                      infoLog);
353             //return;
354         }
355
356         glCompileShader(fragmentShader);
357         glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
358         if (qstrlen(infoLog) > 0) {
359             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling fragent shader:\n %s",
360                      infoLog);
361             //return;
362         }
363
364         GLuint shaderProgram = glCreateProgram();
365         glAttachShader(shaderProgram, vertexShader);
366         glAttachShader(shaderProgram, fragmentShader);
367
368         glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
369         glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
370
371         glLinkProgram(shaderProgram);
372         glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
373         if (qstrlen(infoLog) > 0) {
374             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems linking shaders:\n %s",
375                      infoLog);
376             //return;
377         }
378
379         glUseProgram(shaderProgram);
380         glEnableVertexAttribArray(0);
381         glEnableVertexAttribArray(1);
382
383         int textureUniformLocation = glGetUniformLocation(shaderProgram, "texture");
384         glUniform1i(textureUniformLocation, 0);
385     }
386
387     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
388
389     {
390         GLenum error = glGetError();
391         if (error != GL_NO_ERROR) {
392             qWarning("SharedGraphicsCacheServer::readBackBuffer: glDrawArrays reported error 0x%x",
393                      error);
394         }
395     }
396
397     uchar *data = new uchar[width * height * 4];
398
399     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
400
401     QImage image(width, height, QImage::Format_ARGB32);
402     quint32 *dest = reinterpret_cast<quint32 *>(image.bits());
403     for (int i=0; i<width*height; ++i)
404         dest[i] = qRgba(0xff, 0xff, 0xff, data[i]);
405
406     QByteArray fileName = m_cacheId + " " + QByteArray::number(textureId);
407     fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
408     image.save(QString::fromLocal8Bit(fileName));
409
410     {
411         GLenum error = glGetError();
412         if (error != GL_NO_ERROR) {
413             qWarning("SharedGraphicsCacheServer::readBackBuffer: glReadPixels reported error 0x%x",
414                      error);
415         }
416     }
417
418     glDisableVertexAttribArray(0);
419     glDisableVertexAttribArray(1);
420
421     glDeleteFramebuffers(1, &fboId);
422     glDeleteTextures(1, &tmpTexture);
423
424     delete[] data;
425 }
426 #endif
427
428 namespace {
429     struct TextureContent {
430         QSize size;
431         QVector<glyph_t> glyphs;
432     };
433 }
434
435 void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
436 {
437     Q_ASSERT(QThread::currentThread() == thread());
438
439     waitForGlyphs();
440
441     {
442         QMutexLocker locker(&m_pendingGlyphsMutex);
443         if (m_pendingMissingGlyphs.isEmpty()
444             && m_pendingReadyGlyphs.isEmpty()
445             && m_pendingInvalidatedGlyphs.isEmpty()) {
446             return;
447         }
448
449         {
450             QVector<glyph_t> pendingMissingGlyphs;
451             pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size());
452
453             QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin();
454             while (it != m_pendingMissingGlyphs.constEnd()) {
455                 pendingMissingGlyphs.append(*it);
456                 ++it;
457             }
458
459             markGlyphsToRender(pendingMissingGlyphs);
460         }
461
462         {
463             QVector<glyph_t> filteredPendingInvalidatedGlyphs;
464             filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size());
465
466             QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin();
467             while (it != m_pendingInvalidatedGlyphs.constEnd()) {
468                 bool rerequestGlyph = false;
469
470                 // The glyph was invalidated right after being posted as ready, we throw away
471                 // the ready glyph and rerequest it to be certain
472                 QHash<quint32, PendingGlyph>::iterator pendingGlyphIt = m_pendingReadyGlyphs.find(*it);
473                 if (pendingGlyphIt != m_pendingReadyGlyphs.end()) {
474                     m_sharedGraphicsCache->dereferenceBuffer(pendingGlyphIt.value().buffer);
475                     pendingGlyphIt = m_pendingReadyGlyphs.erase(pendingGlyphIt);
476                     rerequestGlyph = true;
477                 }
478
479                 void *bufferId = m_bufferForGlyph.value(*it, 0);
480                 if (bufferId != 0) {
481                     m_sharedGraphicsCache->dereferenceBuffer(bufferId);
482                     m_bufferForGlyph.remove(*it);
483                     rerequestGlyph = true;
484                 }
485
486                 if (rerequestGlyph)
487                     filteredPendingInvalidatedGlyphs.append(*it);
488
489                 ++it;
490             }
491
492             // If this cache is still using the glyphs, reset the texture held by them, and mark them
493             // to be rendered again since they are still needed.
494             if (!filteredPendingInvalidatedGlyphs.isEmpty()) {
495                 setGlyphsTexture(filteredPendingInvalidatedGlyphs, Texture());
496                 markGlyphsToRender(filteredPendingInvalidatedGlyphs);
497             }
498         }
499
500         {
501             QList<GlyphPosition> glyphPositions;
502
503             QHash<void *, TextureContent> textureContentForBuffer;
504             {
505                 QHash<quint32, PendingGlyph>::iterator it = m_pendingReadyGlyphs.begin();
506                 while (it != m_pendingReadyGlyphs.end()) {
507                     void *currentGlyphBuffer = m_bufferForGlyph.value(it.key(), 0);
508                     if (currentGlyphBuffer != 0) {
509                         if (!m_sharedGraphicsCache->dereferenceBuffer(currentGlyphBuffer)) {
510                             Q_ASSERT(!textureContentForBuffer.contains(currentGlyphBuffer));
511                         }
512                     }
513
514                     PendingGlyph &pendingGlyph  = it.value();
515
516                     // We don't ref or deref the buffer here, since it was already referenced when
517                     // added to the pending ready glyphs
518                     m_bufferForGlyph[it.key()] = pendingGlyph.buffer;
519
520                     textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize;
521                     textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key());
522
523                     GlyphPosition glyphPosition;
524                     glyphPosition.glyph = it.key();
525                     glyphPosition.position = pendingGlyph.position;
526
527                     glyphPositions.append(glyphPosition);
528
529                     ++it;
530                 }
531             }
532
533             setGlyphsPosition(glyphPositions);
534
535             {
536                 QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin();
537                 while (it != textureContentForBuffer.constEnd()) {
538                     Texture texture;
539                     texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
540                     texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key());
541
542 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
543                     saveTexture(texture.textureId, texture.size.width(), texture.size.height());
544 #endif
545                     setGlyphsTexture(it.value().glyphs, texture);
546
547                     ++it;
548                 }
549             }
550         }
551
552         m_pendingMissingGlyphs.clear();
553         m_pendingInvalidatedGlyphs.clear();
554         m_pendingReadyGlyphs.clear();
555     }
556 }
557
558 void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
559                                                             void *bufferId,
560                                                             const QVector<quint32> &itemIds,
561                                                             const QVector<QPoint> &positions)
562 {
563     bool requestedItemsInList = false;
564     {
565         QMutexLocker locker(&m_pendingGlyphsMutex);
566         if (m_cacheId != cacheId)
567             return;
568
569 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
570             qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)",
571                    cacheId.constData(), itemIds.size());
572 #endif
573
574         for (int i=0; i<itemIds.size(); ++i) {
575             if (m_requestedGlyphsThatHaveNotBeenReturned.contains(itemIds.at(i))) {
576                 requestedItemsInList = true;
577                 break;
578             }
579         }
580     }
581
582     if (requestedItemsInList)
583         reportItemsUpdated(cacheId, bufferId,itemIds, positions);
584 }
585
586 void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId,
587                                                           void *bufferId,
588                                                           const QVector<quint32> &itemIds,
589                                                           const QVector<QPoint> &positions)
590 {
591     {
592         QMutexLocker locker(&m_pendingGlyphsMutex);
593         if (m_cacheId != cacheId)
594             return;
595
596         Q_ASSERT(itemIds.size() == positions.size());
597
598 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
599         qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)",
600                cacheId.constData(), itemIds.size());
601 #endif
602
603         for (int i=0; i<itemIds.size(); ++i) {
604             if (m_requestedGlyphs.contains(itemIds.at(i))) {
605                 PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
606                 void *oldBuffer = pendingGlyph.buffer;
607
608                 pendingGlyph.buffer = bufferId;
609                 pendingGlyph.position = positions.at(i);
610
611                 m_sharedGraphicsCache->referenceBuffer(bufferId);
612                 if (oldBuffer != 0)
613                     m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
614
615                 m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
616             }
617         }
618     }
619
620     m_pendingGlyphsCondition.wakeAll();
621     emit glyphsPending();
622 }
623
624 void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId,
625                                                               const QVector<quint32> &itemIds)
626 {
627     {
628         QMutexLocker locker(&m_pendingGlyphsMutex);
629         if (m_cacheId != cacheId)
630             return;
631
632         for (int i=0; i<itemIds.size(); ++i) {
633             if (m_requestedGlyphs.contains(itemIds.at(i)))
634                 m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
635         }
636     }
637
638     emit glyphsPending();
639 }
640
641
642 void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId,
643                                                           const QVector<quint32> &itemIds)
644 {
645     {
646         QMutexLocker locker(&m_pendingGlyphsMutex);
647         if (m_cacheId != cacheId)
648             return;
649
650 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
651         qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)",
652                cacheId.constData(), itemIds.size());
653 #endif
654
655         for (int i=0; i<itemIds.size(); ++i) {
656             if (m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i)))
657                 m_pendingMissingGlyphs.insert(itemIds.at(i));
658         }
659     }
660
661     m_pendingGlyphsCondition.wakeAll();
662     emit glyphsPending();
663 }
664
665 QT_END_NAMESPACE