From 5fd9681a3563e2346c722bcd779e2b16cd4ac9fd Mon Sep 17 00:00:00 2001 From: Michal Klocek Date: Fri, 3 Jul 2015 15:20:46 +0200 Subject: Refactor prefetching tiles Current implementation uses same QGeoCameraTiles object to calculate visible tiles and tiles which should be prefetched. This is semi optimal since frustum intersection algorithm uses bunch of different parameters. Simplify the logic and use separate objects, this way values in both cases are cached and calculation is done when required. This is important since prefetchData can be called anytime from map's API. Remove textured tiles from scene when clearCache called, this will force redownload of all the tiles. Add new unit test qgeotiledmap and move prefetch test due to refactoring. Change-Id: I6a906df405f212436cdd35ac76f95b559e6b7aae Reviewed-by: Alex Blasche --- src/location/maps/qgeocameratiles.cpp | 85 ++++++-------------------- src/location/maps/qgeocameratiles_p.h | 11 +--- src/location/maps/qgeomapscene.cpp | 6 ++ src/location/maps/qgeomapscene_p.h | 2 + src/location/maps/qgeotiledmap.cpp | 112 ++++++++++++++++++++++++++-------- src/location/maps/qgeotiledmap_p.h | 3 +- src/location/maps/qgeotiledmap_p_p.h | 5 +- 7 files changed, 121 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/location/maps/qgeocameratiles.cpp b/src/location/maps/qgeocameratiles.cpp index d2783a5b..5eae7c0b 100644 --- a/src/location/maps/qgeocameratiles.cpp +++ b/src/location/maps/qgeocameratiles.cpp @@ -49,7 +49,6 @@ #include QT_BEGIN_NAMESPACE -#define PREFETCH_FRUSTUM_SCALE 2.0 struct Frustum { @@ -77,7 +76,6 @@ public: QGeoCameraData m_camera; QSize m_screenSize; int m_tileSize; - int m_maxZoom; QSet m_tiles; int m_intZoomLevel; @@ -86,8 +84,9 @@ public: bool m_dirtyGeometry; bool m_dirtyMetadata; + double m_viewExpansion; void updateMetadata(); - void updateGeometry(double viewExpansion = 1.0); + void updateGeometry(); Frustum createFrustum(double fieldOfViewGradient) const; @@ -127,58 +126,6 @@ QGeoCameraTiles::~QGeoCameraTiles() { } -QSet QGeoCameraTiles::prefetchTiles(PrefetchStle style) -{ - int currentIntZoom = static_cast(std::floor(d_ptr->m_camera.zoomLevel())); - double currentFloatZoom = d_ptr->m_camera.zoomLevel(); - - d_ptr->m_tiles.clear(); - d_ptr->m_intZoomLevel = currentIntZoom; - d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel ; - d_ptr->updateGeometry(PREFETCH_FRUSTUM_SCALE); - - switch (style) { - - case PrefetchNeighbourLayer: { - - double zoomFraction = d_ptr->m_camera.zoomLevel() - currentIntZoom; - int nearestNeighbourLayer = zoomFraction > 0.5 ? currentIntZoom + 1 : currentIntZoom - 1; - if (nearestNeighbourLayer <= d_ptr->m_maxZoom && nearestNeighbourLayer >= 0) { - d_ptr->m_intZoomLevel = nearestNeighbourLayer; - d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel; - d_ptr->m_camera.setZoomLevel(d_ptr->m_intZoomLevel); - // Approx heuristic, keeping total # prefetched tiles roughly independent of the - // fractional zoom level. - double neighbourScale = (1.0 + zoomFraction)/2.0; - d_ptr->updateGeometry(PREFETCH_FRUSTUM_SCALE * neighbourScale); - } - break; - } - - case PrefetchTwoNeighbourLayers: { - // This is a simpler strategy, we just prefetch from layer above and below - // for the layer below we only use half the size as this fills the screen - if (currentIntZoom > 0) { - d_ptr->m_intZoomLevel = currentIntZoom - 1; - d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel; - d_ptr->m_camera.setZoomLevel(d_ptr->m_intZoomLevel); - d_ptr->updateGeometry(0.5); - } - if (currentIntZoom < d_ptr->m_maxZoom) { - d_ptr->m_intZoomLevel = currentIntZoom + 1; - d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel; - d_ptr->m_camera.setZoomLevel(d_ptr->m_intZoomLevel); - d_ptr->updateGeometry(1.0); - } - } - } - - d_ptr->m_intZoomLevel = currentIntZoom; - d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel; - d_ptr->m_camera.setZoomLevel(currentFloatZoom); - return d_ptr->m_tiles; -} - void QGeoCameraTiles::setCameraData(const QGeoCameraData &camera) { if (d_ptr->m_camera == camera) @@ -190,6 +137,11 @@ void QGeoCameraTiles::setCameraData(const QGeoCameraData &camera) d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel; } +QGeoCameraData QGeoCameraTiles::cameraData() const +{ + return d_ptr->m_camera; +} + void QGeoCameraTiles::setScreenSize(const QSize &size) { if (d_ptr->m_screenSize == size) @@ -235,21 +187,18 @@ void QGeoCameraTiles::setTileSize(int tileSize) d_ptr->m_tileSize = tileSize; } -int QGeoCameraTiles::tileSize() const +void QGeoCameraTiles::setViewExpansion(double viewExpansion) { - return d_ptr->m_tileSize; + d_ptr->m_viewExpansion = viewExpansion; + d_ptr->m_dirtyGeometry = true; } -void QGeoCameraTiles::setMaximumZoomLevel(int maxZoom) +int QGeoCameraTiles::tileSize() const { - if (d_ptr->m_maxZoom == maxZoom) - return; - - d_ptr->m_dirtyGeometry = true; - d_ptr->m_maxZoom = maxZoom; + return d_ptr->m_tileSize; } -const QSet& QGeoCameraTiles::visibleTiles() +const QSet& QGeoCameraTiles::createTiles() { if (d_ptr->m_dirtyGeometry) { d_ptr->m_tiles.clear(); @@ -268,11 +217,11 @@ const QSet& QGeoCameraTiles::visibleTiles() QGeoCameraTilesPrivate::QGeoCameraTilesPrivate() : m_mapVersion(-1), m_tileSize(0), - m_maxZoom(0), m_intZoomLevel(0), m_sideLength(0), m_dirtyGeometry(false), - m_dirtyMetadata(false) + m_dirtyMetadata(false), + m_viewExpansion(1.0) { } @@ -295,11 +244,11 @@ void QGeoCameraTilesPrivate::updateMetadata() m_tiles = newTiles; } -void QGeoCameraTilesPrivate::updateGeometry(double viewExpansion) +void QGeoCameraTilesPrivate::updateGeometry() { // Find the frustum from the camera / screen / viewport information // The larger frustum when stationary is a form of prefetching - Frustum f = createFrustum(viewExpansion); + Frustum f = createFrustum(m_viewExpansion); // Find the polygon where the frustum intersects the plane of the map PolygonVector footprint = frustumFootprint(f); diff --git a/src/location/maps/qgeocameratiles_p.h b/src/location/maps/qgeocameratiles_p.h index e133f8e6..d10895f8 100644 --- a/src/location/maps/qgeocameratiles_p.h +++ b/src/location/maps/qgeocameratiles_p.h @@ -63,21 +63,16 @@ public: QGeoCameraTiles(); ~QGeoCameraTiles(); - enum PrefetchStle { PrefetchNeighbourLayer, PrefetchTwoNeighbourLayers}; - void setCameraData(const QGeoCameraData &camera); + QGeoCameraData cameraData() const; void setScreenSize(const QSize &size); void setTileSize(int tileSize); - void setMaximumZoomLevel(int maxZoom); - int tileSize() const; - + void setViewExpansion(double viewExpansion); void setPluginString(const QString &pluginString); void setMapType(const QGeoMapType &mapType); void setMapVersion(int mapVersion); - - const QSet& visibleTiles(); - QSet prefetchTiles(PrefetchStle style); + const QSet& createTiles(); protected: QScopedPointer d_ptr; diff --git a/src/location/maps/qgeomapscene.cpp b/src/location/maps/qgeomapscene.cpp index 4a457005..450be60b 100644 --- a/src/location/maps/qgeomapscene.cpp +++ b/src/location/maps/qgeomapscene.cpp @@ -196,6 +196,12 @@ QSet QGeoMapScene::texturedTiles() return textured; } +void QGeoMapScene::clearTexturedTiles() +{ + Q_D(QGeoMapScene); + d->m_textures.clear(); +} + QGeoMapScenePrivate::QGeoMapScenePrivate() : QObjectPrivate(), m_tileSize(0), diff --git a/src/location/maps/qgeomapscene_p.h b/src/location/maps/qgeomapscene_p.h index 34d4a11f..c38612bc 100644 --- a/src/location/maps/qgeomapscene_p.h +++ b/src/location/maps/qgeomapscene_p.h @@ -87,6 +87,8 @@ public: bool verticalLock() const; QSet texturedTiles(); + void clearTexturedTiles(); + Q_SIGNALS: void newTilesVisible(const QSet &newTiles); diff --git a/src/location/maps/qgeotiledmap.cpp b/src/location/maps/qgeotiledmap.cpp index 97747049..7a56c548 100644 --- a/src/location/maps/qgeotiledmap.cpp +++ b/src/location/maps/qgeotiledmap.cpp @@ -47,7 +47,7 @@ #include QT_BEGIN_NAMESPACE - +#define PREFETCH_FRUSTUM_SCALE 2.0 QGeoTiledMap::QGeoTiledMap(QGeoTiledMappingManagerEngine *engine, QObject *parent) : QGeoMap(*new QGeoTiledMapPrivate(engine), parent) @@ -85,6 +85,12 @@ void QGeoTiledMap::updateTile(const QGeoTileSpec &spec) d->updateTile(spec); } +void QGeoTiledMap::setPrefetchStyle(QGeoTiledMap::PrefetchStyle style) +{ + Q_D(QGeoTiledMap); + d->m_prefetchStyle = style; +} + QAbstractGeoTileCache *QGeoTiledMap::tileCache() { Q_D(QGeoTiledMap); @@ -107,6 +113,7 @@ void QGeoTiledMap::clearData() { Q_D(QGeoTiledMap); d->m_cache->clearAll(); + d->m_mapScene->clearTexturedTiles(); } void QGeoTiledMap::handleTileVersionChanged() @@ -157,14 +164,20 @@ QDoubleVector2D QGeoTiledMap::coordinateToItemPosition(const QGeoCoordinate &coo QGeoTiledMapPrivate::QGeoTiledMapPrivate(QGeoTiledMappingManagerEngine *engine) : QGeoMapPrivate(engine), m_cache(engine->tileCache()), - m_cameraTiles(new QGeoCameraTiles()), + m_visibleTiles(new QGeoCameraTiles()), + m_prefetchTiles(new QGeoCameraTiles()), m_mapScene(new QGeoMapScene()), - m_tileRequests(0) + m_tileRequests(0), + m_maxZoomLevel(static_cast(std::ceil(engine->cameraCapabilities().maximumZoomLevel()))), + m_prefetchStyle(QGeoTiledMap::PrefetchTwoNeighbourLayers) { - m_cameraTiles->setMaximumZoomLevel(static_cast(std::ceil(engine->cameraCapabilities().maximumZoomLevel()))); - m_cameraTiles->setTileSize(engine->tileSize().width()); - m_cameraTiles->setPluginString(engine->managerName() + QLatin1Char('_') + QString::number(engine->managerVersion())); - m_mapScene->setTileSize(engine->tileSize().width()); + int tileSize = engine->tileSize().width(); + QString pluginString(engine->managerName() + QLatin1Char('_') + QString::number(engine->managerVersion())); + m_visibleTiles->setTileSize(tileSize); + m_prefetchTiles->setTileSize(tileSize); + m_visibleTiles->setPluginString(pluginString); + m_prefetchTiles->setPluginString(pluginString); + m_mapScene->setTileSize(tileSize); } QGeoTiledMapPrivate::~QGeoTiledMapPrivate() @@ -172,17 +185,65 @@ QGeoTiledMapPrivate::~QGeoTiledMapPrivate() // controller_ is a child of map_, don't need to delete it here delete m_mapScene; - delete m_cameraTiles; + delete m_visibleTiles; + delete m_prefetchTiles; // TODO map items are not deallocated! // However: how to ensure this is done in rendering thread? } + void QGeoTiledMapPrivate::prefetchTiles() { - if (m_tileRequests) - m_tileRequests->requestTiles(m_cameraTiles->prefetchTiles(QGeoCameraTiles::PrefetchTwoNeighbourLayers) - - m_mapScene->texturedTiles()); + if (m_tileRequests) { + + QSet tiles; + QGeoCameraData camera = m_visibleTiles->cameraData(); + int currentIntZoom = static_cast(std::floor(camera.zoomLevel())); + + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(PREFETCH_FRUSTUM_SCALE); + tiles = m_prefetchTiles->createTiles(); + + switch (m_prefetchStyle) { + + case QGeoTiledMap::PrefetchNeighbourLayer: { + double zoomFraction = camera.zoomLevel() - currentIntZoom; + int nearestNeighbourLayer = zoomFraction > 0.5 ? currentIntZoom + 1 : currentIntZoom - 1; + if (nearestNeighbourLayer <= m_maxZoomLevel && nearestNeighbourLayer >= 0) { + camera.setZoomLevel(nearestNeighbourLayer); + // Approx heuristic, keeping total # prefetched tiles roughly independent of the + // fractional zoom level. + double neighbourScale = (1.0 + zoomFraction)/2.0; + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(PREFETCH_FRUSTUM_SCALE * neighbourScale); + tiles += m_prefetchTiles->createTiles(); + } + } + break; + + case QGeoTiledMap::PrefetchTwoNeighbourLayers: { + // This is a simpler strategy, we just prefetch from layer above and below + // for the layer below we only use half the size as this fills the screen + if (currentIntZoom > 0) { + camera.setZoomLevel(currentIntZoom - 1); + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(0.5); + tiles += m_prefetchTiles->createTiles(); + } + + if (currentIntZoom < m_maxZoomLevel) { + camera.setZoomLevel(currentIntZoom + 1); + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(1.0); + tiles += m_prefetchTiles->createTiles(); + } + + } + } + + m_tileRequests->requestTiles(tiles - m_mapScene->texturedTiles()); + } } void QGeoTiledMapPrivate::changeCameraData(const QGeoCameraData &oldCameraData) @@ -210,7 +271,7 @@ void QGeoTiledMapPrivate::changeCameraData(const QGeoCameraData &oldCameraData) cam.setZoomLevel(izl); } - m_cameraTiles->setCameraData(cam); + m_visibleTiles->setCameraData(cam); m_mapScene->setCameraData(cam); updateScene(); } @@ -219,7 +280,7 @@ void QGeoTiledMapPrivate::updateScene() { Q_Q(QGeoTiledMap); // detect if new tiles introduced - const QSet& tiles = m_cameraTiles->visibleTiles(); + const QSet& tiles = m_visibleTiles->createTiles(); bool newTilesIntroduced = !m_mapScene->visibleTiles().contains(tiles); m_mapScene->setVisibleTiles(tiles); @@ -228,7 +289,7 @@ void QGeoTiledMapPrivate::updateScene() // don't request tiles that are already built and textured QList > cachedTiles = - m_tileRequests->requestTiles(m_cameraTiles->visibleTiles() - m_mapScene->texturedTiles()); + m_tileRequests->requestTiles(m_visibleTiles->createTiles() - m_mapScene->texturedTiles()); foreach (const QSharedPointer &tex, cachedTiles) { m_mapScene->addTile(tex->spec, tex); @@ -240,29 +301,30 @@ void QGeoTiledMapPrivate::updateScene() void QGeoTiledMapPrivate::changeActiveMapType(const QGeoMapType mapType) { - m_cameraTiles->setMapType(mapType); + m_visibleTiles->setMapType(mapType); + m_prefetchTiles->setMapType(mapType); } void QGeoTiledMapPrivate::changeTileVersion(int version) { - m_cameraTiles->setMapVersion(version); + m_visibleTiles->setMapVersion(version); + m_prefetchTiles->setMapVersion(version); updateScene(); } void QGeoTiledMapPrivate::mapResized(int width, int height) { Q_Q(QGeoTiledMap); - if (m_cameraTiles) - m_cameraTiles->setScreenSize(QSize(width, height)); - if (m_mapScene) - m_mapScene->setScreenSize(QSize(width, height)); + m_visibleTiles->setScreenSize(QSize(width, height)); + m_prefetchTiles->setScreenSize(QSize(width, height)); + m_mapScene->setScreenSize(QSize(width, height)); if (q) q->setCameraData(q->cameraData()); - if (width > 0 && height > 0 && m_cache && m_cameraTiles) { + if (width > 0 && height > 0 && m_cache) { // absolute minimum size: one tile each side of display, 32-bit colour - int texCacheSize = (width + m_cameraTiles->tileSize() * 2) * - (height + m_cameraTiles->tileSize() * 2) * 4; + int texCacheSize = (width + m_visibleTiles->tileSize() * 2) * + (height + m_visibleTiles->tileSize() * 2) * 4; // multiply by 3 so the 'recent' list in the cache is big enough for // an entire display of tiles @@ -272,14 +334,14 @@ void QGeoTiledMapPrivate::mapResized(int width, int height) int newSize = qMax(m_cache->minTextureUsage(), texCacheSize); m_cache->setMinTextureUsage(newSize); } - q->evaluateCopyrights(m_cameraTiles->visibleTiles()); + q->evaluateCopyrights(m_visibleTiles->createTiles()); } void QGeoTiledMapPrivate::updateTile(const QGeoTileSpec &spec) { Q_Q(QGeoTiledMap); // Only promote the texture up to GPU if it is visible - if (m_cameraTiles->visibleTiles().contains(spec)){ + if (m_visibleTiles->createTiles().contains(spec)){ QSharedPointer tex = m_tileRequests->tileTexture(spec); if (!tex.isNull()) { m_mapScene->addTile(spec, tex); diff --git a/src/location/maps/qgeotiledmap_p.h b/src/location/maps/qgeotiledmap_p.h index 87dac5d1..9e1d1596 100644 --- a/src/location/maps/qgeotiledmap_p.h +++ b/src/location/maps/qgeotiledmap_p.h @@ -74,14 +74,15 @@ class Q_LOCATION_EXPORT QGeoTiledMap : public QGeoMap { Q_OBJECT Q_DECLARE_PRIVATE(QGeoTiledMap) - public: + enum PrefetchStyle { PrefetchNeighbourLayer, PrefetchTwoNeighbourLayers }; QGeoTiledMap(QGeoTiledMappingManagerEngine *engine, QObject *parent); virtual ~QGeoTiledMap(); QAbstractGeoTileCache *tileCache(); QGeoTileRequestManager *requestManager(); void updateTile(const QGeoTileSpec &spec); + void setPrefetchStyle(PrefetchStyle style); QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport = true) const Q_DECL_OVERRIDE; QDoubleVector2D coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport = true) const Q_DECL_OVERRIDE; diff --git a/src/location/maps/qgeotiledmap_p_p.h b/src/location/maps/qgeotiledmap_p_p.h index 2e93a006..15442bd1 100644 --- a/src/location/maps/qgeotiledmap_p_p.h +++ b/src/location/maps/qgeotiledmap_p_p.h @@ -93,9 +93,12 @@ private: private: QAbstractGeoTileCache *m_cache; - QGeoCameraTiles *m_cameraTiles; + QGeoCameraTiles *m_visibleTiles; + QGeoCameraTiles *m_prefetchTiles; QGeoMapScene *m_mapScene; QGeoTileRequestManager *m_tileRequests; + int m_maxZoomLevel; + QGeoTiledMap::PrefetchStyle m_prefetchStyle; Q_DISABLE_COPY(QGeoTiledMapPrivate) }; -- cgit v1.2.1