Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add a simple cache for read tile data from vtpk data sources
The process of reading raw tile data from a vtpk comes with some
cost due to the necessary unzipping of files from the vtpk
archive. We can cut out this cost for previously retrieved tiles
through a simple tile -> raw data cache.

The guts of the caching sits in the generic vector tile data
provider class so it could be potentially used by other vector
tile sources, but profiling reveals that there's not a significant
cost associated with retrieving raw tile data for our
other existing VT providers.
  • Loading branch information
nyalldawson committed May 23, 2023
1 parent 80062ab commit c19c442
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 7 deletions.
30 changes: 30 additions & 0 deletions src/core/vectortile/qgsvectortiledataprovider.cpp
Expand Up @@ -15,6 +15,8 @@

#include "qgsvectortiledataprovider.h"
#include "qgsthreadingutils.h"
#include "qgsreadwritelocker.h"
#include "qgsvectortileloader.h"

#include <QNetworkRequest>
#include <QImage>
Expand All @@ -24,10 +26,12 @@ QgsVectorTileDataProvider::QgsVectorTileDataProvider(
const ProviderOptions &options,
QgsDataProvider::ReadFlags flags )
: QgsDataProvider( uri, options, flags )
, mShared( new QgsVectorTileDataProviderSharedData )
{}

QgsVectorTileDataProvider::QgsVectorTileDataProvider( const QgsVectorTileDataProvider &other )
: QgsDataProvider( other.dataSourceUri( false ), ProviderOptions(), other.mReadFlags )
, mShared( other.mShared )
{
setTransformContext( other.transformContext() );
}
Expand Down Expand Up @@ -107,3 +111,29 @@ QString QgsVectorTileDataProvider::htmlMetadata() const

return QString();
}




QgsVectorTileDataProviderSharedData::QgsVectorTileDataProviderSharedData()
{
mTileCache.setMaxCost( 200 );
}

bool QgsVectorTileDataProviderSharedData::getCachedTileData( QgsVectorTileRawData &data, QgsTileXYZ tile )
{
QgsReadWriteLocker locker( mMutex, QgsReadWriteLocker::Read );
if ( QgsVectorTileRawData *cachedData = mTileCache.object( tile ) )
{
data = *cachedData;
return true;
}

return false;
}

void QgsVectorTileDataProviderSharedData::storeCachedTileData( const QgsVectorTileRawData &data )
{
QgsReadWriteLocker locker( mMutex, QgsReadWriteLocker::Write );
mTileCache.insert( data.id, new QgsVectorTileRawData( data ) );
}
23 changes: 23 additions & 0 deletions src/core/vectortile/qgsvectortiledataprovider.h
Expand Up @@ -19,6 +19,10 @@
#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgsdataprovider.h"
#include "qgstiles.h"

#include <QCache>
#include <QReadWriteLock>

class QgsTileMatrixSet;
class QgsTileXYZ;
Expand All @@ -27,6 +31,20 @@ class QgsVectorTileMatrixSet;

#define SIP_NO_FILE

class QgsVectorTileDataProviderSharedData
{
public:
QgsVectorTileDataProviderSharedData();

bool getCachedTileData( QgsVectorTileRawData &data, QgsTileXYZ );
void storeCachedTileData( const QgsVectorTileRawData &data );

QCache< QgsTileXYZ, QgsVectorTileRawData > mTileCache;

QReadWriteLock mMutex; //!< Access to all data members is guarded by the mutex

};

/**
* Base class for vector tile layer data providers.
*
Expand Down Expand Up @@ -148,6 +166,11 @@ class CORE_EXPORT QgsVectorTileDataProvider : public QgsDataProvider
* into a subset of the GUI properties "Metadata" tab.
*/
virtual QString htmlMetadata() const;

protected:

std::shared_ptr<QgsVectorTileDataProviderSharedData> mShared; //!< Mutable data shared between provider instances

};


Expand Down
38 changes: 31 additions & 7 deletions src/core/vectortile/qgsvtpkvectortiledataprovider.cpp
Expand Up @@ -180,11 +180,17 @@ QgsVectorTileRawData QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatri
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

QgsVectorTileRawData data;
if ( mShared->getCachedTileData( data, id ) )
return data;

QgsDataSourceUri dsUri;
dsUri.setEncodedUri( dataSourceUri() );
QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
reader.open();
return loadFromVtpk( reader, id, feedback );
const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
mShared->storeCachedTileData( rawData );
return rawData;
}

QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback ) const
Expand All @@ -194,8 +200,8 @@ QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsT
QgsDataSourceUri dsUri;
dsUri.setEncodedUri( dataSourceUri() );

QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
reader.open();
// defer actual creation of reader until we need it -- maybe everything is already present in the cache!
std::unique_ptr< QgsVtpkTiles > reader;

QList<QgsVectorTileRawData> rawTiles;
QSet< QgsTileXYZ > fetchedTiles;
Expand All @@ -206,11 +212,29 @@ QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsT
if ( feedback && feedback->isCanceled() )
break;

const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
if ( !rawData.data.isEmpty() )
if ( fetchedTiles.contains( id ) )
continue;

QgsVectorTileRawData data;
if ( mShared->getCachedTileData( data, id ) )
{
rawTiles.append( data );
fetchedTiles.insert( data.id );
}
else
{
rawTiles.append( rawData );
fetchedTiles.insert( rawData.id );
if ( !reader )
{
reader = std::make_unique< QgsVtpkTiles >( dsUri.param( QStringLiteral( "url" ) ) );
reader->open();
}
const QgsVectorTileRawData rawData = loadFromVtpk( *reader, id, feedback );
if ( !rawData.data.isEmpty() && !fetchedTiles.contains( rawData.id ) )
{
rawTiles.append( rawData );
fetchedTiles.insert( rawData.id );
mShared->storeCachedTileData( rawData );
}
}
}
return rawTiles;
Expand Down

0 comments on commit c19c442

Please sign in to comment.