841322e58b2315e8b5320c7cc4a1f8e1597a4f64
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgshareddistancefieldglyphcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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*,QSize,QVector<quint32>,QVector<QPoint>)),
90             this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
91             Qt::DirectConnection);
92     connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
93             this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,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
130     QVector<quint32> glyphsVector;
131     glyphsVector.reserve(glyphs.size());
132
133     QSet<glyph_t>::const_iterator it;
134     for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
135         Q_ASSERT(!m_bufferForGlyph.contains(*it));
136         glyphsVector.append(*it);
137     }
138
139     // Invoke method on queued connection to make sure it's called asynchronously on the
140     // correct thread (requestGlyphs() is called from the rendering thread.)
141     QMetaObject::invokeMethod(m_sharedGraphicsCache, "requestItems", Qt::QueuedConnection,
142                               Q_ARG(QByteArray, m_cacheId),
143                               Q_ARG(QVector<quint32>, glyphsVector));
144 }
145
146 void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
147 {
148     {
149         QMutexLocker locker(&m_pendingGlyphsMutex);
150         while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
151             m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex);
152     }
153 }
154
155 void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
156 {
157     {
158         QMutexLocker locker(&m_pendingGlyphsMutex);
159 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
160         qDebug("QSGSharedDistanceFieldGlyphCache::storeGlyphs() called for %s (%d glyphs)",
161                m_cacheId.constData(), glyphs.size());
162 #endif
163
164         int glyphCount = glyphs.size();
165         QVector<quint32> glyphIds(glyphCount);
166         QVector<QImage> images(glyphCount);
167         QHash<glyph_t, QImage>::const_iterator it = glyphs.constBegin();
168         int i=0;
169         while (it != glyphs.constEnd()) {
170             m_requestedGlyphsThatHaveNotBeenReturned.insert(it.key());
171             glyphIds[i] = it.key();
172             images[i] = it.value();
173
174             ++it; ++i;
175         }
176
177         QMetaObject::invokeMethod(m_sharedGraphicsCache, "insertItems", Qt::QueuedConnection,
178                                   Q_ARG(QByteArray, m_cacheId),
179                                   Q_ARG(QVector<quint32>, glyphIds),
180                                   Q_ARG(QVector<QImage>, images));
181     }
182
183     processPendingGlyphs();
184 }
185
186 void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
187 {
188     Q_UNUSED(glyphs);
189
190     // Intentionally empty. Not required in this implementation, since the glyphs are reference
191     // counted outside and releaseGlyphs() will only be called when there are no more references.
192 }
193
194 void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
195 {
196 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
197     qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)",
198            m_cacheId.constData(), glyphs.size());
199 #endif
200
201     QVector<quint32> glyphsVector;
202     glyphsVector.reserve(glyphs.size());
203
204     QSet<glyph_t>::const_iterator glyphsIt;
205     for (glyphsIt = glyphs.constBegin(); glyphsIt != glyphs.constEnd(); ++glyphsIt) {
206         QHash<glyph_t, void *>::iterator bufferIt = m_bufferForGlyph.find(*glyphsIt);
207         if (bufferIt != m_bufferForGlyph.end()) {
208             void *buffer = bufferIt.value();
209             removeGlyph(*glyphsIt);
210             m_bufferForGlyph.erase(bufferIt);
211             Q_ASSERT(!m_bufferForGlyph.contains(*glyphsIt));
212
213             if (!m_sharedGraphicsCache->dereferenceBuffer(buffer)) {
214 #if !defined(QT_NO_DEBUG)
215                 bufferIt = m_bufferForGlyph.begin();
216                 while (bufferIt != m_bufferForGlyph.end()) {
217                     Q_ASSERT(bufferIt.value() != buffer);
218                     ++bufferIt;
219                 }
220 #endif
221             }
222         }
223
224         glyphsVector.append(*glyphsIt);
225     }
226
227     QMetaObject::invokeMethod(m_sharedGraphicsCache, "releaseItems", Qt::QueuedConnection,
228                               Q_ARG(QByteArray, m_cacheId),
229                               Q_ARG(QVector<quint32>, glyphsVector));
230 }
231
232 void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
233 {
234     bool ok = connect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
235     Q_ASSERT_X(ok, Q_FUNC_INFO, "QML element that owns a glyph node must have triggerPreprocess() slot");
236     Q_UNUSED(ok);
237 }
238
239 void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
240 {
241     disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
242 }
243
244 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
245 #  include <QtOpenGL/private/qglextensions_p.h>
246
247 void QSGSharedDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height)
248 {
249     GLuint fboId;
250     glGenFramebuffers(1, &fboId);
251
252     GLuint tmpTexture = 0;
253     glGenTextures(1, &tmpTexture);
254     glBindTexture(GL_TEXTURE_2D, tmpTexture);
255     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
256     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
257     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
258     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
259     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
260     glBindTexture(GL_TEXTURE_2D, 0);
261
262     glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
263     glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
264                            tmpTexture, 0);
265
266     glActiveTexture(GL_TEXTURE0);
267     glBindTexture(GL_TEXTURE_2D, textureId);
268
269     glDisable(GL_STENCIL_TEST);
270     glDisable(GL_DEPTH_TEST);
271     glDisable(GL_SCISSOR_TEST);
272     glDisable(GL_BLEND);
273
274     GLfloat textureCoordinateArray[8];
275     textureCoordinateArray[0] = 0.0f;
276     textureCoordinateArray[1] = 0.0f;
277     textureCoordinateArray[2] = 1.0f;
278     textureCoordinateArray[3] = 0.0f;
279     textureCoordinateArray[4] = 1.0f;
280     textureCoordinateArray[5] = 1.0f;
281     textureCoordinateArray[6] = 0.0f;
282     textureCoordinateArray[7] = 1.0f;
283
284     GLfloat vertexCoordinateArray[8];
285     vertexCoordinateArray[0] = -1.0f;
286     vertexCoordinateArray[1] = -1.0f;
287     vertexCoordinateArray[2] =  1.0f;
288     vertexCoordinateArray[3] = -1.0f;
289     vertexCoordinateArray[4] =  1.0f;
290     vertexCoordinateArray[5] =  1.0f;
291     vertexCoordinateArray[6] = -1.0f;
292     vertexCoordinateArray[7] =  1.0f;
293
294     glViewport(0, 0, width, height);
295     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
296     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
297
298     {
299         static const char *vertexShaderSource =
300                 "attribute highp   vec4      vertexCoordsArray; \n"
301                 "attribute highp   vec2      textureCoordArray; \n"
302                 "varying   highp   vec2      textureCoords;     \n"
303                 "void main(void) \n"
304                 "{ \n"
305                 "    gl_Position = vertexCoordsArray;   \n"
306                 "    textureCoords = textureCoordArray; \n"
307                 "} \n";
308
309         static const char *fragmentShaderSource =
310                 "varying   highp   vec2      textureCoords; \n"
311                 "uniform   sampler2D         texture;       \n"
312                 "void main() \n"
313                 "{ \n"
314                 "    gl_FragColor = texture2D(texture, textureCoords); \n"
315                 "} \n";
316
317         GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
318         GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
319
320         if (vertexShader == 0 || fragmentShader == 0) {
321             GLenum error = glGetError();
322             qWarning("SharedGraphicsCacheServer::setupShaderPrograms: Failed to create shaders. (GL error: %x)",
323                      error);
324             return;
325         }
326
327         glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
328         glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
329         glCompileShader(vertexShader);
330
331         GLint len = 1;
332         glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
333
334         char infoLog[2048];
335         glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
336         if (qstrlen(infoLog) > 0) {
337             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling vertex shader:\n %s",
338                      infoLog);
339             //return;
340         }
341
342         glCompileShader(fragmentShader);
343         glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
344         if (qstrlen(infoLog) > 0) {
345             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling fragent shader:\n %s",
346                      infoLog);
347             //return;
348         }
349
350         GLuint shaderProgram = glCreateProgram();
351         glAttachShader(shaderProgram, vertexShader);
352         glAttachShader(shaderProgram, fragmentShader);
353
354         glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
355         glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
356
357         glLinkProgram(shaderProgram);
358         glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
359         if (qstrlen(infoLog) > 0) {
360             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems linking shaders:\n %s",
361                      infoLog);
362             //return;
363         }
364
365         glUseProgram(shaderProgram);
366         glEnableVertexAttribArray(0);
367         glEnableVertexAttribArray(1);
368
369         int textureUniformLocation = glGetUniformLocation(shaderProgram, "texture");
370         glUniform1i(textureUniformLocation, 0);
371     }
372
373     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
374
375     {
376         GLenum error = glGetError();
377         if (error != GL_NO_ERROR) {
378             qWarning("SharedGraphicsCacheServer::readBackBuffer: glDrawArrays reported error 0x%x",
379                      error);
380         }
381     }
382
383     uchar *data = new uchar[width * height * 4];
384
385     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
386
387     QImage image(width, height, QImage::Format_ARGB32);
388     quint32 *dest = reinterpret_cast<quint32 *>(image.bits());
389     for (int i=0; i<width*height; ++i)
390         dest[i] = qRgba(0xff, 0xff, 0xff, data[i]);
391
392     QByteArray fileName = m_cacheId + " " + QByteArray::number(textureId);
393     fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
394     image.save(QString::fromLocal8Bit(fileName));
395
396     {
397         GLenum error = glGetError();
398         if (error != GL_NO_ERROR) {
399             qWarning("SharedGraphicsCacheServer::readBackBuffer: glReadPixels reported error 0x%x",
400                      error);
401         }
402     }
403
404     glDisableVertexAttribArray(0);
405     glDisableVertexAttribArray(1);
406
407     glDeleteFramebuffers(1, &fboId);
408     glDeleteTextures(1, &tmpTexture);
409
410     delete[] data;
411 }
412 #endif
413
414 namespace {
415     struct TextureContent {
416         QSize size;
417         QVector<glyph_t> glyphs;
418     };
419 }
420
421 void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
422 {
423     Q_ASSERT(QThread::currentThread() == thread());
424
425     waitForGlyphs();
426
427     {
428         QMutexLocker locker(&m_pendingGlyphsMutex);
429         if (m_pendingMissingGlyphs.isEmpty()
430             && m_pendingReadyGlyphs.isEmpty()
431             && m_pendingInvalidatedGlyphs.isEmpty()) {
432             return;
433         }
434
435         {
436             QVector<glyph_t> pendingMissingGlyphs;
437             pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size());
438
439             QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin();
440             while (it != m_pendingMissingGlyphs.constEnd()) {
441                 pendingMissingGlyphs.append(*it);
442                 ++it;
443             }
444
445             markGlyphsToRender(pendingMissingGlyphs);
446         }
447
448         {
449             QVector<glyph_t> filteredPendingInvalidatedGlyphs;
450             filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size());
451
452             QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin();
453             while (it != m_pendingInvalidatedGlyphs.constEnd()) {
454                 bool rerequestGlyph = false;
455
456                 // The glyph was invalidated right after being posted as ready, we throw away
457                 // the ready glyph and rerequest it to be certain
458                 QHash<quint32, PendingGlyph>::iterator pendingGlyphIt = m_pendingReadyGlyphs.find(*it);
459                 if (pendingGlyphIt != m_pendingReadyGlyphs.end()) {
460                     m_sharedGraphicsCache->dereferenceBuffer(pendingGlyphIt.value().buffer);
461                     pendingGlyphIt = m_pendingReadyGlyphs.erase(pendingGlyphIt);
462                     rerequestGlyph = true;
463                 }
464
465                 void *bufferId = m_bufferForGlyph.value(*it, 0);
466                 if (bufferId != 0) {
467                     m_sharedGraphicsCache->dereferenceBuffer(bufferId);
468                     m_bufferForGlyph.remove(*it);
469                     rerequestGlyph = true;
470                 }
471
472                 if (rerequestGlyph)
473                     filteredPendingInvalidatedGlyphs.append(*it);
474
475                 ++it;
476             }
477
478             // If this cache is still using the glyphs, reset the texture held by them, and mark them
479             // to be rendered again since they are still needed.
480             if (!filteredPendingInvalidatedGlyphs.isEmpty()) {
481                 setGlyphsTexture(filteredPendingInvalidatedGlyphs, Texture());
482                 markGlyphsToRender(filteredPendingInvalidatedGlyphs);
483             }
484         }
485
486         {
487             QList<GlyphPosition> glyphPositions;
488
489             QHash<void *, TextureContent> textureContentForBuffer;
490             {
491                 QHash<quint32, PendingGlyph>::iterator it = m_pendingReadyGlyphs.begin();
492                 while (it != m_pendingReadyGlyphs.end()) {
493                     void *currentGlyphBuffer = m_bufferForGlyph.value(it.key(), 0);
494                     if (currentGlyphBuffer != 0) {
495                         if (!m_sharedGraphicsCache->dereferenceBuffer(currentGlyphBuffer)) {
496                             Q_ASSERT(!textureContentForBuffer.contains(currentGlyphBuffer));
497                         }
498                     }
499
500                     PendingGlyph &pendingGlyph  = it.value();
501
502                     // We don't ref or deref the buffer here, since it was already referenced when
503                     // added to the pending ready glyphs
504                     m_bufferForGlyph[it.key()] = pendingGlyph.buffer;
505
506                     textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize;
507                     textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key());
508
509                     GlyphPosition glyphPosition;
510                     glyphPosition.glyph = it.key();
511                     glyphPosition.position = pendingGlyph.position;
512
513                     glyphPositions.append(glyphPosition);
514
515                     ++it;
516                 }
517             }
518
519             setGlyphsPosition(glyphPositions);
520
521             {
522                 QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin();
523                 while (it != textureContentForBuffer.constEnd()) {
524                     Texture texture;
525                     texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
526                     texture.size = it.value().size;
527
528 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
529                     saveTexture(texture.textureId, texture.size.width(), texture.size.height());
530 #endif
531                     setGlyphsTexture(it.value().glyphs, texture);
532
533                     ++it;
534                 }
535             }
536         }
537
538         m_pendingMissingGlyphs.clear();
539         m_pendingInvalidatedGlyphs.clear();
540         m_pendingReadyGlyphs.clear();
541     }
542 }
543
544 void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
545                                                         void *bufferId, const QSize &bufferSize,
546                                                         const QVector<quint32> &itemIds,
547                                                         const QVector<QPoint> &positions)
548 {
549     {
550         QMutexLocker locker(&m_pendingGlyphsMutex);
551         if (m_cacheId != cacheId)
552             return;
553
554         Q_ASSERT(itemIds.size() == positions.size());
555
556 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
557         qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs, bufferSize: %dx%d)",
558                cacheId.constData(), itemIds.size(), bufferSize.width(), bufferSize.height());
559 #endif
560
561         for (int i=0; i<itemIds.size(); ++i) {
562             PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
563             void *oldBuffer = pendingGlyph.buffer;
564             Q_ASSERT(bufferSize.height() >= pendingGlyph.bufferSize.height());
565
566             pendingGlyph.buffer = bufferId;
567             pendingGlyph.position = positions.at(i);
568             pendingGlyph.bufferSize = bufferSize;
569
570             m_sharedGraphicsCache->referenceBuffer(bufferId);
571             if (oldBuffer != 0)
572                 m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
573
574             m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
575         }
576     }
577
578     m_pendingGlyphsCondition.wakeAll();
579     emit glyphsPending();
580 }
581
582 void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId,
583                                                               const QVector<quint32> &itemIds)
584 {
585     {
586         QMutexLocker locker(&m_pendingGlyphsMutex);
587         if (m_cacheId != cacheId)
588             return;
589
590         for (int i=0; i<itemIds.size(); ++i)
591             m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
592     }
593
594     emit glyphsPending();
595 }
596
597
598 void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId,
599                                                           const QVector<quint32> &itemIds)
600 {
601     {
602         QMutexLocker locker(&m_pendingGlyphsMutex);
603         if (m_cacheId != cacheId)
604             return;
605
606 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
607         qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)",
608                cacheId.constData(), itemIds.size());
609 #endif
610
611         for (int i=0; i<itemIds.size(); ++i) {
612             m_pendingMissingGlyphs.insert(itemIds.at(i));
613             m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
614         }
615     }
616
617     m_pendingGlyphsCondition.wakeAll();
618     emit glyphsPending();
619 }
620
621 QT_END_NAMESPACE