From 995aa163234be3cb40c81b307bc4f47b990a35e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 1 Aug 2017 11:29:16 +0200 Subject: WIP --- .gitignore | 2 +- include/mbgl/storage/response.hpp | 2 - include/mbgl/util/enum.hpp | 3 +- include/mbgl/util/event.hpp | 1 + package.json | 4 +- platform/android/src/http_file_source.cpp | 2 +- platform/darwin/src/http_file_source.mm | 2 +- platform/default/default_file_source.cpp | 4 ++ platform/default/http_file_source.cpp | 2 +- platform/node/src/node_mapbox_gl_native.cpp | 27 ++++++++ platform/node/src/node_request.cpp | 68 ++++++++++++--------- platform/node/test/suite_implementation.js | 20 ++++-- platform/qt/src/http_request.cpp | 8 +-- src/mbgl/algorithm/update_renderables.hpp | 4 +- src/mbgl/annotation/annotation_tile.cpp | 3 + src/mbgl/map/map.cpp | 24 ++++++-- src/mbgl/renderer/buckets/debug_bucket.cpp | 6 +- src/mbgl/renderer/buckets/debug_bucket.hpp | 4 +- src/mbgl/renderer/render_source.cpp | 4 +- src/mbgl/renderer/render_source.hpp | 2 +- src/mbgl/renderer/render_source_observer.hpp | 7 ++- src/mbgl/renderer/render_style.cpp | 25 +++++--- src/mbgl/renderer/render_style.hpp | 4 +- src/mbgl/renderer/render_style_observer.hpp | 4 +- src/mbgl/renderer/render_tile.cpp | 4 +- src/mbgl/renderer/renderer_impl.cpp | 5 +- src/mbgl/renderer/renderer_impl.hpp | 2 +- src/mbgl/renderer/renderer_observer.hpp | 4 +- src/mbgl/renderer/tile_pyramid.cpp | 2 +- src/mbgl/sprite/sprite_loader.cpp | 45 +++++++++++--- src/mbgl/sprite/sprite_loader.hpp | 6 +- src/mbgl/sprite/sprite_loader_observer.hpp | 4 +- src/mbgl/sprite/sprite_loader_worker.cpp | 7 ++- src/mbgl/sprite/sprite_loader_worker.hpp | 4 +- src/mbgl/storage/resource.cpp | 12 ++++ src/mbgl/storage/response.cpp | 31 +++------- src/mbgl/style/observer.hpp | 4 +- src/mbgl/style/source_observer.hpp | 4 +- src/mbgl/style/sources/vector_source.cpp | 17 ++++-- src/mbgl/style/style_impl.cpp | 48 +++++++++------ src/mbgl/style/style_impl.hpp | 6 +- src/mbgl/text/glyph_manager_observer.hpp | 6 +- src/mbgl/tile/geojson_tile.cpp | 2 + src/mbgl/tile/geometry_tile.cpp | 49 +++++++++------ src/mbgl/tile/raster_tile.cpp | 36 +++++++---- src/mbgl/tile/raster_tile.hpp | 1 + src/mbgl/tile/raster_tile_worker.cpp | 1 + src/mbgl/tile/tile.cpp | 19 ++++-- src/mbgl/tile/tile.hpp | 56 +++++++++-------- src/mbgl/tile/tile_loader_impl.hpp | 15 ++--- src/mbgl/tile/tile_observer.hpp | 4 +- src/mbgl/tile/vector_tile.cpp | 10 ++- src/mbgl/util/event.cpp | 1 + test/algorithm/mock.hpp | 5 -- test/algorithm/update_renderables.test.cpp | 55 ++++++++--------- test/api/render_caching.cpp | 10 ++- test/api/render_missing.test.cpp | 4 +- test/fixtures/api/assets/raster/10-163-395.png | Bin 0 -> 17685 bytes test/fixtures/api/assets/raster/6-10-24.png | Bin 0 -> 29243 bytes test/fixtures/api/cached_raster/expected.png | Bin 0 -> 120955 bytes test/map/map.test.cpp | 4 +- test/sprite/sprite_loader.test.cpp | 2 +- test/src/mbgl/test/stub_render_source_observer.hpp | 9 ++- test/src/mbgl/test/stub_style_observer.hpp | 4 +- test/src/mbgl/test/stub_tile_observer.hpp | 2 +- test/src/mbgl/test/util.cpp | 5 +- test/text/glyph_loader.test.cpp | 8 ++- test/tile/geojson_tile.test.cpp | 4 +- test/tile/raster_tile.test.cpp | 4 -- test/tile/vector_tile.test.cpp | 2 - 70 files changed, 472 insertions(+), 283 deletions(-) create mode 100644 test/fixtures/api/assets/raster/10-163-395.png create mode 100644 test/fixtures/api/assets/raster/6-10-24.png create mode 100644 test/fixtures/api/cached_raster/expected.png diff --git a/.gitignore b/.gitignore index b6a8498460..67af4382c8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ xcuserdata /platform/node/test/actual /platform/node/test/diff /mason_packages -/build +/build* /lib /test/node_modules /test/fixtures/*/*_actual.* diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index c532b1216d..cead680aa5 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -54,6 +54,4 @@ public: Error(ResourceStatus, std::string = "", optional = {}); }; -std::ostream& operator<<(std::ostream&, ResourceStatus); - } // namespace mbgl diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp index 369ca86bfd..60d8e7ca69 100644 --- a/include/mbgl/util/enum.hpp +++ b/include/mbgl/util/enum.hpp @@ -3,7 +3,6 @@ #include #include -#include #include namespace mbgl { @@ -23,7 +22,7 @@ template <> const char * Enum::toString(T t) { \ auto it = std::find_if(std::begin(T##_names), std::end(T##_names), \ [&] (const auto& v) { return t == v.first; }); \ - assert(it != std::end(T##_names)); return it->second; \ + return it == std::end(T##_names) ? "" : it->second; \ } \ \ template <> \ diff --git a/include/mbgl/util/event.hpp b/include/mbgl/util/event.hpp index 5fe0baae3c..239b08aa90 100644 --- a/include/mbgl/util/event.hpp +++ b/include/mbgl/util/event.hpp @@ -28,6 +28,7 @@ enum class Event : uint8_t { Android, Crash, Glyph, + Resource, }; struct EventPermutation { diff --git a/package.json b/package.json index 0cd6b450c7..69d8907b1a 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ }, "license": "BSD-2-Clause", "dependencies": { - "node-pre-gyp": "^0.6.28", - "nan": "^2.4.0" + "nan": "^2.4.0", + "node-pre-gyp": "^0.6.36" }, "devDependencies": { "aws-sdk": "^2.3.5", diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp index 8a54b2917a..fd77f8ec85 100644 --- a/platform/android/src/http_file_source.cpp +++ b/platform/android/src/http_file_source.cpp @@ -132,7 +132,7 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code, } else { response.data = std::make_shared(); } - } else if (code == 204 || (code == 404 && resource.kind == ResourceKind::Tile)) { + } else if (code == 204) { response.noContent = true; } else if (code == 304) { response.notModified = true; diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm index 7d42af16df..15fcdff608 100644 --- a/platform/darwin/src/http_file_source.mm +++ b/platform/darwin/src/http_file_source.mm @@ -286,7 +286,7 @@ std::unique_ptr HTTPFileSource::request(const Resource& resource, if (responseCode == 200) { response.data = std::make_shared((const char *)[data bytes], [data length]); - } else if (responseCode == 204 || (responseCode == 404 && resource.kind == ResourceKind::Tile)) { + } else if (responseCode == 204) { response.noContent = true; } else if (responseCode == 304) { response.notModified = true; diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index 10803a18a0..a2ca0de363 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -237,6 +237,10 @@ void DefaultFileSource::setResourceTransform(optional DefaultFileSource::request(const Resource& resource, Callback callback) { + if (resource.necessity == Resource::Optional) { + + } + auto req = std::make_unique(std::move(callback)); req->onCancel([fs = impl->actor(), req = req.get()] () mutable { fs.invoke(&Impl::cancel, req); }); diff --git a/platform/default/http_file_source.cpp b/platform/default/http_file_source.cpp index dc8018fb42..87cb269b3c 100644 --- a/platform/default/http_file_source.cpp +++ b/platform/default/http_file_source.cpp @@ -373,7 +373,7 @@ void HTTPRequest::handleResult(CURLcode code) { } else { response->data = std::make_shared(); } - } else if (responseCode == 204 || (responseCode == 404 && resource.kind == ResourceKind::Tile)) { + } else if (responseCode == 204) { response->noContent = true; } else if (responseCode == 304) { response->notModified = true; diff --git a/platform/node/src/node_mapbox_gl_native.cpp b/platform/node/src/node_mapbox_gl_native.cpp index dddd74aece..a51e047b92 100644 --- a/platform/node/src/node_mapbox_gl_native.cpp +++ b/platform/node/src/node_mapbox_gl_native.cpp @@ -56,6 +56,33 @@ void RegisterModule(v8::Local target, v8::Local module) Nan::New("Resource").ToLocalChecked(), resource); + // Exports ResourceStatus constants. + v8::Local errorCode = Nan::New(); + + Nan::Set(errorCode, + Nan::New("NotFound").ToLocalChecked(), + Nan::New(static_cast(mbgl::ResourceStatus::NotFoundError))); + + Nan::Set(errorCode, + Nan::New("Server").ToLocalChecked(), + Nan::New(static_cast(mbgl::ResourceStatus::ServerError))); + + Nan::Set(errorCode, + Nan::New("Connection").ToLocalChecked(), + Nan::New(static_cast(mbgl::ResourceStatus::ConnectionError))); + + Nan::Set(errorCode, + Nan::New("RateLimit").ToLocalChecked(), + Nan::New(static_cast(mbgl::ResourceStatus::RateLimitError))); + + Nan::Set(errorCode, + Nan::New("Other").ToLocalChecked(), + Nan::New(static_cast(mbgl::ResourceStatus::OtherError))); + + Nan::Set(target, + Nan::New("ErrorCode").ToLocalChecked(), + errorCode); + // Make the exported object inherit from EventEmitter v8::Local require = Nan::Get(module, Nan::New("require").ToLocalChecked()).ToLocalChecked().As(); diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index 29aaa43cd4..4246b7fc2e 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -58,43 +58,47 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo& inf mbgl::Response response; - if (info.Length() < 1) { - response.noContent = true; - } else if (info[0]->IsObject()) { + if (info.Length() >= 1 && info[0]->BooleanValue()) { + // First parameter is an error argument. auto err = Nan::To(info[0]).ToLocalChecked(); - auto msg = Nan::New("message").ToLocalChecked(); - - if (Nan::Has(err, msg).FromJust()) { - request->SetErrorMessage(*Nan::Utf8String( - Nan::Get(err, msg).ToLocalChecked())); - } - } else if (info[0]->IsString()) { - request->SetErrorMessage(*Nan::Utf8String(info[0])); - } else if (info.Length() < 2 || !info[1]->IsObject()) { - return Nan::ThrowTypeError("Second argument must be a response object"); - } else { - auto res = Nan::To(info[1]).ToLocalChecked(); - if (Nan::Has(res, Nan::New("modified").ToLocalChecked()).FromJust()) { - const double modified = Nan::To(Nan::Get(res, Nan::New("modified").ToLocalChecked()).ToLocalChecked()).FromJust(); - if (!std::isnan(modified)) { - response.modified = mbgl::Timestamp{ mbgl::Seconds( - static_cast(modified / 1000)) }; + // Extract the status code from the Error object, if it has one. + mbgl::ResourceStatus status = mbgl::ResourceStatus::OtherError; + if (Nan::Has(err, Nan::New("code").ToLocalChecked()).FromJust()) { + status = static_cast( + Nan::To(Nan::Get(err, Nan::New("code").ToLocalChecked()).ToLocalChecked()) + .FromMaybe(static_cast(mbgl::ResourceStatus::OtherError))); + // Validate the enum value. + switch (status) { + case mbgl::ResourceStatus::NotFoundError: + case mbgl::ResourceStatus::ServerError: + case mbgl::ResourceStatus::ConnectionError: + case mbgl::ResourceStatus::RateLimitError: + case mbgl::ResourceStatus::OtherError: + // Valid error enums. + break; + default: + // Invalid error enums. + return Nan::ThrowTypeError("Invalid error code. Must be one of NotFound, server, Connection, RateLimit, or Other"); } } - if (Nan::Has(res, Nan::New("expires").ToLocalChecked()).FromJust()) { - const double expires = Nan::To(Nan::Get(res, Nan::New("expires").ToLocalChecked()).ToLocalChecked()).FromJust(); - if (!std::isnan(expires)) { - response.expires = mbgl::Timestamp{ mbgl::Seconds( - static_cast(expires / 1000)) }; - } + // Extract the error message from the Error object. + std::string message; + if (Nan::Has(err, Nan::New("message").ToLocalChecked()).FromJust()) { + Nan::Utf8String string( + Nan::Get(err, Nan::New("message").ToLocalChecked()).ToLocalChecked()); + message.assign(*string, string.length()); + } else { + Nan::Utf8String string(err); + message.assign(*string, string.length()); } - if (Nan::Has(res, Nan::New("etag").ToLocalChecked()).FromJust()) { - const Nan::Utf8String etag(Nan::Get(res, Nan::New("etag").ToLocalChecked()).ToLocalChecked()); - response.etag = std::string { *etag, size_t(etag.length()) }; - } + response.error = std::make_unique(status, message); + } else if (info.Length() >= 2) { + // This is a successful response. It has an object as the second parameter that + // may contain data. + auto res = Nan::To(info[1]).ToLocalChecked(); if (Nan::Has(res, Nan::New("data").ToLocalChecked()).FromJust()) { auto data = Nan::Get(res, Nan::New("data").ToLocalChecked()).ToLocalChecked(); @@ -106,7 +110,11 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo& inf } else { return Nan::ThrowTypeError("Response data must be a Buffer"); } + } else { + response.noContent = true; } + } else { + response.noContent = true; } if (request->ErrorMessage()) { diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js index b717ecd2b2..451f7d52e0 100644 --- a/platform/node/test/suite_implementation.js +++ b/platform/node/test/suite_implementation.js @@ -15,15 +15,25 @@ module.exports = function (style, options, callback) { ratio: options.pixelRatio, request: function(req, callback) { request(req.url, {encoding: null}, function (err, response, body) { + var result; if (err) { - callback(err); + err.code = mbgl.ErrorCode.Connection; + } else if (response.statusCode == 200) { + result = { data: body }; + } else if (response.statusCode == 204) { + result = {}; // no content } else if (response.statusCode == 404) { - callback(); - } else if (response.statusCode != 200) { - callback(new Error(response.statusMessage)); + err = new Error(response.statusMessage); + err.code = mbgl.ErrorCode.NotFound; + } else if (response.statusCode == 429) { + err = new Error(response.statusMessage); + err.code = mbgl.ErrorCode.RateLimit; } else { - callback(null, {data: body}); + err = new Error(response.statusMessage); + err.code = (response.statusCode >= 500 && response.statusCode < 600) ? + mbgl.ErrorCode.ServerError : mbgl.ErrorCode.OtherError; } + callback(err, result); }); } }); diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp index d74ed0e7ee..4805204382 100644 --- a/platform/qt/src/http_request.cpp +++ b/platform/qt/src/http_request.cpp @@ -111,12 +111,8 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply) response.notModified = true; break; case 404: { - if (m_resource.kind == ResourceKind::Tile) { - response.noContent = true; - } else { - response.error = std::make_unique( - ResourceStatus::NotFoundError, "HTTP status code 404"); - } + response.error = std::make_unique( + ResourceStatus::NotFoundError, "HTTP status code 404"); break; } case 429: diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp index 0c2266ff47..cc3d2c79f2 100644 --- a/src/mbgl/algorithm/update_renderables.hpp +++ b/src/mbgl/algorithm/update_renderables.hpp @@ -44,7 +44,6 @@ void updateRenderables(GetTileFn getTile, renderTile(idealRenderTileID, *tile); } else { // We are now attempting to load child and parent tiles. - bool parentHasTriedOptional = tile->hasTriedOptional(); bool parentIsLoaded = tile->isLoaded(); // The tile isn't loaded yet, but retain it anyway because it's an ideal tile. @@ -92,7 +91,7 @@ void updateRenderables(GetTileFn getTile, } tile = getTile(parentDataTileID); - if (!tile && (parentHasTriedOptional || parentIsLoaded)) { + if (!tile && parentIsLoaded) { tile = createTile(parentDataTileID); } @@ -102,7 +101,6 @@ void updateRenderables(GetTileFn getTile, // Save the current values, since they're the parent of the next iteration // of the parent tile ascent loop. - parentHasTriedOptional = tile->hasTriedOptional(); parentIsLoaded = tile->isLoaded(); if (tile->isRenderable()) { diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index f2520b75c3..1687ee29e6 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -23,9 +23,12 @@ void AnnotationTile::setNecessity(Necessity) { } void AnnotationTile::setData(std::unique_ptr data_) { + loaded = true; + logDebug("AnnotationTile::setData"); GeometryTile::setData(std::move(data_)); } + class AnnotationTileFeatureData { public: AnnotationTileFeatureData(const AnnotationID id_, diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 7d57f6863e..f4f1f9373a 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -11,10 +11,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -53,11 +55,11 @@ public: void onUpdate(Update) override; void onStyleLoading() override; void onStyleLoaded() override; - void onStyleError(std::exception_ptr) override; + void onStyleError(std::exception_ptr, EventSeverity) override; // RendererObserver void onInvalidate() override; - void onResourceError(std::exception_ptr) override; + void onResourceError(std::exception_ptr, EventSeverity) override; void onWillStartRenderingFrame() override; void onDidFinishRenderingFrame(RenderMode, bool) override; void onWillStartRenderingMap() override; @@ -703,7 +705,7 @@ void Map::Impl::onUpdate(Update flags) { timePoint, transform.getState(), style->impl->getGlyphURL(), - style->impl->spriteLoaded, + style->impl->isSpriteLoaded(), style->impl->getTransitionOptions(), style->impl->getLight()->impl, style->impl->getImageImpls(), @@ -738,12 +740,22 @@ void Map::Impl::onStyleLoaded() { observer.onDidFinishLoadingStyle(); } -void Map::Impl::onStyleError(std::exception_ptr error) { +void Map::Impl::onStyleError(std::exception_ptr error, const EventSeverity) { observer.onDidFailLoadingMap(error); } -void Map::Impl::onResourceError(std::exception_ptr error) { - if (mode == MapMode::Still && stillImageRequest) { +void Map::Impl::onResourceError(std::exception_ptr error, const EventSeverity severity) { + try { + std::rethrow_exception(error); + } catch (const util::ResourceError& err) { + Log::Record(severity, Event::Resource, "%s loading %s from %s: %s", + Enum::toString(err.status), + Enum::toString(err.kind), err.url.c_str(), err.what()); + } catch (const std::exception& err) { + Log::Record(severity, Event::Resource, err.what()); + } + + if (severity == EventSeverity::Error && mode == MapMode::Still && stillImageRequest) { auto request = std::move(stillImageRequest); request->callback(error); } diff --git a/src/mbgl/renderer/buckets/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp index 53c751c443..763ae270ed 100644 --- a/src/mbgl/renderer/buckets/debug_bucket.cpp +++ b/src/mbgl/renderer/buckets/debug_bucket.cpp @@ -12,13 +12,13 @@ namespace mbgl { DebugBucket::DebugBucket(const OverscaledTileID& id, const bool renderable_, - const bool complete_, + const bool loaded_, optional modified_, optional expires_, MapDebugOptions debugMode_, gl::Context& context) : renderable(renderable_), - complete(complete_), + loaded(loaded_), modified(std::move(modified_)), expires(std::move(expires_)), debugMode(debugMode_) { @@ -61,7 +61,7 @@ DebugBucket::DebugBucket(const OverscaledTileID& id, double baseline = 200; if (debugMode & MapDebugOptions::ParseStatus) { const std::string text = util::toString(id) + " - " + - (complete ? "complete" : renderable ? "renderable" : "pending"); + (loaded ? "loaded" : renderable ? "renderable" : "pending"); addText(text, 50, baseline, 5); baseline += 200; } diff --git a/src/mbgl/renderer/buckets/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp index fc3128e944..128d432b4b 100644 --- a/src/mbgl/renderer/buckets/debug_bucket.hpp +++ b/src/mbgl/renderer/buckets/debug_bucket.hpp @@ -21,14 +21,14 @@ class DebugBucket : private util::noncopyable { public: DebugBucket(const OverscaledTileID& id, bool renderable, - bool complete, + bool loaded, optional modified, optional expires, MapDebugOptions, gl::Context&); const bool renderable; - const bool complete; + const bool loaded; const optional modified; const optional expires; const MapDebugOptions debugMode; diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp index 7723a1c7ca..d07d8020a8 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -49,8 +49,8 @@ void RenderSource::onTileChanged(Tile& tile) { observer->onTileChanged(*this, tile.id); } -void RenderSource::onTileError(Tile& tile, std::exception_ptr error) { - observer->onTileError(*this, tile.id, error); +void RenderSource::onTileError(Tile& tile, std::exception_ptr error, const EventSeverity severity) { + observer->onTileError(*this, tile.id, error, severity); } bool RenderSource::isEnabled() const { diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index b565439588..6bc19a9e64 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -84,7 +84,7 @@ protected: bool enabled = false; void onTileChanged(Tile&) final; - void onTileError(Tile&, std::exception_ptr) final; + void onTileError(Tile&, std::exception_ptr, EventSeverity) final; }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_source_observer.hpp b/src/mbgl/renderer/render_source_observer.hpp index 792a18db2b..1494337dd6 100644 --- a/src/mbgl/renderer/render_source_observer.hpp +++ b/src/mbgl/renderer/render_source_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace mbgl { @@ -12,7 +14,10 @@ public: virtual ~RenderSourceObserver() = default; virtual void onTileChanged(RenderSource&, const OverscaledTileID&) {} - virtual void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) {} + virtual void onTileError(RenderSource&, + const OverscaledTileID&, + std::exception_ptr, + EventSeverity = EventSeverity::Error) {} }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp index 3d95b12bc4..f84e6987aa 100644 --- a/src/mbgl/renderer/render_style.cpp +++ b/src/mbgl/renderer/render_style.cpp @@ -422,16 +422,25 @@ void RenderStyle::onLowMemory() { } } -void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", - glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str()); - observer->onResourceError(error); +void RenderStyle::onGlyphsError(const FontStack& fontStack, + const GlyphRange& glyphRange, + std::exception_ptr error, + const EventSeverity severity) { + Log::Record(severity, Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", + glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), + util::toString(error).c_str()); + observer->onResourceError(error, severity); } -void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { - Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", - util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str()); - observer->onResourceError(error); +void RenderStyle::onTileError(RenderSource& source, + const OverscaledTileID& tileID, + std::exception_ptr error, + const EventSeverity severity) { + Log::Record(severity, Event::Style, "Failed to load tile %s for source %s: %s", + util::toString(tileID).c_str(), source.baseImpl->id.c_str(), + util::toString(error).c_str()); + observer->onResourceError(error, severity); + observer->onInvalidate(); } void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) { diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp index 23a640c482..3b45fde834 100644 --- a/src/mbgl/renderer/render_style.hpp +++ b/src/mbgl/renderer/render_style.hpp @@ -79,11 +79,11 @@ private: RenderLight renderLight; // GlyphManagerObserver implementation. - void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; + void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr, EventSeverity) override; // RenderSourceObserver implementation. void onTileChanged(RenderSource&, const OverscaledTileID&) override; - void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; + void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr, EventSeverity) override; RenderStyleObserver* observer; ZoomHistory zoomHistory; diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp index 5852dd68b8..095d7d53e4 100644 --- a/src/mbgl/renderer/render_style_observer.hpp +++ b/src/mbgl/renderer/render_style_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace mbgl { @@ -8,7 +10,7 @@ class RenderStyleObserver { public: virtual ~RenderStyleObserver() = default; virtual void onInvalidate() {} - virtual void onResourceError(std::exception_ptr) {} + virtual void onResourceError(std::exception_ptr, EventSeverity = EventSeverity::Error) {} }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index c9912f0563..c6670893ad 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -97,12 +97,12 @@ void RenderTile::finishRender(PaintParameters& parameters) { if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || - tile.debugBucket->complete != tile.isComplete() || + tile.debugBucket->loaded != tile.isLoaded() || !(tile.debugBucket->modified == tile.modified) || !(tile.debugBucket->expires == tile.expires) || tile.debugBucket->debugMode != parameters.debugOptions) { tile.debugBucket = std::make_unique( - tile.id, tile.isRenderable(), tile.isComplete(), tile.modified, + tile.id, tile.isRenderable(), tile.isLoaded(), tile.modified, tile.expires, parameters.debugOptions, parameters.context); } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 981c09e806..6e79f46ebe 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -69,6 +69,7 @@ void Renderer::Impl::render(View& view, const UpdateParameters& updateParameters }; bool loaded = updateParameters.styleLoaded && renderStyle->isLoaded(); + // fprintf(stderr, "loaded: %d\n", loaded); if (updateParameters.mode == MapMode::Continuous) { if (renderState == RenderState::Never) { @@ -343,8 +344,8 @@ void Renderer::Impl::onInvalidate() { observer->onInvalidate(); } -void Renderer::Impl::onResourceError(std::exception_ptr ptr) { - observer->onResourceError(ptr); +void Renderer::Impl::onResourceError(std::exception_ptr ptr, const EventSeverity severity) { + observer->onResourceError(ptr, severity); } void Renderer::Impl::onLowMemory() { diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 521df6ca63..be10962174 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -37,7 +37,7 @@ public: // RenderStyleObserver implementation void onInvalidate() override; - void onResourceError(std::exception_ptr) override; + void onResourceError(std::exception_ptr, EventSeverity) override; private: void doRender(PaintParameters&); diff --git a/src/mbgl/renderer/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp index 551b5c803e..0cb4c69cef 100644 --- a/src/mbgl/renderer/renderer_observer.hpp +++ b/src/mbgl/renderer/renderer_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace mbgl { @@ -17,7 +19,7 @@ public: virtual void onInvalidate() {} // Resource failed to download / parse - virtual void onResourceError(std::exception_ptr) {} + virtual void onResourceError(std::exception_ptr, EventSeverity = EventSeverity::Error) {} // First frame virtual void onWillStartRenderingMap() {} diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 219f154675..106f82b737 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -31,7 +31,7 @@ TilePyramid::~TilePyramid() = default; bool TilePyramid::isLoaded() const { for (const auto& pair : tiles) { - if (!pair.second->isComplete()) { + if (!pair.second->isLoaded()) { return false; } } diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp index 60ece5ed73..d70f7b9597 100644 --- a/src/mbgl/sprite/sprite_loader.cpp +++ b/src/mbgl/sprite/sprite_loader.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,10 @@ struct SpriteLoader::Loader { std::unique_ptr spriteRequest; std::shared_ptr mailbox; Actor worker; + uint64_t correlationID = 0; + + bool parsed = false; + bool pending = false; }; SpriteLoader::SpriteLoader(float pixelRatio_) @@ -43,15 +48,20 @@ SpriteLoader::~SpriteLoader() = default; void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) { if (url.empty()) { // Treat a non-existent sprite as a successfully loaded empty sprite. + loader.reset(); observer->onSpriteLoaded({}); return; } loader = std::make_unique(scheduler, *this); - loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) { + const auto jsonResource = Resource::spriteJSON(url, pixelRatio); + loader->jsonRequest = fileSource.request(jsonResource, [this, url = jsonResource.url](Response res) { if (res.error) { - observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message))); + const auto severity = loader->json ? EventSeverity::Warning : EventSeverity::Error; + util::ResourceError err(res.error->message, ResourceKind::SpriteJSON, res.error->status, + url); + observer->onSpriteError(std::make_exception_ptr(err), severity); } else if (res.notModified) { return; } else if (res.noContent) { @@ -64,9 +74,13 @@ void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource } }); - loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) { + const auto spriteResource = Resource::spriteImage(url, pixelRatio); + loader->spriteRequest = fileSource.request(spriteResource, [this, url = spriteResource.url](Response res) { if (res.error) { - observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message))); + const auto severity = loader->json ? EventSeverity::Warning : EventSeverity::Error; + util::ResourceError err(res.error->message, ResourceKind::SpriteImage, + res.error->status, url); + observer->onSpriteError(std::make_exception_ptr(err), severity); } else if (res.notModified) { return; } else if (res.noContent) { @@ -79,6 +93,10 @@ void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource }); } +bool SpriteLoader::isLoaded() const { + return !loader || (loader->parsed && !loader->pending); +} + void SpriteLoader::emitSpriteLoadedIfComplete() { assert(loader); @@ -86,14 +104,26 @@ void SpriteLoader::emitSpriteLoadedIfComplete() { return; } - loader->worker.invoke(&SpriteLoaderWorker::parse, loader->image, loader->json); + loader->pending = true; + + ++loader->correlationID; + loader->worker.invoke(&SpriteLoaderWorker::parse, loader->image, loader->json, + loader->correlationID); } -void SpriteLoader::onParsed(std::vector>&& result) { +void SpriteLoader::onParsed(std::vector>&& result, const uint64_t resultCorrelationID) { + loader->parsed = true; + if (resultCorrelationID == loader->correlationID) { + loader->pending = false; + } observer->onSpriteLoaded(std::move(result)); } -void SpriteLoader::onError(std::exception_ptr err) { +void SpriteLoader::onError(std::exception_ptr err, uint64_t const resultCorrelationID) { + loader->parsed = true; + if (resultCorrelationID == loader->correlationID) { + loader->pending = false; + } observer->onSpriteError(err); } @@ -102,3 +132,4 @@ void SpriteLoader::setObserver(SpriteLoaderObserver* observer_) { } } // namespace mbgl + diff --git a/src/mbgl/sprite/sprite_loader.hpp b/src/mbgl/sprite/sprite_loader.hpp index 0daf46be9c..f095084c8c 100644 --- a/src/mbgl/sprite/sprite_loader.hpp +++ b/src/mbgl/sprite/sprite_loader.hpp @@ -23,6 +23,8 @@ public: void load(const std::string& url, Scheduler&, FileSource&); + bool isLoaded() const; + void setObserver(SpriteLoaderObserver*); private: @@ -30,8 +32,8 @@ private: // Invoked by SpriteAtlasWorker friend class SpriteLoaderWorker; - void onParsed(std::vector>&&); - void onError(std::exception_ptr); + void onParsed(std::vector>&&, uint64_t resultCorrelationID); + void onError(std::exception_ptr, uint64_t resultCorrelationID); const float pixelRatio; diff --git a/src/mbgl/sprite/sprite_loader_observer.hpp b/src/mbgl/sprite/sprite_loader_observer.hpp index c730549c2c..9df94199d7 100644 --- a/src/mbgl/sprite/sprite_loader_observer.hpp +++ b/src/mbgl/sprite/sprite_loader_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -15,7 +17,7 @@ public: virtual ~SpriteLoaderObserver() = default; virtual void onSpriteLoaded(std::vector>&&) {} - virtual void onSpriteError(std::exception_ptr) {} + virtual void onSpriteError(std::exception_ptr, EventSeverity = EventSeverity::Error) {} }; } // namespace mbgl diff --git a/src/mbgl/sprite/sprite_loader_worker.cpp b/src/mbgl/sprite/sprite_loader_worker.cpp index 4bded33d53..8258923aa5 100644 --- a/src/mbgl/sprite/sprite_loader_worker.cpp +++ b/src/mbgl/sprite/sprite_loader_worker.cpp @@ -9,7 +9,8 @@ SpriteLoaderWorker::SpriteLoaderWorker(ActorRef, ActorRef image, - std::shared_ptr json) { + std::shared_ptr json, + const uint64_t correlationID) { try { if (!image) { // This shouldn't happen, since we always invoke it with a non-empty pointer. @@ -20,9 +21,9 @@ void SpriteLoaderWorker::parse(std::shared_ptr image, throw std::runtime_error("missing sprite metadata"); } - parent.invoke(&SpriteLoader::onParsed, parseSprite(*image, *json)); + parent.invoke(&SpriteLoader::onParsed, parseSprite(*image, *json), correlationID); } catch (...) { - parent.invoke(&SpriteLoader::onError, std::current_exception()); + parent.invoke(&SpriteLoader::onError, std::current_exception(), correlationID); } } diff --git a/src/mbgl/sprite/sprite_loader_worker.hpp b/src/mbgl/sprite/sprite_loader_worker.hpp index d61e07d14f..8bdcdd6139 100644 --- a/src/mbgl/sprite/sprite_loader_worker.hpp +++ b/src/mbgl/sprite/sprite_loader_worker.hpp @@ -14,7 +14,9 @@ class SpriteLoaderWorker { public: SpriteLoaderWorker(ActorRef, ActorRef); - void parse(std::shared_ptr image, std::shared_ptr json); + void parse(std::shared_ptr image, + std::shared_ptr json, + uint64_t correlationID); private: ActorRef parent; diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp index 96fe5c0f4f..d5bb35b831 100644 --- a/src/mbgl/storage/resource.cpp +++ b/src/mbgl/storage/resource.cpp @@ -4,11 +4,23 @@ #include #include #include +#include #include namespace mbgl { +MBGL_DEFINE_ENUM(ResourceKind, { + { ResourceKind::Unknown, "Unknown" }, + { ResourceKind::Style, "Style" }, + { ResourceKind::Source, "Source" }, + { ResourceKind::Tile, "Tile" }, + { ResourceKind::Glyphs, "Glyphs" }, + { ResourceKind::SpriteImage, "SpriteImage" }, + { ResourceKind::SpriteJSON, "SpriteJSON" }, + { ResourceKind::Image, "Image" }, +}); + static std::string getQuadKey(int32_t x, int32_t y, int8_t z) { std::string quadKey; quadKey.reserve(z); diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp index a40446a8a0..a8bfbd43d9 100644 --- a/src/mbgl/storage/response.cpp +++ b/src/mbgl/storage/response.cpp @@ -1,11 +1,21 @@ #include #include +#include #include #include namespace mbgl { +MBGL_DEFINE_ENUM(ResourceStatus, { + { ResourceStatus::Success, "Success" }, + { ResourceStatus::NotFoundError, "NotFoundError" }, + { ResourceStatus::ServerError, "ServerError" }, + { ResourceStatus::ConnectionError, "ConnectionError" }, + { ResourceStatus::RateLimitError, "RateLimitError" }, + { ResourceStatus::OtherError, "OtherError" }, +}); + Response::Response(const Response& res) { *this = res; } @@ -25,25 +35,4 @@ Response::Error::Error(ResourceStatus reason_, std::string message_, optional + #include namespace mbgl { @@ -13,7 +15,7 @@ public: virtual void onSourceLoaded(Source&) {} virtual void onSourceChanged(Source&) {} - virtual void onSourceError(Source&, std::exception_ptr) {} + virtual void onSourceError(Source&, std::exception_ptr, EventSeverity = EventSeverity::Error) {} // Source description needs to be reloaded virtual void onSourceDescriptionChanged(Source&) {} diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp index ccdd453c75..b7ba869384 100644 --- a/src/mbgl/style/sources/vector_source.cpp +++ b/src/mbgl/style/sources/vector_source.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -46,17 +47,21 @@ void VectorSource::loadDescription(FileSource& fileSource) { const std::string& url = urlOrTileset.get(); req = fileSource.request(Resource::source(url), [this, url](Response res) { - if (res.error) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { + if (!res.error && res.notModified) { return; - } else if (res.noContent) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); + } else if (res.error || res.noContent) { + const auto severity = loaded ? EventSeverity::Warning : EventSeverity::Error; + const std::string& message = + res.error ? res.error->message : "unexpectedly empty TileJSON"; + const util::ResourceError err(message, ResourceKind::Source, res.error->status, url); + observer->onSourceError(*this, std::make_exception_ptr(err), severity); } else { conversion::Error error; optional tileset = conversion::convertJSON(*res.data, error); if (!tileset) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message))); + const auto severity = loaded ? EventSeverity::Warning : EventSeverity::Error; + observer->onSourceError( + *this, std::make_exception_ptr(std::runtime_error(error.message)), severity); return; } diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index 9cc2588ca7..f469769406 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace mbgl { namespace style { @@ -62,10 +63,15 @@ void Style::Impl::loadURL(const std::string& url_) { } if (res.error) { - const std::string message = "loading style failed: " + res.error->message; - Log::Error(Event::Setup, message.c_str()); - observer->onStyleError(std::make_exception_ptr(util::StyleLoadException(message))); - observer->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message))); + const std::string message = "Failed to load style: " + res.error->message; + const auto severity = loaded ? EventSeverity::Warning : EventSeverity::Error; + Log::Record(severity, Event::Setup, message.c_str()); + observer->onStyleError(std::make_exception_ptr(util::StyleLoadException(message)), + severity); + observer->onResourceError( + std::make_exception_ptr(util::ResourceError(res.error->message, ResourceKind::Style, + res.error->status, url)), + severity); } else if (res.notModified || res.noContent) { return; } else { @@ -111,7 +117,6 @@ void Style::Impl::parse(const std::string& json_) { defaultPitch = parser.pitch; setLight(std::make_unique(parser.light)); - spriteLoaded = false; spriteLoader->load(parser.spriteURL, scheduler, fileSource); glyphURL = parser.glyphURL; @@ -264,7 +269,7 @@ bool Style::Impl::isLoaded() const { return false; } - if (!spriteLoaded) { + if (!isSpriteLoaded()) { return false; } @@ -277,6 +282,10 @@ bool Style::Impl::isLoaded() const { return true; } +bool Style::Impl::isSpriteLoaded() const { + return spriteLoader->isLoaded(); +} + void Style::Impl::addImage(std::unique_ptr image) { images.remove(image->getID()); // We permit using addImage to update. images.add(std::move(image)); @@ -306,12 +315,16 @@ void Style::Impl::onSourceChanged(Source& source) { observer->onUpdate(Update::Repaint); } -void Style::Impl::onSourceError(Source& source, std::exception_ptr error) { - lastError = error; - Log::Error(Event::Style, "Failed to load source %s: %s", - source.getID().c_str(), util::toString(error).c_str()); - observer->onSourceError(source, error); - observer->onResourceError(error); +void Style::Impl::onSourceError(Source& source, + std::exception_ptr error, + const EventSeverity severity) { + if (severity == EventSeverity::Error) { + lastError = error; + } + Log::Record(severity, Event::Style, "Failed to load source %s: %s", source.getID().c_str(), + util::toString(error).c_str()); + observer->onSourceError(source, error, severity); + observer->onResourceError(error, severity); } void Style::Impl::onSourceDescriptionChanged(Source& source) { @@ -326,14 +339,15 @@ void Style::Impl::onSpriteLoaded(std::vector>&& images_) for (auto& image : images_) { addImage(std::move(image)); } - spriteLoaded = true; observer->onUpdate(Update::Repaint); // For *-pattern properties. } -void Style::Impl::onSpriteError(std::exception_ptr error) { - lastError = error; - Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str()); - observer->onResourceError(error); +void Style::Impl::onSpriteError(std::exception_ptr error, const EventSeverity severity) { + if (severity == EventSeverity::Error) { + lastError = error; + } + Log::Record(severity, Event::Style, "Failed to load sprite: %s", util::toString(error).c_str()); + observer->onResourceError(error, severity); } void Style::Impl::onLayerChanged(Layer& layer) { diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp index 76f244d5a4..4eead24ca1 100644 --- a/src/mbgl/style/style_impl.hpp +++ b/src/mbgl/style/style_impl.hpp @@ -48,6 +48,7 @@ public: void setObserver(Observer*); bool isLoaded() const; + bool isSpriteLoaded() const; std::exception_ptr getLastError() const { return lastError; @@ -94,7 +95,6 @@ public: bool mutated = false; bool loaded = false; - bool spriteLoaded = false; private: void parse(const std::string&); @@ -124,12 +124,12 @@ private: // SpriteLoaderObserver implementation. void onSpriteLoaded(std::vector>&&) override; - void onSpriteError(std::exception_ptr) override; + void onSpriteError(std::exception_ptr, EventSeverity) override; // SourceObserver implementation. void onSourceLoaded(Source&) override; void onSourceChanged(Source&) override; - void onSourceError(Source&, std::exception_ptr) override; + void onSourceError(Source&, std::exception_ptr, EventSeverity) override; void onSourceDescriptionChanged(Source&) override; // LayerObserver implementation. diff --git a/src/mbgl/text/glyph_manager_observer.hpp b/src/mbgl/text/glyph_manager_observer.hpp index b8678e060a..134998b890 100644 --- a/src/mbgl/text/glyph_manager_observer.hpp +++ b/src/mbgl/text/glyph_manager_observer.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -13,7 +14,10 @@ public: virtual ~GlyphManagerObserver() = default; virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {} - virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {} + virtual void onGlyphsError(const FontStack&, + const GlyphRange&, + std::exception_ptr, + EventSeverity = EventSeverity::Error) {} }; } // namespace mbgl diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index 6b0cd8f947..366ee28ac1 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -106,6 +106,8 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, } void GeoJSONTile::updateData(mapbox::geometry::feature_collection features) { + loaded = true; + logDebug("GeoJSONTile::setData"); GeometryTile::setData(std::make_unique(std::move(features))); } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index fdbad8bd40..f097001af4 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -61,8 +61,7 @@ void GeometryTile::markObsolete() { } void GeometryTile::setError(std::exception_ptr err) { - loaded = true; - renderable = false; + logDebug("GeometryTile::setError"); observer->onTileError(*this, err); } @@ -71,6 +70,8 @@ void GeometryTile::setData(std::unique_ptr data_) { // state despite pending parse operations. pending = true; + logDebug("GeometryTile::setData"); + ++correlationID; worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID); } @@ -81,8 +82,13 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) { } // Mark the tile as pending again if it was complete before to prevent signaling a complete - // state despite pending parse operations. - pending = true; + // state despite pending parse operations. However, we only need to worry about this when we + // know that there's data, merely sending layers over doesn't qualify. + if (parsed) { + pending = true; + } + + logDebug("GeometryTile::setPlacementConfig"); ++correlationID; requestedConfig = desiredConfig; @@ -97,8 +103,13 @@ void GeometryTile::invokePlacement() { void GeometryTile::setLayers(const std::vector>& layers) { // Mark the tile as pending again if it was complete before to prevent signaling a complete - // state despite pending parse operations. - pending = true; + // state despite pending parse operations. However, we only need to worry about this when we + // know that there's data, merely sending layers over doesn't qualify. + if (parsed) { + pending = true; + } + + logDebug("GeometryTile::setLayers"); std::vector> impls; @@ -121,22 +132,19 @@ void GeometryTile::setLayers(const std::vector>& layers) } void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) { - loaded = true; - renderable = true; nonSymbolBuckets = std::move(result.nonSymbolBuckets); featureIndex = std::move(result.featureIndex); data = std::move(result.tileData); collisionTile.reset(); + parsed = true; + renderable = true; + // We're waiting on a onPlacement event for this correlation ID to reset the pending state. (void)resultCorrelationID; + logDebug("GeometryTile::onLayout"); observer->onTileChanged(*this); } void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) { - loaded = true; - renderable = true; - if (resultCorrelationID == correlationID) { - pending = false; - } symbolBuckets = std::move(result.symbolBuckets); collisionTile = std::move(result.collisionTile); if (result.glyphAtlasImage) { @@ -148,14 +156,21 @@ void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorr if (collisionTile.get()) { lastYStretch = collisionTile->yStretch; } + parsed = true; + renderable = true; + if (resultCorrelationID == correlationID) { + pending = false; + } + logDebug("GeometryTile::onPlacement"); observer->onTileChanged(*this); } void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) { - loaded = true; - pending = false; - (void)resultCorrelationID; - renderable = false; + parsed = true; + if (resultCorrelationID == correlationID) { + pending = false; + } + logDebug("GeometryTile::onError"); observer->onTileError(*this, err); } diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index be78a9988b..c7f0ecae97 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -20,6 +20,7 @@ RasterTile::RasterTile(const OverscaledTileID& id_, mailbox(std::make_shared(*util::RunLoop::Get())), worker(parameters.workerScheduler, ActorRef(*this, mailbox)) { + logDebug("RasterTile::RasterTile"); } RasterTile::~RasterTile() = default; @@ -29,9 +30,10 @@ void RasterTile::cancel() { // Called instead of setData() when the data source returns an error while loading this tile. void RasterTile::setError(std::exception_ptr err, const bool complete) { - (void)complete; - loaded = true; - renderable = false; + if (complete) { + failed = true; + } + logDebug("RasterTile::setError"); observer->onTileError(*this, err); } @@ -41,11 +43,19 @@ void RasterTile::setData(optional> data, optional modified_, optional expires_, const bool complete) { - (void)complete; modified = modified_; expires = expires_; + if (complete) { + loaded = true; + } if (data) { + // Mark the tile as pending again if it was complete before to prevent signaling a complete + // state despite pending parse operations. + pending = true; + + logDebug("RasterTile::setData"); + ++correlationID; worker.invoke(&RasterTileWorker::parse, *data, correlationID); } @@ -53,19 +63,23 @@ void RasterTile::setData(optional> data, // Invoked once the worker thread finished parsing the image. void RasterTile::onParsed(std::unique_ptr result, const uint64_t resultCorrelationID) { - (void)resultCorrelationID; bucket = std::move(result); - loaded = true; - renderable = bucket ? true : false; + parsed = true; + renderable = true; + if (resultCorrelationID == correlationID) { + pending = false; + } + logDebug("RasterTile::onParsed"); observer->onTileChanged(*this); } // Invoked when the worker thread fails to parse the image. void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) { - (void)resultCorrelationID; - bucket.reset(); - loaded = true; - renderable = false; + parsed = true; + if (resultCorrelationID == correlationID) { + pending = false; + } + logDebug("RasterTile::onError"); observer->onTileError(*this, err); } diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index f5f6ea944c..1c8032e298 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -54,3 +54,4 @@ private: }; } // namespace mbgl + diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp index e42a78c26a..80cc57c5d5 100644 --- a/src/mbgl/tile/raster_tile_worker.cpp +++ b/src/mbgl/tile/raster_tile_worker.cpp @@ -18,6 +18,7 @@ void RasterTileWorker::parse(std::shared_ptr data, const uint try { auto bucket = std::make_unique(decodeImage(*data)); +// sleep(2); parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID); } catch (...) { parent.invoke(&RasterTile::onError, std::current_exception(), correlationID); diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 35fc31dae1..c060017241 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -5,6 +5,8 @@ #include #include +#include + namespace mbgl { static TileObserver nullObserver; @@ -18,15 +20,20 @@ void Tile::setObserver(TileObserver* observer_) { observer = observer_; } -void Tile::setTriedOptional() { - triedOptional = true; - observer->onTileChanged(*this); -} - void Tile::dumpDebugLogs() const { Log::Info(Event::General, "Tile::id: %s", util::toString(id).c_str()); + Log::Info(Event::General, "Tile::loaded: %s", isLoaded() ? "yes" : "no"); Log::Info(Event::General, "Tile::renderable: %s", isRenderable() ? "yes" : "no"); - Log::Info(Event::General, "Tile::complete: %s", isComplete() ? "yes" : "no"); +} + +void Tile::logDebug(const char* name) const { + // std::cerr << "[" << this << "] " << name << " " << id << " (" + // << "loaded: " << loaded << ", " + // << "parsed: " << parsed << ", " + // << "failed: " << failed << ", " + // << "renderable: " << renderable << ", " + // << "pending: " << pending << ")" << std::endl; + (void)name; } void Tile::queryRenderedFeatures( diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index a1ab6a84b7..800752f8c9 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -68,39 +68,29 @@ public: std::vector& result, const SourceQueryOptions&); - void setTriedOptional(); - - // Returns true when the tile source has received a first response, regardless of whether a load - // error occurred or actual data was loaded. - bool hasTriedOptional() const { - return triedOptional; - } - - // Tile data considered "Renderable" can be used for rendering. Data in - // partial state is still waiting for network resources but can also - // be rendered, although layers will be missing. + // Tile data considered "Renderable" can be used for rendering. It doesn't necessarily mean that + // there is anything to render, since tiles can be empty. Tiles that are still waiting for + // network resources but can also be rendered, although layers will be missing. bool isRenderable() const { return renderable; } - // A tile is "Loaded" when we have received a response from a FileSource, and have attempted to - // parse the tile (if applicable). Tile implementations should set this to true when a load - // error occurred, or after the tile was parsed successfully. + // A tile is "loaded" when we have received a response from a FileSource, and have attempted + // to parse the tile (if applicable), and there are no further operations in progress to acquire + // more, newer, or better data. Furthermore, it means that there are no pending layout/placement + // or parsing operations going on. Completeness doesn't mean that the tile can be rendered, but + // merely that we have exhausted all options to get this tile to a renderable state. Some tiles + // may not be renderable, but complete, e.g. when a raster tile couldn't be loaded, or parsing + // failed. bool isLoaded() const { - return loaded; - } - - // "Completion" of a tile means that we have attempted to load it, and parsed it completely, - // i.e. no parsing or placement operations are pending for that tile. - // Completeness doesn't mean that the tile can be rendered, but merely that we have exhausted - // all options to get this tile to a renderable state. Some tiles may not be renderable, but - // complete, e.g. when a raster tile couldn't be loaded, or parsing failed. - bool isComplete() const { - return loaded && !pending; + return (loaded ? parsed : failed) && !pending; } void dumpDebugLogs() const; + void logDebug(const char*) const; + + // This information is only used for displaying debugging information. const OverscaledTileID id; optional modified; optional expires; @@ -111,10 +101,24 @@ public: virtual float yStretch() const { return 1.0f; } protected: - bool triedOptional = false; + // A tile is loaded when we acquired data for it, and no further operations to acquire more, + // newer, or better data are in progress. + bool loaded = false; + + // A tile is marked as failed when we tried to acquire data, but couldn't, and no further + // operations to acquire more, newer, or better data are in progress. + bool failed = false; + + // A tile is marked as parsed when we attempted to parse its contents. It doesn't necessairly + // mean that we *successfully* parsed it, just that there's been an attempt. + bool parsed = false; + + // Renderability means that we successfully parsed a tile. Note that it doesn't mean there's + // anything to render (e.g. vector tiles may be empty). bool renderable = false; + + // A tile is pending while parsing or placement operations are going on. bool pending = false; - bool loaded = false; TileObserver* observer = nullptr; }; diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp index 5bf9988485..dc57d61e5a 100644 --- a/src/mbgl/tile/tile_loader_impl.hpp +++ b/src/mbgl/tile/tile_loader_impl.hpp @@ -53,11 +53,10 @@ void TileLoader::loadOptional() { assert(!request); resource.necessity = Resource::Optional; +// fprintf(stderr, "requesting %s\n", resource.url.c_str()); request = fileSource.request(resource, [this](Response res) { request.reset(); - tile.setTriedOptional(); - if (res.error && res.error->status == ResourceStatus::NotFoundError) { // When the optional request could not be satisfied, don't treat it as an error. // Instead, we make sure that the next request knows that there has been an optional @@ -92,18 +91,18 @@ template void TileLoader::loadedData(const Response& res) { const bool complete = necessity == Necessity::Optional || resource.necessity == Necessity::Required; if (res.error) { - tile.setError(std::make_exception_ptr(util::ResourceError(res.error->message, resource.kind, - res.error->status, resource.url)), - complete); + util::ResourceError err(res.error->message, resource.kind, res.error->status, resource.url); + tile.setError(std::make_exception_ptr(err), complete); } else if (res.notModified) { resource.priorExpires = res.expires; - // Do not notify the tile; when we get this message, it already has the current + tile.setData({}, res.modified, res.expires, complete); + // Do not notify the tile of new data; when we get this message, it already has the current // version of the data. } else { resource.priorModified = res.modified; resource.priorExpires = res.expires; resource.priorEtag = res.etag; - tile.setData(res.noContent ? nullptr : res.data, res.modified, res.expires, complete); + tile.setData({ res.noContent ? nullptr : res.data }, res.modified, res.expires, complete); } } @@ -112,7 +111,9 @@ void TileLoader::loadRequired() { assert(!request); resource.necessity = Resource::Required; +// fprintf(stderr, "requesting %s\n", resource.url.c_str()); request = fileSource.request(resource, [this](Response res) { loadedData(res); }); } } // namespace mbgl + diff --git a/src/mbgl/tile/tile_observer.hpp b/src/mbgl/tile/tile_observer.hpp index 837b47ae0b..4970ffefb0 100644 --- a/src/mbgl/tile/tile_observer.hpp +++ b/src/mbgl/tile/tile_observer.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace mbgl { @@ -11,7 +13,7 @@ public: virtual ~TileObserver() = default; virtual void onTileChanged(Tile&) {} - virtual void onTileError(Tile&, std::exception_ptr) {} + virtual void onTileError(Tile&, std::exception_ptr, EventSeverity = EventSeverity::Error) {} }; } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index e22ccf0d54..9cdf0d47ef 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -10,6 +10,7 @@ VectorTile::VectorTile(const OverscaledTileID& id_, const TileParameters& parameters, const Tileset& tileset) : GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) { + logDebug("VectorTile::VectorTile"); } void VectorTile::setNecessity(Necessity necessity) { @@ -17,7 +18,10 @@ void VectorTile::setNecessity(Necessity necessity) { } void VectorTile::setError(std::exception_ptr err, const bool complete) { - (void)complete; + if (complete) { + failed = true; + } + logDebug("VectorTile::setError"); GeometryTile::setError(err); } @@ -28,6 +32,10 @@ void VectorTile::setData(optional> data, (void)complete; modified = modified_; expires = expires_; + if (complete) { + loaded = true; + } + logDebug("VectorTile::setData"); if (data) { GeometryTile::setData(*data ? std::make_unique(*data) : nullptr); diff --git a/src/mbgl/util/event.cpp b/src/mbgl/util/event.cpp index 3a3be20f5c..ea534fe412 100644 --- a/src/mbgl/util/event.cpp +++ b/src/mbgl/util/event.cpp @@ -28,6 +28,7 @@ MBGL_DEFINE_ENUM(Event, { { Event::Android, "Android" }, { Event::Crash, "Crash" }, { Event::Glyph, "Glyph" }, + { Event::Resource, "Resource" }, { Event(-1), "Unknown" }, }); diff --git a/test/algorithm/mock.hpp b/test/algorithm/mock.hpp index d87f55343b..67f02bd8f5 100644 --- a/test/algorithm/mock.hpp +++ b/test/algorithm/mock.hpp @@ -26,10 +26,6 @@ struct MockBucket {}; struct MockTileData { MockTileData(const mbgl::OverscaledTileID& tileID_) : tileID(tileID_) {} - bool hasTriedOptional() const { - return triedOptional; - } - bool isRenderable() const { return renderable; } @@ -39,7 +35,6 @@ struct MockTileData { } bool renderable = false; - bool triedOptional = false; bool loaded = false; const mbgl::OverscaledTileID tileID; }; diff --git a/test/algorithm/update_renderables.test.cpp b/test/algorithm/update_renderables.test.cpp index 2d37992579..9c417cf7d5 100644 --- a/test/algorithm/update_renderables.test.cpp +++ b/test/algorithm/update_renderables.test.cpp @@ -171,7 +171,7 @@ TEST(UpdateRenderables, SingleTile) { // Mark the created tile as having the optional request tried. log.clear(); - source.dataTiles[{ 1, 0, { 1, 0, 1 } }]->triedOptional = true; + source.dataTiles[{ 1, 0, { 1, 0, 1 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ @@ -183,7 +183,7 @@ TEST(UpdateRenderables, SingleTile) { GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ... GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile CreateTileDataAction{ { 0, 0, { 0, 0, 0 } } }, // load parent tile - RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // @@ -333,7 +333,7 @@ TEST(UpdateRenderables, DontUseWrongParentTile) { // Now mark the created tile as having the optional request tried. log.clear(); - source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ @@ -345,7 +345,7 @@ TEST(UpdateRenderables, DontUseWrongParentTile) { GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // parent tile, missing CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // find optional parent - RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing }), log); @@ -364,7 +364,7 @@ TEST(UpdateRenderables, DontUseWrongParentTile) { GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ... GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // parent tile not ready - RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // missing parent tile GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, // missing ideal tile @@ -718,7 +718,7 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) { // Mark the created tile as having tried the optional request. log.clear(); - source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ( @@ -728,7 +728,7 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) { GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children! GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // - RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // }), log); @@ -856,7 +856,7 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) { // Now, mark the created tile as found. log.clear(); - source.dataTiles[{ 3, 0, { 2, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 3, 0, { 2, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ @@ -865,7 +865,7 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) { GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, // GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // @@ -1086,7 +1086,7 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 6, 0, { 6, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 6, 0, { 6, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ @@ -1098,7 +1098,7 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent CreateTileDataAction{ { 5, 0, { 5, 0, 0 } } }, // - RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... @@ -1119,7 +1119,7 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... @@ -1130,7 +1130,7 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 5, 0, { 5, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 5, 0, { 5, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ @@ -1141,10 +1141,10 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... CreateTileDataAction{ { 4, 0, { 4, 0, 0 } } }, // - RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... @@ -1154,7 +1154,7 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 4, 0, { 4, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 4, 0, { 4, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ @@ -1165,12 +1165,12 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... CreateTileDataAction{ { 3, 0, { 3, 0, 0 } } }, // - RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... @@ -1179,7 +1179,7 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 3, 0, { 3, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 3, 0, { 3, 0, 0 } }]->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ @@ -1190,14 +1190,14 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), @@ -1217,11 +1217,11 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Required }, // GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Required }, // RenderTileAction{ { 3, 0, 0 }, *tile_3_3_0_0 }, // }), log); @@ -1239,7 +1239,6 @@ TEST(UpdateRenderables, LoadRequiredIfIdealTileCantBeFound) { source.idealTiles.emplace(UnwrappedTileID{ 6, 0, 0 }); auto tile_6_6_0_0 = source.createTileData(OverscaledTileID{ 6, 0, { 6, 0, 0 } }); - tile_6_6_0_0->triedOptional = true; tile_6_6_0_0->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, diff --git a/test/api/render_caching.cpp b/test/api/render_caching.cpp index cec501f84e..44ed15336e 100644 --- a/test/api/render_caching.cpp +++ b/test/api/render_caching.cpp @@ -21,21 +21,19 @@ class FrameObserver : public mbgl::MapObserver { public: void onDidFinishRenderingFrame(RenderMode mode) { if (mode == RenderMode::Full) { - fprintf(stderr, "onDidFinishRenderingFrame: full\n"); +// fprintf(stderr, "onDidFinishRenderingFrame: full\n"); // mbgl::util::RunLoop::Get()->stop(); } else { - fprintf(stderr, "onDidFinishRenderingFrame: partial\n"); - +// fprintf(stderr, "onDidFinishRenderingFrame: partial\n"); } } void onDidFinishRenderingMap(RenderMode mode) { // fprintf(stderr, "onDidFinishRenderingMap\n"); if (mode == RenderMode::Full) { - fprintf(stderr, "onDidFinishRenderingMap: full\n"); +// fprintf(stderr, "onDidFinishRenderingMap: full\n"); mbgl::util::RunLoop::Get()->stop(); } else { - - fprintf(stderr, "onDidFinishRenderingMap: partial\n"); +// fprintf(stderr, "onDidFinishRenderingMap: partial\n"); } } }; diff --git a/test/api/render_missing.test.cpp b/test/api/render_missing.test.cpp index 02c19d359b..54bf2e73c0 100644 --- a/test/api/render_missing.test.cpp +++ b/test/api/render_missing.test.cpp @@ -28,6 +28,8 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { util::RunLoop loop; + Log::setObserver(std::make_unique()); + const auto style = util::read_file("test/fixtures/api/water_missing_tiles.json"); HeadlessBackend backend; @@ -38,8 +40,6 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { ThreadPool threadPool(4); StubRendererFrontend rendererFrontend { std::make_unique(backend, pixelRatio, fileSource, threadPool), view }; - Log::setObserver(std::make_unique()); - Map map { rendererFrontend, MapObserver::nullObserver(), view.getSize(), pixelRatio, fileSource, threadPool, MapMode::Still }; diff --git a/test/fixtures/api/assets/raster/10-163-395.png b/test/fixtures/api/assets/raster/10-163-395.png new file mode 100644 index 0000000000..870512bd55 Binary files /dev/null and b/test/fixtures/api/assets/raster/10-163-395.png differ diff --git a/test/fixtures/api/assets/raster/6-10-24.png b/test/fixtures/api/assets/raster/6-10-24.png new file mode 100644 index 0000000000..2c00eb0dff Binary files /dev/null and b/test/fixtures/api/assets/raster/6-10-24.png differ diff --git a/test/fixtures/api/cached_raster/expected.png b/test/fixtures/api/cached_raster/expected.png new file mode 100644 index 0000000000..4635df7409 Binary files /dev/null and b/test/fixtures/api/cached_raster/expected.png differ diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 6c2ac7b0d7..f8ca896ab3 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -152,7 +152,7 @@ TEST(Map, Offline) { test.fileSource.put(Resource::spriteImage(prefix + "sprite", 1.0), expiredItem("sprite.png")); test.fileSource.put(Resource::tile(prefix + "{z}-{x}-{y}.vector.pbf", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), expiredItem("0-0-0.vector.pbf")); test.fileSource.put(Resource::glyphs(prefix + "{fontstack}/{range}.pbf", {{"Helvetica"}}, {0, 255}), expiredItem("glyph.pbf")); - NetworkStatus::Set(NetworkStatus::Status::Offline); + test.fileSource.setOnlineStatus(false); test.map.getStyle().loadURL(prefix + "style.json"); @@ -160,8 +160,6 @@ TEST(Map, Offline) { test::render(test.map, test.view), 0.0015, 0.1); - - NetworkStatus::Set(NetworkStatus::Status::Online); } TEST(Map, SetStyleInvalidJSON) { diff --git a/test/sprite/sprite_loader.test.cpp b/test/sprite/sprite_loader.test.cpp index ca8162d685..1e291da265 100644 --- a/test/sprite/sprite_loader.test.cpp +++ b/test/sprite/sprite_loader.test.cpp @@ -22,7 +22,7 @@ public: if (spriteLoaded) spriteLoaded(std::move(images)); } - void onSpriteError(std::exception_ptr error) override { + void onSpriteError(std::exception_ptr error, const EventSeverity) override { if (spriteError) spriteError(error); } diff --git a/test/src/mbgl/test/stub_render_source_observer.hpp b/test/src/mbgl/test/stub_render_source_observer.hpp index ff20fff80c..7aa9e7714a 100644 --- a/test/src/mbgl/test/stub_render_source_observer.hpp +++ b/test/src/mbgl/test/stub_render_source_observer.hpp @@ -13,9 +13,12 @@ public: if (tileChanged) tileChanged(source, tileID); }; - void - onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) override { - if (tileError) tileError(source, tileID, error); + void onTileError(RenderSource& source, + const OverscaledTileID& tileID, + std::exception_ptr error, + EventSeverity) override { + if (tileError) + tileError(source, tileID, error); } std::function tileChanged; diff --git a/test/src/mbgl/test/stub_style_observer.hpp b/test/src/mbgl/test/stub_style_observer.hpp index b97911cdb0..f8b19aeaff 100644 --- a/test/src/mbgl/test/stub_style_observer.hpp +++ b/test/src/mbgl/test/stub_style_observer.hpp @@ -18,7 +18,7 @@ public: if (sourceChanged) sourceChanged(source); } - void onSourceError(Source& source, std::exception_ptr error) override { + void onSourceError(Source& source, std::exception_ptr error, EventSeverity) override { if (sourceError) sourceError(source, error); } @@ -26,7 +26,7 @@ public: if (sourceDescriptionChanged) sourceDescriptionChanged(source); } - void onResourceError(std::exception_ptr error) override { + void onResourceError(std::exception_ptr error, EventSeverity) override { if (resourceError) resourceError(error); }; diff --git a/test/src/mbgl/test/stub_tile_observer.hpp b/test/src/mbgl/test/stub_tile_observer.hpp index 43ae4d8360..720ee8739f 100644 --- a/test/src/mbgl/test/stub_tile_observer.hpp +++ b/test/src/mbgl/test/stub_tile_observer.hpp @@ -13,7 +13,7 @@ public: if (tileChanged) tileChanged(tile); } - void onTileError(Tile& tile, std::exception_ptr error) override { + void onTileError(Tile& tile, std::exception_ptr error, EventSeverity) override { if (tileError) tileError(tile, error); } diff --git a/test/src/mbgl/test/util.cpp b/test/src/mbgl/test/util.cpp index 0b1034e315..8b3db8bd95 100644 --- a/test/src/mbgl/test/util.cpp +++ b/test/src/mbgl/test/util.cpp @@ -100,7 +100,10 @@ Server::~Server() { PremultipliedImage render(Map& map, OffscreenView& view) { PremultipliedImage result; - map.renderStill([&](std::exception_ptr) { + map.renderStill([&](std::exception_ptr err) { + if (err) { + std::rethrow_exception(err); + } result = view.readStillImage(); }); diff --git a/test/text/glyph_loader.test.cpp b/test/text/glyph_loader.test.cpp index 41a65778f7..e06fbcb314 100644 --- a/test/text/glyph_loader.test.cpp +++ b/test/text/glyph_loader.test.cpp @@ -15,8 +15,12 @@ public: if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange); } - void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override { - if (glyphsError) glyphsError(fontStack, glyphRange, error); + void onGlyphsError(const FontStack& fontStack, + const GlyphRange& glyphRange, + std::exception_ptr error, + const EventSeverity) override { + if (glyphsError) + glyphsError(fontStack, glyphRange, error); } std::function glyphsLoaded; diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp index 31fb8c1fd0..f476628663 100644 --- a/test/tile/geojson_tile.test.cpp +++ b/test/tile/geojson_tile.test.cpp @@ -68,12 +68,12 @@ TEST(GeoJSONTile, Issue7648) { tile.setObserver(&observer); tile.setPlacementConfig({}); - while (!tile.isComplete()) { + while (!tile.isLoaded()) { test.loop.runOnce(); } tile.updateData(features); - while (!tile.isComplete()) { + while (!tile.isLoaded()) { test.loop.runOnce(); } } diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp index c32ee232b2..450438dd01 100644 --- a/test/tile/raster_tile.test.cpp +++ b/test/tile/raster_tile.test.cpp @@ -47,7 +47,6 @@ TEST(RasterTile, setError) { tile.setError(std::make_exception_ptr(std::runtime_error("test")), 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); - EXPECT_TRUE(tile.isComplete()); } TEST(RasterTile, onError) { @@ -56,7 +55,6 @@ TEST(RasterTile, onError) { tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); - EXPECT_TRUE(tile.isComplete()); } TEST(RasterTile, onParsed) { @@ -65,7 +63,6 @@ TEST(RasterTile, onParsed) { tile.onParsed(std::make_unique(PremultipliedImage{}), 0); EXPECT_TRUE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); - EXPECT_TRUE(tile.isComplete()); } TEST(RasterTile, onParsedEmpty) { @@ -74,5 +71,4 @@ TEST(RasterTile, onParsedEmpty) { tile.onParsed(nullptr, 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); - EXPECT_TRUE(tile.isComplete()); } diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index da9512bc48..30dfbfad21 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -53,7 +53,6 @@ TEST(VectorTile, setError) { tile.setError(std::make_exception_ptr(std::runtime_error("test")), 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); - EXPECT_TRUE(tile.isComplete()); } TEST(VectorTile, onError) { @@ -62,7 +61,6 @@ TEST(VectorTile, onError) { tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0); EXPECT_FALSE(tile.isRenderable()); EXPECT_TRUE(tile.isLoaded()); - EXPECT_TRUE(tile.isComplete()); } TEST(VectorTile, Issue7615) { -- cgit v1.2.1