File indexing completed on 2024-04-14 03:48:05

0001 /*
0002     SPDX-FileCopyrightText: 2005-2007 Torsten Rahn <tackat@kde.org>
0003     SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0004     SPDX-FileCopyrightText: 2008, 2009, 2010 Jens-Michael Hoffmann <jensmh@gmx.de>
0005     SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "StackedTileLoader.h"
0011 
0012 #include "MarbleDebug.h"
0013 #include "MergedLayerDecorator.h"
0014 #include "StackedTile.h"
0015 #include "TileLoader.h"
0016 #include "TileLoaderHelper.h"
0017 #include "MarbleGlobal.h"
0018 
0019 #include <QCache>
0020 #include <QHash>
0021 #include <QReadWriteLock>
0022 #include <QImage>
0023 
0024 
0025 namespace Marble
0026 {
0027 
0028 class StackedTileLoaderPrivate
0029 {
0030 public:
0031     explicit StackedTileLoaderPrivate( MergedLayerDecorator *mergedLayerDecorator )
0032         : m_layerDecorator( mergedLayerDecorator )
0033     {
0034         m_tileCache.setMaxCost( 20000 * 1024 ); // Cache size measured in bytes
0035     }
0036 
0037     MergedLayerDecorator *const m_layerDecorator;
0038     QHash <TileId, StackedTile*>  m_tilesOnDisplay;
0039     QCache <TileId, StackedTile>  m_tileCache;
0040     QReadWriteLock m_cacheLock;
0041 };
0042 
0043 StackedTileLoader::StackedTileLoader( MergedLayerDecorator *mergedLayerDecorator, QObject *parent )
0044     : QObject( parent ),
0045       d( new StackedTileLoaderPrivate( mergedLayerDecorator ) )
0046 {
0047 }
0048 
0049 StackedTileLoader::~StackedTileLoader()
0050 {
0051     qDeleteAll( d->m_tilesOnDisplay );
0052     delete d;
0053 }
0054 
0055 int StackedTileLoader::tileColumnCount( int level ) const
0056 {
0057     return d->m_layerDecorator->tileColumnCount( level );
0058 }
0059 
0060 int StackedTileLoader::tileRowCount( int level ) const
0061 {
0062     return d->m_layerDecorator->tileRowCount( level );
0063 }
0064 
0065 const GeoSceneAbstractTileProjection *StackedTileLoader::tileProjection() const
0066 {
0067     return d->m_layerDecorator->tileProjection();
0068 }
0069 
0070 QSize StackedTileLoader::tileSize() const
0071 {
0072     return d->m_layerDecorator->tileSize();
0073 }
0074 
0075 void StackedTileLoader::resetTilehash()
0076 {
0077     QHash<TileId, StackedTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin();
0078     QHash<TileId, StackedTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
0079     for (; it != end; ++it ) {
0080         Q_ASSERT( it.value()->used() && "contained in m_tilesOnDisplay should imply used()" );
0081         it.value()->setUsed( false );
0082     }
0083 }
0084 
0085 void StackedTileLoader::cleanupTilehash()
0086 {
0087     // Make sure that tiles which haven't been used during the last
0088     // rendering of the map at all get removed from the tile hash.
0089 
0090     QHashIterator<TileId, StackedTile*> it( d->m_tilesOnDisplay );
0091     while ( it.hasNext() ) {
0092         it.next();
0093         if ( !it.value()->used() ) {
0094             // If insert call result is false then the cache is too small to store the tile
0095             // but the item will get deleted nevertheless and the pointer we have
0096             // doesn't get set to zero (so don't delete it in this case or it will crash!)
0097             d->m_tileCache.insert( it.key(), it.value(), it.value()->byteCount() );
0098             d->m_tilesOnDisplay.remove( it.key() );
0099         }
0100     }
0101 }
0102 
0103 const StackedTile* StackedTileLoader::loadTile( TileId const & stackedTileId )
0104 {
0105     // check if the tile is in the hash
0106     d->m_cacheLock.lockForRead();
0107     StackedTile * stackedTile = d->m_tilesOnDisplay.value( stackedTileId, 0 );
0108     d->m_cacheLock.unlock();
0109     if ( stackedTile ) {
0110         stackedTile->setUsed( true );
0111         return stackedTile;
0112     }
0113     // here ends the performance critical section of this method
0114 
0115     d->m_cacheLock.lockForWrite();
0116 
0117     // has another thread loaded our tile due to a race condition?
0118     stackedTile = d->m_tilesOnDisplay.value( stackedTileId, 0 );
0119     if ( stackedTile ) {
0120         Q_ASSERT( stackedTile->used() && "other thread should have marked tile as used" );
0121         d->m_cacheLock.unlock();
0122         return stackedTile;
0123     }
0124 
0125     // the tile was not in the hash so check if it is in the cache
0126     stackedTile = d->m_tileCache.take( stackedTileId );
0127     if ( stackedTile ) {
0128         Q_ASSERT( !stackedTile->used() && "tiles in m_tileCache are invisible and should thus be marked as unused" );
0129         stackedTile->setUsed( true );
0130         d->m_tilesOnDisplay[ stackedTileId ] = stackedTile;
0131         d->m_cacheLock.unlock();
0132         return stackedTile;
0133     }
0134 
0135     // tile (valid) has not been found in hash or cache, so load it from disk
0136     // and place it in the hash from where it will get transferred to the cache
0137 
0138     mDebug() << "load tile from disk:" << stackedTileId;
0139 
0140     stackedTile = d->m_layerDecorator->loadTile( stackedTileId );
0141     Q_ASSERT( stackedTile );
0142     stackedTile->setUsed( true );
0143 
0144     d->m_tilesOnDisplay[ stackedTileId ] = stackedTile;
0145     d->m_cacheLock.unlock();
0146 
0147     emit tileLoaded( stackedTileId );
0148 
0149     return stackedTile;
0150 }
0151 
0152 quint64 StackedTileLoader::volatileCacheLimit() const
0153 {
0154     return d->m_tileCache.maxCost() / 1024;
0155 }
0156 
0157 QList<TileId> StackedTileLoader::visibleTiles() const
0158 {
0159     return d->m_tilesOnDisplay.keys();
0160 }
0161 
0162 int StackedTileLoader::tileCount() const
0163 {
0164     return d->m_tileCache.count() + d->m_tilesOnDisplay.count();
0165 }
0166 
0167 void StackedTileLoader::setVolatileCacheLimit( quint64 kiloBytes )
0168 {
0169     mDebug() << QString("Setting tile cache to %1 kilobytes.").arg( kiloBytes );
0170     d->m_tileCache.setMaxCost( kiloBytes * 1024 );
0171 }
0172 
0173 void StackedTileLoader::updateTile( TileId const &tileId, QImage const &tileImage )
0174 {
0175     const TileId stackedTileId( 0, tileId.zoomLevel(), tileId.x(), tileId.y() );
0176 
0177     StackedTile * displayedTile = d->m_tilesOnDisplay.take( stackedTileId );
0178     if ( displayedTile ) {
0179         Q_ASSERT( !d->m_tileCache.contains( stackedTileId ) );
0180 
0181         StackedTile *const stackedTile = d->m_layerDecorator->updateTile( *displayedTile, tileId, tileImage );
0182         stackedTile->setUsed( true );
0183         d->m_tilesOnDisplay.insert( stackedTileId, stackedTile );
0184 
0185         delete displayedTile;
0186         displayedTile = nullptr;
0187 
0188         emit tileLoaded( stackedTileId );
0189     } else {
0190         d->m_tileCache.remove( stackedTileId );
0191     }
0192 }
0193 
0194 RenderState StackedTileLoader::renderState() const
0195 {
0196     RenderState renderState( "Stacked Tiles" );
0197     QHash<TileId, StackedTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin();
0198     QHash<TileId, StackedTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
0199     for (; it != end; ++it ) {
0200         renderState.addChild( d->m_layerDecorator->renderState( it.key() ) );
0201     }
0202     return renderState;
0203 }
0204 
0205 void StackedTileLoader::clear()
0206 {
0207     qDeleteAll( d->m_tilesOnDisplay );
0208     d->m_tilesOnDisplay.clear();
0209     d->m_tileCache.clear(); // clear the tile cache in physical memory
0210 
0211     emit cleared();
0212 }
0213 
0214 }
0215 
0216 #include "moc_StackedTileLoader.cpp"