e6bd529d5220214fd030710a599ad92339664173
[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 <QtCore/qcoreapplication.h>
54
55 #include <qpa/qplatformsharedgraphicscache.h>
56
57 #include <QtQuick/qquickcanvas.h>
58
59 // #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG
60
61 QT_BEGIN_NAMESPACE
62
63 namespace {
64
65     class QSGInvokeEvent: public QEvent
66     {
67     public:
68         QSGInvokeEvent(QPlatformSharedGraphicsCache *cache,
69                        const QByteArray &cacheId = QByteArray(),
70                        const QVector<quint32> &glyphIds = QVector<quint32>(),
71                        bool inSceneGraphUpdate = false)
72             : QEvent(User)
73             , m_cache(cache)
74             , m_cacheId(cacheId)
75             , m_glyphIds(glyphIds)
76             , m_inSceneGraphUpdate(inSceneGraphUpdate)
77         {}
78
79         bool inSceneGraphUpdate() const { return m_inSceneGraphUpdate; }
80         QPlatformSharedGraphicsCache *cache() const { return m_cache; }
81
82         virtual void invoke() = 0;
83     protected:
84         QPlatformSharedGraphicsCache *m_cache;
85         QByteArray m_cacheId;
86         QVector<quint32> m_glyphIds;
87         bool m_inSceneGraphUpdate;
88     };
89
90     class QSGReleaseItemsEvent: public QSGInvokeEvent
91     {
92     public:
93         QSGReleaseItemsEvent(QPlatformSharedGraphicsCache *cache,
94                              const QByteArray &cacheId,
95                              const QVector<quint32> &glyphIds,
96                              bool inSceneGraphUpdate)
97             : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
98         {
99         }
100
101         void invoke()
102         {
103             m_cache->releaseItems(m_cacheId, m_glyphIds);
104         }
105     };
106
107     class QSGRequestItemsEvent: public QSGInvokeEvent
108     {
109     public:
110         QSGRequestItemsEvent(QPlatformSharedGraphicsCache *cache,
111                              const QByteArray &cacheId,
112                              const QVector<quint32> &glyphIds,
113                              bool inSceneGraphUpdate)
114             : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate)
115         {
116         }
117
118         void invoke()
119         {
120             m_cache->requestItems(m_cacheId, m_glyphIds);
121         }
122     };
123
124     class QSGInsertItemsEvent: public QSGInvokeEvent
125     {
126     public:
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)
133             , m_images(images)
134         {
135         }
136
137         void invoke()
138         {
139             m_cache->insertItems(m_cacheId, m_glyphIds, m_images);
140         }
141
142     private:
143         QVector<QImage> m_images;
144     };
145
146     class QSGEndRequestBatchEvent: public QSGInvokeEvent
147     {
148     public:
149         QSGEndRequestBatchEvent(QPlatformSharedGraphicsCache *cache)
150             : QSGInvokeEvent(cache)
151         {
152         }
153
154         void invoke()
155         {
156             if (m_cache->requestBatchStarted())
157                 m_cache->endRequestBatch();
158         }
159     };
160
161     class QSGMainThreadInvoker: public QObject
162     {
163     public:
164         bool event(QEvent *e)
165         {
166             if (e->type() == QEvent::User) {
167                 Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
168
169                 QSGInvokeEvent *invokeEvent = static_cast<QSGInvokeEvent *>(e);
170                 if (invokeEvent->inSceneGraphUpdate()) {
171                     QPlatformSharedGraphicsCache *cache = invokeEvent->cache();
172                     if (!cache->requestBatchStarted())
173                         cache->beginRequestBatch();
174                 }
175
176                 static_cast<QSGInvokeEvent *>(e)->invoke();
177                 return true;
178             }
179             return QObject::event(e);
180         }
181
182         static QSGMainThreadInvoker *instance()
183         {
184             if (m_invoker == 0) {
185                 m_invoker = new QSGMainThreadInvoker;
186                 m_invoker->moveToThread(QCoreApplication::instance()->thread());
187             }
188
189             return m_invoker;
190         }
191
192     private:
193         static QSGMainThreadInvoker *m_invoker;
194     };
195
196     QSGMainThreadInvoker* QSGMainThreadInvoker::m_invoker = 0;
197 }
198
199 QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
200                                                                    QPlatformSharedGraphicsCache *sharedGraphicsCache,
201                                                                    QSGDistanceFieldGlyphCacheManager *man,
202                                                                    QOpenGLContext *c,
203                                                                    const QRawFont &font)
204     : QSGDistanceFieldGlyphCache(man, c, font)
205     , m_cacheId(cacheId)
206     , m_sharedGraphicsCache(sharedGraphicsCache)
207     , m_isInSceneGraphUpdate(false)
208     , m_hasPostedEvents(false)
209 {
210 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
211     qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p",
212            cacheId.constData(), QThread::currentThreadId());
213 #endif
214
215     Q_ASSERT(sizeof(glyph_t) == sizeof(quint32));
216     Q_ASSERT(sharedGraphicsCache != 0);
217
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);
230
231     QQuickCanvas *canvas = static_cast<QQuickCanvas *>(c->surface());
232     Q_ASSERT(canvas != 0);
233
234     connect(canvas, SIGNAL(beforeSynchronizing()), this, SLOT(sceneGraphUpdateStarted()),
235             Qt::DirectConnection);
236     connect(canvas, SIGNAL(beforeRendering()), this, SLOT(sceneGraphUpdateDone()),
237             Qt::DirectConnection);
238 }
239
240 QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache()
241 {
242     {
243         QHash<glyph_t, void *>::const_iterator it = m_bufferForGlyph.constBegin();
244         while (it != m_bufferForGlyph.constEnd()) {
245             m_sharedGraphicsCache->dereferenceBuffer(it.value());
246             ++it;
247         }
248     }
249
250     {
251         QHash<quint32, PendingGlyph>::const_iterator it = m_pendingReadyGlyphs.constBegin();
252         while (it != m_pendingReadyGlyphs.constEnd()) {
253             m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer);
254             ++it;
255         }
256     }
257 }
258
259 void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
260 {
261     QMutexLocker locker(&m_pendingGlyphsMutex);
262
263 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
264     qDebug("QSGSharedDistanceFieldGlyphCache::requestGlyphs() called for %s (%d glyphs)",
265            m_cacheId.constData(), glyphs.size());
266 #endif
267
268     m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs);
269     m_requestedGlyphs.unite(glyphs);
270
271     QVector<quint32> glyphsVector;
272     glyphsVector.reserve(glyphs.size());
273
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);
278     }
279
280     m_hasPostedEvents = true;
281     QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
282     QCoreApplication::postEvent(invoker, new QSGRequestItemsEvent(m_sharedGraphicsCache,
283                                                                   m_cacheId,
284                                                                   glyphsVector,
285                                                                   m_isInSceneGraphUpdate));
286 }
287
288 void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
289 {
290     Q_ASSERT(!m_isInSceneGraphUpdate);
291     if (m_isInSceneGraphUpdate) {
292         qWarning("QSGSharedDistanceFieldGlyphCache::waitForGlyphs: Called from inside "
293                  "scenegraph update. Will freeze.");
294     }
295
296     {
297         QMutexLocker locker(&m_pendingGlyphsMutex);
298         while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
299             m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex);
300     }
301 }
302
303 void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
304 {
305     {
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());
310 #endif
311
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();
316         int i=0;
317         while (it != glyphs.constEnd()) {
318             m_requestedGlyphsThatHaveNotBeenReturned.insert(it.key());
319             glyphIds[i] = it.key();
320             images[i] = it.value();
321
322             ++it; ++i;
323         }
324
325         m_hasPostedEvents = true;
326         QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
327         QCoreApplication::postEvent(invoker, new QSGInsertItemsEvent(m_sharedGraphicsCache,
328                                                                      m_cacheId,
329                                                                      glyphIds,
330                                                                      images,
331                                                                      m_isInSceneGraphUpdate));
332     }
333
334     processPendingGlyphs();
335 }
336
337 void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
338 {
339     Q_UNUSED(glyphs);
340
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.
343 }
344
345 void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
346 {
347 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
348     qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)",
349            m_cacheId.constData(), glyphs.size());
350 #endif
351
352     m_requestedGlyphs.subtract(glyphs);
353
354     QVector<quint32> glyphsVector;
355     glyphsVector.reserve(glyphs.size());
356
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));
365
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);
371                     ++bufferIt;
372                 }
373 #endif
374             }
375         }
376
377         glyphsVector.append(*glyphsIt);
378     }
379
380     m_hasPostedEvents = true;
381     QSGMainThreadInvoker *mainThreadInvoker = QSGMainThreadInvoker::instance();
382     QCoreApplication::postEvent(mainThreadInvoker, new QSGReleaseItemsEvent(m_sharedGraphicsCache,
383                                                                             m_cacheId,
384                                                                             glyphsVector,
385                                                                             m_isInSceneGraphUpdate));
386 }
387
388 void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
389 {
390     Owner &owner = m_registeredOwners[ownerElement];
391     if (owner.ref == 0) {
392         owner.item = ownerElement;
393
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");
396         Q_UNUSED(ok);
397     }
398     ++owner.ref;
399 }
400
401 void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
402 {
403     QHash<QQuickItem *, Owner>::iterator it = m_registeredOwners.find(ownerElement);
404     if (it != m_registeredOwners.end() && --it->ref <= 0) {
405         if (it->item)
406             disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
407         m_registeredOwners.erase(it);
408     }
409 }
410
411 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
412 #  include <QtOpenGL/private/qglextensions_p.h>
413
414 void QSGSharedDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height)
415 {
416     GLuint fboId;
417     glGenFramebuffers(1, &fboId);
418
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);
428
429     glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
430     glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
431                            tmpTexture, 0);
432
433     glActiveTexture(GL_TEXTURE0);
434     glBindTexture(GL_TEXTURE_2D, textureId);
435
436     glDisable(GL_STENCIL_TEST);
437     glDisable(GL_DEPTH_TEST);
438     glDisable(GL_SCISSOR_TEST);
439     glDisable(GL_BLEND);
440
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;
450
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;
460
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);
464
465     {
466         static const char *vertexShaderSource =
467                 "attribute highp   vec4      vertexCoordsArray; \n"
468                 "attribute highp   vec2      textureCoordArray; \n"
469                 "varying   highp   vec2      textureCoords;     \n"
470                 "void main(void) \n"
471                 "{ \n"
472                 "    gl_Position = vertexCoordsArray;   \n"
473                 "    textureCoords = textureCoordArray; \n"
474                 "} \n";
475
476         static const char *fragmentShaderSource =
477                 "varying   highp   vec2      textureCoords; \n"
478                 "uniform   sampler2D         texture;       \n"
479                 "void main() \n"
480                 "{ \n"
481                 "    gl_FragColor = texture2D(texture, textureCoords); \n"
482                 "} \n";
483
484         GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
485         GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
486
487         if (vertexShader == 0 || fragmentShader == 0) {
488             GLenum error = glGetError();
489             qWarning("SharedGraphicsCacheServer::setupShaderPrograms: Failed to create shaders. (GL error: %x)",
490                      error);
491             return;
492         }
493
494         glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
495         glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
496         glCompileShader(vertexShader);
497
498         GLint len = 1;
499         glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
500
501         char infoLog[2048];
502         glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
503         if (qstrlen(infoLog) > 0) {
504             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling vertex shader:\n %s",
505                      infoLog);
506             //return;
507         }
508
509         glCompileShader(fragmentShader);
510         glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
511         if (qstrlen(infoLog) > 0) {
512             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling fragent shader:\n %s",
513                      infoLog);
514             //return;
515         }
516
517         GLuint shaderProgram = glCreateProgram();
518         glAttachShader(shaderProgram, vertexShader);
519         glAttachShader(shaderProgram, fragmentShader);
520
521         glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
522         glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
523
524         glLinkProgram(shaderProgram);
525         glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
526         if (qstrlen(infoLog) > 0) {
527             qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems linking shaders:\n %s",
528                      infoLog);
529             //return;
530         }
531
532         glUseProgram(shaderProgram);
533         glEnableVertexAttribArray(0);
534         glEnableVertexAttribArray(1);
535
536         int textureUniformLocation = glGetUniformLocation(shaderProgram, "texture");
537         glUniform1i(textureUniformLocation, 0);
538     }
539
540     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
541
542     {
543         GLenum error = glGetError();
544         if (error != GL_NO_ERROR) {
545             qWarning("SharedGraphicsCacheServer::readBackBuffer: glDrawArrays reported error 0x%x",
546                      error);
547         }
548     }
549
550     uchar *data = new uchar[width * height * 4];
551
552     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
553
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]);
558
559     QByteArray fileName = m_cacheId + ' ' + QByteArray::number(textureId);
560     fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
561     image.save(QString::fromLocal8Bit(fileName));
562
563     {
564         GLenum error = glGetError();
565         if (error != GL_NO_ERROR) {
566             qWarning("SharedGraphicsCacheServer::readBackBuffer: glReadPixels reported error 0x%x",
567                      error);
568         }
569     }
570
571     glDisableVertexAttribArray(0);
572     glDisableVertexAttribArray(1);
573
574     glDeleteFramebuffers(1, &fboId);
575     glDeleteTextures(1, &tmpTexture);
576
577     delete[] data;
578 }
579 #endif
580
581 namespace {
582     struct TextureContent {
583         QSize size;
584         QVector<glyph_t> glyphs;
585     };
586 }
587
588 void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
589 {
590     Q_ASSERT(QThread::currentThread() == thread());
591
592     waitForGlyphs();
593
594     {
595         QMutexLocker locker(&m_pendingGlyphsMutex);
596         if (m_pendingMissingGlyphs.isEmpty()
597             && m_pendingReadyGlyphs.isEmpty()
598             && m_pendingInvalidatedGlyphs.isEmpty()) {
599             return;
600         }
601
602         {
603             QVector<glyph_t> pendingMissingGlyphs;
604             pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size());
605
606             QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin();
607             while (it != m_pendingMissingGlyphs.constEnd()) {
608                 pendingMissingGlyphs.append(*it);
609                 ++it;
610             }
611
612             markGlyphsToRender(pendingMissingGlyphs);
613         }
614
615         {
616             QVector<glyph_t> filteredPendingInvalidatedGlyphs;
617             filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size());
618
619             QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin();
620             while (it != m_pendingInvalidatedGlyphs.constEnd()) {
621                 bool rerequestGlyph = false;
622
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;
630                 }
631
632                 void *bufferId = m_bufferForGlyph.value(*it, 0);
633                 if (bufferId != 0) {
634                     m_sharedGraphicsCache->dereferenceBuffer(bufferId);
635                     m_bufferForGlyph.remove(*it);
636                     rerequestGlyph = true;
637                 }
638
639                 if (rerequestGlyph)
640                     filteredPendingInvalidatedGlyphs.append(*it);
641
642                 ++it;
643             }
644
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);
650             }
651         }
652
653         {
654             QList<GlyphPosition> glyphPositions;
655
656             QHash<void *, TextureContent> textureContentForBuffer;
657             {
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));
664                         }
665                     }
666
667                     PendingGlyph &pendingGlyph  = it.value();
668
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;
672
673                     textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize;
674                     textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key());
675
676                     GlyphPosition glyphPosition;
677                     glyphPosition.glyph = it.key();
678                     glyphPosition.position = pendingGlyph.position;
679
680                     glyphPositions.append(glyphPosition);
681
682                     ++it;
683                 }
684             }
685
686             setGlyphsPosition(glyphPositions);
687
688             {
689                 QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin();
690                 while (it != textureContentForBuffer.constEnd()) {
691                     Texture texture;
692                     texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
693                     texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key());
694
695 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
696                     saveTexture(texture.textureId, texture.size.width(), texture.size.height());
697 #endif
698                     setGlyphsTexture(it.value().glyphs, texture);
699
700                     ++it;
701                 }
702             }
703         }
704
705         m_pendingMissingGlyphs.clear();
706         m_pendingInvalidatedGlyphs.clear();
707         m_pendingReadyGlyphs.clear();
708     }
709 }
710
711 void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
712                                                             void *bufferId,
713                                                             const QVector<quint32> &itemIds,
714                                                             const QVector<QPoint> &positions)
715 {
716     bool requestedItemsInList = false;
717     {
718         QMutexLocker locker(&m_pendingGlyphsMutex);
719         if (m_cacheId != cacheId)
720             return;
721
722 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
723             qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)",
724                    cacheId.constData(), itemIds.size());
725 #endif
726
727         for (int i=0; i<itemIds.size(); ++i) {
728             if (m_requestedGlyphsThatHaveNotBeenReturned.contains(itemIds.at(i))) {
729                 requestedItemsInList = true;
730                 break;
731             }
732         }
733     }
734
735     if (requestedItemsInList)
736         reportItemsUpdated(cacheId, bufferId,itemIds, positions);
737 }
738
739 void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId,
740                                                           void *bufferId,
741                                                           const QVector<quint32> &itemIds,
742                                                           const QVector<QPoint> &positions)
743 {
744     {
745         QMutexLocker locker(&m_pendingGlyphsMutex);
746         if (m_cacheId != cacheId)
747             return;
748
749         Q_ASSERT(itemIds.size() == positions.size());
750
751 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
752         qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)",
753                cacheId.constData(), itemIds.size());
754 #endif
755
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;
760
761                 pendingGlyph.buffer = bufferId;
762                 pendingGlyph.position = positions.at(i);
763
764                 m_sharedGraphicsCache->referenceBuffer(bufferId);
765                 if (oldBuffer != 0)
766                     m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
767
768                 m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
769             }
770         }
771     }
772
773     m_pendingGlyphsCondition.wakeAll();
774     emit glyphsPending();
775 }
776
777 void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId,
778                                                               const QVector<quint32> &itemIds)
779 {
780     {
781         QMutexLocker locker(&m_pendingGlyphsMutex);
782         if (m_cacheId != cacheId)
783             return;
784
785         for (int i=0; i<itemIds.size(); ++i) {
786             if (m_requestedGlyphs.contains(itemIds.at(i)))
787                 m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
788         }
789     }
790
791     emit glyphsPending();
792 }
793
794
795 void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId,
796                                                           const QVector<quint32> &itemIds)
797 {
798     {
799         QMutexLocker locker(&m_pendingGlyphsMutex);
800         if (m_cacheId != cacheId)
801             return;
802
803 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
804         qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)",
805                cacheId.constData(), itemIds.size());
806 #endif
807
808         for (int i=0; i<itemIds.size(); ++i) {
809             if (m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i)))
810                 m_pendingMissingGlyphs.insert(itemIds.at(i));
811         }
812     }
813
814     m_pendingGlyphsCondition.wakeAll();
815     emit glyphsPending();
816 }
817
818 void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateStarted()
819 {
820     m_isInSceneGraphUpdate = true;
821     m_hasPostedEvents = false;
822 }
823
824 void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateDone()
825 {
826     m_isInSceneGraphUpdate = false;
827
828     if (m_hasPostedEvents) {
829         QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance();
830         QCoreApplication::postEvent(invoker, new QSGEndRequestBatchEvent(m_sharedGraphicsCache));
831         m_hasPostedEvents = false;
832     }
833 }
834
835 QT_END_NAMESPACE