diff options
29 files changed, 347 insertions, 464 deletions
diff --git a/.mason b/.mason -Subproject ace19da711199cb0283c6243b435575fbec16a9 +Subproject abdc131273c3e50e42f245b1166c27de4c34c58 diff --git a/.travis.yml b/.travis.yml index 1ac66b3862..e5b94a813a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,10 @@ matrix: sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] packages: [ 'gdb', 'clang-3.5', 'libstdc++-4.9-dev', 'libstdc++6', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ] - os: osx + osx_image: beta-xcode6.3 env: FLAVOR=osx BUILDTYPE=Debug - os: osx + osx_image: beta-xcode6.3 env: FLAVOR=ios BUILDTYPE=Release - os: linux env: FLAVOR=android ANDROID_ABI=arm-v7 BUILDTYPE=Release @@ -58,7 +58,15 @@ pod 'MapboxGL' #### Manually -1. Install [appledoc](http://appledoc.gentlebytes.com/appledoc/) for API docs generation. We recommend [`2.2v963`](https://github.com/tomaz/appledoc/releases/tag/v2.2-963), which currently isn't available in Homebrew. +1. Install [appledoc](http://appledoc.gentlebytes.com/appledoc/) 2.2v963 for API docs generation (only this exact version works). + + ``` + curl -L -o appledoc.zip https://github.com/tomaz/appledoc/releases/download/v2.2-963/appledoc.zip + unzip appledoc.zip + cp appledoc /usr/local/bin + cp -Rf Templates/ ~/.appledoc + ``` + 1. Run `make ipackage`. The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, a `Headers` folder, and a `Docs` folder with HTML API documentation. 1. Copy the contents of `build/ios/pkg/static` into your project. It should happen automatically, but ensure that: - `Headers` is in your *Header Search Paths* (`HEADER_SEARCH_PATHS`) build setting. @@ -82,6 +90,7 @@ pod 'MapboxGL' If you want to build from source and/or contribute to development of the project, run `make iproj`, which will create and open an Xcode project which can build the entire library from source as well as an Objective-C test app. If you don't have an Apple Developer account, change the destination from "My Mac" to a simulator such as "iPhone 6" before you run and build the app. +#### Testing You can run `make itest` to run the included integration tests. Requires `gem install xcpretty`. If you want to run the tests in Xcode instead, first `make ipackage` to create a local static library version, then open `test/ios/ios-tests.xcodeproj`, and lastly `Command + U` on the `Mapbox GL Tests` application target. Target devices: iPhone 4S and above (5, 5c, 5s, 6, 6 Plus) and iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2). diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index 8cdd1d50a9..c80186da27 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -29,7 +29,7 @@ enum class Event : uint8_t { ParseStyle, ParseTile, Render, - ResourceLoader, + Style, Database, HttpRequest, Sprite, @@ -47,7 +47,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { { Event::ParseStyle, "ParseStyle" }, { Event::ParseTile, "ParseTile" }, { Event::Render, "Render" }, - { Event::ResourceLoader, "ResourceLoader" }, + { Event::Style, "Style" }, { Event::Database, "Database" }, { Event::HttpRequest, "HttpRequest" }, { Event::Sprite, "Sprite" }, diff --git a/ios/benchmark/MBXBenchViewController.mm b/ios/benchmark/MBXBenchViewController.mm index dfd3b254a3..8883ebff0f 100644 --- a/ios/benchmark/MBXBenchViewController.mm +++ b/ios/benchmark/MBXBenchViewController.mm @@ -33,7 +33,7 @@ [super viewDidLoad]; NSURL* url = [[NSURL alloc] initWithString:@"asset://styles/mapbox-streets-v7.json"]; - self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds accessToken:nil styleURL:url]; + self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds styleURL:url]; self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.mapView.delegate = self; self.mapView.zoomEnabled = NO; diff --git a/ios/docs/install_docs.sh b/ios/docs/install_docs.sh index 7d4bf279f3..8fa768b997 100755 --- a/ios/docs/install_docs.sh +++ b/ios/docs/install_docs.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash if [ -z `which appledoc` ]; then - echo "Unable to find appledoc. Consider installing it from source or Homebrew." + echo "Unable to find appledoc. See https://github.com/mapbox/mapbox-gl-native#manually" exit 1 fi diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m index e7f400c5dc..bf730af945 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -148,6 +148,8 @@ const NSTimeInterval MGLFlushInterval = 60; @property (atomic) NSURLSession *session; @property (atomic) NSData *digicertCert; @property (atomic) NSData *geoTrustCert; +@property (atomic) NSData *testServerCert; +@property (atomic) BOOL usesTestServer; // Main thread only @property (nonatomic) CLLocationManager *locationManager; @@ -239,6 +241,10 @@ const NSTimeInterval MGLFlushInterval = 60; NSString *testURL = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMetricsTestServerURL"]; if (testURL != nil) { MGLMapboxEventsAPIBase = testURL; + _usesTestServer = YES; + } else { + // Explicitly Set For Clarity + _usesTestServer = NO; } _paused = YES; @@ -256,6 +262,10 @@ const NSTimeInterval MGLFlushInterval = 60; if (cerPath != nil) { _digicertCert = [NSData dataWithContentsOfFile:cerPath]; } + cerPath = [resourceBundle pathForResource:@"star_tilestream_net" ofType:@"der"]; + if (cerPath != nil) { + _testServerCert = [NSData dataWithContentsOfFile:cerPath]; + } // Events Control _eventQueue = [[NSMutableArray alloc] init]; @@ -848,7 +858,7 @@ const NSTimeInterval MGLFlushInterval = 60; // Look for a pinned certificate in the server's certificate chain long numKeys = SecTrustGetCertificateCount(serverTrust); - BOOL found = false; + BOOL found = NO; // Try GeoTrust Cert First for (int lc = 0; lc < numKeys; lc++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); @@ -858,7 +868,7 @@ const NSTimeInterval MGLFlushInterval = 60; if ([remoteCertificateData isEqualToData:_geoTrustCert]) { // Found the certificate; continue connecting completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - found = true; + found = YES; break; } } @@ -873,11 +883,27 @@ const NSTimeInterval MGLFlushInterval = 60; if ([remoteCertificateData isEqualToData:_digicertCert]) { // Found the certificate; continue connecting completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - found = true; + found = YES; break; } } + if (!found && _usesTestServer) { + // See if this is test server + for (int lc = 0; lc < numKeys; lc++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); + NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); + + // Compare Remote Key With Local Version + if ([remoteCertificateData isEqualToData:_testServerCert]) { + // Found the certificate; continue connecting + completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + found = YES; + break; + } + } + } + if (!found) { // The certificate wasn't found in GeoTrust nor Digicert. Cancel the connection. completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); diff --git a/platform/ios/resources/star_tilestream_net.der b/platform/ios/resources/star_tilestream_net.der Binary files differnew file mode 100644 index 0000000000..e302cd7686 --- /dev/null +++ b/platform/ios/resources/star_tilestream_net.der diff --git a/scripts/ios/install.sh b/scripts/ios/install.sh index ffa714f36f..da081254f8 100755 --- a/scripts/ios/install.sh +++ b/scripts/ios/install.sh @@ -15,4 +15,10 @@ brew install git mapbox_time "install_awscli" \ brew install awscli +mapbox_time "install_appledoc" \ +wget https://github.com/tomaz/appledoc/releases/download/v2.2-963/appledoc.zip && \ +tar xvzf appledoc.zip && \ +cp appledoc /usr/local/bin && \ +cp -Rf Templates/ ~/.appledoc + mkdir -p ${KIF_SCREENSHOTS} diff --git a/scripts/ios/package.sh b/scripts/ios/package.sh index 35162ce5d1..e4bba0e9f6 100755 --- a/scripts/ios/package.sh +++ b/scripts/ios/package.sh @@ -98,7 +98,7 @@ cp -prv styles/styles "${OUTPUT}/static/${NAME}.bundle/styles" step "Creating API Docs..." if [ -z `which appledoc` ]; then - echo "Unable to find appledoc. Consider installing it from source or Homebrew." + echo "Unable to find appledoc. See https://github.com/mapbox/mapbox-gl-native#manually" exit 1 fi DOCS_OUTPUT="${OUTPUT}/static/Docs" diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index c8948097f7..a834f50655 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -2,19 +2,12 @@ #include <mbgl/map/map_data.hpp> #include <mbgl/map/view.hpp> #include <mbgl/map/environment.hpp> -#include <mbgl/map/source.hpp> #include <mbgl/map/still_image.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/renderer/painter.hpp> -#include <mbgl/text/glyph_store.hpp> - -#include <mbgl/geometry/glyph_atlas.hpp> -#include <mbgl/geometry/sprite_atlas.hpp> -#include <mbgl/geometry/line_atlas.hpp> - #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> @@ -34,10 +27,6 @@ MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, Map envScope(env, ThreadType::Map, "Map"), updated(static_cast<UpdateType>(Update::Nothing)), asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })), - glyphStore(std::make_unique<GlyphStore>(loop, env)), - glyphAtlas(std::make_unique<GlyphAtlas>(1024, 1024)), - spriteAtlas(std::make_unique<SpriteAtlas>(512, 512)), - lineAtlas(std::make_unique<LineAtlas>(512, 512)), texturePool(std::make_unique<TexturePool>()) { assert(Environment::currentlyOn(ThreadType::Map)); @@ -51,14 +40,9 @@ MapContext::~MapContext() { // Explicit resets currently necessary because these abandon resources that need to be // cleaned up by env.performCleanup(); - resourceLoader.reset(); style.reset(); painter.reset(); texturePool.reset(); - lineAtlas.reset(); - spriteAtlas.reset(); - glyphAtlas.reset(); - glyphStore.reset(); env.performCleanup(); @@ -116,21 +100,11 @@ void MapContext::setStyleJSON(const std::string& json, const std::string& base) void MapContext::loadStyleJSON(const std::string& json, const std::string& base) { assert(Environment::currentlyOn(ThreadType::Map)); - resourceLoader.reset(); style.reset(); - - style = std::make_unique<Style>(); - style->base = base; - style->loadJSON((const uint8_t *)json.c_str()); + style = std::make_unique<Style>(json, base, asyncUpdate->get()->loop, env); style->cascade(data.getClasses()); style->setDefaultTransitionDuration(data.getDefaultTransitionDuration()); - - glyphStore->setURL(style->glyph_url); - - resourceLoader = std::make_unique<ResourceLoader>(); - resourceLoader->setObserver(this); - resourceLoader->setStyle(style.get()); - resourceLoader->setGlyphStore(glyphStore.get()); + style->setObserver(this); triggerUpdate(Update::Zoom); } @@ -138,7 +112,7 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) void MapContext::updateTiles() { assert(Environment::currentlyOn(ThreadType::Map)); - resourceLoader->update(data, transformState, *glyphAtlas, *spriteAtlas, *texturePool); + style->update(data, transformState, *texturePool); } void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) { @@ -177,7 +151,7 @@ void MapContext::update() { updateTiles(); - if (style->isLoaded() && resourceLoader->getSprite()->isLoaded()) { + if (style->isLoaded()) { if (!data.getFullyLoaded()) { data.setFullyLoaded(true); } @@ -220,7 +194,7 @@ void MapContext::render() { assert(style); if (!painter) { - painter = std::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas); + painter = std::make_unique<Painter>(); painter->setup(); } @@ -240,7 +214,7 @@ void MapContext::render() { double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) { assert(Environment::currentlyOn(ThreadType::Map)); - const SpritePosition pos = resourceLoader->getSprite()->getSpritePosition(symbol); + const SpritePosition pos = style->sprite->getSpritePosition(symbol); return -pos.height / pos.pixelRatio / 2; } diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index fb9fdb4d4b..8894434242 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -4,8 +4,8 @@ #include <mbgl/map/tile_id.hpp> #include <mbgl/map/update.hpp> #include <mbgl/map/environment.hpp> -#include <mbgl/map/resource_loader.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/util/ptr.hpp> #include <vector> @@ -20,20 +20,15 @@ namespace mbgl { class View; class MapData; -class GlyphStore; -class GlyphAtlas; -class SpriteAtlas; -class LineAtlas; class TexturePool; class Painter; class Sprite; -class Style; class Worker; class StillImage; struct LatLng; struct LatLngBounds; -class MapContext : public ResourceLoader::Observer { +class MapContext : public Style::Observer { public: MapContext(uv_loop_t*, View&, FileSource&, MapData&); ~MapContext(); @@ -59,7 +54,7 @@ public: void setSourceTileCacheSize(size_t size); void onLowMemory(); - // ResourceLoader::Observer implementation. + // Style::Observer implementation. void onTileDataChanged() override; void onResourceLoadingFailed(std::exception_ptr error) override; @@ -81,14 +76,9 @@ private: UpdateType updated { static_cast<UpdateType>(Update::Nothing) }; std::unique_ptr<uv::async> asyncUpdate; - std::unique_ptr<GlyphStore> glyphStore; - std::unique_ptr<GlyphAtlas> glyphAtlas; - std::unique_ptr<SpriteAtlas> spriteAtlas; - std::unique_ptr<LineAtlas> lineAtlas; std::unique_ptr<TexturePool> texturePool; std::unique_ptr<Painter> painter; std::unique_ptr<Style> style; - std::unique_ptr<ResourceLoader> resourceLoader; std::string styleURL; std::string styleJSON; diff --git a/src/mbgl/map/resource_loader.cpp b/src/mbgl/map/resource_loader.cpp deleted file mode 100644 index 8e054c0d82..0000000000 --- a/src/mbgl/map/resource_loader.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include <mbgl/map/resource_loader.hpp> - -#include <mbgl/geometry/sprite_atlas.hpp> -#include <mbgl/map/environment.hpp> -#include <mbgl/map/source.hpp> -#include <mbgl/map/sprite.hpp> -#include <mbgl/map/transform.hpp> -#include <mbgl/style/style.hpp> - -#include <cassert> - -namespace mbgl { - -ResourceLoader::ResourceLoader() { - assert(Environment::currentlyOn(ThreadType::Map)); -} - -ResourceLoader::~ResourceLoader() { - assert(Environment::currentlyOn(ThreadType::Map)); - - for (const auto& source : style_->sources) { - source->setObserver(nullptr); - } - - if (sprite_) { - sprite_->setObserver(nullptr); - } - - if (glyphStore_) { - glyphStore_->setObserver(nullptr); - } -} - -void ResourceLoader::setObserver(Observer* observer) { - assert(Environment::currentlyOn(ThreadType::Map)); - assert(!observer_); - - observer_ = observer; -} - -void ResourceLoader::setStyle(Style* style) { - assert(style); - - style_ = style; - - for (const auto& source : style->sources) { - source->setObserver(this); - source->load(); - } -} - -void ResourceLoader::setGlyphStore(GlyphStore* glyphStore) { - assert(glyphStore); - - if (glyphStore_) { - glyphStore_->setObserver(nullptr); - } - - glyphStore_ = glyphStore; - glyphStore_->setObserver(this); -} - -void ResourceLoader::update(MapData& data, - const TransformState& transform, - GlyphAtlas& glyphAtlas, - SpriteAtlas& spriteAtlas, - TexturePool& texturePool) { - if (!style_) { - return; - } - - const float pixelRatio = transform.getPixelRatio(); - if (!sprite_ || !sprite_->hasPixelRatio(pixelRatio)) { - sprite_ = std::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio); - sprite_->setObserver(this); - - spriteAtlas.resize(pixelRatio); - spriteAtlas.setSprite(sprite_); - } - - bool allTilesUpdated = true; - for (const auto& source : style_->sources) { - if (!source->update(data, transform, *style_, glyphAtlas, *glyphStore_, - spriteAtlas, sprite_, texturePool, shouldReparsePartialTiles_)) { - allTilesUpdated = false; - } - } - - // We can only stop updating "partial" tiles when all of them - // were notified of the arrival of the new resources. - if (allTilesUpdated) { - shouldReparsePartialTiles_ = false; - } -} - -void ResourceLoader::onGlyphRangeLoaded() { - shouldReparsePartialTiles_ = true; - - emitTileDataChanged(); -} - -void ResourceLoader::onGlyphRangeLoadingFailed(std::exception_ptr error) { - emitResourceLoadingFailed(error); -} - -void ResourceLoader::onSourceLoaded() { - emitTileDataChanged(); -} - -void ResourceLoader::onSourceLoadingFailed(std::exception_ptr error) { - emitResourceLoadingFailed(error); -} - -void ResourceLoader::onTileLoaded(bool isNewTile) { - if (isNewTile) { - shouldReparsePartialTiles_ = true; - } - - emitTileDataChanged(); -} - -void ResourceLoader::onTileLoadingFailed(std::exception_ptr error) { - emitResourceLoadingFailed(error); -} - -void ResourceLoader::onSpriteLoaded() { - shouldReparsePartialTiles_ = true; - - emitTileDataChanged(); -} - -void ResourceLoader::onSpriteLoadingFailed(std::exception_ptr error) { - emitResourceLoadingFailed(error); -} - -void ResourceLoader::emitTileDataChanged() { - assert(Environment::currentlyOn(ThreadType::Map)); - - if (observer_) { - observer_->onTileDataChanged(); - } -} - -void ResourceLoader::emitResourceLoadingFailed(std::exception_ptr error) { - assert(Environment::currentlyOn(ThreadType::Map)); - - try { - if (error) { - std::rethrow_exception(error); - } - } catch(const std::exception& e) { - Log::Error(Event::ResourceLoader, e.what()); - } - - if (observer_) { - observer_->onResourceLoadingFailed(error); - } -} - -} diff --git a/src/mbgl/map/resource_loader.hpp b/src/mbgl/map/resource_loader.hpp deleted file mode 100644 index 525e4653a0..0000000000 --- a/src/mbgl/map/resource_loader.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef MBGL_MAP_RESOURCE_LOADER -#define MBGL_MAP_RESOURCE_LOADER - -#include <mbgl/map/source.hpp> -#include <mbgl/map/sprite.hpp> -#include <mbgl/text/glyph_store.hpp> -#include <mbgl/util/noncopyable.hpp> - -#include <string> - -namespace mbgl { - -class GlyphAtlas; -class GlyphStore; -class MapData; -class SpriteAtlas; -class Style; -class TexturePool; -class TransformState; - -// ResourceLoader is responsible for loading and updating the Source(s) owned -// by the Style. The Source object currently owns all the tiles, thus this -// class will notify its observers of any change on these tiles which will -// ultimately cause a new rendering to be triggered. -class ResourceLoader : public GlyphStore::Observer, - public Source::Observer, - public Sprite::Observer, - private util::noncopyable { -public: - class Observer { - public: - virtual ~Observer() = default; - - virtual void onTileDataChanged() = 0; - virtual void onResourceLoadingFailed(std::exception_ptr error) = 0; - }; - - ResourceLoader(); - ~ResourceLoader(); - - void setObserver(Observer* observer); - - // The style object currently owns all the sources. When setting - // a new style we will go through all of them and try to load. - void setStyle(Style* style); - - // TODO: Move GlyphStore to ResourceLoader. We cannot do it now - // because we reset the ResourceLoader every time we change the - // style. - void setGlyphStore(GlyphStore* glyphStore); - - // Fetch the tiles needed by the current viewport and emit a signal when - // a tile is ready so observers can render the tile. - void update(MapData&, const TransformState&, GlyphAtlas&, SpriteAtlas&, TexturePool&); - - // FIXME: There is probably a better place for this. - inline util::ptr<Sprite> getSprite() const { - return sprite_; - } - - // GlyphStore::Observer implementation. - void onGlyphRangeLoaded() override; - void onGlyphRangeLoadingFailed(std::exception_ptr error) override; - - // Source::Observer implementation. - void onSourceLoaded() override; - void onSourceLoadingFailed(std::exception_ptr error) override; - void onTileLoaded(bool isNewTile) override; - void onTileLoadingFailed(std::exception_ptr error) override; - - // Sprite::Observer implementation. - void onSpriteLoaded() override; - void onSpriteLoadingFailed(std::exception_ptr error) override; - -private: - void emitTileDataChanged(); - void emitResourceLoadingFailed(std::exception_ptr error); - - bool shouldReparsePartialTiles_ = false; - - util::ptr<Sprite> sprite_; - - GlyphStore* glyphStore_ = nullptr; - Style* style_ = nullptr; - - Observer* observer_ = nullptr; -}; - -} - -#endif diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 977b6e565f..43d06b69d1 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -42,11 +42,7 @@ using namespace mbgl; #define BUFFER_OFFSET(i) ((char *)nullptr + (i)) -Painter::Painter(SpriteAtlas& spriteAtlas_, GlyphAtlas& glyphAtlas_, LineAtlas& lineAtlas_) - : spriteAtlas(spriteAtlas_) - , glyphAtlas(glyphAtlas_) - , lineAtlas(lineAtlas_) -{ +Painter::Painter() { } Painter::~Painter() { @@ -173,6 +169,10 @@ void Painter::prepareTile(const Tile& tile) { void Painter::render(const Style& style, TransformState state_, TimePoint time) { state = state_; + glyphAtlas = style.glyphAtlas.get(); + spriteAtlas = style.spriteAtlas.get(); + lineAtlas = style.lineAtlas.get(); + std::set<Source*> sources; for (const auto& source : style.sources) { if (source->enabled) { @@ -193,9 +193,9 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time) tileStencilBuffer.upload(); tileBorderBuffer.upload(); - spriteAtlas.upload(); - lineAtlas.upload(); - glyphAtlas.upload(); + spriteAtlas->upload(); + lineAtlas->upload(); + glyphAtlas->upload(); for (const auto& item : order) { if (item.bucket && item.bucket->needsUpload()) { @@ -391,8 +391,8 @@ void Painter::renderBackground(const StyleLayer &layer_desc) { if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque)) return; - SpriteAtlasPosition imagePosA = spriteAtlas.getPosition(properties.image.from, true); - SpriteAtlasPosition imagePosB = spriteAtlas.getPosition(properties.image.to, true); + SpriteAtlasPosition imagePosA = spriteAtlas->getPosition(properties.image.from, true); + SpriteAtlasPosition imagePosB = spriteAtlas->getPosition(properties.image.to, true); float zoomFraction = state.getZoomFraction(); useProgram(patternShader->program); @@ -441,7 +441,7 @@ void Painter::renderBackground(const StyleLayer &layer_desc) { backgroundBuffer.bind(); patternShader->bind(0); - spriteAtlas.bind(true); + spriteAtlas->bind(true); } else { Color color = properties.color; color[0] *= properties.opacity; diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 29f17108d3..465bf5ba33 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -79,7 +79,7 @@ struct RenderItem { class Painter : private util::noncopyable { public: - Painter(SpriteAtlas&, GlyphAtlas&, LineAtlas&); + Painter(); ~Painter(); void setup(); @@ -204,9 +204,9 @@ private: public: FrameHistory frameHistory; - SpriteAtlas& spriteAtlas; - GlyphAtlas& glyphAtlas; - LineAtlas& lineAtlas; + SpriteAtlas* spriteAtlas; + GlyphAtlas* glyphAtlas; + LineAtlas* lineAtlas; std::unique_ptr<PlainShader> plainShader; std::unique_ptr<OutlineShader> outlineShader; diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index b2fdafadbd..5514ebc858 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -63,8 +63,8 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const // Image fill. if (pass == RenderPass::Translucent) { - const SpriteAtlasPosition posA = spriteAtlas.getPosition(properties.image.from, true); - const SpriteAtlasPosition posB = spriteAtlas.getPosition(properties.image.to, true); + const SpriteAtlasPosition posA = spriteAtlas->getPosition(properties.image.from, true); + const SpriteAtlasPosition posB = spriteAtlas->getPosition(properties.image.to, true); float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) / id.overscaling; mat3 patternMatrixA; @@ -91,7 +91,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const patternShader->u_patternmatrix_b = patternMatrixB; MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); - spriteAtlas.bind(true); + spriteAtlas->bind(true); // Draw the actual triangles into the color & stencil buffer. config.depthMask = GL_TRUE; diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index 612e04e121..e084147deb 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -66,9 +66,9 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linesdfShader->u_blur = blur; linesdfShader->u_color = color; - LinePatternPos posA = lineAtlas.getDashPosition(properties.dash_array.from, layout.cap == CapType::Round); - LinePatternPos posB = lineAtlas.getDashPosition(properties.dash_array.to, layout.cap == CapType::Round); - lineAtlas.bind(); + LinePatternPos posA = lineAtlas->getDashPosition(properties.dash_array.from, layout.cap == CapType::Round); + LinePatternPos posB = lineAtlas->getDashPosition(properties.dash_array.to, layout.cap == CapType::Round); + lineAtlas->bind(); float patternratio = std::pow(2.0, std::floor(std::log2(state.getScale())) - id.z) / 8.0 * id.overscaling; float scaleXA = patternratio / posA.width / properties.dash_line_width / properties.dash_array.fromScale; @@ -81,14 +81,14 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linesdfShader->u_patternscale_b = {{ scaleXB, scaleYB }}; linesdfShader->u_tex_y_b = posB.y; linesdfShader->u_image = 0; - linesdfShader->u_sdfgamma = lineAtlas.width / (properties.dash_line_width * std::min(posA.width, posB.width) * 256.0 * state.getPixelRatio()) / 2; + linesdfShader->u_sdfgamma = lineAtlas->width / (properties.dash_line_width * std::min(posA.width, posB.width) * 256.0 * state.getPixelRatio()) / 2; linesdfShader->u_mix = properties.dash_array.t; bucket.drawLineSDF(*linesdfShader); } else if (properties.image.from.size()) { - SpriteAtlasPosition imagePosA = spriteAtlas.getPosition(properties.image.from, true); - SpriteAtlasPosition imagePosB = spriteAtlas.getPosition(properties.image.to, true); + SpriteAtlasPosition imagePosA = spriteAtlas->getPosition(properties.image.from, true); + SpriteAtlasPosition imagePosB = spriteAtlas->getPosition(properties.image.to, true); float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) * id.overscaling; @@ -110,7 +110,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linepatternShader->u_opacity = properties.opacity; MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); - spriteAtlas.bind(true); + spriteAtlas->bind(true); config.depthRange = { strata + strata_epsilon, 1.0f }; // may or may not matter bucket.drawLinePatterns(*linepatternShader); diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index dea6b8a6e6..4f4999b3e2 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -162,7 +162,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c const float fontSize = properties.icon.size != 0 ? properties.icon.size : layout.icon.max_size; const float fontScale = fontSize / 1.0f; - spriteAtlas.bind(state.isChanging() || layout.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1 || sdf); + spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1 || sdf); if (sdf) { renderSDF(bucket, @@ -171,7 +171,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c layout.icon, properties.icon, 1.0f, - {{ float(spriteAtlas.getWidth()) / 4.0f, float(spriteAtlas.getHeight()) / 4.0f }}, + {{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }}, *sdfIconShader, &SymbolBucket::drawIcons); } else { @@ -189,7 +189,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c useProgram(iconShader->program); iconShader->u_matrix = vtxMatrix; iconShader->u_exmatrix = exMatrix; - iconShader->u_texsize = {{ float(spriteAtlas.getWidth()) / 4.0f, float(spriteAtlas.getHeight()) / 4.0f }}; + iconShader->u_texsize = {{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }}; // adjust min/max zooms for variable font sies float zoomAdjust = std::log(fontSize / layout.icon.max_size) / std::log(2); @@ -207,7 +207,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c } if (bucket.hasTextData()) { - glyphAtlas.bind(); + glyphAtlas->bind(); renderSDF(bucket, id, @@ -215,7 +215,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c layout.text, properties.text, 24.0f, - {{ float(glyphAtlas.width) / 4, float(glyphAtlas.height) / 4 }}, + {{ float(glyphAtlas->width) / 4, float(glyphAtlas->height) / 4 }}, *sdfGlyphShader, &SymbolBucket::drawGlyphs); } diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 0acf66eb56..8999af3c7f 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -1,9 +1,13 @@ #include <mbgl/style/style.hpp> #include <mbgl/map/sprite.hpp> #include <mbgl/map/source.hpp> +#include <mbgl/map/transform_state.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_parser.hpp> #include <mbgl/style/style_bucket.hpp> +#include <mbgl/geometry/glyph_atlas.hpp> +#include <mbgl/geometry/sprite_atlas.hpp> +#include <mbgl/geometry/line_atlas.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/platform/log.hpp> @@ -15,15 +19,77 @@ namespace mbgl { -Style::Style() - : mtx(std::make_unique<uv::rwlock>()), +Style::Style(const std::string& data, const std::string&, + uv_loop_t* loop, Environment& env) + : glyphStore(std::make_unique<GlyphStore>(loop, env)), + glyphAtlas(std::make_unique<GlyphAtlas>(1024, 1024)), + spriteAtlas(std::make_unique<SpriteAtlas>(512, 512)), + lineAtlas(std::make_unique<LineAtlas>(512, 512)), + mtx(std::make_unique<uv::rwlock>()), workers(4) { + + rapidjson::Document doc; + doc.Parse<0>((const char *const)data.c_str()); + if (doc.HasParseError()) { + Log::Error(Event::ParseStyle, "Error parsing style JSON at %i: %s", doc.GetErrorOffset(), doc.GetParseError()); + return; + } + + StyleParser parser; + parser.parse(doc); + + sources = parser.getSources(); + layers = parser.getLayers(); + + spriteURL = parser.getSprite(); + glyphStore->setURL(parser.getGlyphURL()); + + for (const auto& source : sources) { + source->setObserver(this); + source->load(); + } + + glyphStore->setObserver(this); } -// Note: This constructor is seemingly empty, but we need to declare it anyway -// because this file includes uv_detail.hpp, which has the declarations necessary -// for deleting the std::unique_ptr<uv::rwlock>. -Style::~Style() {} +Style::~Style() { + for (const auto& source : sources) { + source->setObserver(nullptr); + } + + glyphStore->setObserver(nullptr); + + if (sprite) { + sprite->setObserver(nullptr); + } +} + +void Style::update(MapData& data, + const TransformState& transform, + TexturePool& texturePool) { + const float pixelRatio = transform.getPixelRatio(); + if (!sprite || !sprite->hasPixelRatio(pixelRatio)) { + sprite = std::make_unique<Sprite>(spriteURL, pixelRatio); + sprite->setObserver(this); + + spriteAtlas->resize(pixelRatio); + spriteAtlas->setSprite(sprite); + } + + bool allTilesUpdated = true; + for (const auto& source : sources) { + if (!source->update(data, transform, *this, *glyphAtlas, *glyphStore, + *spriteAtlas, sprite, texturePool, shouldReparsePartialTiles)) { + allTilesUpdated = false; + } + } + + // We can only stop updating "partial" tiles when all of them + // were notified of the arrival of the new resources. + if (allTilesUpdated) { + shouldReparsePartialTiles = false; + } +} void Style::cascade(const std::vector<std::string>& classes) { TimePoint now = Clock::now(); @@ -50,10 +116,6 @@ void Style::recalculate(float z, TimePoint now) { } } -const std::string &Style::getSpriteURL() const { - return sprite_url; -} - void Style::setDefaultTransitionDuration(Duration duration) { defaultTransition.duration = duration; } @@ -67,44 +129,89 @@ bool Style::hasTransitions() const { return false; } -void Style::loadJSON(const uint8_t *const data) { - uv::writelock lock(mtx); +bool Style::isLoaded() const { + for (const auto& source : sources) { + if (!source->isLoaded()) { + return false; + } + } - rapidjson::Document doc; - doc.Parse<0>((const char *const)data); - if (doc.HasParseError()) { - Log::Error(Event::ParseStyle, "Error parsing style JSON at %i: %s", doc.GetErrorOffset(), doc.GetParseError()); - return; + if (sprite && !sprite->isLoaded()) { + return false; } - StyleParser parser; - parser.parse(doc); + return true; +} - sources = parser.getSources(); - layers = parser.getLayers(); - sprite_url = parser.getSprite(); - glyph_url = parser.getGlyphURL(); - loaded = true; +void Style::setObserver(Observer* observer_) { + assert(Environment::currentlyOn(ThreadType::Map)); + assert(!observer); + + observer = observer_; } -bool Style::isLoaded() const { - // TODO: move loading into Style - if (!loaded) { - return false; +void Style::onGlyphRangeLoaded() { + shouldReparsePartialTiles = true; + + emitTileDataChanged(); +} + +void Style::onGlyphRangeLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + +void Style::onSourceLoaded() { + emitTileDataChanged(); +} + +void Style::onSourceLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + +void Style::onTileLoaded(bool isNewTile) { + if (isNewTile) { + shouldReparsePartialTiles = true; } - for (const auto& source : sources) { - if (!source->isLoaded()) { - return false; - } + emitTileDataChanged(); +} + +void Style::onTileLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + +void Style::onSpriteLoaded() { + shouldReparsePartialTiles = true; + + emitTileDataChanged(); +} + +void Style::onSpriteLoadingFailed(std::exception_ptr error) { + emitResourceLoadingFailed(error); +} + +void Style::emitTileDataChanged() { + assert(Environment::currentlyOn(ThreadType::Map)); + + if (observer) { + observer->onTileDataChanged(); } +} - // TODO: move sprite into Style -// if (sprite && !sprite.isLoaded()) { -// return false; -// } +void Style::emitResourceLoadingFailed(std::exception_ptr error) { + assert(Environment::currentlyOn(ThreadType::Map)); - return true; + try { + if (error) { + std::rethrow_exception(error); + } + } catch(const std::exception& e) { + Log::Error(Event::Style, e.what()); + } + + if (observer) { + observer->onResourceLoadingFailed(error); + } } } diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index 42d7b0b9a0..c70f019c43 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -4,6 +4,10 @@ #include <mbgl/style/property_transition.hpp> #include <mbgl/style/zoom_history.hpp> +#include <mbgl/map/source.hpp> +#include <mbgl/map/sprite.hpp> +#include <mbgl/text/glyph_store.hpp> + #include <mbgl/util/uv.hpp> #include <mbgl/util/ptr.hpp> #include <mbgl/util/noncopyable.hpp> @@ -16,33 +20,77 @@ namespace mbgl { -class Source; +class Environment; +class GlyphAtlas; +class GlyphStore; +class SpriteAtlas; +class LineAtlas; class StyleLayer; -class Style : public util::noncopyable { +class Style : public GlyphStore::Observer, + public Source::Observer, + public Sprite::Observer, + public util::noncopyable { public: - Style(); + Style(const std::string& data, + const std::string& base, + uv_loop_t*, Environment&); ~Style(); - void loadJSON(const uint8_t *const data); + class Observer { + public: + virtual ~Observer() = default; + + virtual void onTileDataChanged() = 0; + virtual void onResourceLoadingFailed(std::exception_ptr error) = 0; + }; + + void setObserver(Observer*); + bool isLoaded() const; + // Fetch the tiles needed by the current viewport and emit a signal when + // a tile is ready so observers can render the tile. + void update(MapData&, const TransformState&, TexturePool&); + void cascade(const std::vector<std::string>&); void recalculate(float z, TimePoint now); void setDefaultTransitionDuration(Duration); bool hasTransitions() const; - const std::string &getSpriteURL() const; + std::unique_ptr<GlyphStore> glyphStore; + std::unique_ptr<GlyphAtlas> glyphAtlas; + util::ptr<Sprite> sprite; + std::unique_ptr<SpriteAtlas> spriteAtlas; + std::unique_ptr<LineAtlas> lineAtlas; std::vector<util::ptr<Source>> sources; std::vector<util::ptr<StyleLayer>> layers; - std::string glyph_url; - std::string base; private: - bool loaded = false; - std::string sprite_url; + // GlyphStore::Observer implementation. + void onGlyphRangeLoaded() override; + void onGlyphRangeLoadingFailed(std::exception_ptr error) override; + + // Source::Observer implementation. + void onSourceLoaded() override; + void onSourceLoadingFailed(std::exception_ptr error) override; + void onTileLoaded(bool isNewTile) override; + void onTileLoadingFailed(std::exception_ptr error) override; + + // Sprite::Observer implementation. + void onSpriteLoaded() override; + void onSpriteLoadingFailed(std::exception_ptr error) override; + + void emitTileDataChanged(); + void emitResourceLoadingFailed(std::exception_ptr error); + + bool shouldReparsePartialTiles = false; + + Observer* observer = nullptr; + + std::string spriteURL; PropertyTransition defaultTransition; std::unique_ptr<uv::rwlock> mtx; ZoomHistory zoomHistory; diff --git a/test/ios/MetricsTests.m b/test/ios/MetricsTests.m index 758cf1f5be..7c71ee6739 100644 --- a/test/ios/MetricsTests.m +++ b/test/ios/MetricsTests.m @@ -8,6 +8,9 @@ #import "MapboxGL.h" #import "OHHTTPStubs.h" +const NSUInteger MGLMaximumEventsPerFlush = 20; +const NSTimeInterval MGLFlushInterval = 60; + @interface MGLMapboxEvents (Testing) - (NSString *)appBundleId; @@ -18,9 +21,6 @@ - (NSMutableArray *)eventQueue; - (void)postEvents:(NSArray *)events; - (NSTimer *)timer; -- (NSUInteger)flushAt; -- (NSTimeInterval)flushAfter; -- (void)setFlushAfter:(NSTimeInterval)newFlushAfter; - (void)flush; - (void)startTimer; @@ -28,15 +28,12 @@ @interface MetricsTests : KIFTestCase -@property (nonatomic) NSTimeInterval defaultFlushAfter; - @end @implementation MetricsTests - (void)beforeAll { [tester acknowledgeSystemAlert]; - self.defaultFlushAfter = [[MGLMapboxEvents sharedManager] flushAfter]; } - (void)beforeEach { @@ -44,8 +41,6 @@ [MGLMapboxEvents resumeMetricsCollection]; [MGLAccountManager setAccessToken:@"pk.eyJ1IjoianVzdGluIiwiYSI6IlpDbUJLSUEifQ.4mG8vhelFMju6HpIY-Hi5A"]; - - [[MGLMapboxEvents sharedManager] setFlushAfter:self.defaultFlushAfter]; } - (void)afterEach { @@ -76,7 +71,6 @@ - (void)testFlushAtThreshold { NSUInteger startCount = [[[MGLMapboxEvents sharedManager] eventQueue] count]; - NSUInteger flushAt = [[MGLMapboxEvents sharedManager] flushAt]; XCTestExpectation *queueItemsExpectation = [self expectationWithDescription:@"queue should contain events"]; @@ -89,7 +83,7 @@ } }); - for (NSUInteger i = 0; i < (flushAt - startCount - 1); i++) { + for (NSUInteger i = 0; i < (MGLMaximumEventsPerFlush - startCount - 1); i++) { [self pushFakeEvent]; } @@ -146,8 +140,6 @@ [self waitForExpectationsWithTimeout:1.0 handler:nil]; - [[MGLMapboxEvents sharedManager] setFlushAfter:5]; - id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]]; [[[eventsMock expect] andForwardToRealObject] startTimer]; [self pushFakeEvent]; @@ -155,7 +147,7 @@ XCTAssertEqual([[[MGLMapboxEvents sharedManager] eventQueue] count], 1); XCTAssertNotNil([[MGLMapboxEvents sharedManager] timer]); - XCTAssertEqual([[MGLMapboxEvents sharedManager] flushAfter], [[[MGLMapboxEvents sharedManager] timer] timeInterval]); + XCTAssertEqual(MGLFlushInterval, [[[MGLMapboxEvents sharedManager] timer] timeInterval]); } - (void)testTimerFiresFlush { @@ -174,11 +166,10 @@ [self waitForExpectationsWithTimeout:1.0 handler:nil]; - [[MGLMapboxEvents sharedManager] setFlushAfter:5]; [self pushFakeEvent]; id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]]; [[eventsMock expect] flush]; - [eventsMock verifyWithDelay:[[MGLMapboxEvents sharedManager] flushAfter]]; + [eventsMock verifyWithDelay:MGLFlushInterval]; } - (void)testFlushPostsEvents { diff --git a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme index 911f949fa0..0008282f89 100644 --- a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme +++ b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme @@ -55,6 +55,15 @@ <Test Identifier = "MapViewTests/testDelegateRegionWillChange"> </Test> + <Test + Identifier = "MetricsTests/testFlushPostsEvents"> + </Test> + <Test + Identifier = "MetricsTests/testPostEventsNetworkRequest"> + </Test> + <Test + Identifier = "MetricsTests/testTimerFiresFlush"> + </Test> </SkippedTests> </TestableReference> </Testables> diff --git a/test/miscellaneous/style_parser.cpp b/test/miscellaneous/style_parser.cpp index f5b9bb7e63..7a38ba054d 100644 --- a/test/miscellaneous/style_parser.cpp +++ b/test/miscellaneous/style_parser.cpp @@ -1,6 +1,6 @@ #include "../fixtures/util.hpp" -#include <mbgl/style/style.hpp> +#include <mbgl/style/style_parser.hpp> #include <mbgl/util/io.hpp> #include <rapidjson/document.h> @@ -22,27 +22,23 @@ class StyleParserTest : public ::testing::TestWithParam<std::string> {}; TEST_P(StyleParserTest, ParseStyle) { const std::string &base = "test/fixtures/style_parser/" + GetParam(); - const std::string style_path = base + ".style.json"; - const std::string info = util::read_file(base + ".info.json"); + rapidjson::Document infoDoc; + infoDoc.Parse<0>(util::read_file(base + ".info.json").c_str()); + ASSERT_FALSE(infoDoc.HasParseError()); + ASSERT_TRUE(infoDoc.IsObject()); - // Parse settings. - rapidjson::Document doc; - doc.Parse<0>((const char *const)info.c_str()); - ASSERT_FALSE(doc.HasParseError()); - ASSERT_TRUE(doc.IsObject()); - - std::ifstream stylefile(style_path); - ASSERT_TRUE(stylefile.good()); - std::stringstream stylejson; - stylejson << stylefile.rdbuf(); + rapidjson::Document styleDoc; + styleDoc.Parse<0>(util::read_file(base + ".style.json").c_str()); + ASSERT_FALSE(styleDoc.HasParseError()); + ASSERT_TRUE(styleDoc.IsObject()); FixtureLogObserver* observer = new FixtureLogObserver(); Log::setObserver(std::unique_ptr<Log::Observer>(observer)); - Style style; - style.loadJSON((const uint8_t *)stylejson.str().c_str()); + StyleParser parser; + parser.parse(styleDoc); - for (auto it = doc.MemberBegin(), end = doc.MemberEnd(); it != end; it++) { + for (auto it = infoDoc.MemberBegin(), end = infoDoc.MemberEnd(); it != end; it++) { const std::string name { it->name.GetString(), it->name.GetStringLength() }; const rapidjson::Value &value = it->value; ASSERT_EQ(true, value.IsObject()); diff --git a/test/resources/mock_file_source.cpp b/test/style/mock_file_source.cpp index 42067a2a73..42067a2a73 100644 --- a/test/resources/mock_file_source.cpp +++ b/test/style/mock_file_source.cpp diff --git a/test/resources/mock_file_source.hpp b/test/style/mock_file_source.hpp index bb9fb55a30..bb9fb55a30 100644 --- a/test/resources/mock_file_source.hpp +++ b/test/style/mock_file_source.hpp diff --git a/test/resources/mock_view.hpp b/test/style/mock_view.hpp index 865cd2da55..865cd2da55 100644 --- a/test/resources/mock_view.hpp +++ b/test/style/mock_view.hpp diff --git a/test/resources/resource_loader.cpp b/test/style/resource_loading.cpp index 7d57f47ee6..de3e6b812e 100644 --- a/test/resources/resource_loader.cpp +++ b/test/style/resource_loading.cpp @@ -3,14 +3,10 @@ #include "mock_file_source.hpp" #include "mock_view.hpp" -#include <mbgl/geometry/glyph_atlas.hpp> -#include <mbgl/geometry/sprite_atlas.hpp> #include <mbgl/map/environment.hpp> #include <mbgl/map/map_data.hpp> -#include <mbgl/map/resource_loader.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/style/style.hpp> -#include <mbgl/text/glyph_store.hpp> #include <mbgl/util/exception.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> @@ -21,7 +17,7 @@ using namespace mbgl; namespace { -class MockMapContext : public ResourceLoader::Observer { +class MockMapContext : public Style::Observer { public: MockMapContext(uv_loop_t* loop, View& view, @@ -30,36 +26,19 @@ public: : env_(fileSource), envScope_(env_, ThreadType::Map, "Map"), data_(view, MapMode::Still), - glyphStore_(std::make_unique<GlyphStore>(loop, env_)), - glyphAtlas_(std::make_unique<GlyphAtlas>(1024, 1024)), - spriteAtlas_(std::make_unique<SpriteAtlas>(512, 512)), - texturePool_(std::make_unique<TexturePool>()), - style_(std::make_unique<Style>()), - resourceLoader_(std::make_unique<ResourceLoader>()), - asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })), callback_(callback) { - asyncUpdate->unref(); data_.transform.resize(1000, 1000, 1.0, 1000, 1000); data_.transform.setLatLngZoom({0, 0}, 16); const std::string style = util::read_file("test/fixtures/resources/style.json"); - style_->loadJSON(reinterpret_cast<const uint8_t *>(style.c_str())); - - glyphStore_->setURL(style_->glyph_url); - - resourceLoader_->setGlyphStore(glyphStore_.get()); - resourceLoader_->setObserver(this); - resourceLoader_->setStyle(style_.get()); + style_ = std::make_unique<Style>(style, "", loop, env_), + style_->setObserver(this); } ~MockMapContext() { - resourceLoader_.reset(); style_.reset(); - texturePool_.reset(); - spriteAtlas_.reset(); - glyphAtlas_.reset(); - glyphStore_.reset(); + env_.performCleanup(); } void update() { @@ -69,19 +48,16 @@ public: data_.transform.updateTransitions(now); transformState_ = data_.transform.currentState(); - - resourceLoader_->update( - data_, transformState_, *glyphAtlas_, *spriteAtlas_, *texturePool_); + style_->update(data_, transformState_, texturePool_); } - // ResourceLoader::Observer implementation. + // Style::Observer implementation. void onTileDataChanged() override { - util::ptr<Sprite> sprite = resourceLoader_->getSprite(); - if (sprite && sprite->isLoaded() && style_->isLoaded()) { + update(); + + if (style_->isLoaded()) { callback_(nullptr); } - - asyncUpdate->send(); }; void onResourceLoadingFailed(std::exception_ptr error) override { @@ -94,15 +70,9 @@ private: MapData data_; TransformState transformState_; + TexturePool texturePool_; - std::unique_ptr<GlyphStore> glyphStore_; - std::unique_ptr<GlyphAtlas> glyphAtlas_; - std::unique_ptr<SpriteAtlas> spriteAtlas_; - std::unique_ptr<TexturePool> texturePool_; std::unique_ptr<Style> style_; - std::unique_ptr<ResourceLoader> resourceLoader_; - - std::unique_ptr<uv::async> asyncUpdate; std::function<void(std::exception_ptr error)> callback_; }; @@ -118,7 +88,13 @@ void runTestCase(MockFileSource::Type type, FixtureLogObserver* log = new FixtureLogObserver(); Log::setObserver(std::unique_ptr<Log::Observer>(log)); - auto callback = [&loop, ¶m](std::exception_ptr error) { + auto callback = [type, &loop, ¶m](std::exception_ptr error) { + if (type == MockFileSource::Success) { + EXPECT_TRUE(error == nullptr); + } else { + EXPECT_TRUE(error != nullptr); + } + try { if (error) { std::rethrow_exception(error); @@ -150,7 +126,7 @@ void runTestCase(MockFileSource::Type type, const FixtureLogObserver::LogMessage logMessage { EventSeverity::Error, - Event::ResourceLoader, + Event::Style, int64_t(-1), message, }; @@ -167,21 +143,21 @@ void runTestCase(MockFileSource::Type type, } -class ResourceLoaderTest : public ::testing::TestWithParam<std::string> { +class ResourceLoading : public ::testing::TestWithParam<std::string> { }; -TEST_P(ResourceLoaderTest, Success) { +TEST_P(ResourceLoading, Success) { runTestCase(MockFileSource::Success, GetParam(), std::string()); } -TEST_P(ResourceLoaderTest, RequestFail) { +TEST_P(ResourceLoading, RequestFail) { std::stringstream message; message << "Failed to load [test/fixtures/resources/" << GetParam() << "]: Failed by the test case"; runTestCase(MockFileSource::RequestFail, GetParam(), message.str()); } -TEST_P(ResourceLoaderTest, RequestWithCorruptedData) { +TEST_P(ResourceLoading, RequestWithCorruptedData) { const std::string param(GetParam()); std::stringstream message; @@ -200,5 +176,5 @@ TEST_P(ResourceLoaderTest, RequestWithCorruptedData) { runTestCase(MockFileSource::RequestWithCorruptedData, GetParam(), message.str()); } -INSTANTIATE_TEST_CASE_P(ResourceLoader, ResourceLoaderTest, +INSTANTIATE_TEST_CASE_P(Style, ResourceLoading, ::testing::Values("source.json", "sprite.json", "sprite.png", "vector.pbf", "glyphs.pbf")); diff --git a/test/test.gypi b/test/test.gypi index 24582df992..0e14ac0835 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -58,11 +58,6 @@ 'miscellaneous/variant.cpp', 'miscellaneous/worker.cpp', - 'resources/mock_file_source.cpp', - 'resources/mock_file_source.hpp', - 'resources/mock_view.hpp', - 'resources/resource_loader.cpp', - 'storage/storage.hpp', 'storage/storage.cpp', 'storage/cache_response.cpp', @@ -78,6 +73,11 @@ 'storage/http_load.cpp', 'storage/http_other_loop.cpp', 'storage/http_reading.cpp', + + 'style/mock_file_source.cpp', + 'style/mock_file_source.hpp', + 'style/mock_view.hpp', + 'style/resource_loading.cpp', ], 'libraries': [ '<@(uv_static_libs)', |