summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Wilson <alex.wilson@nokia.com>2012-03-13 17:15:21 +1000
committerQt by Nokia <qt-info@nokia.com>2012-03-14 07:55:50 +0100
commite071be7ff80373b3f4fb523393c9875a8c00e0af (patch)
treec0a9aaaa202077e1a0510424044c3ce4576be15e
parentea2962c23255424e02a446efcea078711f1bcd6a (diff)
downloadqtlocation-e071be7ff80373b3f4fb523393c9875a8c00e0af.tar.gz
New 3Q cache for map tiles, cleanup texture/tile resource management
Replaces the old use of QCache in QGeoTileCache with a new QCache3Q, which is more optimal for many tile workloads. Resource management for textures and tile data is now enforced and managed correctly across threads. Textures are passed around as QSharedPointers to guarantee that cache eviction policy will not interfere with the correctness of rendering. Change-Id: I93eb49ea3ad009d85f394f92c59a4b22962b88a7 Reviewed-by: Alex Wilson <alex.wilson@nokia.com>
-rw-r--r--src/location/maps/maps.pri4
-rw-r--r--src/location/maps/qcache3q_p.h431
-rw-r--r--src/location/maps/qgeocameratiles.cpp6
-rw-r--r--src/location/maps/qgeocameratiles_p.h2
-rw-r--r--src/location/maps/qgeomapgeometry.cpp164
-rw-r--r--src/location/maps/qgeomapgeometry_p.h3
-rw-r--r--src/location/maps/qgeomapimages.cpp21
-rw-r--r--src/location/maps/qgeomapimages_p.h5
-rw-r--r--src/location/maps/qgeotilecache.cpp225
-rw-r--r--src/location/maps/qgeotilecache_p.h72
-rw-r--r--src/location/maps/qgeotiledmapdata.cpp38
-rw-r--r--src/location/maps/qgeotiledmappingmanagerengine.cpp18
-rw-r--r--src/location/maps/qgeotiledmappingmanagerengine.h1
-rw-r--r--src/location/maps/qgeotilefetcher.cpp10
-rw-r--r--src/location/maps/qgeotilefetcher.h2
-rw-r--r--src/location/maps/qgeotilefetcher_p.h3
16 files changed, 796 insertions, 209 deletions
diff --git a/src/location/maps/maps.pri b/src/location/maps/maps.pri
index 22efb353..42086e38 100644
--- a/src/location/maps/maps.pri
+++ b/src/location/maps/maps.pri
@@ -58,7 +58,8 @@ PRIVATE_HEADERS += \
maps/qgeoserviceprovider_p.h \
maps/qgeotilecache_p.h \
maps/qgeotiledmapreply_p.h \
- maps/qgeotilespec_p.h
+ maps/qgeotilespec_p.h \
+ maps/qcache3q_p.h
SOURCES += \
maps/qdoublevector2d.cpp \
@@ -94,3 +95,4 @@ SOURCES += \
maps/qgeotilecache.cpp \
maps/qgeotiledmapreply.cpp \
maps/qgeotilespec.cpp
+
diff --git a/src/location/maps/qcache3q_p.h b/src/location/maps/qcache3q_p.h
new file mode 100644
index 00000000..c8f025d3
--- /dev/null
+++ b/src/location/maps/qcache3q_p.h
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCACHE3Q_H
+#define QCACHE3Q_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include <QtCore/qlinkedlist.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qcache.h>
+#include <QtCore/qsharedpointer.h>
+#include <QDebug>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+template <class Key, class T>
+class QCache3QDefaultEvictionPolicy
+{
+protected:
+ /* called just before a key/value pair is about to be _evicted_ */
+ void aboutToBeEvicted(const Key &key, QSharedPointer<T> obj);
+ /* called just before a key/value pair is about to be removed, by
+ * clear(), remove() or by the destructor (which calls clear) */
+ void aboutToBeRemoved(const Key &key, QSharedPointer<T> obj);
+};
+
+template <class Key, class T>
+void QCache3QDefaultEvictionPolicy<Key,T>::aboutToBeEvicted(const Key &key, QSharedPointer<T> obj)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(obj);
+}
+
+template <class Key, class T>
+void QCache3QDefaultEvictionPolicy<Key,T>::aboutToBeRemoved(const Key &key, QSharedPointer<T> obj)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(obj);
+}
+
+/*
+ * QCache3Q
+ *
+ * A cache template class for managing QSharedPointers to objects with an
+ * associated key. It's a lot like QCache, but uses an alternative algorithm
+ * '3Q' -- which is the '2Q' algorithm plus an extra queue for previously popular
+ * but evicted nodes, and a 'ghost' list of recent evictions to make a better
+ * placement choice if they are requested again.
+ *
+ * New nodes enter the cache on the "newbies" queue, which is evicted LRA
+ * (least-recently-added). If a newbie is popular enough (it has been requested
+ * more than promoteAt times), it will be promoted to a "regular". Regulars
+ * are evicted LRU (least-recently-used). If a regular is under consideration
+ * for eviction, its popularity is compared to the mean popularity of the whole
+ * regulars queue. If it is greater, it is instead moved to the "hobos" queue.
+ * The "hobos" queue is also evicted LRU, but has a maximum size constraint
+ * so eviction from it is less likely than from the regulars.
+ *
+ * Tweakables:
+ * * maxCost = maximum total cost for the whole cache
+ * * minRecent = minimum size that q1 ("newbies") has to be before eviction
+ * from it takes place
+ * * maxOldPopular = maximum size that q3 ("hobos") can reach before eviction
+ * from it takes place
+ * * promoteAt = minimum popularity necessary to promote a node from
+ * "newbie" to "regular"
+ */
+template <class Key, class T, class EvPolicy = QCache3QDefaultEvictionPolicy<Key,T> >
+class QCache3Q : public EvPolicy
+{
+private:
+ class Queue;
+ class Node {
+ public:
+ inline explicit Node() : q(0), n(0), p(0), pop(0), cost(0) {}
+
+ Queue *q;
+ Node *n;
+ Node *p;
+ Key k;
+ QSharedPointer<T> v;
+ quint64 pop; // popularity, incremented each ping
+ int cost;
+ };
+
+ class Queue {
+ public:
+ inline explicit Queue() : f(0), l(0), cost(0), pop(0), size(0) {}
+
+ Node *f;
+ Node *l;
+ int cost; // total cost of nodes on the queue
+ quint64 pop; // sum of popularity values on the queue
+ int size; // size of the queue
+ };
+
+ Queue *q1_; // "newbies": seen only once, evicted LRA (least-recently-added)
+ Queue *q2_; // regular nodes, promoted from newbies, evicted LRU
+ Queue *q3_; // "hobos": evicted from q2 but were very popular (above mean)
+ Queue *q1_evicted_; // ghosts of recently evicted newbies and regulars
+ QHash<Key, Node*> lookup_;
+
+public:
+ explicit QCache3Q(int maxCost = 100, int minRecent = -1, int maxOldPopular = -1);
+ inline ~QCache3Q() { clear(); delete q1_; delete q2_; delete q3_; }
+
+ inline int maxCost() const { return maxCost_; }
+ void setMaxCost(int maxCost, int minRecent=-1, int maxOldPopular=-1);
+
+ inline int promoteAt() const { return promote_; }
+ inline void setPromoteAt(int p) { promote_ = p; }
+
+ inline int totalCost() const { return q1_->cost + q2_->cost + q3_->cost; }
+
+ void clear();
+ bool insert(const Key &key, QSharedPointer<T> object, int cost = 1);
+ QSharedPointer<T> object(const Key &key) const;
+ QSharedPointer<T> operator[](const Key &key) const;
+
+ void remove(const Key &key);
+
+ void printStats();
+
+private:
+ int maxCost_, minRecent_, maxOldPopular_;
+ int hitCount_, missCount_, promote_;
+
+ void rebalance();
+ void unlink(Node *n);
+ void link_front(Node *n, Queue *q);
+};
+
+template <class Key, class T, class EvPolicy>
+void QCache3Q<Key,T,EvPolicy>::printStats()
+{
+ qDebug("\n=== cache %p ===", this);
+ qDebug("hits: %d (%.2f%%)\tmisses: %d\tfill: %.2f%%", hitCount_,
+ 100.0 * float(hitCount_)/(float(hitCount_+missCount_)),
+ missCount_,
+ 100.0 * float(totalCost())/float(maxCost()));
+ qDebug("q1g: size=%d, pop=%llu", q1_evicted_->size, q1_evicted_->pop);
+ qDebug("q1: cost=%d, size=%d, pop=%llu", q1_->cost, q1_->size, q1_->pop);
+ qDebug("q2: cost=%d, size=%d, pop=%llu", q2_->cost, q2_->size, q2_->pop);
+ qDebug("q3: cost=%d, size=%d, pop=%llu", q3_->cost, q3_->size, q3_->pop);
+}
+
+template <class Key, class T, class EvPolicy>
+QCache3Q<Key,T,EvPolicy>::QCache3Q(int maxCost, int minRecent, int maxOldPopular)
+ : q1_(new Queue), q2_(new Queue), q3_(new Queue), q1_evicted_(new Queue),
+ maxCost_(maxCost), minRecent_(minRecent), maxOldPopular_(maxOldPopular),
+ hitCount_(0), missCount_(0), promote_(0)
+{
+ if (minRecent_ < 0)
+ minRecent_ = maxCost_ / 3;
+ if (maxOldPopular_ < 0)
+ maxOldPopular_ = maxCost_ / 5;
+}
+
+template <class Key, class T, class EvPolicy>
+inline void QCache3Q<Key,T,EvPolicy>::setMaxCost(int maxCost, int minRecent, int maxOldPopular)
+{
+ maxCost_ = maxCost;
+ minRecent_ = minRecent;
+ maxOldPopular_ = maxOldPopular;
+ if (minRecent_ < 0)
+ minRecent_ = maxCost_ / 3;
+ if (maxOldPopular_ < 0)
+ maxOldPopular_ = maxCost_ / 5;
+ rebalance();
+}
+
+template <class Key, class T, class EvPolicy>
+bool QCache3Q<Key,T,EvPolicy>::insert(const Key &key, QSharedPointer<T> object, int cost)
+{
+ if (cost > maxCost_) {
+ return false;
+ }
+
+ if (lookup_.contains(key)) {
+ Node *n = lookup_[key];
+ n->v = object;
+ n->q->cost -= n->cost;
+ n->cost = cost;
+ n->q->cost += cost;
+
+ if (n->q == q1_evicted_) {
+ if (n->pop > promote_) {
+ unlink(n);
+ link_front(n, q2_);
+ rebalance();
+ }
+ } else if (n->q != q1_) {
+ Queue *q = n->q;
+ unlink(n);
+ link_front(n, q);
+ rebalance();
+ }
+
+ return true;
+ }
+
+ Node *n = new Node;
+ n->v = object;
+ n->k = key;
+ n->cost = cost;
+ link_front(n, q1_);
+ lookup_[key] = n;
+
+ rebalance();
+
+ return true;
+}
+
+template <class Key, class T, class EvPolicy>
+void QCache3Q<Key,T,EvPolicy>::clear()
+{
+ while (q1_evicted_->f) {
+ Node *n = q1_evicted_->f;
+ unlink(n);
+ delete n;
+ }
+
+ while (q1_->f) {
+ Node *n = q1_->f;
+ unlink(n);
+ EvPolicy::aboutToBeRemoved(n->k, n->v);
+ delete n;
+ }
+
+ while (q2_->f) {
+ Node *n = q2_->f;
+ unlink(n);
+ EvPolicy::aboutToBeRemoved(n->k, n->v);
+ delete n;
+ }
+
+ while (q3_->f) {
+ Node *n = q3_->f;
+ unlink(n);
+ EvPolicy::aboutToBeRemoved(n->k, n->v);
+ delete n;
+ }
+
+ lookup_.clear();
+}
+
+template <class Key, class T, class EvPolicy>
+void QCache3Q<Key,T,EvPolicy>::unlink(Node *n)
+{
+ if (n->n)
+ n->n->p = n->p;
+ if (n->p)
+ n->p->n = n->n;
+ if (n->q->f == n)
+ n->q->f = n->n;
+ if (n->q->l == n)
+ n->q->l = n->p;
+ n->n = 0;
+ n->p = 0;
+ n->q->pop -= n->pop;
+ n->q->cost -= n->cost;
+ n->q->size--;
+ n->q = 0;
+}
+
+template <class Key, class T, class EvPolicy>
+void QCache3Q<Key,T,EvPolicy>::link_front(Node *n, Queue *q)
+{
+ n->n = q->f;
+ n->p = 0;
+ n->q = q;
+ if (q->f)
+ q->f->p = n;
+ q->f = n;
+ if (!q->l)
+ q->l = n;
+
+ q->pop += n->pop;
+ q->cost += n->cost;
+ q->size++;
+}
+
+template <class Key, class T, class EvPolicy>
+void QCache3Q<Key,T,EvPolicy>::rebalance()
+{
+ while (q1_evicted_->size > (q1_->size + q2_->size + q3_->size) * 4) {
+ Node *n = q1_evicted_->l;
+ unlink(n);
+ lookup_.remove(n->k);
+ delete n;
+ }
+
+ while ((q1_->cost + q2_->cost + q3_->cost) > maxCost_) {
+ if (q3_->cost > maxOldPopular_) {
+ Node *n = q3_->l;
+ unlink(n);
+ EvPolicy::aboutToBeEvicted(n->k, n->v);
+ lookup_.remove(n->k);
+ delete n;
+ } else if (q1_->cost > minRecent_) {
+ Node *n = q1_->l;
+ unlink(n);
+ EvPolicy::aboutToBeEvicted(n->k, n->v);
+ n->v.clear();
+ n->cost = 0;
+ link_front(n, q1_evicted_);
+ } else {
+ Node *n = q2_->l;
+ unlink(n);
+ if (n->pop > (q2_->pop / q2_->size)) {
+ link_front(n, q3_);
+ } else {
+ EvPolicy::aboutToBeEvicted(n->k, n->v);
+ n->v.clear();
+ n->cost = 0;
+ link_front(n, q1_evicted_);
+ }
+ }
+ }
+}
+
+template <class Key, class T, class EvPolicy>
+void QCache3Q<Key,T,EvPolicy>::remove(const Key &key)
+{
+ if (!lookup_.contains(key)) {
+ return;
+ }
+ Node *n = lookup_[key];
+ unlink(n);
+ if (n->q != q1_evicted_)
+ EvPolicy::aboutToBeRemoved(n->k, n->v);
+ lookup_.remove(key);
+ delete n;
+}
+
+template <class Key, class T, class EvPolicy>
+QSharedPointer<T> QCache3Q<Key,T,EvPolicy>::object(const Key &key) const
+{
+ if (!lookup_.contains(key)) {
+ const_cast<QCache3Q<Key,T,EvPolicy>*>(this)->missCount_++;
+ return QSharedPointer<T>(0);
+ }
+
+ QCache3Q<Key,T,EvPolicy> *me = const_cast<QCache3Q<Key,T,EvPolicy>*>(this);
+
+ Node *n = me->lookup_[key];
+ n->pop++;
+ n->q->pop++;
+
+ if (n->q == q1_) {
+ me->hitCount_++;
+
+ if (n->pop > (quint64)promote_) {
+ me->unlink(n);
+ me->link_front(n, q2_);
+ me->rebalance();
+ }
+ } else if (n->q != q1_evicted_) {
+ me->hitCount_++;
+
+ Queue *q = n->q;
+ me->unlink(n);
+ me->link_front(n, q);
+ me->rebalance();
+ } else {
+ me->missCount_++;
+ }
+
+ return n->v;
+}
+
+template <class Key, class T, class EvPolicy>
+inline QSharedPointer<T> QCache3Q<Key,T,EvPolicy>::operator[](const Key &key) const
+{
+ return object(key);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCACHE3Q_H
diff --git a/src/location/maps/qgeocameratiles.cpp b/src/location/maps/qgeocameratiles.cpp
index 87eff04d..a6f5b3ec 100644
--- a/src/location/maps/qgeocameratiles.cpp
+++ b/src/location/maps/qgeocameratiles.cpp
@@ -189,6 +189,12 @@ void QGeoCameraTiles::setTileSize(int tileSize)
d->updateGeometry();
}
+int QGeoCameraTiles::tileSize() const
+{
+ Q_D(const QGeoCameraTiles);
+ return d->tileSize_;
+}
+
void QGeoCameraTiles::setMaximumZoomLevel(int maxZoom)
{
Q_D(QGeoCameraTiles);
diff --git a/src/location/maps/qgeocameratiles_p.h b/src/location/maps/qgeocameratiles_p.h
index 2f3bbbc6..5f9c6a1e 100644
--- a/src/location/maps/qgeocameratiles_p.h
+++ b/src/location/maps/qgeocameratiles_p.h
@@ -76,6 +76,8 @@ public:
void setTileSize(int tileSize);
void setMaximumZoomLevel(int maxZoom);
+ int tileSize() const;
+
void setPluginString(const QString &pluginString);
void setMapType(const QGeoMapType &mapType);
diff --git a/src/location/maps/qgeomapgeometry.cpp b/src/location/maps/qgeomapgeometry.cpp
index 94a4eb9b..8d8b518d 100644
--- a/src/location/maps/qgeomapgeometry.cpp
+++ b/src/location/maps/qgeomapgeometry.cpp
@@ -42,6 +42,7 @@
#include "qgeocameradata_p.h"
#include "qgeoprojection_p.h"
+#include "qgeotilecache_p.h"
#include "qgeotilespec.h"
#include "qdoublevector2d_p.h"
@@ -81,11 +82,14 @@ public:
int sideLength_;
QHash<QGeoTileSpec, QGLSceneNode*> nodes_;
+ QHash<QGeoTileSpec, QSharedPointer<QGeoTileTexture> > textures_;
+ QList<QSharedPointer<QGeoTileTexture> > newUploads_;
int minTileX_;
int minTileY_;
int maxTileX_;
int maxTileY_;
+ int tileZ_;
int tileXWrapsBelow_;
double mercatorCenterX_;
@@ -101,14 +105,16 @@ public:
bool useVerticalLock_;
bool verticalLock_;
- void addTile(const QGeoTileSpec &spec, QGLTexture2D *texture);
+ void addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture);
QDoubleVector2D screenPositionToMercator(const QPointF &pos) const;
QPointF mercatorToScreenPosition(const QDoubleVector2D &mercator) const;
void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
- void removeOldTiles(const QSet<QGeoTileSpec> &oldTiles);
- void setTileBounds();
+ void removeTiles(const QSet<QGeoTileSpec> &oldTiles);
+ void updateTiles(const QSet<QGeoTileSpec> &tiles);
+ QGLSceneNode *buildGeometry(const QGeoTileSpec &spec);
+ void setTileBounds(const QSet<QGeoTileSpec> &tiles);
void setupCamera();
void paintGL(QGLPainter *painter);
@@ -154,7 +160,7 @@ void QGeoMapGeometry::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
d->setVisibleTiles(tiles);
}
-void QGeoMapGeometry::addTile(const QGeoTileSpec &spec, QGLTexture2D *texture)
+void QGeoMapGeometry::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
{
Q_D(QGeoMapGeometry);
d->addTile(spec, texture);
@@ -270,7 +276,7 @@ QPointF QGeoMapGeometryPrivate::mercatorToScreenPosition(const QDoubleVector2D &
return QPointF(x + screenOffsetX_, y + screenOffsetY_);
}
-void QGeoMapGeometryPrivate::addTile(const QGeoTileSpec &spec, QGLTexture2D *texture)
+QGLSceneNode *QGeoMapGeometryPrivate::buildGeometry(const QGeoTileSpec &spec)
{
int x = spec.x();
@@ -280,80 +286,128 @@ void QGeoMapGeometryPrivate::addTile(const QGeoTileSpec &spec, QGLTexture2D *tex
if ((x < minTileX_)
|| (maxTileX_ < x)
|| (spec.y() < minTileY_)
- || (maxTileY_ < spec.y())) {
- return;
+ || (maxTileY_ < spec.y())
+ || (spec.zoom() != tileZ_)) {
+ return 0;
}
- QGLSceneNode *node = nodes_.value(spec, 0);
- if (!node) {
+ double edge = scaleFactor_ * tileSize_;
- double edge = scaleFactor_ * tileSize_;
+ QGLBuilder builder;
- QGLBuilder builder;
+ double x1 = (x - minTileX_);
+ double x2 = x1 + 1.0;
- double x1 = (x - minTileX_);
- double x2 = x1 + 1.0;
+ double y1 = (minTileY_ - spec.y());
+ double y2 = y1 - 1.0;
- double y1 = (minTileY_ - spec.y());
- double y2 = y1 - 1.0;
+ x1 *= edge;
+ x2 *= edge;
+ y1 *= edge;
+ y2 *= edge;
- x1 *= edge;
- x2 *= edge;
- y1 *= edge;
- y2 *= edge;
+ QGeometryData g;
- QGeometryData g;
+ QDoubleVector3D n = QDoubleVector3D(0, 0, 1);
- QDoubleVector3D n = QDoubleVector3D(0, 0, 1);
+ g.appendVertex(QVector3D(x1, y1, 0.0));
+ g.appendNormal(n);
+ g.appendTexCoord(QVector2D(0.0, 1.0));
- g.appendVertex(QVector3D(x1, y1, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(0.0, 1.0));
+ g.appendVertex(QVector3D(x1, y2, 0.0));
+ g.appendNormal(n);
+ g.appendTexCoord(QVector2D(0.0, 0.0));
- g.appendVertex(QVector3D(x1, y2, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(0.0, 0.0));
+ g.appendVertex(QVector3D(x2, y2, 0.0));
+ g.appendNormal(n);
+ g.appendTexCoord(QVector2D(1.0, 0.0));
- g.appendVertex(QVector3D(x2, y2, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(1.0, 0.0));
+ g.appendVertex(QVector3D(x2, y1, 0.0));
+ g.appendNormal(n);
+ g.appendTexCoord(QVector2D(1.0, 1.0));
- g.appendVertex(QVector3D(x2, y1, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(1.0, 1.0));
+ builder.addQuads(g);
- builder.addQuads(g);
+ return builder.finalizedSceneNode();
+}
- node = builder.finalizedSceneNode();
+void QGeoMapGeometryPrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
+{
+ QGLSceneNode *node = nodes_.value(spec, 0);
+ if (!node) {
+ node = buildGeometry(spec);
+ if (!node)
+ return;
QGLMaterial *mat = new QGLMaterial(node);
- mat->setTexture(texture);
+ mat->setTexture(texture->texture);
node->setEffect(QGL::LitDecalTexture2D);
node->setMaterial(mat);
sceneNode_->addNode(node);
nodes_.insert(spec, node);
+ textures_.insert(spec, texture);
+ newUploads_ << texture;
+
} else {
// TODO handle texture updates when we make node removal more efficient
- node->material()->setTexture(texture);
+ if (textures_[spec].data() != texture.data()) {
+ textures_.insert(spec, texture);
+ node->material()->setTexture(texture->texture);
+ newUploads_ << texture;
+ }
}
}
void QGeoMapGeometryPrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
{
- // TODO make this more efficient
- removeOldTiles(visibleTiles_);
-
- visibleTiles_ = tiles;
-
// work out the tile bounds for the new geometry
- setTileBounds();
+ setTileBounds(tiles);
// set up the gl camera for the new geometry
setupCamera();
+
+ QSet<QGeoTileSpec> toRemove = visibleTiles_ - tiles;
+ QSet<QGeoTileSpec> toUpdate = visibleTiles_ - toRemove;
+ if (!toRemove.isEmpty())
+ removeTiles(toRemove);
+ if (!toUpdate.isEmpty())
+ updateTiles(toUpdate);
+
+ visibleTiles_ = tiles;
}
-void QGeoMapGeometryPrivate::removeOldTiles(const QSet<QGeoTileSpec> &oldTiles)
+void QGeoMapGeometryPrivate::updateTiles(const QSet<QGeoTileSpec> &tiles)
+{
+ typedef QSet<QGeoTileSpec>::const_iterator iter;
+ iter i = tiles.constBegin();
+ iter end = tiles.constEnd();
+ for (; i != end; ++i) {
+ QGeoTileSpec tile = *i;
+ QGLSceneNode *node = nodes_.value(tile, 0);
+
+ if (node) {
+ sceneNode_->removeNode(node);
+ // TODO: re-use more of the geometry calculations?
+ QGLSceneNode *newNode = buildGeometry(tile);
+
+ if (newNode) {
+ QGLMaterial *mat = new QGLMaterial(newNode);
+ mat->setTexture(textures_[tile]->texture);
+ newNode->setEffect(QGL::LitDecalTexture2D);
+ newNode->setMaterial(mat);
+ sceneNode_->addNode(newNode);
+ nodes_.insert(tile, newNode);
+ } else {
+ nodes_.remove(tile);
+ textures_.remove(tile);
+ }
+ delete node;
+ }
+ }
+}
+
+void QGeoMapGeometryPrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
{
typedef QSet<QGeoTileSpec>::const_iterator iter;
iter i = oldTiles.constBegin();
@@ -366,25 +420,28 @@ void QGeoMapGeometryPrivate::removeOldTiles(const QSet<QGeoTileSpec> &oldTiles)
// TODO protect with mutex?
sceneNode_->removeNode(node);
nodes_.remove(tile);
+ textures_.remove(tile);
delete node;
- // TODO handle deleting of node elsewhere?
}
}
}
-void QGeoMapGeometryPrivate::setTileBounds()
+void QGeoMapGeometryPrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
{
- if (visibleTiles_.isEmpty()) {
+ if (tiles.isEmpty()) {
minTileX_ = -1;
minTileY_ = -1;
maxTileX_ = -1;
maxTileY_ = -1;
+ tileZ_ = -1;
return;
}
typedef QSet<QGeoTileSpec>::const_iterator iter;
- iter i = visibleTiles_.constBegin();
- iter end = visibleTiles_.constEnd();
+ iter i = tiles.constBegin();
+ iter end = tiles.constEnd();
+
+ tileZ_ = i->zoom();
bool hasFarLeft = false;
bool hasFarRight = false;
@@ -414,7 +471,7 @@ void QGeoMapGeometryPrivate::setTileBounds()
}
}
- i = visibleTiles_.constBegin();
+ i = tiles.constBegin();
QGeoTileSpec tile = *i;
@@ -551,6 +608,13 @@ void QGeoMapGeometryPrivate::paintGL(QGLPainter *painter)
// TODO add a shortcut here for when we don't need to repeat and clip the map
+ // do any pending upload/releases
+ while (!newUploads_.isEmpty()) {
+ newUploads_.front()->texture->bind();
+ newUploads_.front()->texture->clearImage();
+ newUploads_.pop_front();
+ }
+
glEnable(GL_SCISSOR_TEST);
painter->setScissor(QRect(screenOffsetX_, screenOffsetY_, screenWidth_, screenHeight_));
diff --git a/src/location/maps/qgeomapgeometry_p.h b/src/location/maps/qgeomapgeometry_p.h
index 57e15291..e8861e92 100644
--- a/src/location/maps/qgeomapgeometry_p.h
+++ b/src/location/maps/qgeomapgeometry_p.h
@@ -68,6 +68,7 @@ class QGLSceneNode;
class QGLCamera;
class QGLPainter;
class QGLTexture2D;
+class QGeoTileTexture;
class QPointF;
@@ -85,7 +86,7 @@ public:
void setUseVerticalLock(bool lock);
- void addTile(const QGeoTileSpec &spec, QGLTexture2D *texture);
+ void addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture);
QDoubleVector2D screenPositionToMercator(const QPointF &pos) const;
QPointF mercatorToScreenPosition(const QDoubleVector2D &mercator) const;
diff --git a/src/location/maps/qgeomapimages.cpp b/src/location/maps/qgeomapimages.cpp
index 91e1d0f3..3f38a898 100644
--- a/src/location/maps/qgeomapimages.cpp
+++ b/src/location/maps/qgeomapimages.cpp
@@ -60,7 +60,7 @@ public:
void tileFetched(const QGeoTileSpec &tile);
QSet<QGeoTileSpec> visible_;
- QSet<QGeoTileSpec> cached_;
+ QList<QSharedPointer<QGeoTileTexture> > cachedTex_;
QSet<QGeoTileSpec> requested_;
};
@@ -78,10 +78,10 @@ void QGeoMapImages::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
d->setVisibleTiles(tiles);
}
-QSet<QGeoTileSpec> QGeoMapImages::cachedTiles() const
+QList<QSharedPointer<QGeoTileTexture> > QGeoMapImages::cachedTiles() const
{
Q_D(const QGeoMapImages);
- return d->cached_;
+ return d->cachedTex_;
}
void QGeoMapImages::tileFetched(const QGeoTileSpec &tile)
@@ -102,22 +102,27 @@ void QGeoMapImagesPrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
{
QSet<QGeoTileSpec> cancelTiles = requested_ - tiles;
QSet<QGeoTileSpec> requestTiles = tiles - visible_ - requested_;
+ QSet<QGeoTileSpec> cached;
- cached_.clear();
+ typedef QSet<QGeoTileSpec>::const_iterator iter;
+
+ cachedTex_.clear();
// remove tiles in cache from request tiles
if (cache_) {
- typedef QSet<QGeoTileSpec>::const_iterator iter;
iter i = requestTiles.constBegin();
iter end = requestTiles.constEnd();
for (; i != end; ++i) {
QGeoTileSpec tile = *i;
- if (cache_->contains(tile))
- cached_.insert(tile);
+ QSharedPointer<QGeoTileTexture> tex = cache_->get(tile);
+ if (tex) {
+ cachedTex_ << tex;
+ cached.insert(tile);
+ }
}
}
- requestTiles -= cached_;
+ requestTiles -= cached;
visible_ = tiles;
diff --git a/src/location/maps/qgeomapimages_p.h b/src/location/maps/qgeomapimages_p.h
index fed625e1..e616d50c 100644
--- a/src/location/maps/qgeomapimages_p.h
+++ b/src/location/maps/qgeomapimages_p.h
@@ -53,6 +53,8 @@
//
#include <QSet>
+#include <QList>
+#include <QSharedPointer>
QT_BEGIN_NAMESPACE
@@ -60,6 +62,7 @@ class QGeoTiledMapData;
class QGeoTiledMappingManagerEngine;
class QGeoTileSpec;
class QGeoTileCache;
+class QGeoTileTexture;
class QGeoMapImagesPrivate;
@@ -70,7 +73,7 @@ public:
~QGeoMapImages();
void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
- QSet<QGeoTileSpec> cachedTiles() const;
+ QList<QSharedPointer<QGeoTileTexture> > cachedTiles() const;
void tileFetched(const QGeoTileSpec &tile);
diff --git a/src/location/maps/qgeotilecache.cpp b/src/location/maps/qgeotilecache.cpp
index e689013c..12910541 100644
--- a/src/location/maps/qgeotilecache.cpp
+++ b/src/location/maps/qgeotilecache.cpp
@@ -56,55 +56,54 @@ Q_DECLARE_METATYPE(QSet<QGeoTileSpec>)
QT_BEGIN_NAMESPACE
-class TileDisk
+class QGeoCachedTileMemory
{
public:
- ~TileDisk()
+ ~QGeoCachedTileMemory()
{
-// qWarning() << "evicting (disk) " << spec;
-// cache->evictFromDiskCache(this);
+ if (cache)
+ cache->evictFromMemoryCache(this);
}
QGeoTileSpec spec;
- QString filename;
QGeoTileCache *cache;
+ QByteArray bytes;
+ QString format;
};
-class TileMemory
+void QCache3QTileEvictionPolicy::aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj)
{
-public:
- ~TileMemory()
- {
-// qWarning() << "evicting (memory) " << spec;
- cache->evictFromMemoryCache(this);
- }
+ Q_UNUSED(key);
+ // set the cache pointer to zero so we can't call evictFromDiskCache
+ obj->cache = 0;
+}
- QGeoTileSpec spec;
- QPixmap pixmap;
- QGeoTileCache *cache;
-};
+void QCache3QTileEvictionPolicy::aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(obj);
+ // leave the pointer set if it's a real eviction
+}
-class TileTexture {
-public:
- ~TileTexture()
- {
-// qWarning() << "evicting (texture) " << spec;
- cache->evictFromTextureCache(this);
- }
+QGeoCachedTileDisk::~QGeoCachedTileDisk()
+{
+ if (cache)
+ cache->evictFromDiskCache(this);
+}
- QGeoTileSpec spec;
- QGLTexture2D *texture;
- QGeoTileCache *cache;
-};
+QGeoTileTexture::~QGeoTileTexture()
+{
+ if (cache)
+ cache->evictFromTextureCache(this);
+}
QGeoTileCache::QGeoTileCache(const QString &directory, QObject *parent)
-: QObject(parent), directory_(directory)
+ : QObject(parent), directory_(directory)
{
qRegisterMetaType<QGeoTileSpec>();
qRegisterMetaType<QList<QGeoTileSpec> >();
qRegisterMetaType<QSet<QGeoTileSpec> >();
-
if (directory_.isEmpty()) {
QString dirname = QLatin1String(".tilecache");
QDir home = QDir::home();
@@ -112,17 +111,23 @@ QGeoTileCache::QGeoTileCache(const QString &directory, QObject *parent)
home.mkdir(dirname);
directory_ = home.filePath(dirname);
}
- qDebug() << __FUNCTION__ << directory_;
- diskCache_.setMaxCost(100 * 1024 * 1024);
- memoryCache_.setMaxCost(50 * 1024 * 1024);
- textureCache_.setMaxCost(100 * 1024 * 1024);
+ setMaxDiskUsage(20 * 1024 * 1024);
+ setMaxMemoryUsage(4 * 1024 * 1024);
+ setMaxTextureUsage(16 * 1024 * 1024);
loadTiles();
}
QGeoTileCache::~QGeoTileCache() {}
+void QGeoTileCache::printStats()
+{
+ textureCache_.printStats();
+ memoryCache_.printStats();
+ diskCache_.printStats();
+}
+
void QGeoTileCache::setMaxDiskUsage(int diskUsage)
{
diskCache_.setMaxCost(diskUsage);
@@ -170,41 +175,63 @@ int QGeoTileCache::textureUsage() const
void QGeoTileCache::GLContextAvailable()
{
- int size = cleanupList_.size();
- for (int i = 0; i < size; ++i) {
- QGLTexture2D* texture = cleanupList_.at(i);
+ QMutexLocker ml(&cleanupMutex_);
+
+ /* Throttle the cleanup to 10 items/frame to avoid blocking the render
+ * for too long. Normally only 6-20 tiles are on screen at a time so
+ * eviction rates shouldn't be much higher than this. */
+ int todo = qMin(cleanupList_.size(), 10);
+ for (int i = 0; i < todo; ++i) {
+ QGLTexture2D *texture = cleanupList_.front();
if (texture) {
texture->release();
+ texture->cleanupResources();
delete texture;
}
+ cleanupList_.pop_front();
}
- cleanupList_.clear();
-}
-
-bool QGeoTileCache::contains(const QGeoTileSpec &spec) const
-{
- return keys_.contains(spec);
}
-QGLTexture2D* QGeoTileCache::get(const QGeoTileSpec &spec)
+QSharedPointer<QGeoTileTexture> QGeoTileCache::get(const QGeoTileSpec &spec)
{
- if (textureCache_.contains(spec)) {
- TileTexture *tt = textureCache_.object(spec);
- return tt->texture;
+ QSharedPointer<QGeoTileTexture> tt = textureCache_.object(spec);
+ if (tt)
+ return tt;
+
+ QSharedPointer<QGeoCachedTileMemory> tm = memoryCache_.object(spec);
+ if (tm) {
+ QPixmap pixmap;
+ if (!pixmap.loadFromData(tm->bytes)) {
+ handleError(spec, QLatin1String("Problem with tile image"));
+ return QSharedPointer<QGeoTileTexture>(0);
+ }
+ QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, pixmap);
+ if (tt)
+ return tt;
}
-// if (memoryCache_.contains(spec)) {
-// TileMemory *tm = memoryCache_.object(spec);
-// TileTexture *tt = addToTextureCache(tm->spec, tm->pixmap);
-// return tt->texture;
-// }
- if (diskCache_.contains(spec)) {
- TileDisk *td = diskCache_.object(spec);
-// TileMemory *tm = addToMemoryCache(td->spec, QPixmap(td->filename));
- TileTexture *tt = addToTextureCache(td->spec, QPixmap(td->filename));
- return tt->texture;
+
+ QSharedPointer<QGeoCachedTileDisk> td = diskCache_.object(spec);
+ if (td) {
+ QStringList parts = td->filename.split('.');
+ QFile file(td->filename);
+ file.open(QIODevice::ReadOnly);
+ QByteArray bytes = file.readAll();
+ file.close();
+
+ QPixmap pixmap;
+ const char *format = (parts.size() == 2 ? parts.at(1).toLocal8Bit().constData() : 0);
+ if (!pixmap.loadFromData(bytes, format)) {
+ handleError(spec, QLatin1String("Problem with tile image"));
+ return QSharedPointer<QGeoTileTexture>(0);
+ }
+
+ addToMemoryCache(spec, bytes, (parts.size() == 2 ? parts.at(1) : ""));
+ QSharedPointer<QGeoTileTexture> tt = addToTextureCache(td->spec, pixmap);
+ if (tt)
+ return tt;
}
- return 0;
+ return QSharedPointer<QGeoTileTexture>();
}
void QGeoTileCache::insert(const QGeoTileSpec &spec,
@@ -212,15 +239,6 @@ void QGeoTileCache::insert(const QGeoTileSpec &spec,
const QString &format,
QGeoTiledMappingManagerEngine::CacheAreas areas)
{
- keys_.insert(spec);
-
- QPixmap pixmap;
- // TODO use format string here to hint to the loading code?
- if (!pixmap.loadFromData(bytes)) {
- handleError(spec, QLatin1String("Problem with tile image"));
- return;
- }
-
if (areas & QGeoTiledMappingManagerEngine::DiskCache) {
QString filename = tileSpecToFilename(spec, format, directory_);
@@ -233,36 +251,32 @@ void QGeoTileCache::insert(const QGeoTileSpec &spec,
}
if (areas & QGeoTiledMappingManagerEngine::MemoryCache) {
-// addToMemoryCache(spec, pixmap);
+ addToMemoryCache(spec, bytes, format);
}
- if (areas & QGeoTiledMappingManagerEngine::TextureCache) {
- addToTextureCache(spec, pixmap);
- }
+ /* inserts do not hit the texture cache -- this actually reduces overall
+ * cache hit rates because many tiles come too late to be useful
+ * and act as a poison */
}
-void QGeoTileCache::evictFromDiskCache(TileDisk *td)
+void QGeoTileCache::evictFromDiskCache(QGeoCachedTileDisk *td)
{
- keys_.remove(td->spec);
QFile::remove(td->filename);
}
-void QGeoTileCache::evictFromMemoryCache(TileMemory * /* tm */)
+void QGeoTileCache::evictFromMemoryCache(QGeoCachedTileMemory * /* tm */)
{
}
-void QGeoTileCache::evictFromTextureCache(TileTexture *tt)
+void QGeoTileCache::evictFromTextureCache(QGeoTileTexture *tt)
{
+ QMutexLocker ml(&cleanupMutex_);
cleanupList_ << tt->texture;
}
-TileDisk* QGeoTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename)
+QSharedPointer<QGeoCachedTileDisk> QGeoTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename)
{
- keys_.insert(spec);
-
-// qWarning() << "adding (disk) " << spec;
-
- TileDisk *td = new TileDisk;
+ QSharedPointer<QGeoCachedTileDisk> td(new QGeoCachedTileDisk);
td->spec = spec;
td->filename = filename;
td->cache = this;
@@ -270,56 +284,39 @@ TileDisk* QGeoTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString
QFileInfo fi(filename);
int diskCost = fi.size();
- diskCache_.insert(spec,
- td,
- diskCost);
-
+ diskCache_.insert(spec, td, diskCost);
return td;
}
-TileMemory* QGeoTileCache::addToMemoryCache(const QGeoTileSpec &spec, const QPixmap &pixmap)
+QSharedPointer<QGeoCachedTileMemory> QGeoTileCache::addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format)
{
- keys_.insert(spec);
-
-// qWarning() << "adding (memory) " << spec;
-
- TileMemory *tm = new TileMemory;
- tm->spec = spec;
- tm->pixmap = pixmap;
- tm->cache = this;
-
- int memoryCost = pixmap.width() * pixmap.height() * pixmap.depth() / 8;
+ QSharedPointer<QGeoCachedTileMemory> tt(new QGeoCachedTileMemory);
+ tt->spec = spec;
+ tt->cache = this;
+ tt->bytes = bytes;
+ tt->format = format;
- memoryCache_.insert(spec,
- tm,
- memoryCost);
+ int cost = bytes.size();
+ memoryCache_.insert(spec, tt, cost);
- return tm;
+ return tt;
}
-TileTexture* QGeoTileCache::addToTextureCache(const QGeoTileSpec &spec, const QPixmap &pixmap)
+QSharedPointer<QGeoTileTexture> QGeoTileCache::addToTextureCache(const QGeoTileSpec &spec, const QPixmap &pixmap)
{
- keys_.insert(spec);
-
-// qWarning() << "adding (texture) " << spec;
-
- TileTexture *tt = new TileTexture;
+ QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture);
tt->spec = spec;
tt->texture = new QGLTexture2D();
tt->texture->setPixmap(pixmap);
tt->texture->setHorizontalWrap(QGL::ClampToEdge);
tt->texture->setVerticalWrap(QGL::ClampToEdge);
-
-// tt->texture->bind();
-// tt->texture->clearImage();
-
tt->cache = this;
- int textureCost = pixmap.width() * pixmap.height() * pixmap.depth() / 8;;
+ /* Do not bind/cleanImage on the texture here -- it needs to be done
+ * in the render thread (by qgeomapgeometry) */
- textureCache_.insert(spec,
- tt,
- textureCost);
+ int textureCost = pixmap.width() * pixmap.height() * pixmap.depth() / 8;
+ textureCache_.insert(spec, tt, textureCost);
return tt;
}
@@ -347,8 +344,6 @@ void QGeoTileCache::loadTiles()
addToDiskCache(spec, filename);
tiles++;
}
- qDebug() << __FUNCTION__ << " loaded this many map tiles to cache: " << tiles;
-
}
QString QGeoTileCache::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory)
diff --git a/src/location/maps/qgeotilecache_p.h b/src/location/maps/qgeotilecache_p.h
index 33120a75..58ef18c0 100644
--- a/src/location/maps/qgeotilecache_p.h
+++ b/src/location/maps/qgeotilecache_p.h
@@ -56,24 +56,58 @@
#include <QObject>
#include <QCache>
+#include "qcache3q_p.h"
#include <QSet>
+#include <QMutex>
+#include <QTimer>
+#include "qgeotilespec.h"
#include "qgeotiledmappingmanagerengine.h"
QT_BEGIN_NAMESPACE
-class QGeoTileSpec;
-class QGeoTile;
-
-class TileDisk;
-class TileMemory;
-class TileTexture;
+class QGeoMappingManager;
+class QGeoTile;
+class QGeoCachedTileMemory;
+class QGeoTileCache;
class QGLTexture2D;
class QPixmap;
class QThread;
+/* This would be internal to qgeotilecache.cpp except that the eviction
+ * policy can't be defined without it being concrete here */
+class QGeoCachedTileDisk
+{
+public:
+ ~QGeoCachedTileDisk();
+
+ QGeoTileSpec spec;
+ QString filename;
+ QString format;
+ QGeoTileCache *cache;
+};
+
+/* This is also used in the mapgeometry */
+class QGeoTileTexture {
+public:
+ ~QGeoTileTexture();
+
+ QGeoTileSpec spec;
+ QGLTexture2D *texture;
+ QGeoTileCache *cache;
+};
+
+/* Custom eviction policy for the disk cache, to avoid deleting all the files
+ * when the application closes */
+class QCache3QTileEvictionPolicy : public QCache3QDefaultEvictionPolicy<QGeoTileSpec,QGeoCachedTileDisk>
+{
+protected:
+ void aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj);
+ void aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj);
+};
+
class Q_LOCATION_EXPORT QGeoTileCache : public QObject
{
Q_OBJECT
@@ -95,12 +129,11 @@ public:
void GLContextAvailable();
- bool contains(const QGeoTileSpec &spec) const;
- QGLTexture2D* get(const QGeoTileSpec &spec);
+ QSharedPointer<QGeoTileTexture> get(const QGeoTileSpec &spec);
- void evictFromDiskCache(TileDisk *td);
- void evictFromMemoryCache(TileMemory *tm);
- void evictFromTextureCache(TileTexture *tt);
+ void evictFromDiskCache(QGeoCachedTileDisk *td);
+ void evictFromMemoryCache(QGeoCachedTileMemory *tm);
+ void evictFromTextureCache(QGeoTileTexture *tt);
void insert(const QGeoTileSpec &spec,
const QByteArray &bytes,
@@ -108,22 +141,25 @@ public:
QGeoTiledMappingManagerEngine::CacheAreas areas = QGeoTiledMappingManagerEngine::AllCaches);
void handleError(const QGeoTileSpec &spec, const QString &errorString);
+public slots:
+ void printStats();
+
private:
void loadTiles();
- TileDisk* addToDiskCache(const QGeoTileSpec &spec, const QString &filename);
- TileMemory* addToMemoryCache(const QGeoTileSpec &spec, const QPixmap &pixmap);
- TileTexture* addToTextureCache(const QGeoTileSpec &spec, const QPixmap &pixmap);
+ QSharedPointer<QGeoCachedTileDisk> addToDiskCache(const QGeoTileSpec &spec, const QString &filename);
+ QSharedPointer<QGeoCachedTileMemory> addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format);
+ QSharedPointer<QGeoTileTexture> addToTextureCache(const QGeoTileSpec &spec, const QPixmap &pixmap);
static QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory);
static QGeoTileSpec filenameToTileSpec(const QString &filename);
QString directory_;
- QSet<QGeoTileSpec> keys_;
- QCache<QGeoTileSpec, TileDisk > diskCache_;
- QCache<QGeoTileSpec, TileMemory > memoryCache_;
- QCache<QGeoTileSpec, TileTexture > textureCache_;
+ QCache3Q<QGeoTileSpec, QGeoCachedTileDisk, QCache3QTileEvictionPolicy > diskCache_;
+ QCache3Q<QGeoTileSpec, QGeoCachedTileMemory > memoryCache_;
+ QCache3Q<QGeoTileSpec, QGeoTileTexture > textureCache_;
+ QMutex cleanupMutex_;
QList<QGLTexture2D*> cleanupList_;
};
diff --git a/src/location/maps/qgeotiledmapdata.cpp b/src/location/maps/qgeotiledmapdata.cpp
index 7036ea47..2b869988 100644
--- a/src/location/maps/qgeotiledmapdata.cpp
+++ b/src/location/maps/qgeotiledmapdata.cpp
@@ -264,17 +264,11 @@ void QGeoTiledMapDataPrivate::changeCameraData(const QGeoCameraData &oldCameraDa
if (mapImages_) {
mapImages_->setVisibleTiles(visibleTiles_);
- //QSet<QGeoTileSpec> cachedTiles = mapImages_->cachedTiles();
- // TODO make this more efficient
- QSet<QGeoTileSpec> cachedTiles = visibleTiles_;
-
- typedef QSet<QGeoTileSpec>::const_iterator iter;
- iter i = cachedTiles.constBegin();
- iter end = cachedTiles.constEnd();
- for (; i != end; ++i) {
- QGeoTileSpec tile = *i;
- if (cache_->contains(tile))
- mapGeometry_->addTile(tile, cache_->get(tile));
+ QList<QSharedPointer<QGeoTileTexture> > cachedTiles =
+ mapImages_->cachedTiles();
+
+ foreach (QSharedPointer<QGeoTileTexture> tex, cachedTiles) {
+ mapGeometry_->addTile(tex->spec, tex);
}
if (!cachedTiles.isEmpty())
@@ -294,12 +288,29 @@ void QGeoTiledMapDataPrivate::resized(int width, int height)
cameraTiles_->setScreenSize(QSize(width, height));
mapGeometry_->setScreenSize(QSize(width, height));
map_->setCameraData(map_->cameraData());
+
+ if (width > 0 && height > 0 && cache_ && cameraTiles_) {
+ // absolute minimum size: one tile each side of display, 32-bit colour
+ int texCacheSize = (width + cameraTiles_->tileSize()*2) *
+ (height + cameraTiles_->tileSize()*2) * 4;
+
+ // triple it for good measure
+ texCacheSize *= 3;
+
+ int newSize = qMax(cache_->maxTextureUsage(), texCacheSize);
+ if (newSize == texCacheSize) {
+ qDebug("Increasing texcache size to %d bytes", newSize);
+ cache_->setMaxTextureUsage(newSize);
+ }
+ }
}
void QGeoTiledMapDataPrivate::tileFetched(const QGeoTileSpec &spec)
{
- if (cache_->contains(spec))
- mapGeometry_->addTile(spec, cache_->get(spec));
+ QSharedPointer<QGeoTileTexture> tex = cache_->get(spec);
+ if (tex) {
+ mapGeometry_->addTile(spec, tex);
+ }
mapImages_->tileFetched(spec);
map_->update();
}
@@ -307,6 +318,7 @@ void QGeoTiledMapDataPrivate::tileFetched(const QGeoTileSpec &spec)
void QGeoTiledMapDataPrivate::paintGL(QGLPainter *painter)
{
mapGeometry_->paintGL(painter);
+ cache_->GLContextAvailable();
}
QGeoCoordinate QGeoTiledMapDataPrivate::screenPositionToCoordinate(const QPointF &pos) const
diff --git a/src/location/maps/qgeotiledmappingmanagerengine.cpp b/src/location/maps/qgeotiledmappingmanagerengine.cpp
index 023170e4..c7c53ef4 100644
--- a/src/location/maps/qgeotiledmappingmanagerengine.cpp
+++ b/src/location/maps/qgeotiledmappingmanagerengine.cpp
@@ -142,6 +142,24 @@ void QGeoTiledMappingManagerEngine::deregisterMap(QGeoTiledMapData *map)
} else {
d_ptr->caches.insert(cache, maps);
}
+
+ d_ptr->mapHash.remove(map);
+
+ QHash<QGeoTileSpec, QSet<QGeoTiledMapData*> > newTileHash = d_ptr->tileHash;
+ typedef QHash<QGeoTileSpec, QSet<QGeoTiledMapData*> >::const_iterator h_iter;
+ h_iter hi = d_ptr->tileHash.constBegin();
+ h_iter hend = d_ptr->tileHash.constEnd();
+ for (; hi != hend; ++hi) {
+ QSet<QGeoTiledMapData*> maps = hi.value();
+ if (maps.contains(map)) {
+ maps.remove(map);
+ if (maps.isEmpty())
+ newTileHash.remove(hi.key());
+ else
+ newTileHash.insert(hi.key(), maps);
+ }
+ }
+ d_ptr->tileHash = newTileHash;
}
void QGeoTiledMappingManagerEngine::updateTileRequests(QGeoTiledMapData *map,
diff --git a/src/location/maps/qgeotiledmappingmanagerengine.h b/src/location/maps/qgeotiledmappingmanagerengine.h
index acfedec2..7b4afc99 100644
--- a/src/location/maps/qgeotiledmappingmanagerengine.h
+++ b/src/location/maps/qgeotiledmappingmanagerengine.h
@@ -76,7 +76,6 @@ public:
enum CacheArea {
DiskCache = 0x01,
MemoryCache = 0x02,
- TextureCache = 0x04,
AllCaches = 0xFF
};
Q_DECLARE_FLAGS(CacheAreas, CacheArea)
diff --git a/src/location/maps/qgeotilefetcher.cpp b/src/location/maps/qgeotilefetcher.cpp
index 8f1ae934..615b9350 100644
--- a/src/location/maps/qgeotilefetcher.cpp
+++ b/src/location/maps/qgeotilefetcher.cpp
@@ -105,6 +105,8 @@ void QGeoTileFetcher::updateTileRequests(const QSet<QGeoTileSpec> &tilesAdded,
{
Q_D(QGeoTileFetcher);
+ QMutexLocker ml(&d->queueMutex_);
+
if (d->stopped_)
return;
@@ -126,6 +128,10 @@ void QGeoTileFetcher::cancelTileRequests(const QSet<QGeoTileSpec> &tiles)
for (; tile != end; ++tile) {
QGeoTiledMapReply* reply = d->invmap_.value(*tile, 0);
if (reply) {
+ /* when we call abort() it will directly emit finished(), so
+ * we must disconnect it first to avoid recursing on the lock */
+ disconnect(reply, SIGNAL(finished()),
+ this, SLOT(finished()));
d->invmap_.remove(*tile);
reply->abort();
reply->deleteLater();
@@ -141,6 +147,8 @@ void QGeoTileFetcher::requestNextTile()
{
Q_D(QGeoTileFetcher);
+ QMutexLocker ml(&d->queueMutex_);
+
if (d->stopped_)
return;
@@ -172,6 +180,8 @@ void QGeoTileFetcher::finished()
{
Q_D(QGeoTileFetcher);
+ QMutexLocker ml(&d->queueMutex_);
+
QGeoTiledMapReply *reply = qobject_cast<QGeoTiledMapReply*>(sender());
if (!reply)
return;
diff --git a/src/location/maps/qgeotilefetcher.h b/src/location/maps/qgeotilefetcher.h
index 51fd134b..9a6cd820 100644
--- a/src/location/maps/qgeotilefetcher.h
+++ b/src/location/maps/qgeotilefetcher.h
@@ -70,9 +70,9 @@ public Q_SLOTS:
void threadStarted();
void threadFinished();
void updateTileRequests(const QSet<QGeoTileSpec> &tilesAdded, const QSet<QGeoTileSpec> &tilesRemoved);
- void cancelTileRequests(const QSet<QGeoTileSpec> &tiles);
private Q_SLOTS:
+ void cancelTileRequests(const QSet<QGeoTileSpec> &tiles);
void requestNextTile();
void finished();
diff --git a/src/location/maps/qgeotilefetcher_p.h b/src/location/maps/qgeotilefetcher_p.h
index 659cc817..b18efadc 100644
--- a/src/location/maps/qgeotilefetcher_p.h
+++ b/src/location/maps/qgeotilefetcher_p.h
@@ -58,6 +58,8 @@
#include <QMap>
#include <QLocale>
#include <QTimer>
+#include <QMutex>
+#include <QMutexLocker>
#include <QHash>
#include "qgeomaptype.h"
@@ -80,6 +82,7 @@ public:
bool stopped_;
bool initialized_;
QTimer *timer_;
+ QMutex queueMutex_;
QList<QGeoTileSpec> queue_;
QHash<QGeoTileSpec, QGeoTiledMapReply*> invmap_;