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 "MergedLayerDecorator.h"
14#include "StackedTile.h"
15#include "TileLoader.h"
16#include "TileLoaderHelper.h"
17#include "MarbleGlobal.h"
18
19#include <QCache>
20#include <QHash>
21#include <QReadWriteLock>
22#include <QImage>
23
24
25namespace Marble
26{
27
28class StackedTileLoaderPrivate
29{
30public:
31 explicit StackedTileLoaderPrivate( MergedLayerDecorator *mergedLayerDecorator )
32 : m_layerDecorator( mergedLayerDecorator )
33 {
34 m_tileCache.setMaxCost( 20000 * 1024 ); // Cache size measured in bytes
35 }
36
37 MergedLayerDecorator *const m_layerDecorator;
38 QHash <TileId, StackedTile*> m_tilesOnDisplay;
39 QCache <TileId, StackedTile> m_tileCache;
40 QReadWriteLock m_cacheLock;
41};
42
43StackedTileLoader::StackedTileLoader( MergedLayerDecorator *mergedLayerDecorator, QObject *parent )
44 : QObject( parent ),
45 d( new StackedTileLoaderPrivate( mergedLayerDecorator ) )
46{
47}
48
49StackedTileLoader::~StackedTileLoader()
50{
51 qDeleteAll( d->m_tilesOnDisplay );
52 delete d;
53}
54
55int StackedTileLoader::tileColumnCount( int level ) const
56{
57 return d->m_layerDecorator->tileColumnCount( level );
58}
59
60int StackedTileLoader::tileRowCount( int level ) const
61{
62 return d->m_layerDecorator->tileRowCount( level );
63}
64
65const GeoSceneAbstractTileProjection *StackedTileLoader::tileProjection() const
66{
67 return d->m_layerDecorator->tileProjection();
68}
69
70QSize StackedTileLoader::tileSize() const
71{
72 return d->m_layerDecorator->tileSize();
73}
74
76{
77 QHash<TileId, StackedTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin();
78 QHash<TileId, StackedTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
79 for (; it != end; ++it ) {
80 Q_ASSERT( it.value()->used() && "contained in m_tilesOnDisplay should imply used()" );
81 it.value()->setUsed( false );
82 }
83}
84
86{
87 // Make sure that tiles which haven't been used during the last
88 // rendering of the map at all get removed from the tile hash.
89
90 QHashIterator<TileId, StackedTile*> it( d->m_tilesOnDisplay );
91 while ( it.hasNext() ) {
92 it.next();
93 if ( !it.value()->used() ) {
94 // If insert call result is false then the cache is too small to store the tile
95 // but the item will get deleted nevertheless and the pointer we have
96 // doesn't get set to zero (so don't delete it in this case or it will crash!)
97 d->m_tileCache.insert( it.key(), it.value(), it.value()->byteCount() );
98 d->m_tilesOnDisplay.remove( it.key() );
99 }
100 }
101}
102
103const StackedTile* StackedTileLoader::loadTile( TileId const & stackedTileId )
104{
105 // check if the tile is in the hash
106 d->m_cacheLock.lockForRead();
107 StackedTile * stackedTile = d->m_tilesOnDisplay.value( stackedTileId, 0 );
108 d->m_cacheLock.unlock();
109 if ( stackedTile ) {
110 stackedTile->setUsed( true );
111 return stackedTile;
112 }
113 // here ends the performance critical section of this method
114
115 d->m_cacheLock.lockForWrite();
116
117 // has another thread loaded our tile due to a race condition?
118 stackedTile = d->m_tilesOnDisplay.value( stackedTileId, 0 );
119 if ( stackedTile ) {
120 Q_ASSERT( stackedTile->used() && "other thread should have marked tile as used" );
121 d->m_cacheLock.unlock();
122 return stackedTile;
123 }
124
125 // the tile was not in the hash so check if it is in the cache
126 stackedTile = d->m_tileCache.take( stackedTileId );
127 if ( stackedTile ) {
128 Q_ASSERT( !stackedTile->used() && "tiles in m_tileCache are invisible and should thus be marked as unused" );
129 stackedTile->setUsed( true );
130 d->m_tilesOnDisplay[ stackedTileId ] = stackedTile;
131 d->m_cacheLock.unlock();
132 return stackedTile;
133 }
134
135 // tile (valid) has not been found in hash or cache, so load it from disk
136 // and place it in the hash from where it will get transferred to the cache
137
138 mDebug() << "load tile from disk:" << stackedTileId;
139
140 stackedTile = d->m_layerDecorator->loadTile( stackedTileId );
142 stackedTile->setUsed( true );
143
144 d->m_tilesOnDisplay[ stackedTileId ] = stackedTile;
145 d->m_cacheLock.unlock();
146
147 emit tileLoaded( stackedTileId );
148
149 return stackedTile;
150}
151
153{
154 return d->m_tileCache.maxCost() / 1024;
155}
156
158{
159 return d->m_tilesOnDisplay.keys();
160}
161
163{
164 return d->m_tileCache.count() + d->m_tilesOnDisplay.count();
165}
166
168{
169 mDebug() << QString("Setting tile cache to %1 kilobytes.").arg( kiloBytes );
170 d->m_tileCache.setMaxCost( kiloBytes * 1024 );
171}
172
173void StackedTileLoader::updateTile( TileId const &tileId, QImage const &tileImage )
174{
175 const TileId stackedTileId( 0, tileId.zoomLevel(), tileId.x(), tileId.y() );
176
177 StackedTile * displayedTile = d->m_tilesOnDisplay.take( stackedTileId );
178 if ( displayedTile ) {
179 Q_ASSERT( !d->m_tileCache.contains( stackedTileId ) );
180
181 StackedTile *const stackedTile = d->m_layerDecorator->updateTile( *displayedTile, tileId, tileImage );
182 stackedTile->setUsed( true );
183 d->m_tilesOnDisplay.insert( stackedTileId, stackedTile );
184
185 delete displayedTile;
186 displayedTile = nullptr;
187
188 emit tileLoaded( stackedTileId );
189 } else {
190 d->m_tileCache.remove( stackedTileId );
191 }
192}
193
194RenderState StackedTileLoader::renderState() const
195{
196 RenderState renderState( "Stacked Tiles" );
197 QHash<TileId, StackedTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin();
198 QHash<TileId, StackedTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
199 for (; it != end; ++it ) {
200 renderState.addChild( d->m_layerDecorator->renderState( it.key() ) );
201 }
202 return renderState;
203}
204
206{
207 qDeleteAll( d->m_tilesOnDisplay );
208 d->m_tilesOnDisplay.clear();
209 d->m_tileCache.clear(); // clear the tile cache in physical memory
210
211 emit cleared();
212}
213
214}
215
216#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.
T qobject_cast(QObject *object)
void lockForRead()
void lockForWrite()
QString arg(Args &&... args) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.