Marble

StackedTileLoader.cpp
1/*
2 SPDX-FileCopyrightText: 2005-2007 Torsten Rahn <tackat@kde.org>
3 SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
4 SPDX-FileCopyrightText: 2008, 2009, 2010 Jens-Michael Hoffmann <jensmh@gmx.de>
5 SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "StackedTileLoader.h"
11
12#include "MarbleDebug.h"
13#include "MarbleGlobal.h"
14#include "MergedLayerDecorator.h"
15#include "StackedTile.h"
16#include "TileLoader.h"
17#include "TileLoaderHelper.h"
18
19#include <QCache>
20#include <QHash>
21#include <QImage>
22#include <QReadWriteLock>
23
24namespace Marble
25{
26
27class StackedTileLoaderPrivate
28{
29public:
30 explicit StackedTileLoaderPrivate(MergedLayerDecorator *mergedLayerDecorator)
31 : m_layerDecorator(mergedLayerDecorator)
32 {
33 m_tileCache.setMaxCost(20000 * 1024); // Cache size measured in bytes
34 }
35
36 MergedLayerDecorator *const m_layerDecorator;
37 QHash<TileId, StackedTile *> m_tilesOnDisplay;
39 QReadWriteLock m_cacheLock;
40};
41
42StackedTileLoader::StackedTileLoader(MergedLayerDecorator *mergedLayerDecorator, QObject *parent)
43 : QObject(parent)
44 , d(new StackedTileLoaderPrivate(mergedLayerDecorator))
45{
46}
47
48StackedTileLoader::~StackedTileLoader()
49{
50 qDeleteAll(d->m_tilesOnDisplay);
51 delete d;
52}
53
54int StackedTileLoader::tileColumnCount(int level) const
55{
56 return d->m_layerDecorator->tileColumnCount(level);
57}
58
59int StackedTileLoader::tileRowCount(int level) const
60{
61 return d->m_layerDecorator->tileRowCount(level);
62}
63
64const GeoSceneAbstractTileProjection *StackedTileLoader::tileProjection() const
65{
66 return d->m_layerDecorator->tileProjection();
67}
68
69QSize StackedTileLoader::tileSize() const
70{
71 return d->m_layerDecorator->tileSize();
72}
73
75{
76 QHash<TileId, StackedTile *>::const_iterator it = d->m_tilesOnDisplay.constBegin();
77 QHash<TileId, StackedTile *>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
78 for (; it != end; ++it) {
79 Q_ASSERT(it.value()->used() && "contained in m_tilesOnDisplay should imply used()");
80 it.value()->setUsed(false);
81 }
82}
83
85{
86 // Make sure that tiles which haven't been used during the last
87 // rendering of the map at all get removed from the tile hash.
88
89 QHashIterator<TileId, StackedTile *> it(d->m_tilesOnDisplay);
90 while (it.hasNext()) {
91 it.next();
92 if (!it.value()->used()) {
93 // If insert call result is false then the cache is too small to store the tile
94 // but the item will get deleted nevertheless and the pointer we have
95 // doesn't get set to zero (so don't delete it in this case or it will crash!)
96 d->m_tileCache.insert(it.key(), it.value(), it.value()->byteCount());
97 d->m_tilesOnDisplay.remove(it.key());
98 }
99 }
100}
101
102const StackedTile *StackedTileLoader::loadTile(TileId const &stackedTileId)
103{
104 // check if the tile is in the hash
105 d->m_cacheLock.lockForRead();
106 StackedTile *stackedTile = d->m_tilesOnDisplay.value(stackedTileId, nullptr);
107 d->m_cacheLock.unlock();
108 if (stackedTile) {
109 stackedTile->setUsed(true);
110 return stackedTile;
111 }
112 // here ends the performance critical section of this method
113
114 d->m_cacheLock.lockForWrite();
115
116 // has another thread loaded our tile due to a race condition?
117 stackedTile = d->m_tilesOnDisplay.value(stackedTileId, nullptr);
118 if (stackedTile) {
119 Q_ASSERT(stackedTile->used() && "other thread should have marked tile as used");
120 d->m_cacheLock.unlock();
121 return stackedTile;
122 }
123
124 // the tile was not in the hash so check if it is in the cache
125 stackedTile = d->m_tileCache.take(stackedTileId);
126 if (stackedTile) {
127 Q_ASSERT(!stackedTile->used() && "tiles in m_tileCache are invisible and should thus be marked as unused");
128 stackedTile->setUsed(true);
129 d->m_tilesOnDisplay[stackedTileId] = stackedTile;
130 d->m_cacheLock.unlock();
131 return stackedTile;
132 }
133
134 // tile (valid) has not been found in hash or cache, so load it from disk
135 // and place it in the hash from where it will get transferred to the cache
136
137 mDebug() << "load tile from disk:" << stackedTileId;
138
139 stackedTile = d->m_layerDecorator->loadTile(stackedTileId);
140 Q_ASSERT(stackedTile);
141 stackedTile->setUsed(true);
142
143 d->m_tilesOnDisplay[stackedTileId] = stackedTile;
144 d->m_cacheLock.unlock();
145
146 Q_EMIT tileLoaded(stackedTileId);
147
148 return stackedTile;
149}
150
152{
153 return d->m_tileCache.maxCost() / 1024;
154}
155
157{
158 return d->m_tilesOnDisplay.keys();
159}
160
162{
163 return d->m_tileCache.count() + d->m_tilesOnDisplay.count();
164}
165
167{
168 mDebug() << QStringLiteral("Setting tile cache to %1 kilobytes.").arg(kiloBytes);
169 d->m_tileCache.setMaxCost(kiloBytes * 1024);
170}
171
172void StackedTileLoader::updateTile(TileId const &tileId, QImage const &tileImage)
173{
174 const TileId stackedTileId(0, tileId.zoomLevel(), tileId.x(), tileId.y());
175
176 StackedTile *displayedTile = d->m_tilesOnDisplay.take(stackedTileId);
177 if (displayedTile) {
178 Q_ASSERT(!d->m_tileCache.contains(stackedTileId));
179
180 StackedTile *const stackedTile = d->m_layerDecorator->updateTile(*displayedTile, tileId, tileImage);
181 stackedTile->setUsed(true);
182 d->m_tilesOnDisplay.insert(stackedTileId, stackedTile);
183
184 delete displayedTile;
185 displayedTile = nullptr;
186
187 Q_EMIT tileLoaded(stackedTileId);
188 } else {
189 d->m_tileCache.remove(stackedTileId);
190 }
191}
192
193RenderState StackedTileLoader::renderState() const
194{
195 RenderState renderState(QString::fromLatin1("Stacked Tiles"));
196 QHash<TileId, StackedTile *>::const_iterator it = d->m_tilesOnDisplay.constBegin();
197 QHash<TileId, StackedTile *>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
198 for (; it != end; ++it) {
199 renderState.addChild(d->m_layerDecorator->renderState(it.key()));
200 }
201 return renderState;
202}
203
205{
206 qDeleteAll(d->m_tilesOnDisplay);
207 d->m_tilesOnDisplay.clear();
208 d->m_tileCache.clear(); // clear the tile cache in physical memory
209
210 Q_EMIT cleared();
211}
212
213}
214
215#include "moc_StackedTileLoader.cpp"
void clear()
Effectively triggers a reload of all tiles that are currently in use and clears the tile cache in phy...
int tileCount() const
Return the number of tiles in the cache.
void cleanupTilehash()
Cleans up the internal tile hash.
void resetTilehash()
Resets the internal tile hash.
quint64 volatileCacheLimit() const
Returns the limit of the volatile (in RAM) cache.
StackedTileLoader(MergedLayerDecorator *mergedLayerDecorator, QObject *parent=nullptr)
Creates a new tile loader.
QList< TileId > visibleTiles() const
Reloads the tiles that are currently displayed.
const StackedTile * loadTile(TileId const &stackedTileId)
Loads a tile and returns it.
void setVolatileCacheLimit(quint64 kiloBytes)
Set the limit of the volatile (in RAM) cache.
A single tile that consists of a stack of Tile layers.
Definition StackedTile.h:51
const QList< QKeySequence > & end()
Binds a QML item to a specific geodetic location in screen coordinates.
bool hasNext() const const
const Key & key() const const
const T & value() const const
Q_EMITQ_EMIT
void lockForRead()
void lockForWrite()
QString fromLatin1(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:08:53 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.