summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gyp/common.gypi1
-rw-r--r--include/mbgl/ios/MGLMapView.h3
-rw-r--r--include/mbgl/ios/MGLMapboxEvents.h1
-rw-r--r--include/mbgl/map/environment.hpp (renamed from src/mbgl/map/environment.hpp)0
-rw-r--r--include/mbgl/map/map.hpp7
-rw-r--r--ios/MapboxGL.podspec2
-rw-r--r--ios/app/MBXViewController.mm28
-rw-r--r--macosx/mapboxgl-app.gyp1
-rw-r--r--platform/ios/MGLMapView.mm53
-rw-r--r--platform/ios/MGLMapboxEvents.m6
-rw-r--r--platform/ios/MGLMetricsLocationManager.m1
-rw-r--r--src/mbgl/map/live_tile_data.cpp24
-rw-r--r--src/mbgl/map/map.cpp23
-rw-r--r--src/mbgl/map/source.cpp34
-rw-r--r--src/mbgl/map/source.hpp5
-rw-r--r--src/mbgl/map/tile_cache.cpp60
-rw-r--r--src/mbgl/map/tile_cache.hpp30
-rw-r--r--src/mbgl/map/tile_data.cpp6
m---------styles0
19 files changed, 246 insertions, 39 deletions
diff --git a/gyp/common.gypi b/gyp/common.gypi
index 01f1451dd0..fbfa687117 100644
--- a/gyp/common.gypi
+++ b/gyp/common.gypi
@@ -23,6 +23,7 @@
],
'GCC_WARN_PEDANTIC': 'YES',
'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.9',
},
}, {
'cflags_cc': [
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
index f2aef61830..8717b5e8a1 100644
--- a/include/mbgl/ios/MGLMapView.h
+++ b/include/mbgl/ios/MGLMapView.h
@@ -279,6 +279,9 @@ IB_DESIGNABLE
/** Toggle the current value of debugActive. */
- (void)toggleDebug;
+/** Empties the in-memory tile cache. */
+- (void)emptyMemoryCache;
+
/** Resets the map to the minimum zoom level, a center coordinate of (0, 0), and a northern heading. */
- (void)resetPosition;
diff --git a/include/mbgl/ios/MGLMapboxEvents.h b/include/mbgl/ios/MGLMapboxEvents.h
index b10d79e114..b68b9b1b33 100644
--- a/include/mbgl/ios/MGLMapboxEvents.h
+++ b/include/mbgl/ios/MGLMapboxEvents.h
@@ -11,6 +11,7 @@ extern NSString *const MGLEventKeyZoomLevel;
extern NSString *const MGLEventKeySpeed;
extern NSString *const MGLEventKeyCourse;
extern NSString *const MGLEventKeyAltitude;
+extern NSString *const MGLEventKeyHorizontalAccuracy;
extern NSString *const MGLEventKeyVerticalAccuracy;
extern NSString *const MGLEventKeyPushEnabled;
extern NSString *const MGLEventKeyEmailEnabled;
diff --git a/src/mbgl/map/environment.hpp b/include/mbgl/map/environment.hpp
index 1cd33a5e6d..1cd33a5e6d 100644
--- a/src/mbgl/map/environment.hpp
+++ b/include/mbgl/map/environment.hpp
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 38a37a8057..8c7f5f3961 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -152,6 +152,11 @@ public:
std::vector<uint32_t> getAnnotationsInBounds(const LatLngBounds&);
LatLngBounds getBoundsForAnnotations(const std::vector<uint32_t>&);
+ // Memory
+ void setSourceTileCacheSize(size_t);
+ size_t getSourceTileCacheSize() const { return sourceCacheSize; }
+ void onLowMemory();
+
// Debug
void setDebug(bool value);
void toggleDebug();
@@ -196,6 +201,8 @@ private:
void updateAnnotationTiles(const std::vector<TileID>&);
+ size_t sourceCacheSize;
+
enum class Mode : uint8_t {
None, // we're not doing any processing
Continuous, // continually updating map
diff --git a/ios/MapboxGL.podspec b/ios/MapboxGL.podspec
index 2df7062892..b67f7faee7 100644
--- a/ios/MapboxGL.podspec
+++ b/ios/MapboxGL.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'MapboxGL'
- m.version = '0.2.10'
+ m.version = '0.2.13'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa bindings.'
diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm
index 509cbbb0e6..13235beb07 100644
--- a/ios/app/MBXViewController.mm
+++ b/ios/app/MBXViewController.mm
@@ -11,8 +11,8 @@
static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000];
static NSArray *const kStyleNames = @[
- @"Emerald",
@"Mapbox Streets",
+ @"Emerald",
@"Light",
@"Dark",
@"Bright",
@@ -21,10 +21,6 @@ static NSArray *const kStyleNames = @[
@"Satellite",
];
-static NSDictionary *const kStyleFileNames = @{
- @"Mapbox Streets": @"mapbox-streets",
-};
-
static NSString *const kStyleVersion = @"7";
@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
@@ -149,6 +145,7 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
otherButtonTitles:@"Reset North",
@"Reset Position",
@"Toggle Debug",
+ @"Empty Memory",
@"Add 100 Points",
@"Add 1,000 Points",
@"Add 10,000 Points",
@@ -174,18 +171,22 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 3)
{
- [self parseFeaturesAddingCount:100];
+ [self.mapView emptyMemoryCache];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 4)
{
- [self parseFeaturesAddingCount:1000];
+ [self parseFeaturesAddingCount:100];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 5)
{
- [self parseFeaturesAddingCount:10000];
+ [self parseFeaturesAddingCount:1000];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 6)
{
+ [self parseFeaturesAddingCount:10000];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7)
+ {
[self.mapView removeAnnotations:self.mapView.annotations];
}
}
@@ -251,16 +252,11 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
if (index == [kStyleNames count]) index = 0;
styleName = [kStyleNames objectAtIndex:index];
}
-
- NSString *styleFileName = kStyleFileNames[styleName];
- if (!styleFileName) {
- styleFileName = [NSString stringWithFormat:@"%@-v%@",
- [styleName.lowercaseString stringByReplacingOccurrencesOfString:@" " withString:@"-"],
- kStyleVersion];
- }
self.mapView.styleURL = [NSURL URLWithString:
- [NSString stringWithFormat:@"asset://styles/%@.json", styleFileName]];
+ [NSString stringWithFormat:@"asset://styles/%@-v%@.json",
+ [[styleName lowercaseString] stringByReplacingOccurrencesOfString:@" " withString:@"-"],
+ kStyleVersion]];
[titleButton setTitle:styleName forState:UIControlStateNormal];
}
diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gyp
index 39aef6435b..9817f9d97c 100644
--- a/macosx/mapboxgl-app.gyp
+++ b/macosx/mapboxgl-app.gyp
@@ -54,7 +54,6 @@
'OTHER_LDFLAGS': [ '<@(ldflags)' ],
'SDKROOT': 'macosx',
'INFOPLIST_FILE': 'Info.plist',
- 'MACOSX_DEPLOYMENT_TARGET': '10.9',
'CLANG_ENABLE_OBJC_ARC': 'YES'
},
}
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index 68fae64fac..22a3457aa8 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -45,7 +45,7 @@ const std::string &defaultCacheDatabase() {
static dispatch_once_t loadGLExtensions;
-NSString *const MGLDefaultStyleName = @"Emerald";
+NSString *const MGLDefaultStyleName = @"mapbox-streets";
NSString *const MGLStyleVersion = @"7";
NSString *const MGLDefaultStyleMarkerSymbolName = @"default_marker";
NSString *const MGLMapboxAccessTokenManagerURLDisplayString = @"mapbox.com/account/apps";
@@ -67,6 +67,8 @@ static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
@property (nonatomic) EAGLContext *context;
@property (nonatomic) GLKView *glView;
+/** A static view to simulate and obscure `glView` when the app is in the background. */
+@property (nonatomic) UIView *glSnapshotView;
@property (nonatomic) NSOperationQueue *regionChangeDelegateQueue;
@property (nonatomic) UIImageView *compass;
@property (nonatomic) UIImageView *logoBug;
@@ -240,6 +242,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[self insertSubview:_glView atIndex:0];
_glView.contentMode = UIViewContentModeCenter;
+
[self setBackgroundColor:[UIColor clearColor]];
// load extensions
@@ -364,6 +367,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
//
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
// set initial position
//
@@ -559,8 +563,31 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// This is the delegate of the GLKView object's display call.
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
- mbglView->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
- mbglMap->renderSync();
+ if (self.glSnapshotView)
+ {
+ NSUInteger snapshotIdx = [self.subviews indexOfObject:self.glSnapshotView];
+ NSUInteger glIdx = [self.subviews indexOfObject:self.glView];
+ NSAssert(snapshotIdx > glIdx,
+ @"The snapshot view is the %luth subview, behind the GL view, which is the %luth. "
+ @"The snapshot view should obscure the GL view to prevent crashing while the app is in the background.",
+ (unsigned long)snapshotIdx, (unsigned long)glIdx);
+ }
+ else
+ {
+ mbglView->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
+
+ CGFloat zoomFactor = mbglMap->getMaxZoom() - mbglMap->getMinZoom() + 1;
+ CGFloat cpuFactor = (CGFloat)[[NSProcessInfo processInfo] processorCount];
+ CGFloat memoryFactor = (CGFloat)[[NSProcessInfo processInfo] physicalMemory] / 1000 / 1000 / 1000;
+ CGFloat sizeFactor = ((CGFloat)mbglMap->getState().getWidth() / mbgl::util::tileSize) *
+ ((CGFloat)mbglMap->getState().getHeight() / mbgl::util::tileSize);
+
+ NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
+
+ mbglMap->setSourceTileCacheSize(cacheSize);
+
+ mbglMap->renderSync();
+ }
}
// This gets called when the view dimension changes, e.g. because the device is being rotated.
@@ -583,6 +610,10 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
[MGLMapboxEvents flush];
+ self.glSnapshotView = [self.glView snapshotViewAfterScreenUpdates:YES];
+ self.glSnapshotView.autoresizingMask = self.glView.autoresizingMask;
+ [self insertSubview:self.glSnapshotView aboveSubview:self.glView];
+
mbglMap->stop();
[self.glView deleteDrawable];
@@ -593,6 +624,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[self.glView bindDrawable];
mbglMap->start();
+
+ [self.glSnapshotView removeFromSuperview];
+ self.glSnapshotView = nil;
}
- (void)tintColorDidChange
@@ -1144,6 +1178,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
mbglMap->toggleDebug();
}
+- (void)emptyMemoryCache
+{
+ mbglMap->onLowMemory();
+}
+
#pragma mark - Geography -
+ (NSSet *)keyPathsForValuesAffectingCenterCoordinate
@@ -2428,7 +2467,8 @@ class MBGLView : public mbgl::View
[EAGLContext setCurrentContext:nil];
}
- void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
+ void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight)
+ {
View::resize(width, height, ratio, fbWidth, fbHeight);
}
@@ -2544,4 +2584,9 @@ class MBGLView : public mbgl::View
self.rotateEnabled = allowsRotating;
}
+- (void)didReceiveMemoryWarning
+{
+ mbglMap->onLowMemory();
+}
+
@end
diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m
index 90822e6832..605c6a3fa0 100644
--- a/platform/ios/MGLMapboxEvents.m
+++ b/platform/ios/MGLMapboxEvents.m
@@ -24,6 +24,7 @@ NSString *const MGLEventKeyZoomLevel = @"zoom";
NSString *const MGLEventKeySpeed = @"speed";
NSString *const MGLEventKeyCourse = @"course";
NSString *const MGLEventKeyAltitude = @"altitude";
+NSString *const MGLEventKeyHorizontalAccuracy = @"horizontalAccuracy";
NSString *const MGLEventKeyVerticalAccuracy = @"verticalAccuracy";
NSString *const MGLEventKeyPushEnabled = @"enabled.push";
NSString *const MGLEventKeyEmailEnabled = @"enabled.email";
@@ -289,14 +290,13 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
// mapbox-events stock attributes
[evt setObject:event forKey:@"event"];
[evt setObject:@(1) forKey:@"version"];
- [evt setObject:[weakSelf formatDate:[NSDate date]] forKey:@"created"];
+ [evt setObject:[weakSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
[evt setObject:weakSelf.instanceID forKey:@"instance"];
[evt setObject:weakSelf.advertiserId forKey:@"advertiserId"];
[evt setObject:weakSelf.vendorId forKey:@"vendorId"];
[evt setObject:weakSelf.appBundleId forKeyedSubscript:@"appBundleId"];
// mapbox-events-ios stock attributes
- [evt setValue:[weakSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
[evt setValue:weakSelf.model forKey:@"model"];
[evt setValue:weakSelf.iOSVersion forKey:@"operatingSystem"];
[evt setValue:[weakSelf getDeviceOrientation] forKey:@"orientation"];
@@ -571,7 +571,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
// Can be called from any thread.
//
- (NSString *) getCurrentCellularNetworkConnectionType {
- CTTelephonyNetworkInfo *telephonyInfo = [CTTelephonyNetworkInfo new];
+ CTTelephonyNetworkInfo *telephonyInfo = [[CTTelephonyNetworkInfo alloc] init];
NSString *radioTech = telephonyInfo.currentRadioAccessTechnology;
if (radioTech == nil) {
diff --git a/platform/ios/MGLMetricsLocationManager.m b/platform/ios/MGLMetricsLocationManager.m
index b8b52f38d7..af9b0a71bc 100644
--- a/platform/ios/MGLMetricsLocationManager.m
+++ b/platform/ios/MGLMetricsLocationManager.m
@@ -47,6 +47,7 @@
MGLEventKeySpeed: @(loc.speed),
MGLEventKeyCourse: @(loc.course),
MGLEventKeyAltitude: @(loc.altitude),
+ MGLEventKeyHorizontalAccuracy: @(loc.horizontalAccuracy),
MGLEventKeyVerticalAccuracy: @(loc.verticalAccuracy)
}];
}
diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp
index 6456e51538..8b4f46deb5 100644
--- a/src/mbgl/map/live_tile_data.cpp
+++ b/src/mbgl/map/live_tile_data.cpp
@@ -31,13 +31,14 @@ void LiveTileData::parse() {
return;
}
- try {
- if (!style) {
- throw std::runtime_error("style isn't present in LiveTileData object anymore");
- }
+ const LiveTile* tile = annotationManager.getTile(id);
+
+ if (tile) {
+ try {
+ if (!style) {
+ throw std::runtime_error("style isn't present in LiveTileData object anymore");
+ }
- const LiveTile* tile = annotationManager.getTile(id);
- if (tile) {
// Parsing creates state that is encapsulated in TileParser. While parsing,
// the TileParser object writes results into this objects. All other state
// is going to be discarded afterwards.
@@ -47,13 +48,16 @@ void LiveTileData::parse() {
style.reset();
parser.parse();
- } else {
+ } catch (const std::exception& ex) {
+ Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
state = State::obsolete;
+ return;
}
- } catch (const std::exception& ex) {
- Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
+ } else {
+ // Clear the style so that we don't have a cycle in the shared_ptr references.
+ style.reset();
+
state = State::obsolete;
- return;
}
if (state != State::obsolete) {
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index f76845e3a7..adaf22d60f 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -818,3 +818,26 @@ void Map::render() {
triggerUpdate();
}
}
+
+void Map::setSourceTileCacheSize(size_t size) {
+ if (size != getSourceTileCacheSize()) {
+ invokeTask([=] {
+ sourceCacheSize = size;
+ if (!style) return;
+ for (const auto &source : style->sources) {
+ source->setCacheSize(sourceCacheSize);
+ }
+ env->performCleanup();
+ });
+ }
+}
+
+void Map::onLowMemory() {
+ invokeTask([=] {
+ if (!style) return;
+ for (const auto &source : style->sources) {
+ source->onLowMemory();
+ }
+ env->performCleanup();
+ });
+};
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index 29f84171d1..3245bab2e3 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -245,6 +245,10 @@ TileData::State Source::addTile(Map &map, Worker &worker,
}
if (!new_tile.data) {
+ new_tile.data = cache.get(id.to_uint64());
+ }
+
+ if (!new_tile.data) {
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
new_tile.data =
@@ -394,20 +398,33 @@ void Source::update(Map &map,
}
}
+ if (info.type != SourceType::Raster && cache.getSize() == 0) {
+ size_t conservativeCacheSize = ((float)map.getState().getWidth() / util::tileSize) *
+ ((float)map.getState().getHeight() / util::tileSize) *
+ (map.getMaxZoom() - map.getMinZoom() + 1) *
+ 0.5;
+ cache.setSize(conservativeCacheSize);
+ }
+
+ auto& tileCache = cache;
+ auto& type = info.type;
+
// Remove tiles that we definitely don't need, i.e. tiles that are not on
// the required list.
std::set<TileID> retain_data;
- util::erase_if(tiles, [&retain, &retain_data](std::pair<const TileID, std::unique_ptr<Tile>> &pair) {
+ util::erase_if(tiles, [&retain, &retain_data, &tileCache, &type](std::pair<const TileID, std::unique_ptr<Tile>> &pair) {
Tile &tile = *pair.second;
bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end();
if (!obsolete) {
retain_data.insert(tile.data->id);
+ } else if (type != SourceType::Raster && tile.data->ready()) {
+ tileCache.add(tile.id.to_uint64(), tile.data);
}
return obsolete;
});
// Remove all the expired pointers from the set.
- util::erase_if(tile_data, [&retain_data](std::pair<const TileID, std::weak_ptr<TileData>> &pair) {
+ util::erase_if(tile_data, [&retain_data, &tileCache](std::pair<const TileID, std::weak_ptr<TileData>> &pair) {
const util::ptr<TileData> tile = pair.second.lock();
if (!tile) {
return true;
@@ -415,7 +432,9 @@ void Source::update(Map &map,
bool obsolete = retain_data.find(tile->id) == retain_data.end();
if (obsolete) {
- tile->cancel();
+ if (!tileCache.has(tile->id.to_uint64())) {
+ tile->cancel();
+ }
return true;
} else {
return false;
@@ -426,10 +445,19 @@ void Source::update(Map &map,
}
void Source::invalidateTiles(const std::vector<TileID>& ids) {
+ cache.clear();
for (auto& id : ids) {
tiles.erase(id);
tile_data.erase(id);
}
}
+void Source::setCacheSize(size_t size) {
+ cache.setSize(size);
+}
+
+void Source::onLowMemory() {
+ cache.clear();
+}
+
}
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 625647ac5e..035e862284 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -3,6 +3,7 @@
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/tile_data.hpp>
+#include <mbgl/map/tile_cache.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/util/noncopyable.hpp>
@@ -72,6 +73,9 @@ public:
std::forward_list<Tile *> getLoadedTiles() const;
+ void setCacheSize(size_t);
+ void onLowMemory();
+
SourceInfo info;
bool enabled;
@@ -96,6 +100,7 @@ private:
std::map<TileID, std::unique_ptr<Tile>> tiles;
std::map<TileID, std::weak_ptr<TileData>> tile_data;
+ TileCache cache;
};
}
diff --git a/src/mbgl/map/tile_cache.cpp b/src/mbgl/map/tile_cache.cpp
new file mode 100644
index 0000000000..71eb500be6
--- /dev/null
+++ b/src/mbgl/map/tile_cache.cpp
@@ -0,0 +1,60 @@
+#include <mbgl/map/tile_cache.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+void TileCache::setSize(size_t size_) {
+ size = size_;
+
+ while (orderedKeys.size() > size) {
+ auto key = orderedKeys.front();
+ orderedKeys.pop_front();
+ tiles.erase(key);
+ }
+
+ assert(orderedKeys.size() <= size);
+
+ tiles.reserve(size);
+ orderedKeys.resize(size);
+}
+
+void TileCache::add(uint64_t key, std::shared_ptr<TileData> data) {
+
+ assert(tiles.find(key) == tiles.end());
+
+ tiles.emplace(key, data);
+ orderedKeys.push_back(key);
+
+ if (orderedKeys.size() > size) {
+ get(orderedKeys.front());
+ }
+
+ assert(orderedKeys.size() <= size);
+};
+
+std::shared_ptr<TileData> TileCache::get(uint64_t key) {
+
+ std::shared_ptr<TileData> data;
+
+ auto it = tiles.find(key);
+ if (it != tiles.end()) {
+ data = it->second;
+ tiles.erase(it);
+ orderedKeys.remove(key);
+ assert(data->ready());
+ }
+
+ return data;
+};
+
+bool TileCache::has(uint64_t key) {
+ return tiles.find(key) != tiles.end();
+}
+
+void TileCache::clear() {
+ orderedKeys.clear();
+ tiles.clear();
+}
+
+};
diff --git a/src/mbgl/map/tile_cache.hpp b/src/mbgl/map/tile_cache.hpp
new file mode 100644
index 0000000000..26d830c123
--- /dev/null
+++ b/src/mbgl/map/tile_cache.hpp
@@ -0,0 +1,30 @@
+#ifndef MBGL_MAP_TILE_CACHE
+#define MBGL_MAP_TILE_CACHE
+
+#include <mbgl/map/tile_data.hpp>
+
+#include <list>
+#include <unordered_map>
+
+namespace mbgl {
+
+class TileCache {
+public:
+ TileCache(size_t size_ = 0) : size(size_) {}
+
+ void setSize(size_t);
+ size_t getSize() const { return size; };
+ void add(uint64_t key, std::shared_ptr<TileData> data);
+ std::shared_ptr<TileData> get(uint64_t key);
+ bool has(uint64_t key);
+ void clear();
+private:
+ std::unordered_map<uint64_t, std::shared_ptr<TileData>> tiles;
+ std::list<uint64_t> orderedKeys;
+
+ size_t size;
+};
+
+};
+
+#endif
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index 72e356bbf2..bb8d18d12f 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -64,5 +64,9 @@ void TileData::reparse(Worker& worker, std::function<void()> callback) {
EnvironmentScope scope(tile->env, ThreadType::TileWorker, "TileWorker_" + tile->name);
tile->parse();
},
- callback);
+ [tile, callback]() {
+ // `tile` is bound in this lambda to ensure that if it's the last owning pointer,
+ // destruction happens on the map thread, not the worker thread.
+ callback();
+ });
}
diff --git a/styles b/styles
-Subproject 890d7dd5a18f06a8e475eaf9e2dcc67456ad3b2
+Subproject 86aecb8e997c54c9d2bf6ac494fea132e375ad0