diff options
author | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-10-18 13:11:31 -0700 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-10-18 14:58:29 -0700 |
commit | 90a7cf87a32f6787e57e3852ac9d3015d8112621 (patch) | |
tree | 9f4d18736ee976b0071b54dd0610b8ac0a22a14f | |
parent | 1c633072fcea7ad153ab6f8ec40dd72d83541ead (diff) | |
download | qtlocation-mapboxgl-90a7cf87a32f6787e57e3852ac9d3015d8112621.tar.gz |
Bump Mapbox GL Native
mapbox-gl-native @ 10f7af19ce1ec61f37459f9cd75e2a0c89a0c790
153 files changed, 2658 insertions, 1027 deletions
diff --git a/deps/geojson/0.4.0/include/mapbox/geojson.hpp b/deps/geojson/0.4.2/include/mapbox/geojson.hpp index 4b62751e15..4b62751e15 100644 --- a/deps/geojson/0.4.0/include/mapbox/geojson.hpp +++ b/deps/geojson/0.4.2/include/mapbox/geojson.hpp diff --git a/deps/geojson/0.4.0/include/mapbox/geojson/rapidjson.hpp b/deps/geojson/0.4.2/include/mapbox/geojson/rapidjson.hpp index 360eefd010..360eefd010 100644 --- a/deps/geojson/0.4.0/include/mapbox/geojson/rapidjson.hpp +++ b/deps/geojson/0.4.2/include/mapbox/geojson/rapidjson.hpp diff --git a/deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp b/deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp index 2ffd420dbb..48d81e4114 100644 --- a/deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp +++ b/deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp @@ -6,6 +6,9 @@ #include <rapidjson/document.h> #include <rapidjson/writer.h> #include <rapidjson/stringbuffer.h> +#include <rapidjson/error/en.h> + +#include <sstream> namespace mapbox { namespace geojson { @@ -170,13 +173,11 @@ feature convert<feature>(const rapidjson_value &json) { } auto const &prop_itr = json.FindMember("properties"); - - if (prop_itr == json_end) - throw error("Feature must have a properties property"); - - const auto &json_props = prop_itr->value; - if (!json_props.IsNull()) { - result.properties = convert<prop_map>(json_props); + if (prop_itr != json_end) { + const auto &json_props = prop_itr->value; + if (!json_props.IsNull()) { + result.properties = convert<prop_map>(json_props); + } } return result; @@ -227,6 +228,11 @@ template <class T> T parse(const std::string &json) { rapidjson_document d; d.Parse(json.c_str()); + if (d.HasParseError()) { + std::stringstream message; + message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); + throw error(message.str()); + } return convert<T>(d); } diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp index b758be3929..ee1bbbca8d 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp @@ -13,11 +13,11 @@ namespace mapbox { namespace geojsonvt { -using geometry = mapbox::geometry::geometry<double>; -using feature = mapbox::geometry::feature<double>; -using feature_collection = mapbox::geometry::feature_collection<double>; +using geometry = mapbox::geometry::geometry<double>; +using feature = mapbox::geometry::feature<double>; +using feature_collection = mapbox::geometry::feature_collection<double>; using geometry_collection = mapbox::geometry::geometry_collection<double>; -using geojson = mapbox::util::variant<geometry, feature, feature_collection>; +using geojson = mapbox::util::variant<geometry, feature, feature_collection>; struct ToFeatureCollection { feature_collection operator()(const feature_collection& value) const { @@ -31,7 +31,18 @@ struct ToFeatureCollection { } }; -struct Options { +struct TileOptions { + // simplification tolerance (higher means simpler) + double tolerance = 3; + + // tile extent + uint16_t extent = 4096; + + // tile buffer on each side + uint16_t buffer = 64; +}; + +struct Options : TileOptions { // max zoom to preserve detail on uint8_t maxZoom = 18; @@ -43,15 +54,6 @@ struct Options { // whether to tile solid square tiles further bool solidChildren = false; - - // simplification tolerance (higher means simpler) - double tolerance = 3; - - // tile extent - uint16_t extent = 4096; - - // tile buffer on each side - uint16_t buffer = 64; }; const Tile empty_tile{}; @@ -60,6 +62,31 @@ inline uint64_t toID(uint8_t z, uint32_t x, uint32_t y) { return (((1ull << z) * y + x) * 32) + z; } +inline const Tile geoJSONToTile(const geojson& geojson_, + uint8_t z, + uint32_t x, + uint32_t y, + const TileOptions& options = TileOptions(), + bool wrap = false, + bool clip = false) { + + const auto features_ = geojson::visit(geojson_, ToFeatureCollection{}); + auto z2 = 1u << z; + auto tolerance = (options.tolerance / options.extent) / z2; + auto features = detail::convert(features_, tolerance); + if (wrap) { + features = detail::wrap(features, double(options.buffer) / options.extent); + } + if (clip) { + const double p = options.buffer / options.extent; + + const auto left = detail::clip<0>(features, (x - p) / z2, (x + 1 + p) / z2, -1, 2); + features = detail::clip<1>(left, (y - p) / z2, (y + 1 + p) / z2, -1, 2); + } + return detail::InternalTile({ features, z, x, y, options.extent, options.buffer, tolerance }) + .tile; +} + class GeoJSONVT { public: const Options options; @@ -68,7 +95,7 @@ public: const Options& options_ = Options()) : options(options_) { - const uint32_t z2 = std::pow(2, options.maxZoom); + const uint32_t z2 = 1u << options.maxZoom; auto converted = detail::convert(features_, (options.tolerance / options.extent) / z2); auto features = detail::wrap(converted, double(options.buffer) / options.extent); @@ -88,7 +115,7 @@ public: if (z > options.maxZoom) throw std::runtime_error("Requested zoom higher than maxZoom: " + std::to_string(z)); - const uint32_t z2 = std::pow(2, z); + const uint32_t z2 = 1u << z; const uint32_t x = ((x_ % z2) + z2) % z2; // wrap tile x coordinate const uint64_t id = toID(z, x, y); diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/clip.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/clip.hpp index 212e2979d0..212e2979d0 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/clip.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/clip.hpp diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/convert.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/convert.hpp index ba28f449b3..ba28f449b3 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/convert.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/convert.hpp diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/simplify.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/simplify.hpp index be0165b0f1..be0165b0f1 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/simplify.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/simplify.hpp diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/tile.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/tile.hpp index ccb230bb9c..ccb230bb9c 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/tile.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/tile.hpp diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/types.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/types.hpp index b54357a7b9..b54357a7b9 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/types.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/types.hpp diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/wrap.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/wrap.hpp index 495ccc5800..495ccc5800 100644 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/wrap.hpp +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/wrap.hpp diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp index 406de425d4..0a20993352 100644 --- a/include/mbgl/actor/message.hpp +++ b/include/mbgl/actor/message.hpp @@ -64,6 +64,32 @@ public: std::promise<ResultType> promise; }; +template <class Object, class MemberFn, class ArgsTuple> +class AskMessageImpl<void, Object, MemberFn, ArgsTuple> : public Message { +public: + AskMessageImpl(std::promise<void> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)), + promise(std::move(promise_)) { + } + + void operator()() override { + ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()); + promise.set_value(); + } + + template <std::size_t... I> + void ask(std::index_sequence<I...>) { + (object.*memberFn)(std::move(std::get<I>(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; + std::promise<void> promise; +}; + namespace actor { template <class Object, class MemberFn, class... Args> diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 7d6678dc93..5ba23a76dd 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -8,6 +8,7 @@ #include <mbgl/util/size.hpp> #include <mbgl/annotation/annotation.hpp> #include <mbgl/map/camera.hpp> +#include <mbgl/util/geometry.hpp> #include <cstdint> #include <string> @@ -42,7 +43,8 @@ public: // Register a callback that will get called (on the render thread) when all resources have // been loaded and a complete render occurs. using StillImageCallback = std::function<void (std::exception_ptr)>; - void renderStill(StillImageCallback callback); + void renderStill(StillImageCallback); + void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback); // Triggers a repaint. void triggerRepaint(); @@ -65,6 +67,10 @@ public: void jumpTo(const CameraOptions&); void easeTo(const CameraOptions&, const AnimationOptions&); void flyTo(const CameraOptions&, const AnimationOptions&); + CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&, optional<double> bearing = {}) const; + CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&, optional<double> bearing = {}) const; + CameraOptions cameraForGeometry(const Geometry<double>&, const EdgeInsets&, optional<double> bearing = {}) const; + LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; // Position void moveBy(const ScreenCoordinate&, const AnimationOptions& = {}); @@ -81,9 +87,6 @@ public: double getZoom() const; void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {}); void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const AnimationOptions& = {}); - CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&) const; - CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&) const; - LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; void resetZoom(); // Bounds @@ -124,6 +127,14 @@ public: void setViewportMode(ViewportMode); ViewportMode getViewportMode() const; + // Projection mode + void setAxonometric(bool); + bool getAxonometric() const; + void setXSkew(double ySkew); + double getXSkew() const; + void setYSkew(double ySkew); + double getYSkew() const; + // Size void setSize(Size); Size getSize() const; diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index 95828a1b79..be8abb2c29 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -28,6 +28,8 @@ public: const optional<std::string> programCacheDir = {}); ~Renderer(); + void markContextLost(); + void setObserver(RendererObserver*); void render(const UpdateParameters&); diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp index f7d19a1791..295838c71b 100644 --- a/include/mbgl/renderer/renderer_backend.hpp +++ b/include/mbgl/renderer/renderer_backend.hpp @@ -35,6 +35,8 @@ public: // set to the current state. virtual void bind() = 0; + virtual Size getFramebufferSize() const = 0; + protected: // Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations // must call the API-specific version that obtains the function pointer for this function, diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 9911e0ce67..b9c8de5052 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -34,7 +34,7 @@ public: uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE); ~DefaultFileSource() override; - bool supportsOptionalRequests() const override { + bool supportsCacheOnlyRequests() const override { return true; } @@ -140,6 +140,7 @@ public: void resume(); // For testing only. + void setOnlineStatus(bool); void put(const Resource&, const Response&); class Impl; diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 404c683fdb..0709a1c245 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -24,11 +24,11 @@ public: // not be executed. virtual std::unique_ptr<AsyncRequest> request(const Resource&, Callback) = 0; - // When a file source supports optional requests, it must return true. - // Optional requests are requests that aren't as urgent, but could be useful, e.g. + // When a file source supports consulting a local cache only, it must return true. + // Cache-only requests are requests that aren't as urgent, but could be useful, e.g. // to cover part of the map while loading. The FileSource should only do cheap actions to // retrieve the data, e.g. load it from a cache, but not from the internet. - virtual bool supportsOptionalRequests() const { + virtual bool supportsCacheOnlyRequests() const { return false; } }; diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index 818cfe2ba5..ef4a499e83 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -30,13 +30,15 @@ public: OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float); /* Private */ - std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; - + std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; + uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; const std::string styleURL; const LatLngBounds bounds; const double minZoom; const double maxZoom; const float pixelRatio; +private: + Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; }; /* diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp index ffd75662e6..28d70ce544 100644 --- a/include/mbgl/storage/online_file_source.hpp +++ b/include/mbgl/storage/online_file_source.hpp @@ -24,6 +24,9 @@ public: std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; + // For testing only. + void setOnlineStatus(bool); + private: friend class OnlineFileRequest; diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index 5d44f4869f..318fa389f4 100644 --- a/include/mbgl/storage/resource.hpp +++ b/include/mbgl/storage/resource.hpp @@ -4,6 +4,8 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/font_stack.hpp> #include <mbgl/util/tileset.hpp> +#include <mbgl/util/util.hpp> +#include <mbgl/util/traits.hpp> #include <string> @@ -30,18 +32,28 @@ public: int8_t z; }; - enum Necessity : bool { - Optional = false, - Required = true, + enum class LoadingMethod : uint8_t { + None = 0b00, + Cache = 0b01, + Network = 0b10, + + CacheOnly = Cache, + NetworkOnly = Network, + All = Cache | Network, }; - Resource(Kind kind_, std::string url_, optional<TileData> tileData_ = {}, Necessity necessity_ = Required) + Resource(Kind kind_, + std::string url_, + optional<TileData> tileData_ = {}, + LoadingMethod loadingMethod_ = LoadingMethod::All) : kind(kind_), - necessity(necessity_), + loadingMethod(loadingMethod_), url(std::move(url_)), tileData(std::move(tileData_)) { } + bool hasLoadingMethod(LoadingMethod method); + static Resource style(const std::string& url); static Resource source(const std::string& url); static Resource tile(const std::string& urlTemplate, @@ -50,7 +62,7 @@ public: int32_t y, int8_t z, Tileset::Scheme scheme, - Necessity = Required); + LoadingMethod = LoadingMethod::All); static Resource glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange); @@ -59,7 +71,7 @@ public: static Resource image(const std::string& url); Kind kind; - Necessity necessity; + LoadingMethod loadingMethod; std::string url; // Includes auxiliary data if this is a tile request. @@ -71,4 +83,21 @@ public: std::shared_ptr<const std::string> priorData; }; + +MBGL_CONSTEXPR Resource::LoadingMethod operator|(Resource::LoadingMethod a, Resource::LoadingMethod b) { + return Resource::LoadingMethod(mbgl::underlying_type(a) | mbgl::underlying_type(b)); +} + +MBGL_CONSTEXPR Resource::LoadingMethod& operator|=(Resource::LoadingMethod& a, Resource::LoadingMethod b) { + return (a = a | b); +} + +MBGL_CONSTEXPR Resource::LoadingMethod operator&(Resource::LoadingMethod a, Resource::LoadingMethod b) { + return Resource::LoadingMethod(mbgl::underlying_type(a) & mbgl::underlying_type(b)); +} + +inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) { + return (loadingMethod & method) != Resource::LoadingMethod::None; +} + } // namespace mbgl diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 711f008e83..508400141b 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -2,7 +2,6 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/variant.hpp> #include <string> #include <memory> diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 19383d90ce..1c9c18250c 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -14,6 +14,16 @@ struct Converter<GeoJSONOptions> { optional<GeoJSONOptions> operator()(const V& value, Error& error) const { GeoJSONOptions options; + const auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + if (toNumber(*minzoomValue)) { + options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue)); + } else { + error = { "GeoJSON source minzoom value must be a number" }; + return {}; + } + } + const auto maxzoomValue = objectMember(value, "maxzoom"); if (maxzoomValue) { if (toNumber(*maxzoomValue)) { diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp index 9252297d75..59b0e7be32 100644 --- a/include/mbgl/style/conversion/make_property_setters.hpp +++ b/include/mbgl/style/conversion/make_property_setters.hpp @@ -45,17 +45,18 @@ auto makeLayoutPropertySetters() { result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>; result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>; result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>; + result["icon-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>; result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>; result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>; result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>; result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>; result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>; result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>; - result["text-max-width"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>; + result["text-max-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>; result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>; - result["text-letter-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>; + result["text-letter-spacing"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>; result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>; - result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextAnchorType>, &SymbolLayer::setTextAnchor>; + result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>; result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>; result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>; result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>; diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index 79a353b047..bf3387f95b 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -39,6 +39,14 @@ struct CustomLayerRenderParameters { using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&); /** + * Called when the system has destroyed the underlying GL context. The + * `CustomLayerDeinitializeFunction` will not be called in this case, however + * `CustomLayerInitializeFunction` will be called instead to prepare for a new render. + * + */ +using CustomLayerContextLostFunction = void (*)(void* context); + +/** * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This * method is called once, from the main thread, at a point when the GL context is active. * @@ -51,8 +59,16 @@ public: CustomLayer(const std::string& id, CustomLayerInitializeFunction, CustomLayerRenderFunction, + CustomLayerContextLostFunction, CustomLayerDeinitializeFunction, void* context); + + CustomLayer(const std::string& id, + CustomLayerInitializeFunction, + CustomLayerRenderFunction, + CustomLayerDeinitializeFunction, + void* context); + ~CustomLayer() final; // Visibility diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 6e355c0057..a72baa0b4e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -98,6 +98,10 @@ public: DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const; void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>); + static DataDrivenPropertyValue<SymbolAnchorType> getDefaultIconAnchor(); + DataDrivenPropertyValue<SymbolAnchorType> getIconAnchor() const; + void setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType>); + static PropertyValue<AlignmentType> getDefaultIconPitchAlignment(); PropertyValue<AlignmentType> getIconPitchAlignment() const; void setIconPitchAlignment(PropertyValue<AlignmentType>); @@ -122,25 +126,25 @@ public: DataDrivenPropertyValue<float> getTextSize() const; void setTextSize(DataDrivenPropertyValue<float>); - static PropertyValue<float> getDefaultTextMaxWidth(); - PropertyValue<float> getTextMaxWidth() const; - void setTextMaxWidth(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultTextMaxWidth(); + DataDrivenPropertyValue<float> getTextMaxWidth() const; + void setTextMaxWidth(DataDrivenPropertyValue<float>); static PropertyValue<float> getDefaultTextLineHeight(); PropertyValue<float> getTextLineHeight() const; void setTextLineHeight(PropertyValue<float>); - static PropertyValue<float> getDefaultTextLetterSpacing(); - PropertyValue<float> getTextLetterSpacing() const; - void setTextLetterSpacing(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultTextLetterSpacing(); + DataDrivenPropertyValue<float> getTextLetterSpacing() const; + void setTextLetterSpacing(DataDrivenPropertyValue<float>); static DataDrivenPropertyValue<TextJustifyType> getDefaultTextJustify(); DataDrivenPropertyValue<TextJustifyType> getTextJustify() const; void setTextJustify(DataDrivenPropertyValue<TextJustifyType>); - static DataDrivenPropertyValue<TextAnchorType> getDefaultTextAnchor(); - DataDrivenPropertyValue<TextAnchorType> getTextAnchor() const; - void setTextAnchor(DataDrivenPropertyValue<TextAnchorType>); + static DataDrivenPropertyValue<SymbolAnchorType> getDefaultTextAnchor(); + DataDrivenPropertyValue<SymbolAnchorType> getTextAnchor() const; + void setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType>); static PropertyValue<float> getDefaultTextMaxAngle(); PropertyValue<float> getTextMaxAngle() const; diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index 2dcfec51aa..5bdf1ef957 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -12,6 +12,7 @@ namespace style { struct GeoJSONOptions { // GeoJSON-VT options + uint8_t minzoom = 0; uint8_t maxzoom = 18; uint16_t buffer = 128; double tolerance = 0.375; diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index 44b16f16e7..2ed95f08b8 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -4,7 +4,9 @@ namespace mbgl { -// TODO: should be in public source.hpp header and style namespace +namespace style { + +// TODO: should be in public source.hpp header enum class SourceType : uint8_t { Vector, Raster, @@ -14,8 +16,6 @@ enum class SourceType : uint8_t { Image }; -namespace style { - enum class VisibilityType : bool { Visible, None, @@ -68,7 +68,7 @@ enum class TextJustifyType : uint8_t { Right }; -enum class TextAnchorType : uint8_t { +enum class SymbolAnchorType : uint8_t { Center, Left, Right, diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp new file mode 100644 index 0000000000..0457dd3a07 --- /dev/null +++ b/include/mbgl/tile/tile_id.hpp @@ -0,0 +1,263 @@ +#pragma once + +#include <mbgl/util/constants.hpp> + +#include <cstdint> +#include <array> +#include <tuple> +#include <forward_list> +#include <algorithm> +#include <iosfwd> +#include <cassert> + +namespace mbgl { + +class OverscaledTileID; +class CanonicalTileID; +class UnwrappedTileID; + +// Has integer z/x/y coordinates +// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) +// Used for requesting data; represents data tiles that exist out there. +// z is never larger than the source's maxzoom +class CanonicalTileID { +public: + CanonicalTileID(uint8_t z, uint32_t x, uint32_t y); + bool operator==(const CanonicalTileID&) const; + bool operator!=(const CanonicalTileID&) const; + bool operator<(const CanonicalTileID&) const; + bool isChildOf(const CanonicalTileID&) const; + CanonicalTileID scaledTo(uint8_t z) const; + std::array<CanonicalTileID, 4> children() const; + + const uint8_t z; + const uint32_t x; + const uint32_t y; +}; + +::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs); +namespace util { +std::string toString(const CanonicalTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data +// z is never larger than the source's maxzoom +// z/x/y describe the +class OverscaledTileID { +public: + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID); + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y); + OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); + explicit OverscaledTileID(const CanonicalTileID&); + explicit OverscaledTileID(CanonicalTileID&&); + bool operator==(const OverscaledTileID&) const; + bool operator!=(const OverscaledTileID&) const; + bool operator<(const OverscaledTileID&) const; + bool isChildOf(const OverscaledTileID&) const; + uint32_t overscaleFactor() const; + OverscaledTileID scaledTo(uint8_t z) const; + UnwrappedTileID toUnwrapped() const; + + const uint8_t overscaledZ; + const int16_t wrap; + const CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); +namespace util { +std::string toString(const OverscaledTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world +// Used for describing what position tiles are getting rendered at (= calc the matrix) +// z is never larger than the source's maxzoom +class UnwrappedTileID { +public: + UnwrappedTileID(uint8_t z, int64_t x, int64_t y); + UnwrappedTileID(int16_t wrap, CanonicalTileID); + bool operator==(const UnwrappedTileID&) const; + bool operator!=(const UnwrappedTileID&) const; + bool operator<(const UnwrappedTileID&) const; + bool isChildOf(const UnwrappedTileID&) const; + std::array<UnwrappedTileID, 4> children() const; + OverscaledTileID overscaleTo(uint8_t z) const; + float pixelsToTileUnits(float pixelValue, float zoom) const; + + const int16_t wrap; + const CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); +namespace util { +std::string toString(const UnwrappedTileID&); +} // namespace util + +inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) { + assert(z <= 32); + assert(x < (1ull << z)); + assert(y < (1ull << z)); +} + +inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const { + return z == rhs.z && x == rhs.x && y == rhs.y; +} + +inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const { + return z != rhs.z || x != rhs.x || y != rhs.y; +} + +inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const { + return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y); +} + +inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const { + // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined. + return parent.z == 0 || + (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z))); +} + +inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const { + if (targetZ <= z) { + return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same + } else { + return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child + } +} + +inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const { + const uint8_t childZ = z + 1; + const uint32_t childX = x * 2; + const uint32_t childY = y * 2; + return { { + { childZ, childX, childY }, + { childZ, childX, childY + 1 }, + { childZ, childX + 1, childY }, + { childZ, childX + 1, childY + 1 }, + } }; +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(z), wrap(0), canonical(z, x, y) { +} + +inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(std::forward<CanonicalTileID>(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { + return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical; +} + +inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { + return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { + return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical); +} + +inline uint32_t OverscaledTileID::overscaleFactor() const { + return 1u << (overscaledZ - canonical.z); +} + +inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { + return overscaledZ > rhs.overscaledZ && + (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical)); +} + +inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { + return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) }; +} + +inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { + return { wrap, canonical }; +} + +inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) + : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), + canonical( + z_, + static_cast<uint32_t>(x_ - wrap * (1ll << z_)), + y_ < 0 ? 0 : std::min(static_cast<uint32_t>(y_), static_cast<uint32_t>(1ull << z_) - 1)) { +} + +inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_) + : wrap(wrap_), canonical(std::move(canonical_)) { +} + +inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const { + return wrap == rhs.wrap && canonical == rhs.canonical; +} + +inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const { + return wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { + return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); +} + +inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { + return wrap == parent.wrap && canonical.isChildOf(parent.canonical); +} + +inline std::array<UnwrappedTileID, 4> UnwrappedTileID::children() const { + const uint8_t childZ = canonical.z + 1; + const uint32_t childX = canonical.x * 2; + const uint32_t childY = canonical.y * 2; + return { { + { wrap, { childZ, childX, childY } }, + { wrap, { childZ, childX, childY + 1 } }, + { wrap, { childZ, childX + 1, childY } }, + { wrap, { childZ, childX + 1, childY + 1 } }, + } }; +} + +inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { + assert(overscaledZ >= canonical.z); + return { overscaledZ, wrap, canonical }; +} + +inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { + return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z))); +} + +} // namespace mbgl + +namespace std { + +template <> +struct hash<mbgl::CanonicalTileID> { + size_t operator()(const mbgl::CanonicalTileID& id) const; +}; + +template <> +struct hash<mbgl::UnwrappedTileID> { + size_t operator()(const mbgl::UnwrappedTileID& id) const; +}; + +template <> +struct hash<mbgl::OverscaledTileID> { + size_t operator()(const mbgl::OverscaledTileID& id) const; +}; + +} // namespace std + diff --git a/include/mbgl/tile/tile_necessity.hpp b/include/mbgl/tile/tile_necessity.hpp new file mode 100644 index 0000000000..e51bf51d10 --- /dev/null +++ b/include/mbgl/tile/tile_necessity.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace mbgl { + +// Tiles can have two states: optional or required. +// - optional means that only low-cost actions should be taken to obtain the data +// (e.g. load from cache, but accept stale data) +// - required means that every effort should be taken to obtain the data (e.g. load +// from internet and keep the data fresh if it expires) +enum class TileNecessity : bool { + Optional = false, + Required = true, +}; + +} // namespace mbgl diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index eb5c201793..d5e55065c4 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -61,7 +61,6 @@ extern const bool tileParseWarnings; extern const bool styleParseWarnings; extern const bool spriteWarnings; extern const bool renderWarnings; -extern const bool renderTree; extern const bool labelTextMissingWarning; extern const bool missingFontStackWarning; extern const bool missingFontFaceWarning; diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp index 6dc16bc514..a28c59a47d 100644 --- a/include/mbgl/util/geometry.hpp +++ b/include/mbgl/util/geometry.hpp @@ -2,6 +2,7 @@ #include <mapbox/geometry/geometry.hpp> #include <mapbox/geometry/point_arithmetic.hpp> +#include <mapbox/geometry/for_each_point.hpp> namespace mbgl { @@ -58,4 +59,9 @@ struct ToFeatureType { FeatureType operator()(const mapbox::geometry::geometry_collection<T> &) const { return FeatureType::Unknown; } }; +template <class T, typename F> +auto forEachPoint(const Geometry<T>& g, F f) { + mapbox::geometry::for_each_point(g, f); +} + } // namespace mbgl diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index cb28f3da8d..4887058f79 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -5,6 +5,7 @@ #include <mbgl/util/size.hpp> #include <string> +#include <cstring> #include <memory> #include <algorithm> @@ -91,6 +92,31 @@ public: operator=(std::move(newImage)); } + // Clears the rect area specified by `pt` and `size` from `dstImage`. + static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) { + if (size.isEmpty()) { + return; + } + + if (!dstImg.valid()) { + throw std::invalid_argument("invalid destination for image clear"); + } + + if (size.width > dstImg.size.width || + size.height > dstImg.size.height || + pt.x > dstImg.size.width - size.width || + pt.y > dstImg.size.height - size.height) { + throw std::out_of_range("out of range destination coordinates for image clear"); + } + + uint8_t* dstData = dstImg.data.get(); + + for (uint32_t y = 0; y < size.height; y++) { + const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels; + std::memset(dstData + dstOffset, 0, size.width * channels); + } + } + // Copy image data within `rect` from `src` to the rectangle of the same size at `pt` // in `dst`. If the specified bounds exceed the bounds of the source or destination, // throw `std::out_of_range`. Must not be used to move data within a single Image. diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 3cc1146513..f64502c5bc 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -75,10 +75,7 @@ public: } static Point<double> project(const LatLng& latLng, double scale) { - return Point<double> { - util::LONGITUDE_MAX + latLng.longitude(), - util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) - } * worldSize(scale) / util::DEGREES_MAX; + return project_(latLng, worldSize(scale)); } static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -89,6 +86,23 @@ public: wrapMode }; } + + // Project lat, lon to point in a zoom-dependent world size + static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { + const double t2z = tileSize * std::pow(2, zoom); + Point<double> pt = project_(point, t2z); + // Flip y coordinate + auto x = std::round(std::min(pt.x, t2z)); + auto y = std::round(std::min(t2z - pt.y, t2z)); + return { x, y }; + } +private: + static Point<double> project_(const LatLng& latLng, double worldSize) { + return Point<double> { + util::LONGITUDE_MAX + latLng.longitude(), + util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) + } * worldSize / util::DEGREES_MAX; + } }; } // namespace mbgl diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp new file mode 100644 index 0000000000..672eebf6db --- /dev/null +++ b/include/mbgl/util/thread.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/util/platform.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/util.hpp> + +#include <cassert> +#include <future> +#include <memory> +#include <mutex> +#include <queue> +#include <string> +#include <thread> +#include <utility> + +namespace mbgl { +namespace util { + +// Manages a thread with `Object`. + +// Upon creation of this object, it launches a thread and creates an object of type `Object` +// in that thread. When the `Thread<>` object is destructed, the destructor waits +// for thread termination. The `Thread<>` constructor blocks until the thread and +// the `Object` are fully created, so after the object creation, it's safe to obtain the +// `Object` stored in this thread. The thread created will always have low priority on +// the platforms that support setting thread priority. +// +// The following properties make this class different from `ThreadPool`: +// +// - Only one thread is created. +// - `Object` will live in a single thread, providing thread affinity. +// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>` +// - A `RunLoop` is created for the `Object` thread. +// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events. +// +template<class Object> +class Thread : public Scheduler { +public: + template <class... Args> + Thread(const std::string& name, Args&&... args) { + std::promise<void> running; + + thread = std::thread([&] { + platform::setCurrentThreadName(name); + platform::makeThreadLowPriority(); + + util::RunLoop loop_(util::RunLoop::Type::New); + loop = &loop_; + + object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...); + running.set_value(); + + loop->run(); + loop = nullptr; + }); + + running.get_future().get(); + } + + ~Thread() override { + if (paused) { + resume(); + } + + std::promise<void> joinable; + + // Kill the actor, so we don't get more + // messages posted on this scheduler after + // we delete the RunLoop. + loop->invoke([&] { + object.reset(); + joinable.set_value(); + }); + + joinable.get_future().get(); + + loop->stop(); + thread.join(); + } + + // Returns a non-owning reference to `Object` that + // can be used to send messages to `Object`. It is safe + // to the non-owning reference to outlive this object + // and be used after the `Thread<>` gets destroyed. + ActorRef<std::decay_t<Object>> actor() const { + return object->self(); + } + + // Pauses the `Object` thread. It will prevent the object to wake + // up from events such as timers and file descriptor I/O. Messages + // sent to a paused `Object` will be queued and only processed after + // `resume()` is called. + void pause() { + MBGL_VERIFY_THREAD(tid); + + assert(!paused); + + paused = std::make_unique<std::promise<void>>(); + resumed = std::make_unique<std::promise<void>>(); + + auto pausing = paused->get_future(); + + loop->invoke([this] { + auto resuming = resumed->get_future(); + paused->set_value(); + resuming.get(); + }); + + pausing.get(); + } + + // Resumes the `Object` thread previously paused by `pause()`. + void resume() { + MBGL_VERIFY_THREAD(tid); + + assert(paused); + + resumed->set_value(); + + resumed.reset(); + paused.reset(); + } + +private: + MBGL_STORE_THREAD(tid); + + void schedule(std::weak_ptr<Mailbox> mailbox) override { + { + std::lock_guard<std::mutex> lock(mutex); + queue.push(mailbox); + } + + loop->invoke([this] { receive(); }); + } + + void receive() { + std::unique_lock<std::mutex> lock(mutex); + + auto mailbox = queue.front(); + queue.pop(); + lock.unlock(); + + Mailbox::maybeReceive(mailbox); + } + + std::mutex mutex; + std::queue<std::weak_ptr<Mailbox>> queue; + std::thread thread; + std::unique_ptr<Actor<Object>> object; + + std::unique_ptr<std::promise<void>> paused; + std::unique_ptr<std::promise<void>> resumed; + + util::RunLoop* loop = nullptr; +}; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native.pro b/mapbox-gl-native.pro index 0cf5d8de11..9f9f411477 100644 --- a/mapbox-gl-native.pro +++ b/mapbox-gl-native.pro @@ -60,6 +60,8 @@ qtConfig(system-zlib) { TR_EXCLUDE += $$PWD/* qtConfig(icu) { + QMAKE_USE_PRIVATE += icu + SOURCES += \ platform/default/bidi.cpp } else { @@ -153,7 +155,6 @@ SOURCES += \ src/mbgl/renderer/render_light.cpp \ src/mbgl/renderer/render_source.cpp \ src/mbgl/renderer/render_static_data.cpp \ - src/mbgl/renderer/render_style.cpp \ src/mbgl/renderer/render_tile.cpp \ src/mbgl/renderer/renderer.cpp \ src/mbgl/renderer/renderer_backend.cpp \ @@ -185,7 +186,6 @@ SOURCES += \ src/mbgl/sprite/sprite_loader.cpp \ src/mbgl/sprite/sprite_loader_worker.cpp \ src/mbgl/sprite/sprite_parser.cpp \ - src/mbgl/storage/file_source_request.cpp \ src/mbgl/storage/network_status.cpp \ src/mbgl/storage/resource.cpp \ src/mbgl/storage/resource_transform.cpp \ @@ -254,6 +254,7 @@ SOURCES += \ src/mbgl/tile/raster_tile_worker.cpp \ src/mbgl/tile/tile.cpp \ src/mbgl/tile/tile_cache.cpp \ + src/mbgl/tile/tile_id_hash.cpp \ src/mbgl/tile/tile_id_io.cpp \ src/mbgl/tile/vector_tile.cpp \ src/mbgl/tile/vector_tile_data.cpp \ @@ -292,14 +293,15 @@ SOURCES += \ src/parsedate/parsedate.c \ platform/default/asset_file_source.cpp \ platform/default/default_file_source.cpp \ + platform/default/file_source_request.cpp \ platform/default/local_file_source.cpp \ - platform/default/online_file_source.cpp \ + platform/default/logging_stderr.cpp \ platform/default/mbgl/storage/offline.cpp \ platform/default/mbgl/storage/offline_database.cpp \ platform/default/mbgl/storage/offline_download.cpp \ - platform/default/logging_stderr.cpp \ + platform/default/mbgl/util/default_thread_pool.cpp \ platform/default/mbgl/util/shared_thread_pool.cpp \ - platform/default/mbgl/util/default_thread_pool.cpp + platform/default/online_file_source.cpp HEADERS += \ platform/qt/include/qmapbox.hpp \ @@ -323,10 +325,10 @@ INCLUDEPATH += \ deps/cheap-ruler/2.5.3/include \ deps/earcut/0.12.3 \ deps/earcut/0.12.3/include \ - deps/geojson/0.4.0 \ - deps/geojson/0.4.0/include \ - deps/geojsonvt/6.2.1 \ - deps/geojsonvt/6.2.1/include \ + deps/geojson/0.4.2 \ + deps/geojson/0.4.2/include \ + deps/geojsonvt/6.3.0 \ + deps/geojsonvt/6.3.0/include \ deps/geometry/0.9.2 \ deps/geometry/0.9.2/include \ deps/kdbush/0.1.1-1 \ diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index 9c8a38a308..608b782ab9 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -28,10 +28,15 @@ namespace mbgl { class DefaultFileSource::Impl { public: - Impl(ActorRef<Impl>, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize) + Impl(ActorRef<Impl> self, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize) : assetFileSource(assetFileSource_) - , localFileSource(std::make_unique<LocalFileSource>()) - , offlineDatabase(cachePath, maximumCacheSize) { + , localFileSource(std::make_unique<LocalFileSource>()) { + // Initialize the Database asynchronously so as to not block Actor creation. + self.invoke(&Impl::initializeOfflineDatabase, cachePath, maximumCacheSize); + } + + void initializeOfflineDatabase(std::string cachePath, uint64_t maximumCacheSize) { + offlineDatabase = std::make_unique<OfflineDatabase>(cachePath, maximumCacheSize); } void setAPIBaseURL(const std::string& url) { @@ -56,7 +61,7 @@ public: void listRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) { try { - callback({}, offlineDatabase.listRegions()); + callback({}, offlineDatabase->listRegions()); } catch (...) { callback(std::current_exception(), {}); } @@ -66,7 +71,7 @@ public: const OfflineRegionMetadata& metadata, std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) { try { - callback({}, offlineDatabase.createRegion(definition, metadata)); + callback({}, offlineDatabase->createRegion(definition, metadata)); } catch (...) { callback(std::current_exception(), {}); } @@ -76,7 +81,7 @@ public: const OfflineRegionMetadata& metadata, std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) { try { - callback({}, offlineDatabase.updateMetadata(regionID, metadata)); + callback({}, offlineDatabase->updateMetadata(regionID, metadata)); } catch (...) { callback(std::current_exception(), {}); } @@ -93,7 +98,7 @@ public: void deleteRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) { try { downloads.erase(region.getID()); - offlineDatabase.deleteRegion(std::move(region)); + offlineDatabase->deleteRegion(std::move(region)); callback({}); } catch (...) { callback(std::current_exception()); @@ -121,43 +126,43 @@ public: tasks[req] = localFileSource->request(resource, callback); } else { // Try the offline database - Resource revalidation = resource; - - const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires; - if (!hasPrior || resource.necessity == Resource::Optional) { - auto offlineResponse = offlineDatabase.get(resource); - - if (resource.necessity == Resource::Optional && !offlineResponse) { - // Ensure there's always a response that we can send, so the caller knows that - // there's no optional data available in the cache. - offlineResponse.emplace(); - offlineResponse->noContent = true; - offlineResponse->error = std::make_unique<Response::Error>( - Response::Error::Reason::NotFound, "Not found in offline database"); - } - - if (offlineResponse) { - revalidation.priorModified = offlineResponse->modified; - revalidation.priorExpires = offlineResponse->expires; - revalidation.priorEtag = offlineResponse->etag; + if (resource.hasLoadingMethod(Resource::LoadingMethod::Cache)) { + auto offlineResponse = offlineDatabase->get(resource); + + if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) { + if (!offlineResponse) { + // Ensure there's always a response that we can send, so the caller knows that + // there's no optional data available in the cache, when it's the only place + // we're supposed to load from. + offlineResponse.emplace(); + offlineResponse->noContent = true; + offlineResponse->error = std::make_unique<Response::Error>( + Response::Error::Reason::NotFound, "Not found in offline database"); + } else if (!offlineResponse->isUsable()) { + // Don't return resources the server requested not to show when they're stale. + // Even if we can't directly use the response, we may still use it to send a + // conditional HTTP request, which is why we're saving it above. + offlineResponse->error = std::make_unique<Response::Error>( + Response::Error::Reason::NotFound, "Cached resource is unusable"); + } + callback(*offlineResponse); + } else if (offlineResponse) { + // Copy over the fields so that we can use them when making a refresh request. + resource.priorModified = offlineResponse->modified; + resource.priorExpires = offlineResponse->expires; + resource.priorEtag = offlineResponse->etag; + resource.priorData = offlineResponse->data; - // Don't return resources the server requested not to show when they're stale. - // Even if we can't directly use the response, we may still use it to send a - // conditional HTTP request. if (offlineResponse->isUsable()) { callback(*offlineResponse); - } else { - // Since we can't return the data immediately, we'll have to hold on so that - // we can return it later in case we get a 304 Not Modified response. - revalidation.priorData = offlineResponse->data; } } } // Get from the online file source - if (resource.necessity == Resource::Required) { - tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) mutable { - this->offlineDatabase.put(revalidation, onlineResponse); + if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) { + tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable { + this->offlineDatabase->put(resource, onlineResponse); callback(onlineResponse); }); } @@ -169,11 +174,15 @@ public: } void setOfflineMapboxTileCountLimit(uint64_t limit) { - offlineDatabase.setOfflineMapboxTileCountLimit(limit); + offlineDatabase->setOfflineMapboxTileCountLimit(limit); + } + + void setOnlineStatus(const bool status) { + onlineFileSource.setOnlineStatus(status); } void put(const Resource& resource, const Response& response) { - offlineDatabase.put(resource, response); + offlineDatabase->put(resource, response); } private: @@ -183,13 +192,13 @@ private: return *it->second; } return *downloads.emplace(regionID, - std::make_unique<OfflineDownload>(regionID, offlineDatabase.getRegionDefinition(regionID), offlineDatabase, onlineFileSource)).first->second; + std::make_unique<OfflineDownload>(regionID, offlineDatabase->getRegionDefinition(regionID), *offlineDatabase, onlineFileSource)).first->second; } // shared so that destruction is done on the creating thread const std::shared_ptr<FileSource> assetFileSource; const std::unique_ptr<FileSource> localFileSource; - OfflineDatabase offlineDatabase; + std::unique_ptr<OfflineDatabase> offlineDatabase; OnlineFileSource onlineFileSource; std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks; std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads; @@ -298,6 +307,10 @@ void DefaultFileSource::resume() { // For testing only: +void DefaultFileSource::setOnlineStatus(const bool status) { + impl->actor().invoke(&Impl::setOnlineStatus, status); +} + void DefaultFileSource::put(const Resource& resource, const Response& response) { impl->actor().invoke(&Impl::put, resource, response); } diff --git a/platform/default/file_source_request.cpp b/platform/default/file_source_request.cpp new file mode 100644 index 0000000000..09ea8cc32a --- /dev/null +++ b/platform/default/file_source_request.cpp @@ -0,0 +1,37 @@ +#include <mbgl/storage/file_source_request.hpp> + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> + +namespace mbgl { + +FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback) + : responseCallback(callback) + , mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())) { +} + +FileSourceRequest::~FileSourceRequest() { + if (cancelCallback) { + cancelCallback(); + } + + mailbox->close(); +} + +void FileSourceRequest::onCancel(std::function<void()>&& callback) { + cancelCallback = std::move(callback); +} + +void FileSourceRequest::setResponse(const Response& response) { + // Copy, because calling the callback will sometimes self + // destroy this object. We cannot move because this method + // can be called more than one. + auto callback = responseCallback; + callback(response); +} + +ActorRef<FileSourceRequest> FileSourceRequest::actor() { + return ActorRef<FileSourceRequest>(*this, mailbox); +} + +} // namespace mbgl diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp index fe77b80985..edf637a560 100644 --- a/platform/default/mbgl/gl/headless_backend.cpp +++ b/platform/default/mbgl/gl/headless_backend.cpp @@ -64,6 +64,10 @@ void HeadlessBackend::bind() { context_.viewport = { 0, 0, size }; } +Size HeadlessBackend::getFramebufferSize() const { + return size; +} + void HeadlessBackend::updateAssumedState() { // no-op } diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp index 8d86ea8c47..66f861e213 100644 --- a/platform/default/mbgl/gl/headless_backend.hpp +++ b/platform/default/mbgl/gl/headless_backend.hpp @@ -15,6 +15,7 @@ public: ~HeadlessBackend() override; void bind() override; + Size getFramebufferSize() const override; void updateAssumedState() override; void setSize(Size); diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp index ad03706be7..5d2932258a 100644 --- a/platform/default/mbgl/gl/headless_frontend.cpp +++ b/platform/default/mbgl/gl/headless_frontend.cpp @@ -5,11 +5,11 @@ namespace mbgl { -HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler) - : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler) { +HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir) + : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir) { } -HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler) +HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir) : size(size_), pixelRatio(pixelRatio_), backend({ static_cast<uint32_t>(size.width * pixelRatio), @@ -20,7 +20,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fi renderer->render(*updateParameters); } }), - renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler)) { + renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler, GLContextMode::Unique, programCacheDir)) { } HeadlessFrontend::~HeadlessFrontend() = default; diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp index 18d0d2527b..33503bc13b 100644 --- a/platform/default/mbgl/gl/headless_frontend.hpp +++ b/platform/default/mbgl/gl/headless_frontend.hpp @@ -3,6 +3,7 @@ #include <mbgl/renderer/renderer_frontend.hpp> #include <mbgl/gl/headless_backend.hpp> #include <mbgl/util/async_task.hpp> +#include <mbgl/util/optional.hpp> #include <memory> @@ -16,8 +17,8 @@ class Map; class HeadlessFrontend : public RendererFrontend { public: - HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&); - HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&); + HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {}); + HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {}); ~HeadlessFrontend() override; void reset() override; diff --git a/platform/default/mbgl/map/map_snapshotter.cpp b/platform/default/mbgl/map/map_snapshotter.cpp new file mode 100644 index 0000000000..95c46344fe --- /dev/null +++ b/platform/default/mbgl/map/map_snapshotter.cpp @@ -0,0 +1,76 @@ +#include <mbgl/map/map_snapshotter.hpp> + +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/gl/headless_frontend.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/util/event.hpp> + +namespace mbgl { + +class MapSnapshotter::Impl { +public: + Impl(FileSource&, + Scheduler&, + const std::string& styleURL, + const Size&, + const float pixelRatio, + const CameraOptions&, + const optional<LatLngBounds> region, + const optional<std::string> programCacheDir); + + void snapshot(ActorRef<MapSnapshotter::Callback>); + +private: + HeadlessFrontend frontend; + Map map; +}; + +MapSnapshotter::Impl::Impl(FileSource& fileSource, + Scheduler& scheduler, + const std::string& styleURL, + const Size& size, + const float pixelRatio, + const CameraOptions& cameraOptions, + const optional<LatLngBounds> region, + const optional<std::string> programCacheDir) + : frontend(size, pixelRatio, fileSource, scheduler, programCacheDir) + , map(frontend, MapObserver::nullObserver(), size, pixelRatio, fileSource, scheduler, MapMode::Still) { + + map.getStyle().loadURL(styleURL); + + map.jumpTo(cameraOptions); + + // Set region, if specified + if (region) { + mbgl::EdgeInsets insets = { 0, 0, 0, 0 }; + std::vector<LatLng> latLngs = { region->southwest(), region->northeast() }; + map.jumpTo(map.cameraForLatLngs(latLngs, insets)); + } +} + +void MapSnapshotter::Impl::snapshot(ActorRef<MapSnapshotter::Callback> callback) { + map.renderStill([this, callback = std::move(callback)] (std::exception_ptr error) mutable { + callback.invoke(&MapSnapshotter::Callback::operator(), error, error ? PremultipliedImage() : frontend.readStillImage()); + }); +} + +MapSnapshotter::MapSnapshotter(FileSource& fileSource, + Scheduler& scheduler, + const std::string& styleURL, + const Size& size, + const float pixelRatio, + const CameraOptions& cameraOptions, + const optional<LatLngBounds> region, + const optional<std::string> programCacheDir) + : impl(std::make_unique<util::Thread<MapSnapshotter::Impl>>("Map Snapshotter", fileSource, scheduler, styleURL, size, pixelRatio, cameraOptions, region, programCacheDir)) { +} + +MapSnapshotter::~MapSnapshotter() = default; + +void MapSnapshotter::snapshot(ActorRef<MapSnapshotter::Callback> callback) { + impl->actor().invoke(&Impl::snapshot, std::move(callback)); +} + +} // namespace mbgl diff --git a/platform/default/mbgl/map/map_snapshotter.hpp b/platform/default/mbgl/map/map_snapshotter.hpp new file mode 100644 index 0000000000..0afa848fd8 --- /dev/null +++ b/platform/default/mbgl/map/map_snapshotter.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/util/image.hpp> +#include <mbgl/util/thread.hpp> +#include <mbgl/util/optional.hpp> + +#include <exception> +#include <string> +#include <functional> + +namespace mbgl { + +template<class> class ActorRef; +struct CameraOptions; +class FileSource; +class Size; +class LatLngBounds; + +class MapSnapshotter { +public: + MapSnapshotter(FileSource& fileSource, + Scheduler& scheduler, + const std::string& styleURL, + const Size&, + const float pixelRatio, + const CameraOptions&, + const optional<LatLngBounds> region, + const optional<std::string> cacheDir = {}); + + ~MapSnapshotter(); + + using Callback = std::function<void (std::exception_ptr, PremultipliedImage)>; + void snapshot(ActorRef<Callback>); + +private: + class Impl; + std::unique_ptr<util::Thread<Impl>> impl; +}; + +} // namespace mbgl diff --git a/platform/default/mbgl/storage/file_source_request.hpp b/platform/default/mbgl/storage/file_source_request.hpp new file mode 100644 index 0000000000..6bd0d44df6 --- /dev/null +++ b/platform/default/mbgl/storage/file_source_request.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/async_request.hpp> + +#include <memory> +#include <functional> + +namespace mbgl { + +class Mailbox; + +class FileSourceRequest : public AsyncRequest { +public: + FileSourceRequest(FileSource::Callback&& callback); + ~FileSourceRequest() final; + + void onCancel(std::function<void()>&& callback); + void setResponse(const Response& res); + + ActorRef<FileSourceRequest> actor(); + +private: + FileSource::Callback responseCallback = nullptr; + std::function<void()> cancelCallback = nullptr; + + std::shared_ptr<Mailbox> mailbox; +}; + +} // namespace mbgl diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index fd2d47819b..7670790be9 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -1,6 +1,7 @@ #include <mbgl/storage/offline.hpp> #include <mbgl/util/tile_cover.hpp> #include <mbgl/util/tileset.hpp> +#include <mbgl/util/projection.hpp> #include <rapidjson/document.h> #include <rapidjson/stringbuffer.h> @@ -23,18 +24,12 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( } } -std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); - double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); - - assert(minZ >= 0); - assert(maxZ >= 0); - assert(minZ < std::numeric_limits<uint8_t>::max()); - assert(maxZ < std::numeric_limits<uint8_t>::max()); +std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { + const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); std::vector<CanonicalTileID> result; - for (uint8_t z = minZ; z <= maxZ; z++) { + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { for (const auto& tile : util::tileCover(bounds, z)) { result.emplace_back(tile.canonical); } @@ -43,6 +38,28 @@ std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(Sourc return result; } +uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { + + const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); + unsigned long result = 0;; + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { + result += util::tileCount(bounds, z, tileSize); + } + + return result; +} + +Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { + double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); + double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); + + assert(minZ >= 0); + assert(maxZ >= 0); + assert(minZ < std::numeric_limits<uint8_t>::max()); + assert(maxZ < std::numeric_limits<uint8_t>::max()); + return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) }; +} + OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.Parse<0>(region.c_str()); diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 7f0001f64b..ff61114888 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -80,7 +80,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) { if (urlOrTileset.is<Tileset>()) { result.requiredResourceCount += - definition.tileCover(type, tileSize, urlOrTileset.get<Tileset>().zoomRange).size(); + definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange); } else { result.requiredResourceCount += 1; const auto& url = urlOrTileset.get<std::string>(); @@ -90,7 +90,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error); if (tileset) { result.requiredResourceCount += - definition.tileCover(type, tileSize, (*tileset).zoomRange).size(); + definition.tileCount(type, tileSize, (*tileset).zoomRange); } } else { result.requiredResourceCountIsPrecise = false; diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp index c978ded931..437f221c11 100644 --- a/platform/default/mbgl/storage/offline_download.hpp +++ b/platform/default/mbgl/storage/offline_download.hpp @@ -60,7 +60,7 @@ private: std::deque<Resource> resourcesRemaining; void queueResource(Resource); - void queueTiles(SourceType, uint16_t tileSize, const Tileset&); + void queueTiles(style::SourceType, uint16_t tileSize, const Tileset&); }; } // namespace mbgl diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp index 1c594f87a0..d685109b95 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -117,13 +117,24 @@ public: } void activateRequest(OnlineFileRequest* request) { - activeRequests.insert(request); - request->request = httpFileSource.request(request->resource, [=] (Response response) { + auto callback = [=](Response response) { activeRequests.erase(request); - activatePendingRequest(); request->request.reset(); request->completed(response); - }); + activatePendingRequest(); + }; + + activeRequests.insert(request); + + if (online) { + request->request = httpFileSource.request(request->resource, callback); + } else { + Response response; + response.error = std::make_unique<Response::Error>(Response::Error::Reason::Connection, + "Online connectivity is disabled."); + callback(response); + } + assert(pendingRequestsMap.size() == pendingRequestsList.size()); } @@ -153,6 +164,11 @@ public: resourceTransform = std::move(transform); } + void setOnlineStatus(const bool status) { + online = status; + networkIsReachableAgain(); + } + private: void networkIsReachableAgain() { for (auto& request : allRequests) { @@ -178,6 +194,7 @@ private: std::unordered_map<OnlineFileRequest*, std::list<OnlineFileRequest*>::iterator> pendingRequestsMap; std::unordered_set<OnlineFileRequest*> activeRequests; + bool online = true; HTTPFileSource httpFileSource; util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) }; }; @@ -389,4 +406,10 @@ ActorRef<OnlineFileRequest> OnlineFileRequest::actor() { return ActorRef<OnlineFileRequest>(*this, mailbox); } +// For testing only: + +void OnlineFileSource::setOnlineStatus(const bool status) { + impl->setOnlineStatus(status); +} + } // namespace mbgl diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index e2fb283989..ebbd949921 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -10,6 +10,8 @@ #include <QString> #include <QStringList> +#include <functional> + class QMapboxGLPrivate; // This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style @@ -59,6 +61,9 @@ public: QString apiBaseUrl() const; void setApiBaseUrl(const QString &); + std::function<std::string(const std::string &&)> resourceTransform() const; + void setResourceTransform(const std::function<std::string(const std::string &&)> &); + private: GLContextMode m_contextMode; ConstrainMode m_constrainMode; @@ -69,6 +74,7 @@ private: QString m_assetPath; QString m_accessToken; QString m_apiBaseUrl; + std::function<std::string(const std::string &&)> m_resourceTransform; }; struct Q_DECL_EXPORT QMapboxGLCameraOptions { diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index c7c4cf1e4a..074ef280aa 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -29,6 +29,7 @@ #include <mbgl/util/run_loop.hpp> #include <mbgl/util/shared_thread_pool.hpp> #include <mbgl/util/traits.hpp> +#include <mbgl/actor/scheduler.hpp> #if QT_VERSION >= 0x050000 #include <QGuiApplication> @@ -362,6 +363,27 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url) } /*! + Returns resource transformation callback used to transform requested URLs. +*/ +std::function<std::string(const std::string &&)> QMapboxGLSettings::resourceTransform() const +{ + return m_resourceTransform; +} + +/*! + Sets the resource transformation callback. + + When given, resource transformation callback will be used to transform the + requested resource URLs before they are requested from internet. This can be + used add or remove custom parameters, or reroute certain requests to other + servers or endpoints. +*/ +void QMapboxGLSettings::setResourceTransform(const std::function<std::string(const std::string &&)> &transform) +{ + m_resourceTransform = transform; +} + +/*! \class QMapboxGL \brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine. @@ -1499,13 +1521,25 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.cacheDatabaseMaximumSize())) , threadPool(mbgl::sharedThreadPool()) { + // Setup resource transform if needed + if (settings.resourceTransform()) { + m_resourceTransform = + std::make_unique< mbgl::Actor<mbgl::ResourceTransform> >( *mbgl::Scheduler::GetCurrent(), + [callback = settings.resourceTransform()] + (mbgl::Resource::Kind , const std::string&& url_) -> std::string { + return callback(std::move(url_)); + } + ); + fileSourceObj->setResourceTransform(m_resourceTransform->self()); + } + // Setup and connect the renderer frontend frontend = std::make_unique<QMapboxGLRendererFrontend>( std::make_unique<mbgl::Renderer>(*this, pixelRatio, *fileSourceObj, *threadPool, static_cast<mbgl::GLContextMode>(settings.contextMode())), *this); connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate())); - + mapObj = std::make_unique<mbgl::Map>( *frontend, *this, sanitizedSize(size), @@ -1528,18 +1562,18 @@ QMapboxGLPrivate::~QMapboxGLPrivate() { } -mbgl::Size QMapboxGLPrivate::framebufferSize() const { +mbgl::Size QMapboxGLPrivate::getFramebufferSize() const { return sanitizedSize(fbSize); } void QMapboxGLPrivate::updateAssumedState() { assumeFramebufferBinding(fbObject); - assumeViewport(0, 0, framebufferSize()); + assumeViewport(0, 0, getFramebufferSize()); } void QMapboxGLPrivate::bind() { setFramebufferBinding(fbObject); - setViewport(0, 0, framebufferSize()); + setViewport(0, 0, getFramebufferSize()); } void QMapboxGLPrivate::invalidate() diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index 7b0dd8c192..5e12b44a20 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -3,15 +3,19 @@ #include "qmapboxgl.hpp" #include "qmapboxgl_renderer_frontend_p.hpp" +#include <mbgl/actor/actor.hpp> #include <mbgl/map/map.hpp> #include <mbgl/renderer/renderer_backend.hpp> #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/storage/resource_transform.hpp> #include <QObject> #include <QSize> +#include <memory> + class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver { Q_OBJECT @@ -20,10 +24,10 @@ public: explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio); virtual ~QMapboxGLPrivate(); - mbgl::Size framebufferSize() const; // mbgl::RendererBackend implementation. void bind() final; + mbgl::Size getFramebufferSize() const final; void updateAssumedState() final; void activate() final {} void deactivate() final {} @@ -68,4 +72,7 @@ signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); void copyrightsChanged(const QString ©rightsHtml); + +private: + std::unique_ptr< mbgl::Actor<mbgl::ResourceTransform> > m_resourceTransform; }; diff --git a/src/mbgl/algorithm/generate_clip_ids_impl.hpp b/src/mbgl/algorithm/generate_clip_ids_impl.hpp index db62214220..6a316dcdad 100644 --- a/src/mbgl/algorithm/generate_clip_ids_impl.hpp +++ b/src/mbgl/algorithm/generate_clip_ids_impl.hpp @@ -17,7 +17,7 @@ void ClipIDGenerator::update(std::vector<std::reference_wrapper<Renderable>> ren const auto end = renderables.end(); for (auto it = renderables.begin(); it != end; it++) { auto& renderable = it->get(); - if (!renderable.used) { + if (!renderable.used || !renderable.needsClipping) { continue; } diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp index 0c2266ff47..c583b6b2b6 100644 --- a/src/mbgl/algorithm/update_renderables.hpp +++ b/src/mbgl/algorithm/update_renderables.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/tile/tile_id.hpp> +#include <mbgl/tile/tile_necessity.hpp> #include <mbgl/util/range.hpp> -#include <mbgl/storage/resource.hpp> #include <unordered_set> @@ -40,15 +40,15 @@ void updateRenderables(GetTileFn getTile, // if (source has the tile and bucket is loaded) { if (tile->isRenderable()) { - retainTile(*tile, Resource::Necessity::Required); + retainTile(*tile, TileNecessity::Required); renderTile(idealRenderTileID, *tile); } else { // We are now attempting to load child and parent tiles. - bool parentHasTriedOptional = tile->hasTriedOptional(); + bool parentHasTriedOptional = tile->hasTriedCache(); bool parentIsLoaded = tile->isLoaded(); // The tile isn't loaded yet, but retain it anyway because it's an ideal tile. - retainTile(*tile, Resource::Necessity::Required); + retainTile(*tile, TileNecessity::Required); covered = true; overscaledZ = dataTileZoom + 1; if (overscaledZ > zoomRange.max) { @@ -56,7 +56,7 @@ void updateRenderables(GetTileFn getTile, const auto childDataTileID = idealDataTileID.scaledTo(overscaledZ); tile = getTile(childDataTileID); if (tile && tile->isRenderable()) { - retainTile(*tile, Resource::Necessity::Optional); + retainTile(*tile, TileNecessity::Optional); renderTile(idealRenderTileID, *tile); } else { covered = false; @@ -67,7 +67,7 @@ void updateRenderables(GetTileFn getTile, const OverscaledTileID childDataTileID(overscaledZ, idealRenderTileID.wrap, childTileID); tile = getTile(childDataTileID); if (tile && tile->isRenderable()) { - retainTile(*tile, Resource::Necessity::Optional); + retainTile(*tile, TileNecessity::Optional); renderTile(childDataTileID.toUnwrapped(), *tile); } else { // At least one child tile doesn't exist, so we are going to look for @@ -97,12 +97,19 @@ void updateRenderables(GetTileFn getTile, } if (tile) { - retainTile(*tile, parentIsLoaded ? Resource::Necessity::Required - : Resource::Necessity::Optional); + if (!parentIsLoaded) { + // We haven't completed loading the child, so we only do an optional + // (cache) request in an attempt to quickly load data that we can show. + retainTile(*tile, TileNecessity::Optional); + } else { + // Now that we've checked the child and know for sure that we can't load + // it, we attempt to load the parent from the network. + retainTile(*tile, TileNecessity::Required); + } // Save the current values, since they're the parent of the next iteration // of the parent tile ascent loop. - parentHasTriedOptional = tile->hasTriedOptional(); + parentHasTriedOptional = tile->hasTriedCache(); parentIsLoaded = tile->isLoaded(); if (tile->isRenderable()) { diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index dbf8387ae0..1f2d01e9eb 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -39,19 +39,22 @@ AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, cons Annotation::visit(annotation, [&] (const auto& annotation_) { this->add(id, annotation_, maxZoom); }); + dirty = true; return id; } -Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { +bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { std::lock_guard<std::mutex> lock(mutex); - return Annotation::visit(annotation, [&] (const auto& annotation_) { - return this->update(id, annotation_, maxZoom); + Annotation::visit(annotation, [&] (const auto& annotation_) { + this->update(id, annotation_, maxZoom); }); + return dirty; } void AnnotationManager::removeAnnotation(const AnnotationID& id) { std::lock_guard<std::mutex> lock(mutex); remove(id); + dirty = true; } void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) { @@ -72,49 +75,45 @@ void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annota impl.updateStyle(*style.get().impl); } -Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { - Update result = Update::Nothing; - +void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { auto it = symbolAnnotations.find(id); if (it == symbolAnnotations.end()) { assert(false); // Attempt to update a non-existent symbol annotation - return result; + return; } const SymbolAnnotation& existing = it->second->annotation; if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) { - result |= Update::AnnotationData; + dirty = true; remove(id); add(id, annotation, maxZoom); } - - return result; } -Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { +void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { auto it = shapeAnnotations.find(id); if (it == shapeAnnotations.end()) { assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; + return; } shapeAnnotations.erase(it); add(id, annotation, maxZoom); - return Update::AnnotationData; + dirty = true; } -Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { +void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { auto it = shapeAnnotations.find(id); if (it == shapeAnnotations.end()) { assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; + return; } shapeAnnotations.erase(it); add(id, annotation, maxZoom); - return Update::AnnotationData; + dirty = true; } void AnnotationManager::remove(const AnnotationID& id) { @@ -187,8 +186,11 @@ void AnnotationManager::updateStyle() { void AnnotationManager::updateData() { std::lock_guard<std::mutex> lock(mutex); - for (auto& tile : tiles) { - tile->setData(getTileData(tile->id.canonical)); + if (dirty) { + for (auto& tile : tiles) { + tile->setData(getTileData(tile->id.canonical)); + } + dirty = false; } } diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index dee823bc0f..a028a8f1ba 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -3,7 +3,6 @@ #include <mbgl/annotation/annotation.hpp> #include <mbgl/annotation/symbol_annotation_impl.hpp> #include <mbgl/style/image.hpp> -#include <mbgl/map/update.hpp> #include <mbgl/util/noncopyable.hpp> #include <mutex> @@ -30,7 +29,7 @@ public: ~AnnotationManager(); AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom); - Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom); + bool updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom); void removeAnnotation(const AnnotationID&); void addImage(std::unique_ptr<style::Image>); @@ -53,9 +52,9 @@ private: void add(const AnnotationID&, const LineAnnotation&, const uint8_t); void add(const AnnotationID&, const FillAnnotation&, const uint8_t); - Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t); - Update update(const AnnotationID&, const LineAnnotation&, const uint8_t); - Update update(const AnnotationID&, const FillAnnotation&, const uint8_t); + void update(const AnnotationID&, const SymbolAnnotation&, const uint8_t); + void update(const AnnotationID&, const LineAnnotation&, const uint8_t); + void update(const AnnotationID&, const FillAnnotation&, const uint8_t); void remove(const AnnotationID&); @@ -67,6 +66,8 @@ private: std::mutex mutex; + bool dirty = false; + AnnotationID nextID = 0; using SymbolAnnotationTree = boost::geometry::index::rtree<std::shared_ptr<const SymbolAnnotationImpl>, boost::geometry::index::rstar<16, 4>>; diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index 0596d60f4f..d405418a45 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -19,9 +19,6 @@ AnnotationTile::~AnnotationTile() { annotationManager.removeTile(*this); } -void AnnotationTile::setNecessity(Necessity) { -} - class AnnotationTileFeatureData { public: AnnotationTileFeatureData(const AnnotationID id_, diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index 88505c50e3..a4d1e66802 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -14,8 +14,6 @@ public: AnnotationTile(const OverscaledTileID&, const TileParameters&); ~AnnotationTile() override; - void setNecessity(Necessity) final; - private: AnnotationManager& annotationManager; }; diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index c2e6191d1d..34fb576727 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -60,9 +60,9 @@ std::vector<std::reference_wrapper<RenderTile>> RenderAnnotationSource::getRende std::unordered_map<std::string, std::vector<Feature>> RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options); + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index 4000c4b04a..6209c943f1 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -26,7 +26,7 @@ public: std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> @@ -43,7 +43,7 @@ private: template <> inline bool RenderSource::is<RenderAnnotationSource>() const { - return baseImpl->type == SourceType::Annotations; + return baseImpl->type == style::SourceType::Annotations; } } // namespace mbgl diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 4c4e985369..1adb933e44 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -1,5 +1,4 @@ #include <mbgl/geometry/feature_index.hpp> -#include <mbgl/renderer/render_style.hpp> #include <mbgl/renderer/render_layer.hpp> #include <mbgl/renderer/query.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> @@ -9,7 +8,7 @@ #include <mbgl/math/minmax.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/tile/tile_id.hpp> #include <mapbox/geometry/envelope.hpp> @@ -32,19 +31,6 @@ void FeatureIndex::insert(const GeometryCollection& geometries, } } -static bool vectorContains(const std::vector<std::string>& vector, const std::string& s) { - return std::find(vector.begin(), vector.end(), s) != vector.end(); -} - -static bool vectorsIntersect(const std::vector<std::string>& vectorA, const std::vector<std::string>& vectorB) { - for (const auto& a : vectorA) { - if (vectorContains(vectorB, a)) { - return true; - } - } - return false; -} - static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { return a.sortIndex > b.sortIndex; } @@ -53,36 +39,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& return a.sortIndex < b.sortIndex; } -static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions, - const RenderStyle& style, - const GeometryTile& tile, - const float pixelsToTileUnits) { - - // Determine the additional radius needed factoring in property functions - float additionalRadius = 0; - auto getQueryRadius = [&](const RenderLayer& layer) { - auto bucket = tile.getBucket(*layer.baseImpl); - if (bucket) { - additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits); - } - }; - - if (queryOptions.layerIDs) { - for (const auto& layerID : *queryOptions.layerIDs) { - const RenderLayer* layer = style.getRenderLayer(layerID); - if (layer) { - getQueryRadius(*layer); - } - } - } else { - for (const RenderLayer* layer : style.getRenderLayers()) { - getQueryRadius(*layer); - } - } - - return std::min<int16_t>(util::EXTENT, additionalRadius); -} - void FeatureIndex::query( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, @@ -92,13 +48,13 @@ void FeatureIndex::query( const RenderedQueryOptions& queryOptions, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const CollisionTile* collisionTile, - const GeometryTile& tile) const { + const float additionalQueryRadius) const { // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; - const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits); + const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits); // Query the grid index mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry); @@ -113,7 +69,7 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits); } // Query symbol features, if they've been placed. @@ -124,7 +80,7 @@ void FeatureIndex::query( std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale); std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); + addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits); } } @@ -135,30 +91,39 @@ void FeatureIndex::addFeature( const RenderedQueryOptions& options, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const float bearing, const float pixelsToTileUnits) const { - auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName); - if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) { - return; - } - - auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); - assert(sourceLayer); + auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* { + for (const auto& layer : layers) { + if (layer->getID() == layerID) { + return layer; + } + } + return nullptr; + }; - auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); - assert(geometryTileFeature); + // Lazily calculated. + std::unique_ptr<GeometryTileLayer> sourceLayer; + std::unique_ptr<GeometryTileFeature> geometryTileFeature; - for (const auto& layerID : layerIDs) { - if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) { + for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) { + const RenderLayer* renderLayer = getRenderLayer(layerID); + if (!renderLayer) { continue; } - auto renderLayer = style.getRenderLayer(layerID); - if (!renderLayer || - (!renderLayer->is<RenderSymbolLayer>() && - !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) { + if (!geometryTileFeature) { + sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); + assert(sourceLayer); + + geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); + assert(geometryTileFeature); + } + + if (!renderLayer->is<RenderSymbolLayer>() && + !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) { continue; } diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index 83f339a9de..2ae7da33df 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -11,9 +11,8 @@ namespace mbgl { -class GeometryTile; class RenderedQueryOptions; -class RenderStyle; +class RenderLayer; class CollisionTile; class CanonicalTileID; @@ -42,9 +41,9 @@ public: const RenderedQueryOptions& options, const GeometryTileData&, const CanonicalTileID&, - const RenderStyle&, + const std::vector<const RenderLayer*>&, const CollisionTile*, - const GeometryTile& tile) const; + const float additionalQueryRadius) const; static optional<GeometryCoordinates> translateQueryGeometry( const GeometryCoordinates& queryGeometry, @@ -63,7 +62,7 @@ private: const RenderedQueryOptions& options, const GeometryTileData&, const CanonicalTileID&, - const RenderStyle&, + const std::vector<const RenderLayer*>&, const float bearing, const float pixelsToTileUnits) const; diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index e18f1e0bcf..d0c538efb0 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -87,7 +87,9 @@ static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mis Context::Context() = default; Context::~Context() { - reset(); + if (cleanupOnDestruction) { + reset(); + } } void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) { @@ -457,6 +459,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) { Framebuffer Context::createFramebuffer(const Texture& color, const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) { + if (color.size != depthTarget.size) { + throw std::runtime_error("Renderbuffer size mismatch"); + } auto fbo = createFramebuffer(); bindFramebuffer = fbo; MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0)); @@ -481,7 +486,7 @@ Context::createTexture(const Size size, const void* data, TextureFormat format, void Context::updateTexture( TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) { - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = id; MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), size.width, size.height, 0, static_cast<GLenum>(format), GL_UNSIGNED_BYTE, @@ -495,7 +500,7 @@ void Context::bindTexture(Texture& obj, TextureWrap wrapX, TextureWrap wrapY) { if (filter != obj.filter || mipmap != obj.mipmap || wrapX != obj.wrapX || wrapY != obj.wrapY) { - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = obj.texture; if (filter != obj.filter || mipmap != obj.mipmap) { @@ -526,7 +531,7 @@ void Context::bindTexture(Texture& obj, } else if (texture[unit] != obj.texture) { // We are checking first to avoid setting the active texture without a subsequent // texture bind. - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = obj.texture; } } @@ -558,7 +563,7 @@ void Context::setDirtyState() { clearStencil.setDirty(); program.setDirty(); lineWidth.setDirty(); - activeTexture.setDirty(); + activeTextureUnit.setDirty(); pixelStorePack.setDirty(); pixelStoreUnpack.setDirty(); #if not MBGL_USE_GLES2 @@ -709,8 +714,10 @@ void Context::performCleanup() { if (!abandonedTextures.empty()) { for (const auto id : abandonedTextures) { - if (activeTexture == id) { - activeTexture.setDirty(); + for (auto& binding : texture) { + if (binding == id) { + binding.setDirty(); + } } } MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data())); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 9923567276..528113cbba 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -190,7 +190,13 @@ public: return vertexArray.get(); } + void setCleanupOnDestruction(bool cleanup) { + cleanupOnDestruction = cleanup; + } + private: + bool cleanupOnDestruction = true; + std::unique_ptr<extension::Debugging> debugging; std::unique_ptr<extension::VertexArray> vertexArray; #if MBGL_HAS_BINARY_PROGRAMS @@ -198,7 +204,7 @@ private: #endif public: - State<value::ActiveTexture> activeTexture; + State<value::ActiveTextureUnit> activeTextureUnit; State<value::BindFramebuffer> bindFramebuffer; State<value::Viewport> viewport; State<value::ScissorTest> scissorTest; diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp index cc8ff13268..0592557a7f 100644 --- a/src/mbgl/gl/renderbuffer.hpp +++ b/src/mbgl/gl/renderbuffer.hpp @@ -9,9 +9,23 @@ namespace gl { template <RenderbufferType renderbufferType> class Renderbuffer { public: + Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false) + : size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) { + } + using type = std::integral_constant<RenderbufferType, renderbufferType>; Size size; - gl::UniqueRenderbuffer renderbuffer; + UniqueRenderbuffer renderbuffer; + + void shouldClear(bool clear) { + dirty = clear; + } + bool needsClearing() { + return dirty; + } + +private: + bool dirty; }; } // namespace gl diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp index 89014fe6bc..092403af0d 100644 --- a/src/mbgl/gl/value.cpp +++ b/src/mbgl/gl/value.cpp @@ -242,13 +242,13 @@ LineWidth::Type LineWidth::Get() { return lineWidth; } -const constexpr ActiveTexture::Type ActiveTexture::Default; +const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default; -void ActiveTexture::Set(const Type& value) { +void ActiveTextureUnit::Set(const Type& value) { MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value)); } -ActiveTexture::Type ActiveTexture::Get() { +ActiveTextureUnit::Type ActiveTextureUnit::Get() { GLint activeTexture; MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture)); return static_cast<Type>(activeTexture - GL_TEXTURE0); diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp index 19e9af194f..7b85a5ff4b 100644 --- a/src/mbgl/gl/value.hpp +++ b/src/mbgl/gl/value.hpp @@ -165,7 +165,7 @@ struct LineWidth { static Type Get(); }; -struct ActiveTexture { +struct ActiveTextureUnit { using Type = TextureUnit; static const constexpr Type Default = 0; static void Set(const Type&); diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 229b8f2ee2..2c90b69b08 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -205,11 +205,11 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph const Shaping result = getShaping( /* string */ text, /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ? - layout.get<TextMaxWidth>() * oneEm : 0, + layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0, /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm, /* anchor */ layout.evaluate<TextAnchor>(zoom, feature), /* justify */ layout.evaluate<TextJustify>(zoom, feature), - /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f, + /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f, /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm), /* verticalHeight */ oneEm, /* writingMode */ writingMode, @@ -233,6 +233,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph shapedIcon = PositionedIcon::shapeIcon( imagePositions.at(*feature.icon), layout.evaluate<IconOffset>(zoom, feature), + layout.evaluate<IconAnchor>(zoom, feature), layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD); if (image->second->sdf) { sdfIcons = true; diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index 8ccd6a0410..279d251f8f 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -94,10 +94,12 @@ namespace mbgl { } - Point<float> project(const Point<float>& point, const mat4& matrix) { + typedef std::pair<Point<float>,float> PointAndCameraDistance; + + PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) { vec4 pos = {{ point.x, point.y, 0, 1 }}; matrix::transformMat4(pos, pos, matrix); - return { static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }; + return {{ static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }, pos[3] }; } float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) { @@ -150,9 +152,20 @@ namespace mbgl { NotEnoughRoom, NeedsFlipping }; + + Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) { + // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane + // If it did, that would mean our label extended all the way out from within the viewport to a (very distant) + // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the + // plane of the camera. + const Point<float> projectedUnitVertex = project(previousTilePoint + util::unit<float>(previousTilePoint - currentTilePoint), projectionMatrix).first; + const Point<float> projectedUnitSegment = previousProjectedPoint - projectedUnitVertex; + + return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag<float>(projectedUnitSegment))); + } optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, - Point<float> anchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { + const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { const float combinedOffsetX = flip ? offsetX - lineOffsetX : @@ -172,8 +185,8 @@ namespace mbgl { int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1; - Point<float> current = anchorPoint; - Point<float> prev = anchorPoint; + Point<float> current = projectedAnchorPoint; + Point<float> prev = projectedAnchorPoint; float distanceToPrev = 0.0; float currentSegmentDistance = 0.0; const float absOffsetX = std::abs(combinedOffsetX); @@ -185,7 +198,18 @@ namespace mbgl { if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) return {}; prev = current; - current = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix); + PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix); + if (projection.second > 0) { + current = projection.first; + } else { + // The vertex is behind the plane of the camera, so we can't project it + // Instead, we'll create a vertex along the line that's far enough to include the glyph + const Point<float> previousTilePoint = distanceToPrev == 0 ? + tileAnchorPoint : + convertPoint<float>(line.at(currentIndex - dir)); + const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex)); + current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix); + } distanceToPrev += currentSegmentDistance; currentSegmentDistance = util::dist<float>(prev, current); @@ -211,29 +235,28 @@ namespace mbgl { const mat4& posMatrix, const mat4& labelPlaneMatrix, const mat4& glCoordMatrix, - gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, + const Point<float>& projectedAnchorPoint) { const float fontScale = fontSize / 24.0; const float lineOffsetX = symbol.lineOffset[0] * fontSize; const float lineOffsetY = symbol.lineOffset[1] * fontSize; - const Point<float> anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix); - std::vector<PlacedGlyph> placedGlyphs; if (symbol.glyphOffsets.size() > 1) { const float firstGlyphOffset = symbol.glyphOffsets.front(); const float lastGlyphOffset = symbol.glyphOffsets.back(); - optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); if (!firstPlacedGlyph) return PlacementResult::NotEnoughRoom; - optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); if (!lastPlacedGlyph) return PlacementResult::NotEnoughRoom; - const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix); - const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix); + const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first; + const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first; if (keepUpright && !flip && (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) { @@ -244,7 +267,7 @@ namespace mbgl { for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) { const float glyphOffsetX = symbol.glyphOffsets[glyphIndex]; // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed - auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); placedGlyphs.push_back(*placedGlyph); } placedGlyphs.push_back(*lastPlacedGlyph); @@ -252,15 +275,23 @@ namespace mbgl { // Only a single glyph to place // So, determine whether to flip based on projected angle of the line segment it's on if (keepUpright && !flip) { - const Point<float> a = project(convertPoint<float>(symbol.line.at(symbol.segment)), posMatrix); - const Point<float> b = project(convertPoint<float>(symbol.line.at(symbol.segment + 1)), posMatrix); + const Point<float> a = project(symbol.anchorPoint, posMatrix).first; + const Point<float> tileSegmentEnd = convertPoint<float>(symbol.line.at(symbol.segment + 1)); + const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix); + // We know the anchor will be in the viewport, but the end of the line segment may be + // behind the plane of the camera, in which case we can use a point at any arbitrary (closer) + // point on the segment. + const Point<float> b = (projectedVertex.second > 0) ? + projectedVertex.first : + projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix); + if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) { return PlacementResult::NeedsFlipping; } } assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all const float glyphOffsetX = symbol.glyphOffsets.front(); - optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, + optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); if (!singleGlyph) return PlacementResult::NotEnoughRoom; @@ -275,6 +306,7 @@ namespace mbgl { return PlacementResult::OK; } + void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols, const mat4& posMatrix, const style::SymbolPropertyValues& values, const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) { @@ -311,12 +343,14 @@ namespace mbgl { const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? fontSize * perspectiveRatio : fontSize / perspectiveRatio; + + const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first; - PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray); + PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint); if (placeUnflipped == PlacementResult::NotEnoughRoom || (placeUnflipped == PlacementResult::NeedsFlipping && - placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray) == PlacementResult::NotEnoughRoom)) { + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == PlacementResult::NotEnoughRoom)) { hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); } } diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index d5a5923e6c..378bd40ab7 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -50,7 +50,7 @@ public: // StyleObserver void onSourceChanged(style::Source&) override; - void onUpdate(Update) override; + void onUpdate() override; void onStyleLoading() override; void onStyleLoaded() override; void onStyleError(std::exception_ptr) override; @@ -166,11 +166,18 @@ void Map::renderStill(StillImageCallback callback) { impl->stillImageRequest = std::make_unique<StillImageRequest>(std::move(callback)); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); +} + +void Map::renderStill(const CameraOptions& camera, MapDebugOptions debugOptions, StillImageCallback callback) { + impl->cameraMutated = true; + impl->debugOptions = debugOptions; + impl->transform.jumpTo(camera); + renderStill(std::move(callback)); } void Map::triggerRepaint() { - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } #pragma mark - Map::Impl RendererObserver @@ -194,8 +201,11 @@ void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepai observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode)); if (needsRepaint || transform.inTransition()) { - onUpdate(Update::Repaint); + onUpdate(); } + } else if (stillImageRequest && rendererFullyLoaded) { + auto request = std::move(stillImageRequest); + request->callback(nullptr); } } @@ -206,9 +216,6 @@ void Map::Impl::onDidFinishRenderingMap() { loading = false; observer.onDidFinishLoadingMap(); } - } else if (stillImageRequest) { - auto request = std::move(stillImageRequest); - request->callback(nullptr); } }; @@ -233,12 +240,12 @@ void Map::setStyle(std::unique_ptr<Style> style) { void Map::cancelTransitions() { impl->transform.cancelTransitions(); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setGestureInProgress(bool inProgress) { impl->transform.setGestureInProgress(inProgress); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } bool Map::isGestureInProgress() const { @@ -266,19 +273,19 @@ CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const { void Map::jumpTo(const CameraOptions& camera) { impl->cameraMutated = true; impl->transform.jumpTo(camera); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.easeTo(camera, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.flyTo(camera, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } #pragma mark - Position @@ -286,7 +293,7 @@ void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) void Map::moveBy(const ScreenCoordinate& point, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.moveBy(point, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) { @@ -297,13 +304,13 @@ void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) { void Map::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setLatLng(latLng, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setLatLng(latLng, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } LatLng Map::getLatLng(const EdgeInsets& padding) const { @@ -319,7 +326,7 @@ void Map::resetPosition(const EdgeInsets& padding) { camera.padding = padding; camera.zoom = 0; impl->transform.jumpTo(camera); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } @@ -333,13 +340,13 @@ void Map::setZoom(double zoom, const AnimationOptions& animation) { void Map::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setZoom(zoom, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setZoom(double zoom, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setZoom(zoom, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } double Map::getZoom() const { @@ -354,30 +361,30 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOption void Map::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setLatLngZoom(latLng, zoom, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } -CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding) const { +CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding, optional<double> bearing) const { return cameraForLatLngs({ bounds.northwest(), bounds.southwest(), bounds.southeast(), bounds.northeast(), - }, padding); + }, padding, bearing); } -CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding) const { +CameraOptions cameraForLatLngs(const std::vector<LatLng>& latLngs, const Transform& transform, const EdgeInsets& padding) { CameraOptions options; if (latLngs.empty()) { return options; } - + Size size = transform.getState().getSize(); // Calculate the bounds of the possibly rotated shape with respect to the viewport. ScreenCoordinate nePixel = {-INFINITY, -INFINITY}; ScreenCoordinate swPixel = {INFINITY, INFINITY}; - double viewportHeight = getSize().height; + double viewportHeight = size.height; for (LatLng latLng : latLngs) { - ScreenCoordinate pixel = impl->transform.latLngToScreenCoordinate(latLng); + ScreenCoordinate pixel = transform.latLngToScreenCoordinate(latLng); swPixel.x = std::min(swPixel.x, pixel.x); nePixel.x = std::max(nePixel.x, pixel.x); swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y); @@ -389,14 +396,14 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed // Calculate the zoom level. double minScale = INFINITY; if (width > 0 || height > 0) { - double scaleX = double(getSize().width) / width; - double scaleY = double(getSize().height) / height; + double scaleX = double(size.width) / width; + double scaleY = double(size.height) / height; scaleX -= (padding.left() + padding.right()) / width; scaleY -= (padding.top() + padding.bottom()) / height; minScale = util::min(scaleX, scaleY); } - double zoom = getZoom() + util::log2(minScale); - zoom = util::clamp(zoom, getMinZoom(), getMaxZoom()); + double zoom = transform.getZoom() + util::log2(minScale); + zoom = util::clamp(zoom, transform.getState().getMinZoom(), transform.getState().getMaxZoom()); // Calculate the center point of a virtual bounds that is extended in all directions by padding. ScreenCoordinate centerPixel = nePixel + swPixel; @@ -414,11 +421,34 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed // CameraOptions origin is at the top-left corner. centerPixel.y = viewportHeight - centerPixel.y; - options.center = latLngForPixel(centerPixel); + options.center = transform.screenCoordinateToLatLng(centerPixel); options.zoom = zoom; return options; } +CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding, optional<double> bearing) const { + if(bearing) { + double angle = -*bearing * util::DEG2RAD; // Convert to radians + Transform transform(impl->transform.getState()); + transform.setAngle(angle); + CameraOptions options = mbgl::cameraForLatLngs(latLngs, transform, padding); + options.angle = angle; + return options; + } else { + return mbgl::cameraForLatLngs(latLngs, impl->transform, padding); + } +} + +CameraOptions Map::cameraForGeometry(const Geometry<double>& geometry, const EdgeInsets& padding, optional<double> bearing) const { + + std::vector<LatLng> latLngs; + forEachPoint(geometry, [&](const Point<double>& pt) { + latLngs.push_back({ pt.y, pt.x }); + }); + return cameraForLatLngs(latLngs, padding, bearing); + +} + LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const { Transform shallow { impl->transform.getState() }; Size size = shallow.getState().getSize(); @@ -444,7 +474,7 @@ optional<LatLngBounds> Map::getLatLngBounds() const { void Map::setLatLngBounds(optional<LatLngBounds> bounds) { impl->cameraMutated = true; impl->transform.setLatLngBounds(bounds); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setMinZoom(const double minZoom) { @@ -495,7 +525,7 @@ double Map::getMaxPitch() const { void Map::setSize(const Size size) { impl->transform.resize(size); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } Size Map::getSize() const { @@ -507,7 +537,7 @@ Size Map::getSize() const { void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.rotateBy(first, second, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setBearing(double degrees, const AnimationOptions& animation) { @@ -518,13 +548,13 @@ void Map::setBearing(double degrees, const AnimationOptions& animation) { void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setBearing(double degrees, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setAngle(-degrees * util::DEG2RAD, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } double Map::getBearing() const { @@ -534,7 +564,7 @@ double Map::getBearing() const { void Map::resetNorth(const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setAngle(0, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } #pragma mark - Pitch @@ -547,7 +577,7 @@ void Map::setPitch(double pitch, const AnimationOptions& animation) { void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setPitch(pitch * util::DEG2RAD, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } double Map::getPitch() const { @@ -558,7 +588,7 @@ double Map::getPitch() const { void Map::setNorthOrientation(NorthOrientation orientation) { impl->transform.setNorthOrientation(orientation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } NorthOrientation Map::getNorthOrientation() const { @@ -569,7 +599,7 @@ NorthOrientation Map::getNorthOrientation() const { void Map::setConstrainMode(mbgl::ConstrainMode mode) { impl->transform.setConstrainMode(mode); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } ConstrainMode Map::getConstrainMode() const { @@ -580,13 +610,42 @@ ConstrainMode Map::getConstrainMode() const { void Map::setViewportMode(mbgl::ViewportMode mode) { impl->transform.setViewportMode(mode); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } ViewportMode Map::getViewportMode() const { return impl->transform.getViewportMode(); } +#pragma mark - Projection mode + +void Map::setAxonometric(bool axonometric) { + impl->transform.setAxonometric(axonometric); + impl->onUpdate(); +} + +bool Map::getAxonometric() const { + return impl->transform.getAxonometric(); +} + +void Map::setXSkew(double xSkew) { + impl->transform.setXSkew(xSkew); + impl->onUpdate(); +} + +double Map::getXSkew() const { + return impl->transform.getXSkew(); +} + +void Map::setYSkew(double ySkew) { + impl->transform.setYSkew(ySkew); + impl->onUpdate(); +} + +double Map::getYSkew() const { + return impl->transform.getYSkew(); +} + #pragma mark - Projection ScreenCoordinate Map::pixelForLatLng(const LatLng& latLng) const { @@ -618,24 +677,26 @@ double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) { AnnotationID Map::addAnnotation(const Annotation& annotation) { auto result = impl->annotationManager.addAnnotation(annotation, getMaxZoom()); - impl->onUpdate(Update::AnnotationData); + impl->onUpdate(); return result; } void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) { - impl->onUpdate(impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom())); + if (impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom())) { + impl->onUpdate(); + } } void Map::removeAnnotation(AnnotationID annotation) { impl->annotationManager.removeAnnotation(annotation); - impl->onUpdate(Update::AnnotationData); + impl->onUpdate(); } #pragma mark - Toggles void Map::setDebug(MapDebugOptions debugOptions) { impl->debugOptions = debugOptions; - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::cycleDebugOptions() { @@ -659,7 +720,7 @@ void Map::cycleDebugOptions() { else impl->debugOptions = MapDebugOptions::TileBorders; - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } MapDebugOptions Map::getDebug() const { @@ -683,18 +744,14 @@ void Map::Impl::onSourceChanged(style::Source& source) { } void Map::Impl::onInvalidate() { - onUpdate(Update::Repaint); + onUpdate(); } -void Map::Impl::onUpdate(Update flags) { +void Map::Impl::onUpdate() { TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max(); transform.updateTransitions(timePoint); - if (flags & Update::AnnotationData) { - annotationManager.updateData(); - } - UpdateParameters params = { style->impl->isLoaded(), mode, @@ -709,8 +766,6 @@ void Map::Impl::onUpdate(Update flags) { style->impl->getImageImpls(), style->impl->getSourceImpls(), style->impl->getLayerImpls(), - scheduler, - fileSource, annotationManager, prefetchZoomDelta, bool(stillImageRequest) diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 2bb25af28f..105adf0400 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -527,6 +527,32 @@ ViewportMode Transform::getViewportMode() const { return state.getViewportMode(); } +#pragma mark - Projection mode + +void Transform::setAxonometric(bool axonometric) { + state.axonometric = axonometric; +} + +bool Transform::getAxonometric() const { + return state.axonometric; +} + +void Transform::setXSkew(double xSkew) { + state.xSkew = xSkew; +} + +double Transform::getXSkew() const { + return state.xSkew; +} + +void Transform::setYSkew(double ySkew) { + state.ySkew = ySkew; +} + +double Transform::getYSkew() const { + return state.ySkew; +} + #pragma mark - Transition void Transform::startTransition(const CameraOptions& camera, diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 749228bdf5..d429c57661 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -125,6 +125,14 @@ public: void setViewportMode(ViewportMode); ViewportMode getViewportMode() const; + // Projection mode + void setAxonometric(bool); + bool getAxonometric() const; + void setXSkew(double xSkew); + double getXSkew() const; + void setYSkew(double ySkew); + double getYSkew() const; + // Transitions bool inTransition() const; void updateTransitions(const TimePoint& now); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 4606e3eece..d79a65c61e 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -66,6 +66,15 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const { matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f, pixel_y() - size.height / 2.0f, 0); + if (axonometric) { + // mat[11] controls perspective + projMatrix[11] = 0; + + // mat[8], mat[9] control x-skew, y-skew + projMatrix[8] = xSkew; + projMatrix[9] = ySkew; + } + matrix::scale(projMatrix, projMatrix, 1, 1, 1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom())); } @@ -132,7 +141,7 @@ double TransformState::getZoom() const { return scaleZoom(scale); } -int32_t TransformState::getIntegerZoom() const { +uint8_t TransformState::getIntegerZoom() const { return getZoom(); } diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 4d6b89573e..0dd6d5a15e 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -47,7 +47,7 @@ public: // Zoom double getZoom() const; - int32_t getIntegerZoom() const; + uint8_t getIntegerZoom() const; double getZoomFraction() const; // Bounds @@ -134,6 +134,9 @@ private: // `fov = 2 * arctan((height / 2) / (height * 1.5))` double fov = 0.6435011087932844; double pitch = 0.0; + double xSkew = 0.0; + double ySkew = 1.0; + bool axonometric = false; // cache values for spherical mercator math double Bc = Projection::worldSize(scale) / util::DEGREES_MAX; diff --git a/src/mbgl/map/zoom_history.hpp b/src/mbgl/map/zoom_history.hpp index ddec53e6af..7821499d72 100644 --- a/src/mbgl/map/zoom_history.hpp +++ b/src/mbgl/map/zoom_history.hpp @@ -29,7 +29,7 @@ struct ZoomHistory { if (lastFloorZoom > floorZ) { lastIntegerZoom = floorZ + 1; lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now; - } else if (lastFloorZoom < floorZ || lastIntegerZoom != floorZ) { + } else if (lastFloorZoom < floorZ) { lastIntegerZoom = floorZ; lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now; } diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index 3a38453d30..d023ec7d83 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -23,7 +23,7 @@ inline uint16_t packUint8Pair(T a, T b) { MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset); -MBGL_DEFINE_ATTRIBUTE(int16_t, 3, a_pos_normal); +MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_normal); MBGL_DEFINE_ATTRIBUTE(float, 3, a_projected_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_label_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos); diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp index f9e91f569f..faf57ef19b 100644 --- a/src/mbgl/programs/line_program.cpp +++ b/src/mbgl/programs/line_program.cpp @@ -10,7 +10,7 @@ namespace mbgl { using namespace style; -static_assert(sizeof(LineLayoutVertex) == 10, "expected LineLayoutVertex size"); +static_assert(sizeof(LineLayoutVertex) == 12, "expected LineLayoutVertex size"); template <class Values, class...Args> Values makeValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp index 95b9362b85..da9964e623 100644 --- a/src/mbgl/programs/line_program.hpp +++ b/src/mbgl/programs/line_program.hpp @@ -59,7 +59,8 @@ public: {{ p.x, p.y, - static_cast<int16_t>(attributes::packUint8Pair(round ? 1 : 0, up ? 1 : 0)) + static_cast<int16_t>(round ? 1 : 0), + static_cast<int16_t>(up ? 1 : -1) }}, {{ // add 128 to store a byte in an unsigned byte diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp index 692747bca4..2ef6be0c4f 100644 --- a/src/mbgl/renderer/image_manager.cpp +++ b/src/mbgl/renderer/image_manager.cpp @@ -39,6 +39,13 @@ void ImageManager::removeImage(const std::string& id) { auto it = patterns.find(id); if (it != patterns.end()) { + // Clear pattern from the atlas image. + const uint32_t x = it->second.bin->x; + const uint32_t y = it->second.bin->y; + const uint32_t w = it->second.bin->w; + const uint32_t h = it->second.bin->h; + PremultipliedImage::clear(atlasImage, { x, y }, { w, h }); + shelfPack.unref(*it->second.bin); patterns.erase(it); } @@ -52,23 +59,23 @@ const style::Image::Impl* ImageManager::getImage(const std::string& id) const { return nullptr; } -void ImageManager::getImages(ImageRequestor& requestor, ImageDependencies dependencies) { +void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) { // If the sprite has been loaded, or if all the icon dependencies are already present // (i.e. if they've been addeded via runtime styling), then notify the requestor immediately. // Otherwise, delay notification until the sprite is loaded. At that point, if any of the // dependencies are still unavailable, we'll just assume they are permanently missing. bool hasAllDependencies = true; if (!isLoaded()) { - for (const auto& dependency : dependencies) { + for (const auto& dependency : pair.first) { if (images.find(dependency) == images.end()) { hasAllDependencies = false; } } } if (isLoaded() || hasAllDependencies) { - notify(requestor, dependencies); + notify(requestor, std::move(pair)); } else { - requestors.emplace(&requestor, std::move(dependencies)); + requestors.emplace(&requestor, std::move(pair)); } } @@ -76,17 +83,17 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) { requestors.erase(&requestor); } -void ImageManager::notify(ImageRequestor& requestor, const ImageDependencies& dependencies) const { +void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const { ImageMap response; - for (const auto& dependency : dependencies) { + for (const auto& dependency : pair.first) { auto it = images.find(dependency); if (it != images.end()) { response.emplace(*it); } } - requestor.onImagesAvailable(response); + requestor.onImagesAvailable(response, pair.second); } void ImageManager::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp index 1c9d67f47d..f72ba9fb53 100644 --- a/src/mbgl/renderer/image_manager.hpp +++ b/src/mbgl/renderer/image_manager.hpp @@ -21,7 +21,7 @@ class Context; class ImageRequestor { public: virtual ~ImageRequestor() = default; - virtual void onImagesAvailable(ImageMap) = 0; + virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0; }; /* @@ -50,15 +50,15 @@ public: void updateImage(Immutable<style::Image::Impl>); void removeImage(const std::string&); - void getImages(ImageRequestor&, ImageDependencies); + void getImages(ImageRequestor&, ImageRequestPair&&); void removeRequestor(ImageRequestor&); private: - void notify(ImageRequestor&, const ImageDependencies&) const; + void notify(ImageRequestor&, const ImageRequestPair&) const; bool loaded = false; - std::unordered_map<ImageRequestor*, ImageDependencies> requestors; + std::unordered_map<ImageRequestor*, ImageRequestPair> requestors; ImageMap images; // Pattern stuff diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index ae0c4b026b..7ece3970da 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -16,8 +16,12 @@ RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) RenderCustomLayer::~RenderCustomLayer() { assert(BackendScope::exists()); - if (initialized && impl().deinitializeFn) { - impl().deinitializeFn(impl().context); + if (initialized) { + if (contextDestroyed && impl().contextLostFn ) { + impl().contextLostFn(impl().context); + } else if (!contextDestroyed && impl().deinitializeFn) { + impl().deinitializeFn(impl().context); + } } } diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index d8e9d93811..32ed9da8da 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -19,8 +19,13 @@ public: const style::CustomLayer::Impl& impl() const; + void markContextDestroyed() { + contextDestroyed = true; + }; + private: bool initialized = false; + bool contextDestroyed = false; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index 6295f62b21..fbd6160e8a 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -37,8 +37,9 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { evaluated = unevaluated.evaluate(parameters); - passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent - : RenderPass::None; + passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; } bool RenderFillExtrusionLayer::hasTransition() const { @@ -50,113 +51,100 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* return; } - const auto size = parameters.context.viewport.getCurrentValue().size; + if (parameters.pass == RenderPass::Pass3D) { + const auto& size = parameters.staticData.backendSize; - if (!parameters.staticData.extrusionTexture || parameters.staticData.extrusionTexture->getSize() != size) { - parameters.staticData.extrusionTexture = OffscreenTexture(parameters.context, size, OffscreenTextureAttachment::Depth); - } - - parameters.staticData.extrusionTexture->bind(); - - parameters.context.setStencilMode(gl::StencilMode::disabled()); - parameters.context.setDepthMode(parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite)); - parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {}); - - if (evaluated.get<FillExtrusionPattern>().from.empty()) { - for (const RenderTile& tile : renderTiles) { - assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); - FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); - - parameters.programs.fillExtrusion.get(evaluated).draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), - FillExtrusionUniforms::values( - tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), - evaluated.get<FillExtrusionTranslateAnchor>(), - parameters.state), - parameters.state, - parameters.evaluatedLight - ), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.triangleSegments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), - getID()); + if (!renderTexture || renderTexture->getSize() != size) { + renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer); } - } else { - optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from); - optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to); - if (!imagePosA || !imagePosB) { - return; + renderTexture->bind(); + + optional<float> depthClearValue = {}; + if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0; + // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass. + parameters.staticData.depthRenderbuffer->shouldClear(false); + + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {}); + + if (evaluated.get<FillExtrusionPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusion.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.state, parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } else { + optional<ImagePosition> imagePosA = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from); + optional<ImagePosition> imagePosB = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusionPattern.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionPatternUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, + evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state, + -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, + parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } } - parameters.imageManager.bind(parameters.context, 0); - - for (const RenderTile& tile : renderTiles) { - assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); - FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); - - parameters.programs.fillExtrusionPattern.get(evaluated).draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), - FillExtrusionPatternUniforms::values( - tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), - evaluated.get<FillExtrusionTranslateAnchor>(), - parameters.state), - parameters.imageManager.getPixelSize(), - *imagePosA, - *imagePosB, - evaluated.get<FillExtrusionPattern>(), - tile.id, - parameters.state, - -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, - parameters.evaluatedLight - ), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.triangleSegments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), - getID()); - } - } + } else if (parameters.pass == RenderPass::Translucent) { + parameters.context.bindTexture(renderTexture->getTexture()); - parameters.backend.bind(); - parameters.context.bindTexture(parameters.staticData.extrusionTexture->getTexture()); - - mat4 viewportMat; - matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); - - const Properties<>::PossiblyEvaluated properties; - - parameters.programs.extrusionTexture.draw( - parameters.context, - gl::Triangles(), - gl::DepthMode::disabled(), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), - ExtrusionTextureProgram::UniformValues{ - uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, - uniforms::u_image::Value{ 0 }, - uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } - }, - parameters.staticData.extrusionTextureVertexBuffer, - parameters.staticData.quadTriangleIndexBuffer, - parameters.staticData.extrusionTextureSegments, - ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, - properties, - parameters.state.getZoom(), - getID()); + const auto& size = parameters.staticData.backendSize; + + mat4 viewportMat; + matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); + + const Properties<>::PossiblyEvaluated properties; + + parameters.programs.extrusionTexture.draw( + parameters.context, gl::Triangles(), gl::DepthMode::disabled(), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + ExtrusionTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 0 }, + uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } }, + parameters.staticData.extrusionTextureVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.extrusionTextureSegments, + ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + parameters.state.getZoom(), getID()); + } } bool RenderFillExtrusionLayer::queryIntersectsFeature( diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index a53e00ca6f..838494cf91 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -3,6 +3,8 @@ #include <mbgl/renderer/render_layer.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> #include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/offscreen_texture.hpp> namespace mbgl { @@ -30,6 +32,8 @@ public: style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated; const style::FillExtrusionLayer::Impl& impl() const; + + optional<OffscreenTexture> renderTexture; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 394642a50d..22cb9563c1 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -99,7 +99,9 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) { draw(parameters.programs.fill, gl::Triangles(), - parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite), + parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque + ? gl::DepthMode::ReadWrite + : gl::DepthMode::ReadOnly), *bucket.triangleIndexBuffer, bucket.triangleSegments); } diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index ebdaecd3a3..299db844bc 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -1,6 +1,5 @@ #include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/renderer/update_parameters.hpp> -#include <mbgl/renderer/render_style.hpp> #include <mbgl/renderer/render_static_data.hpp> #include <mbgl/map/transform_state.hpp> @@ -11,17 +10,19 @@ PaintParameters::PaintParameters(gl::Context& context_, GLContextMode contextMode_, RendererBackend& backend_, const UpdateParameters& updateParameters, - RenderStyle& style, + const EvaluatedLight& evaluatedLight_, RenderStaticData& staticData_, - FrameHistory& frameHistory_) + FrameHistory& frameHistory_, + ImageManager& imageManager_, + LineAtlas& lineAtlas_) : context(context_), backend(backend_), state(updateParameters.transformState), - evaluatedLight(style.getRenderLight().getEvaluated()), + evaluatedLight(evaluatedLight_), staticData(staticData_), frameHistory(frameHistory_), - imageManager(*style.imageManager), - lineAtlas(*style.lineAtlas), + imageManager(imageManager_), + lineAtlas(lineAtlas_), mapMode(updateParameters.mode), debugOptions(updateParameters.debugOptions), contextMode(contextMode_), @@ -61,6 +62,10 @@ gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Ma return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } }; } +gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const { + return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } }; +} + gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const { return gl::StencilMode { gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) }, diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index e9d3562a75..4a2c2c6f12 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -15,7 +15,6 @@ namespace mbgl { class RendererBackend; class UpdateParameters; -class RenderStyle; class RenderStaticData; class FrameHistory; class Programs; @@ -31,9 +30,11 @@ public: GLContextMode, RendererBackend&, const UpdateParameters&, - RenderStyle&, + const EvaluatedLight&, RenderStaticData&, - FrameHistory&); + FrameHistory&, + ImageManager&, + LineAtlas&); gl::Context& context; RendererBackend& backend; @@ -59,6 +60,7 @@ public: Programs& programs; gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const; + gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const; gl::StencilMode stencilModeForClipping(const ClipID&) const; gl::ColorMode colorModeForRenderPass() const; diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp index ae2b923ba1..5d18304129 100644 --- a/src/mbgl/renderer/render_pass.hpp +++ b/src/mbgl/renderer/render_pass.hpp @@ -11,6 +11,7 @@ enum class RenderPass : uint8_t { None = 0, Opaque = 1 << 0, Translucent = 1 << 1, + Pass3D = 1 << 2, }; MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) { diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index b565439588..8293923ff6 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -18,7 +18,6 @@ namespace mbgl { class PaintParameters; class TransformState; class RenderTile; -class RenderStyle; class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; @@ -63,7 +62,7 @@ public: virtual std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const = 0; virtual std::vector<Feature> diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp index 07a47b4c8f..cf58c31f4d 100644 --- a/src/mbgl/renderer/render_static_data.hpp +++ b/src/mbgl/renderer/render_static_data.hpp @@ -4,7 +4,6 @@ #include <mbgl/gl/index_buffer.hpp> #include <mbgl/programs/programs.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/offscreen_texture.hpp> #include <string> @@ -26,7 +25,9 @@ public: SegmentVector<RasterAttributes> rasterSegments; SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; - optional<OffscreenTexture> extrusionTexture; + optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer; + bool has3D = false; + Size backendSize; Programs programs; diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index b498972f5c..3db02393d2 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -28,6 +28,7 @@ public: mat4 matrix; mat4 nearClippedMatrix; bool used = false; + bool needsClipping = false; mat4 translatedMatrix(const std::array<float, 2>& translate, style::TranslateAnchorType anchor, diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp index 9f4b897d6e..e915f5e146 100644 --- a/src/mbgl/renderer/renderer.cpp +++ b/src/mbgl/renderer/renderer.cpp @@ -1,6 +1,6 @@ #include <mbgl/renderer/renderer.hpp> #include <mbgl/renderer/renderer_impl.hpp> -#include <mbgl/renderer/update_parameters.hpp> +#include <mbgl/renderer/backend_scope.hpp> #include <mbgl/annotation/annotation_manager.hpp> namespace mbgl { @@ -15,7 +15,14 @@ Renderer::Renderer(RendererBackend& backend, contextMode_, std::move(programCacheDir_))) { } -Renderer::~Renderer() = default; +Renderer::~Renderer() { + BackendScope guard { impl->backend }; + impl.reset(); +} + +void Renderer::markContextLost() { + impl->markContextLost(); +} void Renderer::setObserver(RendererObserver* observer) { impl->setObserver(observer); @@ -72,6 +79,7 @@ void Renderer::dumpDebugLogs() { } void Renderer::onLowMemory() { + BackendScope guard { impl->backend }; impl->onLowMemory(); } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index dd3c0d41a1..6a8c18792e 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -1,13 +1,32 @@ +#include <mbgl/annotation/annotation_manager.hpp> #include <mbgl/renderer/renderer_impl.hpp> -#include <mbgl/renderer/render_style.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/render_layer.hpp> #include <mbgl/renderer/render_static_data.hpp> -#include <mbgl/renderer/render_item.hpp> #include <mbgl/renderer/update_parameters.hpp> #include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/transition_parameters.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/style_diff.hpp> +#include <mbgl/renderer/query.hpp> #include <mbgl/renderer/backend_scope.hpp> #include <mbgl/renderer/image_manager.hpp> #include <mbgl/gl/debugging.hpp> #include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/text/glyph_manager.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/logging.hpp> namespace mbgl { @@ -24,20 +43,36 @@ Renderer::Impl::Impl(RendererBackend& backend_, Scheduler& scheduler_, GLContextMode contextMode_, const optional<std::string> programCacheDir_) - : backend(backend_) - , observer(&nullObserver()) - , contextMode(contextMode_) - , pixelRatio(pixelRatio_) - , programCacheDir(programCacheDir_) - , renderStyle(std::make_unique<RenderStyle>(scheduler_, fileSource_)) { - - renderStyle->setObserver(this); + : backend(backend_) + , scheduler(scheduler_) + , fileSource(fileSource_) + , observer(&nullObserver()) + , contextMode(contextMode_) + , pixelRatio(pixelRatio_) + , programCacheDir(programCacheDir_) + , glyphManager(std::make_unique<GlyphManager>(fileSource)) + , imageManager(std::make_unique<ImageManager>()) + , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })) + , imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()) + , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()) + , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()) + , renderLight(makeMutable<Light::Impl>()) { + glyphManager->setObserver(this); } Renderer::Impl::~Impl() { - BackendScope guard { backend }; - renderStyle.reset(); - staticData.reset(); + assert(BackendScope::exists()); + + if (contextLost) { + // Signal all RenderCustomLayers that the context was lost + // before cleaning up + for (const auto& entry : renderLayers) { + RenderLayer& layer = *entry.second; + if (layer.is<RenderCustomLayer>()) { + layer.as<RenderCustomLayer>()->markContextDestroyed(); + } + } + } }; void Renderer::Impl::setObserver(RendererObserver* observer_) { @@ -45,12 +80,164 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { } void Renderer::Impl::render(const UpdateParameters& updateParameters) { - // Don't load/render anyting in still mode until explicitly requested. - if (updateParameters.mode == MapMode::Still && !updateParameters.stillImageRequest) return; + if (updateParameters.mode == MapMode::Still) { + // Don't load/render anyting in still mode until explicitly requested. + if (!updateParameters.stillImageRequest) { + return; + } + + // Reset zoom history state. + zoomHistory.first = true; + } assert(BackendScope::exists()); + + updateParameters.annotationManager.updateData(); + + const bool zoomChanged = zoomHistory.update(updateParameters.transformState.getZoom(), updateParameters.timePoint); + + const TransitionParameters transitionParameters { + updateParameters.timePoint, + updateParameters.mode == MapMode::Continuous ? updateParameters.transitionOptions : TransitionOptions() + }; + + const PropertyEvaluationParameters evaluationParameters { + zoomHistory, + updateParameters.timePoint, + updateParameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero() + }; + + const TileParameters tileParameters { + updateParameters.pixelRatio, + updateParameters.debugOptions, + updateParameters.transformState, + scheduler, + fileSource, + updateParameters.mode, + updateParameters.annotationManager, + *imageManager, + *glyphManager, + updateParameters.prefetchZoomDelta + }; + + glyphManager->setURL(updateParameters.glyphURL); + + // Update light. + const bool lightChanged = renderLight.impl != updateParameters.light; + + if (lightChanged) { + renderLight.impl = updateParameters.light; + renderLight.transition(transitionParameters); + } + + if (lightChanged || zoomChanged || renderLight.hasTransition()) { + renderLight.evaluate(evaluationParameters); + } + + + const ImageDifference imageDiff = diffImages(imageImpls, updateParameters.images); + imageImpls = updateParameters.images; + + // Remove removed images from sprite atlas. + for (const auto& entry : imageDiff.removed) { + imageManager->removeImage(entry.first); + } + + // Add added images to sprite atlas. + for (const auto& entry : imageDiff.added) { + imageManager->addImage(entry.second); + } + + // Update changed images. + for (const auto& entry : imageDiff.changed) { + imageManager->updateImage(entry.second.after); + } + + imageManager->setLoaded(updateParameters.spriteLoaded); + + + const LayerDifference layerDiff = diffLayers(layerImpls, updateParameters.layers); + layerImpls = updateParameters.layers; + + // Remove render layers for removed layers. + for (const auto& entry : layerDiff.removed) { + renderLayers.erase(entry.first); + } + + // Create render layers for newly added layers. + for (const auto& entry : layerDiff.added) { + renderLayers.emplace(entry.first, RenderLayer::create(entry.second)); + } + + // Update render layers for changed layers. + for (const auto& entry : layerDiff.changed) { + renderLayers.at(entry.first)->setImpl(entry.second.after); + } + + // Update layers for class and zoom changes. + for (const auto& entry : renderLayers) { + RenderLayer& layer = *entry.second; + const bool layerAdded = layerDiff.added.count(entry.first); + const bool layerChanged = layerDiff.changed.count(entry.first); + + if (layerAdded || layerChanged) { + layer.transition(transitionParameters); + } + + if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { + layer.evaluate(evaluationParameters); + } + } + + + const SourceDifference sourceDiff = diffSources(sourceImpls, updateParameters.sources); + sourceImpls = updateParameters.sources; + + // Remove render layers for removed sources. + for (const auto& entry : sourceDiff.removed) { + renderSources.erase(entry.first); + } + + // Create render sources for newly added sources. + for (const auto& entry : sourceDiff.added) { + std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second); + renderSource->setObserver(this); + renderSources.emplace(entry.first, std::move(renderSource)); + } + + const bool hasImageDiff = !(imageDiff.added.empty() && imageDiff.removed.empty() && imageDiff.changed.empty()); + + // Update all sources. + for (const auto& source : *sourceImpls) { + std::vector<Immutable<Layer::Impl>> filteredLayers; + bool needsRendering = false; + bool needsRelayout = false; + + for (const auto& layer : *layerImpls) { + if (layer->type == LayerType::Background || + layer->type == LayerType::Custom || + layer->source != source->id) { + continue; + } + + if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) { + needsRendering = true; + } + + if (!needsRelayout && (hasImageDiff || hasLayoutDifference(layerDiff, layer->id))) { + needsRelayout = true; + } + + filteredLayers.push_back(layer); + } + + renderSources.at(source->id)->update(source, + filteredLayers, + needsRendering, + needsRelayout, + tileParameters); + } - renderStyle->update(updateParameters); transformState = updateParameters.transformState; if (!staticData) { @@ -63,60 +250,138 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { contextMode, backend, updateParameters, - *renderStyle, + renderLight.getEvaluated(), *staticData, - frameHistory + frameHistory, + *imageManager, + *lineAtlas }; - bool loaded = updateParameters.styleLoaded && renderStyle->isLoaded(); + bool loaded = updateParameters.styleLoaded && isLoaded(); + if (updateParameters.mode == MapMode::Still && !loaded) { + return; + } - if (updateParameters.mode == MapMode::Continuous) { - if (renderState == RenderState::Never) { - observer->onWillStartRenderingMap(); - } + if (renderState == RenderState::Never) { + observer->onWillStartRenderingMap(); + } + + observer->onWillStartRenderingFrame(); + + backend.updateAssumedState(); - observer->onWillStartRenderingFrame(); + if (parameters.contextMode == GLContextMode::Shared) { + parameters.context.setDirtyState(); + } - backend.updateAssumedState(); + Color backgroundColor; - doRender(parameters); - parameters.context.performCleanup(); + class RenderItem { + public: + RenderLayer& layer; + RenderSource* source; + }; + + std::vector<RenderItem> order; - observer->onDidFinishRenderingFrame( - loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial, - renderStyle->hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION) - ); + for (auto& layerImpl : *layerImpls) { + RenderLayer* layer = getRenderLayer(layerImpl->id); + assert(layer); - if (!loaded) { - renderState = RenderState::Partial; - } else if (renderState != RenderState::Fully) { - renderState = RenderState::Fully; - observer->onDidFinishRenderingMap(); + if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) { + parameters.staticData.has3D = true; } - } else if (loaded) { - observer->onWillStartRenderingMap(); - observer->onWillStartRenderingFrame(); - backend.updateAssumedState(); + if (!layer->needsRendering(zoomHistory.lastZoom)) { + continue; + } - doRender(parameters); + if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) { + const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; + if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) { + // This is a solid background. We can use glClear(). + backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>(); + } else { + // This is a textured background, or not the bottommost layer. We need to render it with a quad. + order.emplace_back(RenderItem { *layer, nullptr }); + } + continue; + } - observer->onDidFinishRenderingFrame(RendererObserver::RenderMode::Full, false); - observer->onDidFinishRenderingMap(); + if (layer->is<RenderCustomLayer>()) { + order.emplace_back(RenderItem { *layer, nullptr }); + continue; + } - // Cleanup only after signaling completion - parameters.context.performCleanup(); - } -} + RenderSource* source = getRenderSource(layer->baseImpl->source); + if (!source) { + Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str()); + continue; + } -void Renderer::Impl::doRender(PaintParameters& parameters) { - if (parameters.contextMode == GLContextMode::Shared) { - parameters.context.setDirtyState(); - } + const bool symbolLayer = layer->is<RenderSymbolLayer>(); + + auto sortedTiles = source->getRenderTiles(); + if (symbolLayer) { + // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn + // on top of each other, with lower symbols being drawn on top of higher symbols. + std::sort(sortedTiles.begin(), sortedTiles.end(), [&](const RenderTile& a, const RenderTile& b) { + Point<float> pa(a.id.canonical.x, a.id.canonical.y); + Point<float> pb(b.id.canonical.x, b.id.canonical.y); - RenderData renderData = renderStyle->getRenderData(parameters.debugOptions, parameters.state.getAngle()); - const std::vector<RenderItem>& order = renderData.order; - const std::unordered_set<RenderSource*>& sources = renderData.sources; + auto par = util::rotate(pa, parameters.state.getAngle()); + auto pbr = util::rotate(pb, parameters.state.getAngle()); + + return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); + }); + } else { + std::sort(sortedTiles.begin(), sortedTiles.end(), + [](const auto& a, const auto& b) { return a.get().id < b.get().id; }); + } + + std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion; + for (auto& sortedTile : sortedTiles) { + auto& tile = sortedTile.get(); + if (!tile.tile.isRenderable()) { + continue; + } + + // We're not clipping symbol layers, so when we have both parents and children of symbol + // layers, we drop all children in favor of their parent to avoid duplicate labels. + // See https://github.com/mapbox/mapbox-gl-native/issues/2482 + if (symbolLayer) { + bool skip = false; + // Look back through the buckets we decided to render to find out whether there is + // already a bucket from this layer that is a parent of this tile. Tiles are ordered + // by zoom level when we obtain them from getTiles(). + for (auto it = sortedTilesForInsertion.rbegin(); + it != sortedTilesForInsertion.rend(); ++it) { + if (tile.tile.id.isChildOf(it->get().tile.id)) { + skip = true; + break; + } + } + if (skip) { + continue; + } + } + + auto bucket = tile.tile.getBucket(*layer->baseImpl); + if (bucket) { + sortedTilesForInsertion.emplace_back(tile); + tile.used = true; + + // We only need clipping when we're _not_ drawing a symbol layer. The only exception + // for symbol layers is when we're rendering still images. See render_symbol_layer.cpp + // for the exception we make there. + if (!symbolLayer || parameters.mapMode == MapMode::Still) { + tile.needsClipping = true; + } + } + } + layer->setRenderTiles(std::move(sortedTilesForInsertion)); + order.emplace_back(RenderItem { *layer, source }); + } frameHistory.record(parameters.timePoint, parameters.state.getZoom(), @@ -130,6 +395,39 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { parameters.imageManager.upload(parameters.context, 0); parameters.lineAtlas.upload(parameters.context, 0); parameters.frameHistory.upload(parameters.context, 0); + + // Update all clipping IDs + upload buckets. + for (const auto& entry : renderSources) { + if (entry.second->isEnabled()) { + entry.second->startRender(parameters); + } + } + } + + // - 3D PASS ------------------------------------------------------------------------------------- + // Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same + // depth rbo between them. + if (parameters.staticData.has3D) { + parameters.staticData.backendSize = parameters.backend.getFramebufferSize(); + + MBGL_DEBUG_GROUP(parameters.context, "3d"); + parameters.pass = RenderPass::Pass3D; + + if (!parameters.staticData.depthRenderbuffer || + parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) { + parameters.staticData.depthRenderbuffer = + parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize); + } + parameters.staticData.depthRenderbuffer->shouldClear(true); + + uint32_t i = static_cast<uint32_t>(order.size()) - 1; + for (auto it = order.begin(); it != order.end(); ++it, --i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } } // - CLEAR ------------------------------------------------------------------------------------- @@ -140,7 +438,7 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { parameters.backend.bind(); parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw) ? Color::black() - : renderData.backgroundColor, + : backgroundColor, 1.0f, 0); } @@ -148,13 +446,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { // - CLIPPING MASKS ---------------------------------------------------------------------------- // Draws the clipping masks to the stencil buffer. { - MBGL_DEBUG_GROUP(parameters.context, "clip"); - - // Update all clipping IDs. - for (const auto& source : sources) { - source->startRender(parameters); - } - MBGL_DEBUG_GROUP(parameters.context, "clipping masks"); static const style::FillPaintProperties::PossiblyEvaluated properties {}; @@ -220,10 +511,7 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { } #endif - int indent = 0; - // Actually render the layers - if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } parameters.depthRangeSize = 1 - (order.size() + 2) * parameters.numSublayers * parameters.depthEpsilon; @@ -233,10 +521,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { parameters.pass = RenderPass::Opaque; MBGL_DEBUG_GROUP(parameters.context, "opaque"); - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", "opaque"); - } - uint32_t i = 0; for (auto it = order.rbegin(); it != order.rend(); ++it, ++i) { parameters.currentLayer = i; @@ -245,10 +529,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { it->layer.render(parameters, it->source); } } - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); - } } // - TRANSLUCENT PASS -------------------------------------------------------------------------- @@ -257,10 +537,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { parameters.pass = RenderPass::Translucent; MBGL_DEBUG_GROUP(parameters.context, "translucent"); - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", "translucent"); - } - uint32_t i = static_cast<uint32_t>(order.size()) - 1; for (auto it = order.begin(); it != order.end(); ++it, --i) { parameters.currentLayer = i; @@ -269,14 +545,8 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { it->layer.render(parameters, it->source); } } - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); - } } - if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; } - // - DEBUG PASS -------------------------------------------------------------------------------- // Renders debug overlays. { @@ -286,8 +556,10 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { // This guarantees that we have at least one function per tile called. // When only rendering layers via the stylesheet, it's possible that we don't // ever visit a tile during rendering. - for (const auto& source : sources) { - source->finishRender(parameters); + for (const auto& entry : renderSources) { + if (entry.second->isEnabled()) { + entry.second->finishRender(parameters); + } } } @@ -319,43 +591,159 @@ void Renderer::Impl::doRender(PaintParameters& parameters) { { MBGL_DEBUG_GROUP(parameters.context, "cleanup"); - parameters.context.activeTexture = 1; + parameters.context.activeTextureUnit = 1; parameters.context.texture[1] = 0; - parameters.context.activeTexture = 0; + parameters.context.activeTextureUnit = 0; parameters.context.texture[0] = 0; parameters.context.bindVertexArray = 0; } + + observer->onDidFinishRenderingFrame( + loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial, + updateParameters.mode == MapMode::Continuous && (hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION)) + ); + + if (!loaded) { + renderState = RenderState::Partial; + } else if (renderState != RenderState::Fully) { + renderState = RenderState::Fully; + observer->onDidFinishRenderingMap(); + } + + // Cleanup only after signaling completion + parameters.context.performCleanup(); } std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const { - return renderStyle->queryRenderedFeatures(geometry, transformState, options); + std::vector<const RenderLayer*> layers; + if (options.layerIDs) { + for (const auto& layerID : *options.layerIDs) { + if (const RenderLayer* layer = getRenderLayer(layerID)) { + layers.emplace_back(layer); + } + } + } else { + for (const auto& entry : renderLayers) { + layers.emplace_back(entry.second.get()); + } + } + + std::unordered_set<std::string> sourceIDs; + for (const RenderLayer* layer : layers) { + sourceIDs.emplace(layer->baseImpl->source); + } + + std::unordered_map<std::string, std::vector<Feature>> resultsByLayer; + for (const auto& sourceID : sourceIDs) { + if (RenderSource* renderSource = getRenderSource(sourceID)) { + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options); + std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); + } + } + + std::vector<Feature> result; + + if (resultsByLayer.empty()) { + return result; + } + + // Combine all results based on the style layer order. + for (const auto& layerImpl : *layerImpls) { + const RenderLayer* layer = getRenderLayer(layerImpl->id); + if (!layer->needsRendering(zoomHistory.lastZoom)) { + continue; + } + auto it = resultsByLayer.find(layer->baseImpl->id); + if (it != resultsByLayer.end()) { + std::move(it->second.begin(), it->second.end(), std::back_inserter(result)); + } + } + + return result; } std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const { - const RenderSource* source = renderStyle->getRenderSource(sourceID); + const RenderSource* source = getRenderSource(sourceID); if (!source) return {}; return source->querySourceFeatures(options); } -void Renderer::Impl::onInvalidate() { +void Renderer::Impl::onLowMemory() { + assert(BackendScope::exists()); + backend.getContext().performCleanup(); + for (const auto& entry : renderSources) { + entry.second->onLowMemory(); + } observer->onInvalidate(); } -void Renderer::Impl::onResourceError(std::exception_ptr ptr) { - observer->onResourceError(ptr); +void Renderer::Impl::dumDebugLogs() { + for (const auto& entry : renderSources) { + entry.second->dumpDebugLogs(); + } + + imageManager->dumpDebugLogs(); } -void Renderer::Impl::onLowMemory() { - BackendScope guard { backend }; - backend.getContext().performCleanup(); - renderStyle->onLowMemory(); - observer->onInvalidate(); +RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) { + auto it = renderLayers.find(id); + return it != renderLayers.end() ? it->second.get() : nullptr; } -void Renderer::Impl::dumDebugLogs() { - renderStyle->dumpDebugLogs(); +const RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) const { + auto it = renderLayers.find(id); + return it != renderLayers.end() ? it->second.get() : nullptr; +} + +RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const { + auto it = renderSources.find(id); + return it != renderSources.end() ? it->second.get() : nullptr; +} + +bool Renderer::Impl::hasTransitions() const { + if (renderLight.hasTransition()) { + return true; + } + + for (const auto& entry : renderLayers) { + if (entry.second->hasTransition()) { + return true; + } + } + + return false; +} + +bool Renderer::Impl::isLoaded() const { + for (const auto& entry: renderSources) { + if (!entry.second->isLoaded()) { + return false; + } + } + + if (!imageManager->isLoaded()) { + return false; + } + + return true; +} + +void Renderer::Impl::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 Renderer::Impl::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 Renderer::Impl::onTileChanged(RenderSource&, const OverscaledTileID&) { + observer->onInvalidate(); } } // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 079b00d0bb..30e7f70722 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -1,11 +1,16 @@ #pragma once #include <mbgl/renderer/renderer.hpp> -#include <mbgl/renderer/renderer_backend.hpp> -#include <mbgl/renderer/renderer_observer.hpp> -#include <mbgl/renderer/render_style_observer.hpp> +#include <mbgl/renderer/render_source_observer.hpp> +#include <mbgl/renderer/render_light.hpp> #include <mbgl/renderer/frame_history.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/map/zoom_history.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/text/glyph_manager_observer.hpp> #include <memory> #include <string> @@ -13,17 +18,31 @@ namespace mbgl { +class RendererBackend; +class RendererObserver; +class RenderSource; +class RenderLayer; class UpdateParameters; -class PaintParameters; -class RenderStyle; class RenderStaticData; - -class Renderer::Impl : public RenderStyleObserver { +class RenderedQueryOptions; +class SourceQueryOptions; +class FileSource; +class Scheduler; +class GlyphManager; +class ImageManager; +class LineAtlas; + +class Renderer::Impl : public GlyphManagerObserver, + public RenderSourceObserver{ public: Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode, const optional<std::string> programCacheDir); ~Impl() final; + void markContextLost() { + contextLost = true; + }; + void setObserver(RendererObserver*); void render(const UpdateParameters&); @@ -34,16 +53,28 @@ public: void onLowMemory(); void dumDebugLogs(); - // RenderStyleObserver implementation - void onInvalidate() override; - void onResourceError(std::exception_ptr) override; - private: - void doRender(PaintParameters&); + bool isLoaded() const; + bool hasTransitions() const; + + RenderSource* getRenderSource(const std::string& id) const; + + RenderLayer* getRenderLayer(const std::string& id); + const RenderLayer* getRenderLayer(const std::string& id) const; + + // GlyphManagerObserver implementation. + void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; + + // RenderSourceObserver implementation. + void onTileChanged(RenderSource&, const OverscaledTileID&) override; + void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; friend class Renderer; RendererBackend& backend; + Scheduler& scheduler; + FileSource& fileSource; + RendererObserver* observer; const GLContextMode contextMode; @@ -58,10 +89,23 @@ private: RenderState renderState = RenderState::Never; FrameHistory frameHistory; + ZoomHistory zoomHistory; TransformState transformState; - std::unique_ptr<RenderStyle> renderStyle; + std::unique_ptr<GlyphManager> glyphManager; + std::unique_ptr<ImageManager> imageManager; + std::unique_ptr<LineAtlas> lineAtlas; std::unique_ptr<RenderStaticData> staticData; + + Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls; + Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls; + Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls; + + std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources; + std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers; + RenderLight renderLight; + + bool contextLost = false; }; } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index df8bcc0ae7..504db78ea3 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -2,6 +2,7 @@ #include <mbgl/renderer/render_tile.hpp> #include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/tile/geojson_tile.hpp> +#include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> #include <mbgl/algorithm/generate_clip_ids_impl.hpp> @@ -34,19 +35,26 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_, GeoJSONData* data_ = impl().getData(); - if (!data_) { - return; - } - if (data_ != data) { data = data_; tilePyramid.cache.clear(); - for (auto const& item : tilePyramid.tiles) { - static_cast<GeoJSONTile*>(item.second.get())->updateData(data->getTile(item.first.canonical)); + if (data) { + const uint8_t maxZ = impl().getZoomRange().max; + for (const auto& pair : tilePyramid.tiles) { + if (pair.first.canonical.z <= maxZ) { + static_cast<GeoJSONTile*>(pair.second.get())->updateData(data->getTile(pair.first.canonical)); + } + } } } + if (!data) { + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + return; + } + tilePyramid.update(layers, needsRendering, needsRelayout, @@ -75,9 +83,9 @@ std::vector<std::reference_wrapper<RenderTile>> RenderGeoJSONSource::getRenderTi std::unordered_map<std::string, std::vector<Feature>> RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options); + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index b84156ab95..b2e06c68d4 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -30,7 +30,7 @@ public: std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> @@ -48,7 +48,7 @@ private: template <> inline bool RenderSource::is<RenderGeoJSONSource>() const { - return baseImpl->type == SourceType::GeoJSON; + return baseImpl->type == style::SourceType::GeoJSON; } } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index 50d5b17ec2..9140e01711 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -83,7 +83,7 @@ void RenderImageSource::finishRender(PaintParameters& parameters) { std::unordered_map<std::string, std::vector<Feature>> RenderImageSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, - const RenderStyle&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions&) const { return std::unordered_map<std::string, std::vector<Feature>> {}; } diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index fc1a462090..8d80838c3b 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -31,7 +31,7 @@ public: std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; @@ -52,7 +52,7 @@ private: template <> inline bool RenderSource::is<RenderImageSource>() const { - return baseImpl->type == SourceType::Image; + return baseImpl->type == style::SourceType::Image; } } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index cbd874f647..bcd719365d 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -73,7 +73,7 @@ std::vector<std::reference_wrapper<RenderTile>> RenderRasterSource::getRenderTil std::unordered_map<std::string, std::vector<Feature>> RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, - const RenderStyle&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions&) const { return std::unordered_map<std::string, std::vector<Feature>> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 1f4678da9f..e1bf5798ff 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -26,7 +26,7 @@ public: std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> @@ -44,7 +44,7 @@ private: template <> inline bool RenderSource::is<RenderRasterSource>() const { - return baseImpl->type == SourceType::Raster; + return baseImpl->type == style::SourceType::Raster; } } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index 47bccdaca8..ca3071c6b0 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -76,9 +76,9 @@ std::vector<std::reference_wrapper<RenderTile>> RenderVectorSource::getRenderTil std::unordered_map<std::string, std::vector<Feature>> RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options); + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 256ad4e800..ac319a167e 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -26,7 +26,7 @@ public: std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> @@ -44,7 +44,7 @@ private: template <> inline bool RenderSource::is<RenderVectorSource>() const { - return baseImpl->type == SourceType::Vector; + return baseImpl->type == style::SourceType::Vector; } } // namespace mbgl diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 219f154675..3e2311089d 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -121,7 +121,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer // we're actively using, e.g. as a replacement for tile that aren't loaded yet. std::set<OverscaledTileID> retain; - auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void { + auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void { if (retain.emplace(tile.id).second) { tile.setNecessity(necessity); } @@ -171,7 +171,26 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer cache.setSize(conservativeCacheSize); } - removeStaleTiles(retain); + // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep + // and removes items from tiles that don't have the corresponding key in the retain set. + { + auto tilesIt = tiles.begin(); + auto retainIt = retain.begin(); + while (tilesIt != tiles.end()) { + if (retainIt == retain.end() || tilesIt->first < *retainIt) { + if (!needsRelayout) { + tilesIt->second->setNecessity(TileNecessity::Optional); + cache.add(tilesIt->first, std::move(tilesIt->second)); + } + tiles.erase(tilesIt++); + } else { + if (!(*retainIt < tilesIt->first)) { + ++tilesIt; + } + ++retainIt; + } + } + } for (auto& pair : tiles) { const PlacementConfig config { parameters.transformState.getAngle(), @@ -184,29 +203,9 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer } } -// Moves all tiles to the cache except for those specified in the retain set. -void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) { - // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep - // and removes items from tiles that don't have the corresponding key in the retain set. - auto tilesIt = tiles.begin(); - auto retainIt = retain.begin(); - while (tilesIt != tiles.end()) { - if (retainIt == retain.end() || tilesIt->first < *retainIt) { - tilesIt->second->setNecessity(Tile::Necessity::Optional); - cache.add(tilesIt->first, std::move(tilesIt->second)); - tiles.erase(tilesIt++); - } else { - if (!(*retainIt < tilesIt->first)) { - ++tilesIt; - } - ++retainIt; - } - } -} - std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || geometry.empty()) { @@ -249,7 +248,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered renderTile.tile.queryRenderedFeatures(result, tileSpaceQueryGeometry, transformState, - style, + layers, options); } diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index d940f378fa..ac4572b103 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -21,7 +21,7 @@ namespace mbgl { class PaintParameters; class TransformState; class RenderTile; -class RenderStyle; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class TileParameters; @@ -37,7 +37,7 @@ public: bool needsRendering, bool needsRelayout, const TileParameters&, - SourceType type, + style::SourceType type, uint16_t tileSize, Range<uint8_t> zoomRange, std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); @@ -50,7 +50,7 @@ public: std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options) const; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const; @@ -63,8 +63,6 @@ public: bool enabled = false; - void removeStaleTiles(const std::set<OverscaledTileID>&); - std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles; TileCache cache; diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp index 181b11784d..b54abc050d 100644 --- a/src/mbgl/renderer/update_parameters.hpp +++ b/src/mbgl/renderer/update_parameters.hpp @@ -13,8 +13,6 @@ namespace mbgl { -class Scheduler; -class FileSource; class AnnotationManager; class UpdateParameters { @@ -34,8 +32,6 @@ public: const Immutable<std::vector<Immutable<style::Source::Impl>>> sources; const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers; - Scheduler& scheduler; - FileSource& fileSource; AnnotationManager& annotationManager; const uint8_t prefetchZoomDelta; diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp index f68cc91377..c700295a15 100644 --- a/src/mbgl/shaders/line.cpp +++ b/src/mbgl/shaders/line.cpp @@ -21,7 +21,7 @@ const char* line::vertexSource = R"MBGL_SHADER( // #define scale 63.0 #define scale 0.015873016 -attribute vec3 a_pos_normal; +attribute vec4 a_pos_normal; attribute vec4 a_data; uniform mat4 u_matrix; @@ -133,12 +133,9 @@ void main() { vec2 pos = a_pos_normal.xy; - // transform y normal so that 0 => -1 and 1 => 1 - // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap + // x is 1 if it's a round cap, 0 otherwise // y is 1 if the normal points up, and -1 if it points down - mediump vec2 normal = unpack_float(a_pos_normal.z); - normal.y = sign(normal.y - 0.5); - + mediump vec2 normal = a_pos_normal.zw; v_normal = normal; // these transformations used to be applied in the JS and native code bases. diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp index f1e64577e2..f8d785ade9 100644 --- a/src/mbgl/shaders/line_pattern.cpp +++ b/src/mbgl/shaders/line_pattern.cpp @@ -23,7 +23,7 @@ const char* line_pattern::vertexSource = R"MBGL_SHADER( // Retina devices need a smaller distance to avoid aliasing. #define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0 -attribute vec3 a_pos_normal; +attribute vec4 a_pos_normal; attribute vec4 a_data; uniform mat4 u_matrix; @@ -121,12 +121,9 @@ void main() { vec2 pos = a_pos_normal.xy; - // transform y normal so that 0 => -1 and 1 => 1 - // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap + // x is 1 if it's a round cap, 0 otherwise // y is 1 if the normal points up, and -1 if it points down - mediump vec2 normal = unpack_float(a_pos_normal.z); - normal.y = sign(normal.y - 0.5); - + mediump vec2 normal = a_pos_normal.zw; v_normal = normal; // these transformations used to be applied in the JS and native code bases. diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp index dd81433543..c5d50566e8 100644 --- a/src/mbgl/shaders/line_sdf.cpp +++ b/src/mbgl/shaders/line_sdf.cpp @@ -23,7 +23,7 @@ const char* line_sdf::vertexSource = R"MBGL_SHADER( // Retina devices need a smaller distance to avoid aliasing. #define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0 -attribute vec3 a_pos_normal; +attribute vec4 a_pos_normal; attribute vec4 a_data; uniform mat4 u_matrix; @@ -159,12 +159,9 @@ void main() { vec2 pos = a_pos_normal.xy; - // transform y normal so that 0 => -1 and 1 => 1 - // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap + // x is 1 if it's a round cap, 0 otherwise // y is 1 if the normal points up, and -1 if it points down - mediump vec2 normal = unpack_float(a_pos_normal.z); - normal.y = sign(normal.y - 0.5); - + mediump vec2 normal = a_pos_normal.zw; v_normal = normal; // these transformations used to be applied in the JS and native code bases. diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp index 94bba7f8bf..207dd2ee69 100644 --- a/src/mbgl/storage/resource.cpp +++ b/src/mbgl/storage/resource.cpp @@ -61,17 +61,19 @@ Resource Resource::image(const std::string& url) { } Resource Resource::spriteImage(const std::string& base, float pixelRatio) { - return Resource { - Resource::Kind::SpriteImage, - base + (pixelRatio > 1 ? "@2x" : "") + ".png" - }; + util::URL url(base); + return Resource{ Resource::Kind::SpriteImage, + base.substr(0, url.path.first + url.path.second) + + (pixelRatio > 1 ? "@2x" : "") + ".png" + + base.substr(url.query.first, url.query.second) }; } Resource Resource::spriteJSON(const std::string& base, float pixelRatio) { - return Resource { - Resource::Kind::SpriteJSON, - base + (pixelRatio > 1 ? "@2x" : "") + ".json" - }; + util::URL url(base); + return Resource{ Resource::Kind::SpriteJSON, + base.substr(0, url.path.first + url.path.second) + + (pixelRatio > 1 ? "@2x" : "") + ".json" + + base.substr(url.query.first, url.query.second) }; } Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange) { @@ -95,7 +97,7 @@ Resource Resource::tile(const std::string& urlTemplate, int32_t y, int8_t z, Tileset::Scheme scheme, - Necessity necessity) { + LoadingMethod loadingMethod) { bool supportsRatio = urlTemplate.find("{ratio}") != std::string::npos; if (scheme == Tileset::Scheme::TMS) { y = (1 << z) - y - 1; @@ -131,7 +133,7 @@ Resource Resource::tile(const std::string& urlTemplate, y, z }, - necessity + loadingMethod }; } diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp index 1a30a1f1c7..dd179f5376 100644 --- a/src/mbgl/style/function/categorical_stops.cpp +++ b/src/mbgl/style/function/categorical_stops.cpp @@ -34,7 +34,7 @@ template class CategoricalStops<std::array<float, 2>>; template class CategoricalStops<std::string>; template class CategoricalStops<TextTransformType>; template class CategoricalStops<TextJustifyType>; -template class CategoricalStops<TextAnchorType>; +template class CategoricalStops<SymbolAnchorType>; template class CategoricalStops<LineJoinType>; } // namespace style diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp index 7815f4aca0..0ac6fda846 100644 --- a/src/mbgl/style/function/identity_stops.cpp +++ b/src/mbgl/style/function/identity_stops.cpp @@ -50,12 +50,12 @@ optional<TextJustifyType> IdentityStops<TextJustifyType>::evaluate(const Value& } template <> -optional<TextAnchorType> IdentityStops<TextAnchorType>::evaluate(const Value& value) const { +optional<SymbolAnchorType> IdentityStops<SymbolAnchorType>::evaluate(const Value& value) const { if (!value.is<std::string>()) { return {}; } - return Enum<TextAnchorType>::toEnum(value.get<std::string>()); + return Enum<SymbolAnchorType>::toEnum(value.get<std::string>()); } template <> diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp index 75dc83206c..e439e42695 100644 --- a/src/mbgl/style/image_impl.hpp +++ b/src/mbgl/style/image_impl.hpp @@ -28,5 +28,6 @@ public: using ImageMap = std::unordered_map<std::string, Immutable<style::Image::Impl>>; using ImageDependencies = std::set<std::string>; +using ImageRequestPair = std::pair<ImageDependencies, uint64_t>; } // namespace mbgl diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp index e37382d5ef..854c771847 100644 --- a/src/mbgl/style/layers/custom_layer.cpp +++ b/src/mbgl/style/layers/custom_layer.cpp @@ -8,9 +8,18 @@ namespace style { CustomLayer::CustomLayer(const std::string& layerID, CustomLayerInitializeFunction init, CustomLayerRenderFunction render, + CustomLayerContextLostFunction contextLost, CustomLayerDeinitializeFunction deinit, void* context) - : Layer(makeMutable<Impl>(layerID, init, render, deinit, context)) { + : Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) { +} + +CustomLayer::CustomLayer(const std::string& layerID, + CustomLayerInitializeFunction init, + CustomLayerRenderFunction render, + CustomLayerDeinitializeFunction deinit, + void* context) + : Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) { } CustomLayer::~CustomLayer() = default; diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp index 42e60c582c..1de268d2e2 100644 --- a/src/mbgl/style/layers/custom_layer_impl.cpp +++ b/src/mbgl/style/layers/custom_layer_impl.cpp @@ -6,12 +6,14 @@ namespace style { CustomLayer::Impl::Impl(const std::string& id_, CustomLayerInitializeFunction initializeFn_, CustomLayerRenderFunction renderFn_, + CustomLayerContextLostFunction contextLostFn_, CustomLayerDeinitializeFunction deinitializeFn_, void* context_) : Layer::Impl(LayerType::Custom, id_, std::string()) { initializeFn = initializeFn_; renderFn = renderFn_; deinitializeFn = deinitializeFn_; + contextLostFn = contextLostFn_; context = context_; } diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp index defbbe6894..62efbbe15b 100644 --- a/src/mbgl/style/layers/custom_layer_impl.hpp +++ b/src/mbgl/style/layers/custom_layer_impl.hpp @@ -14,6 +14,7 @@ public: Impl(const std::string& id, CustomLayerInitializeFunction, CustomLayerRenderFunction, + CustomLayerContextLostFunction, CustomLayerDeinitializeFunction, void* context); @@ -22,6 +23,7 @@ public: CustomLayerInitializeFunction initializeFn = nullptr; CustomLayerRenderFunction renderFn = nullptr; + CustomLayerContextLostFunction contextLostFn = nullptr; CustomLayerDeinitializeFunction deinitializeFn = nullptr; void* context = nullptr; }; diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index 803ae7397e..9a944657ca 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -332,6 +332,22 @@ void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> va baseImpl = std::move(impl_); observer->onLayerChanged(*this); } +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultIconAnchor() { + return IconAnchor::defaultValue(); +} + +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getIconAnchor() const { + return impl().layout.get<IconAnchor>(); +} + +void SymbolLayer::setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) { + if (value == getIconAnchor()) + return; + auto impl_ = mutableImpl(); + impl_->layout.get<IconAnchor>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} PropertyValue<AlignmentType> SymbolLayer::getDefaultIconPitchAlignment() { return IconPitchAlignment::defaultValue(); } @@ -428,15 +444,15 @@ void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) { baseImpl = std::move(impl_); observer->onLayerChanged(*this); } -PropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() { return TextMaxWidth::defaultValue(); } -PropertyValue<float> SymbolLayer::getTextMaxWidth() const { +DataDrivenPropertyValue<float> SymbolLayer::getTextMaxWidth() const { return impl().layout.get<TextMaxWidth>(); } -void SymbolLayer::setTextMaxWidth(PropertyValue<float> value) { +void SymbolLayer::setTextMaxWidth(DataDrivenPropertyValue<float> value) { if (value == getTextMaxWidth()) return; auto impl_ = mutableImpl(); @@ -460,15 +476,15 @@ void SymbolLayer::setTextLineHeight(PropertyValue<float> value) { baseImpl = std::move(impl_); observer->onLayerChanged(*this); } -PropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() { return TextLetterSpacing::defaultValue(); } -PropertyValue<float> SymbolLayer::getTextLetterSpacing() const { +DataDrivenPropertyValue<float> SymbolLayer::getTextLetterSpacing() const { return impl().layout.get<TextLetterSpacing>(); } -void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) { +void SymbolLayer::setTextLetterSpacing(DataDrivenPropertyValue<float> value) { if (value == getTextLetterSpacing()) return; auto impl_ = mutableImpl(); @@ -492,15 +508,15 @@ void SymbolLayer::setTextJustify(DataDrivenPropertyValue<TextJustifyType> value) baseImpl = std::move(impl_); observer->onLayerChanged(*this); } -DataDrivenPropertyValue<TextAnchorType> SymbolLayer::getDefaultTextAnchor() { +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultTextAnchor() { return TextAnchor::defaultValue(); } -DataDrivenPropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const { +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getTextAnchor() const { return impl().layout.get<TextAnchor>(); } -void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<TextAnchorType> value) { +void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) { if (value == getTextAnchor()) return; auto impl_ = mutableImpl(); diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index fe6ab38e92..436b5cbd4f 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -87,6 +87,11 @@ struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> { static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; } }; +struct IconAnchor : DataDrivenLayoutProperty<SymbolAnchorType> { + static constexpr const char * key = "icon-anchor"; + static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; } +}; + struct IconPitchAlignment : LayoutProperty<AlignmentType> { static constexpr const char * key = "icon-pitch-alignment"; static AlignmentType defaultValue() { return AlignmentType::Auto; } @@ -117,7 +122,7 @@ struct TextSize : DataDrivenLayoutProperty<float> { static float defaultValue() { return 16; } }; -struct TextMaxWidth : LayoutProperty<float> { +struct TextMaxWidth : DataDrivenLayoutProperty<float> { static constexpr const char * key = "text-max-width"; static float defaultValue() { return 10; } }; @@ -127,7 +132,7 @@ struct TextLineHeight : LayoutProperty<float> { static float defaultValue() { return 1.2; } }; -struct TextLetterSpacing : LayoutProperty<float> { +struct TextLetterSpacing : DataDrivenLayoutProperty<float> { static constexpr const char * key = "text-letter-spacing"; static float defaultValue() { return 0; } }; @@ -137,9 +142,9 @@ struct TextJustify : DataDrivenLayoutProperty<TextJustifyType> { static TextJustifyType defaultValue() { return TextJustifyType::Center; } }; -struct TextAnchor : DataDrivenLayoutProperty<TextAnchorType> { +struct TextAnchor : DataDrivenLayoutProperty<SymbolAnchorType> { static constexpr const char * key = "text-anchor"; - static TextAnchorType defaultValue() { return TextAnchorType::Center; } + static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; } }; struct TextMaxAngle : LayoutProperty<float> { @@ -259,6 +264,7 @@ class SymbolLayoutProperties : public Properties< IconPadding, IconKeepUpright, IconOffset, + IconAnchor, IconPitchAlignment, TextPitchAlignment, TextRotationAlignment, diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp index ea19c599e9..cc6378b366 100644 --- a/src/mbgl/style/observer.hpp +++ b/src/mbgl/style/observer.hpp @@ -1,7 +1,6 @@ #pragma once #include <mbgl/style/source_observer.hpp> -#include <mbgl/map/update.hpp> #include <exception> @@ -12,7 +11,7 @@ class Observer : public SourceObserver { public: virtual void onStyleLoading() {} virtual void onStyleLoaded() {} - virtual void onUpdate(Update) {} + virtual void onUpdate() {} virtual void onStyleError(std::exception_ptr) {} virtual void onResourceError(std::exception_ptr) {} }; diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index efa4018b46..fd6d7d3013 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -71,7 +71,7 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON) GeoJSONSource::Impl::~Impl() = default; Range<uint8_t> GeoJSONSource::Impl::getZoomRange() const { - return { 0, options.maxzoom }; + return { options.minzoom, options.maxzoom }; } GeoJSONData* GeoJSONSource::Impl::getData() const { diff --git a/src/mbgl/style/sources/image_source.cpp b/src/mbgl/style/sources/image_source.cpp index 9b60ba1a48..fa268da0ef 100644 --- a/src/mbgl/style/sources/image_source.cpp +++ b/src/mbgl/style/sources/image_source.cpp @@ -59,7 +59,7 @@ void ImageSource::loadDescription(FileSource& fileSource) { if (req || loaded) { return; } - const Resource imageResource { Resource::Image, *url, {}, Resource::Necessity::Required }; + const Resource imageResource { Resource::Image, *url, {} }; req = fileSource.request(imageResource, [this](Response res) { if (res.error) { diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index 0fb49d1d22..37907d3f60 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -88,7 +88,7 @@ void Style::Impl::parse(const std::string& json_) { } mutated = false; - loaded = true; + loaded = false; json = json_; sources.clear(); @@ -118,6 +118,7 @@ void Style::Impl::parse(const std::string& json_) { spriteLoader->load(parser.spriteURL, scheduler, fileSource); glyphURL = parser.glyphURL; + loaded = true; observer->onStyleLoaded(); } @@ -203,7 +204,7 @@ Layer* Style::Impl::addLayer(std::unique_ptr<Layer> layer, optional<std::string> } layer->setObserver(this); - observer->onUpdate(Update::Repaint); + observer->onUpdate(); return layers.add(std::move(layer), before); } @@ -213,7 +214,7 @@ std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) { if (layer) { layer->setObserver(nullptr); - observer->onUpdate(Update::Repaint); + observer->onUpdate(); } return layer; @@ -288,13 +289,13 @@ void Style::Impl::setObserver(style::Observer* observer_) { void Style::Impl::onSourceLoaded(Source& source) { sources.update(source); observer->onSourceLoaded(source); - observer->onUpdate(Update::Repaint); + observer->onUpdate(); } void Style::Impl::onSourceChanged(Source& source) { sources.update(source); observer->onSourceChanged(source); - observer->onUpdate(Update::Repaint); + observer->onUpdate(); } void Style::Impl::onSourceError(Source& source, std::exception_ptr error) { @@ -318,7 +319,7 @@ void Style::Impl::onSpriteLoaded(std::vector<std::unique_ptr<Image>>&& images_) addImage(std::move(image)); } spriteLoaded = true; - observer->onUpdate(Update::Repaint); // For *-pattern properties. + observer->onUpdate(); // For *-pattern properties. } void Style::Impl::onSpriteError(std::exception_ptr error) { @@ -329,11 +330,11 @@ void Style::Impl::onSpriteError(std::exception_ptr error) { void Style::Impl::onLayerChanged(Layer& layer) { layers.update(layer); - observer->onUpdate(Update::Repaint); + observer->onUpdate(); } void Style::Impl::onLightChanged(const Light&) { - observer->onUpdate(Update::Repaint); + observer->onUpdate(); } void Style::Impl::dumpDebugLogs() const { diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp index 4fbf767e11..0a1781e01b 100644 --- a/src/mbgl/style/types.cpp +++ b/src/mbgl/style/types.cpp @@ -53,16 +53,16 @@ MBGL_DEFINE_ENUM(SymbolPlacementType, { { SymbolPlacementType::Line, "line" }, }); -MBGL_DEFINE_ENUM(TextAnchorType, { - { TextAnchorType::Center, "center" }, - { TextAnchorType::Left, "left" }, - { TextAnchorType::Right, "right" }, - { TextAnchorType::Top, "top" }, - { TextAnchorType::Bottom, "bottom" }, - { TextAnchorType::TopLeft, "top-left" }, - { TextAnchorType::TopRight, "top-right" }, - { TextAnchorType::BottomLeft, "bottom-left" }, - { TextAnchorType::BottomRight, "bottom-right" } +MBGL_DEFINE_ENUM(SymbolAnchorType, { + { SymbolAnchorType::Center, "center" }, + { SymbolAnchorType::Left, "left" }, + { SymbolAnchorType::Right, "right" }, + { SymbolAnchorType::Top, "top" }, + { SymbolAnchorType::Bottom, "bottom" }, + { SymbolAnchorType::TopLeft, "top-left" }, + { SymbolAnchorType::TopRight, "top-right" }, + { SymbolAnchorType::BottomLeft, "bottom-left" }, + { SymbolAnchorType::BottomRight, "bottom-right" } }); MBGL_DEFINE_ENUM(TextJustifyType, { diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index 916d39ae62..c79a1938c1 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -36,8 +36,9 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD for (const auto& range : ranges) { auto it = entry.ranges.find(range); if (it == entry.ranges.end() || !it->second.parsed) { - GlyphRequest& request = requestRange(entry, fontStack, range); + GlyphRequest& request = entry.ranges[range]; request.requestors[&requestor] = dependencies; + requestRange(request, fontStack, range); } } } @@ -49,18 +50,14 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD } } -GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) { - GlyphRequest& request = entry.ranges[range]; - +void GlyphManager::requestRange(GlyphRequest& request, const FontStack& fontStack, const GlyphRange& range) { if (request.req) { - return request; + return; } request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) { processResponse(res, fontStack, range); }); - - return request; } void GlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) { diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp index 00df079462..de2b9cde7b 100644 --- a/src/mbgl/text/glyph_manager.hpp +++ b/src/mbgl/text/glyph_manager.hpp @@ -58,7 +58,7 @@ private: std::unordered_map<FontStack, Entry, FontStackHash> entries; - GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&); + void requestRange(GlyphRequest&, const FontStack&, const GlyphRange&); void processResponse(const Response&, const FontStack&, const GlyphRange&); void notify(GlyphRequestor&, const GlyphDependencies&); diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp index 1e1279341d..48b24b5f41 100644 --- a/src/mbgl/text/placement_config.hpp +++ b/src/mbgl/text/placement_config.hpp @@ -13,9 +13,9 @@ public: bool operator==(const PlacementConfig& rhs) const { return angle == rhs.angle && pitch == rhs.pitch && - cameraToCenterDistance == rhs.cameraToCenterDistance && - (pitch * util::RAD2DEG < 25 || cameraToTileDistance == rhs.cameraToTileDistance) && - debug == rhs.debug; + debug == rhs.debug && + ((pitch * util::RAD2DEG < 25) || + (cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance)); } bool operator!=(const PlacementConfig& rhs) const { diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index a40ef0cf39..5d688ea539 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -11,12 +11,63 @@ namespace mbgl { -PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, const float iconRotation) { +struct AnchorAlignment { + AnchorAlignment(float horizontal_, float vertical_) + : horizontalAlign(horizontal_), verticalAlign(vertical_) { + } + + float horizontalAlign; + float verticalAlign; +}; + +AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) { + float horizontalAlign = 0.5; + float verticalAlign = 0.5; + + switch (anchor) { + case style::SymbolAnchorType::Top: + case style::SymbolAnchorType::Bottom: + case style::SymbolAnchorType::Center: + break; + case style::SymbolAnchorType::Right: + case style::SymbolAnchorType::TopRight: + case style::SymbolAnchorType::BottomRight: + horizontalAlign = 1; + break; + case style::SymbolAnchorType::Left: + case style::SymbolAnchorType::TopLeft: + case style::SymbolAnchorType::BottomLeft: + horizontalAlign = 0; + break; + } + + switch (anchor) { + case style::SymbolAnchorType::Left: + case style::SymbolAnchorType::Right: + case style::SymbolAnchorType::Center: + break; + case style::SymbolAnchorType::Bottom: + case style::SymbolAnchorType::BottomLeft: + case style::SymbolAnchorType::BottomRight: + verticalAlign = 1; + break; + case style::SymbolAnchorType::Top: + case style::SymbolAnchorType::TopLeft: + case style::SymbolAnchorType::TopRight: + verticalAlign = 0; + break; + } + + return AnchorAlignment(horizontalAlign, verticalAlign); +} + +PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) { + AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor); float dx = iconOffset[0]; float dy = iconOffset[1]; - float x1 = dx - image.displaySize()[0] / 2.0f; + float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign; float x2 = x1 + image.displaySize()[0]; - float y1 = dy - image.displaySize()[1] / 2.0f; + float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign; float y2 = y1 + image.displaySize()[1]; return PositionedIcon { image, y1, y2, x1, x2, iconRotation }; @@ -200,7 +251,7 @@ void shapeLines(Shaping& shaping, const std::vector<std::u16string>& lines, const float spacing, const float lineHeight, - const style::TextAnchorType textAnchor, + const style::SymbolAnchorType textAnchor, const style::TextJustifyType textJustify, const float verticalHeight, const WritingModeType writingMode, @@ -258,58 +309,23 @@ void shapeLines(Shaping& shaping, y += lineHeight; } - float horizontalAlign = 0.5; - float verticalAlign = 0.5; - - switch (textAnchor) { - case style::TextAnchorType::Top: - case style::TextAnchorType::Bottom: - case style::TextAnchorType::Center: - break; - case style::TextAnchorType::Right: - case style::TextAnchorType::TopRight: - case style::TextAnchorType::BottomRight: - horizontalAlign = 1; - break; - case style::TextAnchorType::Left: - case style::TextAnchorType::TopLeft: - case style::TextAnchorType::BottomLeft: - horizontalAlign = 0; - break; - } - - switch (textAnchor) { - case style::TextAnchorType::Left: - case style::TextAnchorType::Right: - case style::TextAnchorType::Center: - break; - case style::TextAnchorType::Bottom: - case style::TextAnchorType::BottomLeft: - case style::TextAnchorType::BottomRight: - verticalAlign = 1; - break; - case style::TextAnchorType::Top: - case style::TextAnchorType::TopLeft: - case style::TextAnchorType::TopRight: - verticalAlign = 0; - break; - } + auto anchorAlign = getAnchorAlignment(textAnchor); - align(shaping, justify, horizontalAlign, verticalAlign, - maxLineLength, lineHeight, lines.size()); + align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength, + lineHeight, lines.size()); const uint32_t height = lines.size() * lineHeight; // Calculate the bounding box - shaping.top += -verticalAlign * height; + shaping.top += -anchorAlign.verticalAlign * height; shaping.bottom = shaping.top + height; - shaping.left += -horizontalAlign * maxLineLength; + shaping.left += -anchorAlign.horizontalAlign * maxLineLength; shaping.right = shaping.left + maxLineLength; } const Shaping getShaping(const std::u16string& logicalInput, const float maxWidth, const float lineHeight, - const style::TextAnchorType textAnchor, + const style::SymbolAnchorType textAnchor, const style::TextJustifyType textJustify, const float spacing, const Point<float>& translate, diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index 00e4ec55f8..0a961849e5 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -32,7 +32,10 @@ private: float _angle; public: - static PositionedIcon shapeIcon(const ImagePosition&, const std::array<float, 2>& iconOffset, const float iconRotation); + static PositionedIcon shapeIcon(const ImagePosition&, + const std::array<float, 2>& iconOffset, + style::SymbolAnchorType iconAnchor, + const float iconRotation); const ImagePosition& image() const { return _image; } float top() const { return _top; } @@ -45,7 +48,7 @@ public: const Shaping getShaping(const std::u16string& string, float maxWidth, float lineHeight, - style::TextAnchorType textAnchor, + style::SymbolAnchorType textAnchor, style::TextJustifyType textJustify, float spacing, const Point<float>& translate, diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index 5d8339d775..d648d2e5ff 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -1,104 +1,11 @@ #include <mbgl/tile/geojson_tile.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/tile/geojson_tile_data.hpp> #include <mbgl/renderer/query.hpp> #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/util/string.hpp> - -#include <mapbox/geojsonvt.hpp> -#include <supercluster.hpp> namespace mbgl { -// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have -// one layer, and it is always returned regardless of which layer is requested. - -class GeoJSONTileFeature : public GeometryTileFeature { -public: - const mapbox::geometry::feature<int16_t>& feature; - - GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& feature_) - : feature(feature_) { - } - - FeatureType getType() const override { - return apply_visitor(ToFeatureType(), feature.geometry); - } - - PropertyMap getProperties() const override { - return feature.properties; - } - - optional<FeatureIdentifier> getID() const override { - return feature.id; - } - - GeometryCollection getGeometries() const override { - GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry); - - // https://github.com/mapbox/geojson-vt-cpp/issues/44 - if (getType() == FeatureType::Polygon) { - geometry = fixupPolygons(geometry); - } - - return geometry; - } - - optional<Value> getValue(const std::string& key) const override { - auto it = feature.properties.find(key); - if (it != feature.properties.end()) { - return optional<Value>(it->second); - } - return optional<Value>(); - } -}; - -class GeoJSONTileLayer : public GeometryTileLayer { -public: - GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_) - : features(std::move(features_)) { - } - - std::size_t featureCount() const override { - return features->size(); - } - - std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override { - return std::make_unique<GeoJSONTileFeature>((*features)[i]); - } - - std::string getName() const override { - return ""; - } - -private: - std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features; -}; - -class GeoJSONTileData : public GeometryTileData { -public: - GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_) - : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>( - std::move(features_))) { - } - - GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_) - : features(std::move(features_)) { - } - - std::unique_ptr<GeometryTileData> clone() const override { - return std::make_unique<GeoJSONTileData>(features); - } - - std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override { - return std::make_unique<GeoJSONTileLayer>(features); - } - - -private: - std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features; -}; - GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, std::string sourceID_, const TileParameters& parameters, @@ -110,8 +17,6 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, void GeoJSONTile::updateData(mapbox::geometry::feature_collection<int16_t> features) { setData(std::make_unique<GeoJSONTileData>(std::move(features))); } - -void GeoJSONTile::setNecessity(Necessity) {} void GeoJSONTile::querySourceFeatures( std::vector<Feature>& result, diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp index d8a0a379d7..270406267c 100644 --- a/src/mbgl/tile/geojson_tile.hpp +++ b/src/mbgl/tile/geojson_tile.hpp @@ -15,8 +15,6 @@ public: mapbox::geometry::feature_collection<int16_t>); void updateData(mapbox::geometry::feature_collection<int16_t>); - - void setNecessity(Necessity) final; void querySourceFeatures( std::vector<Feature>& result, diff --git a/src/mbgl/tile/geojson_tile_data.hpp b/src/mbgl/tile/geojson_tile_data.hpp new file mode 100644 index 0000000000..3402c2a009 --- /dev/null +++ b/src/mbgl/tile/geojson_tile_data.hpp @@ -0,0 +1,94 @@ +#include <mbgl/tile/geometry_tile_data.hpp> + +namespace mbgl { + +// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have +// one layer, and it is always returned regardless of which layer is requested. + +class GeoJSONTileFeature : public GeometryTileFeature { +public: + const mapbox::geometry::feature<int16_t>& feature; + + GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& feature_) + : feature(feature_) { + } + + FeatureType getType() const override { + return apply_visitor(ToFeatureType(), feature.geometry); + } + + PropertyMap getProperties() const override { + return feature.properties; + } + + optional<FeatureIdentifier> getID() const override { + return feature.id; + } + + GeometryCollection getGeometries() const override { + GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry); + + // https://github.com/mapbox/geojson-vt-cpp/issues/44 + if (getType() == FeatureType::Polygon) { + geometry = fixupPolygons(geometry); + } + + return geometry; + } + + optional<Value> getValue(const std::string& key) const override { + auto it = feature.properties.find(key); + if (it != feature.properties.end()) { + return optional<Value>(it->second); + } + return optional<Value>(); + } +}; + +class GeoJSONTileLayer : public GeometryTileLayer { +public: + GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_) + : features(std::move(features_)) { + } + + std::size_t featureCount() const override { + return features->size(); + } + + std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override { + return std::make_unique<GeoJSONTileFeature>((*features)[i]); + } + + std::string getName() const override { + return ""; + } + +private: + std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features; +}; + +class GeoJSONTileData : public GeometryTileData { +public: + GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_) + : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>( + std::move(features_))) { + } + + GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_) + : features(std::move(features_)) { + } + + std::unique_ptr<GeometryTileData> clone() const override { + return std::make_unique<GeoJSONTileData>(features); + } + + std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override { + return std::make_unique<GeoJSONTileLayer>(features); + } + + +private: + std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features; +}; + +} // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 20056e355d..8c018ce3aa 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -18,7 +18,6 @@ #include <mbgl/text/collision_tile.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/util/chrono.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/actor/scheduler.hpp> @@ -28,6 +27,22 @@ namespace mbgl { using namespace style; +/* + Correlation between GeometryTile and GeometryTileWorker is safeguarded by two + correlation schemes: + + GeometryTile's 'correlationID' is used for ensuring the tile will be flagged + as non-pending only when the placement coming from the last operation (as in + 'setData', 'setLayers', 'setPlacementConfig') occurs. This is important for + still mode rendering as we want to render only when all layout and placement + operations are completed. + + GeometryTileWorker's 'imageCorrelationID' is used for checking whether an + image request reply coming from `GeometryTile` is valid. Previous image + request replies are ignored as they result in incomplete placement attempts + that could flag the tile as non-pending too early. + */ + GeometryTile::GeometryTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters) @@ -42,8 +57,8 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_, parameters.pixelRatio), glyphManager(parameters.glyphManager), imageManager(parameters.imageManager), - placementThrottler(Milliseconds(300), [this] { invokePlacement(); }), - lastYStretch(1.0f) { + lastYStretch(1.0f), + mode(parameters.mode) { } GeometryTile::~GeometryTile() { @@ -62,7 +77,6 @@ void GeometryTile::markObsolete() { void GeometryTile::setError(std::exception_ptr err) { loaded = true; - renderable = false; observer->onTileError(*this, err); } @@ -86,7 +100,7 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) { ++correlationID; requestedConfig = desiredConfig; - placementThrottler.invoke(); + invokePlacement(); } void GeometryTile::invokePlacement() { @@ -120,9 +134,10 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID); } -void GeometryTile::onLayout(LayoutResult result) { +void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) { loaded = true; renderable = true; + (void)resultCorrelationID; nonSymbolBuckets = std::move(result.nonSymbolBuckets); featureIndex = std::move(result.featureIndex); data = std::move(result.tileData); @@ -130,10 +145,10 @@ void GeometryTile::onLayout(LayoutResult result) { observer->onTileChanged(*this); } -void GeometryTile::onPlacement(PlacementResult result) { +void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) { loaded = true; renderable = true; - if (result.correlationID == correlationID) { + if (resultCorrelationID == correlationID) { pending = false; } symbolBuckets = std::move(result.symbolBuckets); @@ -150,10 +165,11 @@ void GeometryTile::onPlacement(PlacementResult result) { observer->onTileChanged(*this); } -void GeometryTile::onError(std::exception_ptr err) { +void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) { loaded = true; - pending = false; - renderable = false; + if (resultCorrelationID == correlationID) { + pending = false; + } observer->onTileError(*this, err); } @@ -165,12 +181,12 @@ void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) { glyphManager.getGlyphs(*this, std::move(glyphDependencies)); } -void GeometryTile::onImagesAvailable(ImageMap images) { - worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images)); +void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) { + worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID); } -void GeometryTile::getImages(ImageDependencies imageDependencies) { - imageManager.getImages(*this, std::move(imageDependencies)); +void GeometryTile::getImages(ImageRequestPair pair) { + imageManager.getImages(*this, std::move(pair)); } void GeometryTile::upload(gl::Context& context) { @@ -214,11 +230,20 @@ void GeometryTile::queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) { if (!featureIndex || !data) return; + // Determine the additional radius needed factoring in property functions + float additionalRadius = 0; + for (const RenderLayer* layer : layers) { + auto bucket = getBucket(*layer->baseImpl); + if (bucket) { + additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer)); + } + } + featureIndex->query(result, queryGeometry, transformState.getAngle(), @@ -227,9 +252,9 @@ void GeometryTile::queryRenderedFeatures( options, *data, id.canonical, - style, + layers, collisionTile.get(), - *this); + additionalRadius); } void GeometryTile::querySourceFeatures( diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index c45762742b..a478aad504 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -19,7 +19,6 @@ namespace mbgl { class GeometryTileData; -class RenderStyle; class RenderLayer; class SourceQueryOptions; class TileParameters; @@ -41,10 +40,10 @@ public: void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override; void onGlyphsAvailable(GlyphMap) override; - void onImagesAvailable(ImageMap) override; + void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override; void getGlyphs(GlyphDependencies); - void getImages(ImageDependencies); + void getImages(ImageRequestPair); void upload(gl::Context&) override; Bucket* getBucket(const style::Layer::Impl&) const override; @@ -56,7 +55,7 @@ public: std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState&, - const RenderStyle&, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) override; void querySourceFeatures( @@ -70,18 +69,15 @@ public: std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets; std::unique_ptr<FeatureIndex> featureIndex; std::unique_ptr<GeometryTileData> tileData; - uint64_t correlationID; LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_, std::unique_ptr<FeatureIndex> featureIndex_, - std::unique_ptr<GeometryTileData> tileData_, - uint64_t correlationID_) + std::unique_ptr<GeometryTileData> tileData_) : nonSymbolBuckets(std::move(nonSymbolBuckets_)), featureIndex(std::move(featureIndex_)), - tileData(std::move(tileData_)), - correlationID(correlationID_) {} + tileData(std::move(tileData_)) {} }; - void onLayout(LayoutResult); + void onLayout(LayoutResult, uint64_t correlationID); class PlacementResult { public: @@ -89,22 +85,19 @@ public: std::unique_ptr<CollisionTile> collisionTile; optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; - uint64_t correlationID; PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_, std::unique_ptr<CollisionTile> collisionTile_, optional<AlphaImage> glyphAtlasImage_, - optional<PremultipliedImage> iconAtlasImage_, - uint64_t correlationID_) + optional<PremultipliedImage> iconAtlasImage_) : symbolBuckets(std::move(symbolBuckets_)), collisionTile(std::move(collisionTile_)), glyphAtlasImage(std::move(glyphAtlasImage_)), - iconAtlasImage(std::move(iconAtlasImage_)), - correlationID(correlationID_) {} + iconAtlasImage(std::move(iconAtlasImage_)) {} }; - void onPlacement(PlacementResult); + void onPlacement(PlacementResult, uint64_t correlationID); - void onError(std::exception_ptr); + void onError(std::exception_ptr, uint64_t correlationID); float yStretch() const override; @@ -140,9 +133,9 @@ private: std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; std::unique_ptr<CollisionTile> collisionTile; - - util::Throttler placementThrottler; + float lastYStretch; + const MapMode mode; public: optional<gl::Texture> glyphAtlasTexture; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index add1ea343c..50429420c3 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -88,7 +88,7 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, break; } } catch (...) { - parent.invoke(&GeometryTile::onError, std::current_exception()); + parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID); } } @@ -112,7 +112,7 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_, break; } } catch (...) { - parent.invoke(&GeometryTile::onError, std::current_exception()); + parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID); } } @@ -136,7 +136,7 @@ void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, ui break; } } catch (...) { - parent.invoke(&GeometryTile::onError, std::current_exception()); + parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID); } } @@ -161,7 +161,7 @@ void GeometryTileWorker::symbolDependenciesChanged() { break; } } catch (...) { - parent.invoke(&GeometryTile::onError, std::current_exception()); + parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID); } } @@ -187,7 +187,7 @@ void GeometryTileWorker::coalesced() { break; } } catch (...) { - parent.invoke(&GeometryTile::onError, std::current_exception()); + parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID); } } @@ -216,14 +216,12 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) { symbolDependenciesChanged(); } -void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap) { - imageMap = std::move(newImageMap); - for (const auto& pair : imageMap) { - auto it = pendingImageDependencies.find(pair.first); - if (it != pendingImageDependencies.end()) { - pendingImageDependencies.erase(it); - } +void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) { + if (imageCorrelationID != imageCorrelationID_) { + return; // Ignore outdated image request replies. } + imageMap = std::move(newImageMap); + pendingImageDependencies.clear(); symbolDependenciesChanged(); } @@ -244,7 +242,7 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) { pendingImageDependencies = imageDependencies; if (!pendingImageDependencies.empty()) { - parent.invoke(&GeometryTile::getImages, pendingImageDependencies); + parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID)); } } @@ -359,8 +357,7 @@ void GeometryTileWorker::redoLayout() { std::move(buckets), std::move(featureIndex), *data ? (*data)->clone() : nullptr, - correlationID - }); + }, correlationID); attemptPlacement(); } @@ -424,8 +421,7 @@ void GeometryTileWorker::attemptPlacement() { std::move(collisionTile), std::move(glyphAtlasImage), std::move(iconAtlasImage), - correlationID - }); + }, correlationID); } } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp index 7f80c3b4f7..1425daa7a1 100644 --- a/src/mbgl/tile/geometry_tile_worker.hpp +++ b/src/mbgl/tile/geometry_tile_worker.hpp @@ -38,7 +38,7 @@ public: void setPlacementConfig(PlacementConfig, uint64_t correlationID); void onGlyphsAvailable(GlyphMap glyphs); - void onImagesAvailable(ImageMap images); + void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID); private: void coalesced(); @@ -70,6 +70,7 @@ private: State state = Idle; uint64_t correlationID = 0; + uint64_t imageCorrelationID = 0; // Outer optional indicates whether we've received it or not. optional<std::vector<Immutable<style::Layer::Impl>>> layers; diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index b940e342d7..85fcea77b7 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -29,29 +29,35 @@ void RasterTile::cancel() { void RasterTile::setError(std::exception_ptr err) { loaded = true; - renderable = false; observer->onTileError(*this, err); } -void RasterTile::setData(std::shared_ptr<const std::string> data, - optional<Timestamp> modified_, - optional<Timestamp> expires_) { +void RasterTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) { modified = modified_; expires = expires_; - worker.invoke(&RasterTileWorker::parse, data); } -void RasterTile::onParsed(std::unique_ptr<RasterBucket> result) { +void RasterTile::setData(std::shared_ptr<const std::string> data) { + pending = true; + ++correlationID; + worker.invoke(&RasterTileWorker::parse, data, correlationID); +} + +void RasterTile::onParsed(std::unique_ptr<RasterBucket> result, const uint64_t resultCorrelationID) { bucket = std::move(result); loaded = true; + if (resultCorrelationID == correlationID) { + pending = false; + } renderable = bucket ? true : false; observer->onTileChanged(*this); } -void RasterTile::onError(std::exception_ptr err) { - bucket.reset(); +void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) { loaded = true; - renderable = false; + if (resultCorrelationID == correlationID) { + pending = false; + } observer->onTileError(*this, err); } @@ -71,7 +77,7 @@ void RasterTile::setMask(TileMask&& mask) { } } -void RasterTile::setNecessity(Necessity necessity) { +void RasterTile::setNecessity(TileNecessity necessity) { loader.setNecessity(necessity); } diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index 28a27b2b37..192769ed8f 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -22,12 +22,11 @@ public: const Tileset&); ~RasterTile() final; - void setNecessity(Necessity) final; + void setNecessity(TileNecessity) final; void setError(std::exception_ptr); - void setData(std::shared_ptr<const std::string> data, - optional<Timestamp> modified_, - optional<Timestamp> expires_); + void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); + void setData(std::shared_ptr<const std::string> data); void cancel() override; @@ -36,8 +35,8 @@ public: void setMask(TileMask&&) override; - void onParsed(std::unique_ptr<RasterBucket> result); - void onError(std::exception_ptr); + void onParsed(std::unique_ptr<RasterBucket> result, uint64_t correlationID); + void onError(std::exception_ptr, uint64_t correlationID); private: TileLoader<RasterTile> loader; @@ -45,6 +44,8 @@ private: std::shared_ptr<Mailbox> mailbox; Actor<RasterTileWorker> worker; + uint64_t correlationID = 0; + // Contains the Bucket object for the tile. Buckets are render // objects and they get added by tile parsing operations. std::unique_ptr<RasterBucket> bucket; diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp index 3c8af97b40..4afa876429 100644 --- a/src/mbgl/tile/raster_tile_worker.cpp +++ b/src/mbgl/tile/raster_tile_worker.cpp @@ -10,17 +10,17 @@ RasterTileWorker::RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTi : parent(std::move(parent_)) { } -void RasterTileWorker::parse(std::shared_ptr<const std::string> data) { +void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID) { if (!data) { - parent.invoke(&RasterTile::onParsed, nullptr); // No data; empty tile. + parent.invoke(&RasterTile::onParsed, nullptr, correlationID); // No data; empty tile. return; } try { auto bucket = std::make_unique<RasterBucket>(decodeImage(*data)); - parent.invoke(&RasterTile::onParsed, std::move(bucket)); + parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID); } catch (...) { - parent.invoke(&RasterTile::onError, std::current_exception()); + parent.invoke(&RasterTile::onError, std::current_exception(), correlationID); } } diff --git a/src/mbgl/tile/raster_tile_worker.hpp b/src/mbgl/tile/raster_tile_worker.hpp index 44bc37ca5d..520973c3c3 100644 --- a/src/mbgl/tile/raster_tile_worker.hpp +++ b/src/mbgl/tile/raster_tile_worker.hpp @@ -13,7 +13,7 @@ class RasterTileWorker { public: RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile>); - void parse(std::shared_ptr<const std::string> data); + void parse(std::shared_ptr<const std::string> data, uint64_t correlationID); private: ActorRef<RasterTile> parent; diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 35fc31dae1..f36a472e72 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -18,7 +18,7 @@ void Tile::setObserver(TileObserver* observer_) { observer = observer_; } -void Tile::setTriedOptional() { +void Tile::setTriedCache() { triedOptional = true; observer->onTileChanged(*this); } @@ -33,7 +33,7 @@ void Tile::queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>&, const GeometryCoordinates&, const TransformState&, - const RenderStyle&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions&) {} void Tile::querySourceFeatures( diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index a1ab6a84b7..8be7c4d862 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -6,6 +6,7 @@ #include <mbgl/util/feature.hpp> #include <mbgl/util/tile_coordinate.hpp> #include <mbgl/tile/tile_id.hpp> +#include <mbgl/tile/tile_necessity.hpp> #include <mbgl/renderer/tile_mask.hpp> #include <mbgl/renderer/bucket.hpp> #include <mbgl/tile/geometry_tile_data.hpp> @@ -23,7 +24,7 @@ class DebugBucket; class TransformState; class TileObserver; class PlacementConfig; -class RenderStyle; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; @@ -38,14 +39,7 @@ public: void setObserver(TileObserver* observer); - // Tiles can have two states: optional or required. - // - optional means that only low-cost actions should be taken to obtain the data - // (e.g. load from cache, but accept stale data) - // - required means that every effort should be taken to obtain the data (e.g. load - // from internet and keep the data fresh if it expires) - using Necessity = Resource::Necessity; - - virtual void setNecessity(Necessity) = 0; + virtual void setNecessity(TileNecessity) {} // Mark this tile as no longer needed and cancel any pending work. virtual void cancel() = 0; @@ -61,18 +55,18 @@ public: std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState&, - const RenderStyle&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options); virtual void querySourceFeatures( std::vector<Feature>& result, const SourceQueryOptions&); - void setTriedOptional(); + void setTriedCache(); // 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 { + bool hasTriedCache() const { return triedOptional; } diff --git a/src/mbgl/tile/tile_id_hash.cpp b/src/mbgl/tile/tile_id_hash.cpp new file mode 100644 index 0000000000..4a1f185817 --- /dev/null +++ b/src/mbgl/tile/tile_id_hash.cpp @@ -0,0 +1,29 @@ +#include <mbgl/tile/tile_id.hpp> + +#include <boost/functional/hash.hpp> + +namespace std { + +size_t hash<mbgl::CanonicalTileID>::operator()(const mbgl::CanonicalTileID& id) const { + std::size_t seed = 0; + boost::hash_combine(seed, id.x); + boost::hash_combine(seed, id.y); + boost::hash_combine(seed, id.z); + return seed; +} + +size_t hash<mbgl::UnwrappedTileID>::operator()(const mbgl::UnwrappedTileID& id) const { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical)); + boost::hash_combine(seed, id.wrap); + return seed; +} + +size_t hash<mbgl::OverscaledTileID>::operator()(const mbgl::OverscaledTileID& id) const { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical)); + boost::hash_combine(seed, id.overscaledZ); + return seed; +} + +} // namespace std diff --git a/src/mbgl/tile/tile_loader.hpp b/src/mbgl/tile/tile_loader.hpp index bc408ebaf6..92ca74330f 100644 --- a/src/mbgl/tile/tile_loader.hpp +++ b/src/mbgl/tile/tile_loader.hpp @@ -21,12 +21,10 @@ public: const Tileset&); ~TileLoader(); - using Necessity = Resource::Necessity; - - void setNecessity(Necessity newNecessity) { + void setNecessity(TileNecessity newNecessity) { if (newNecessity != necessity) { necessity = newNecessity; - if (necessity == Necessity::Required) { + if (necessity == TileNecessity::Required) { makeRequired(); } else { makeOptional(); @@ -45,12 +43,12 @@ private: // an up-to-date version or load new data void makeOptional(); - void loadOptional(); + void loadFromCache(); void loadedData(const Response&); - void loadRequired(); + void loadFromNetwork(); T& tile; - Necessity necessity; + TileNecessity necessity; Resource resource; FileSource& fileSource; std::unique_ptr<AsyncRequest> request; diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp index 899cbaf9b0..1b29638269 100644 --- a/src/mbgl/tile/tile_loader_impl.hpp +++ b/src/mbgl/tile/tile_loader_impl.hpp @@ -15,32 +15,31 @@ TileLoader<T>::TileLoader(T& tile_, const TileParameters& parameters, const Tileset& tileset) : tile(tile_), - necessity(Necessity::Optional), + necessity(TileNecessity::Optional), resource(Resource::tile( tileset.tiles.at(0), parameters.pixelRatio, id.canonical.x, id.canonical.y, id.canonical.z, - tileset.scheme)), + tileset.scheme, + Resource::LoadingMethod::CacheOnly)), fileSource(parameters.fileSource) { assert(!request); - if (fileSource.supportsOptionalRequests()) { + if (fileSource.supportsCacheOnlyRequests()) { // When supported, the first request is always optional, even if the TileLoader // is marked as required. That way, we can let the first optional request continue // to load when the TileLoader is later changed from required to optional. If we // started out with a required request, we'd have to cancel everything, including the // initial optional part of the request. - loadOptional(); + loadFromCache(); + } else if (necessity == TileNecessity::Required) { + // When the file source doesn't support cache-only requests, and we definiitely need this + // data, we can start out with a network request immediately. + loadFromNetwork(); } else { - // When the FileSource doesn't support optional requests, we do nothing until the + // When the FileSource doesn't support cache-only requests, we do nothing until the // data is definitely required. - if (necessity == Necessity::Required) { - loadRequired(); - } else { - // We're using this field to check whether the pending request is optional or required. - resource.necessity = Resource::Optional; - } } } @@ -48,26 +47,31 @@ template <typename T> TileLoader<T>::~TileLoader() = default; template <typename T> -void TileLoader<T>::loadOptional() { +void TileLoader<T>::loadFromCache() { assert(!request); - resource.necessity = Resource::Optional; + resource.loadingMethod = Resource::LoadingMethod::CacheOnly; request = fileSource.request(resource, [this](Response res) { request.reset(); - tile.setTriedOptional(); + tile.setTriedCache(); if (res.error && res.error->reason == Response::Error::Reason::NotFound) { - // 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 - // request before by setting one of the prior* fields. - resource.priorExpires = Timestamp{ Seconds::zero() }; + // When the cache-only request could not be satisfied, don't treat it as an error. + // A cache lookup could still return data, _and_ an error, in particular when we were + // able to find the data, but it is expired and the Cache-Control headers indicated that + // we aren't allowed to use expired responses. In this case, we still get the data which + // we can use in our conditional network request. + resource.priorModified = res.modified; + resource.priorExpires = res.expires; + resource.priorEtag = res.etag; + resource.priorData = res.data; } else { loadedData(res); } - if (necessity == Necessity::Required) { - loadRequired(); + if (necessity == TileNecessity::Required) { + loadFromNetwork(); } }); } @@ -75,14 +79,15 @@ void TileLoader<T>::loadOptional() { template <typename T> void TileLoader<T>::makeRequired() { if (!request) { - loadRequired(); + loadFromNetwork(); } } template <typename T> void TileLoader<T>::makeOptional() { - if (resource.necessity == Resource::Required && request) { - // Abort a potential HTTP request. + if (resource.loadingMethod == Resource::LoadingMethod::NetworkOnly && request) { + // Abort the current request, but only when we know that we're specifically querying for a + // network resource only. request.reset(); } } @@ -95,19 +100,23 @@ void TileLoader<T>::loadedData(const Response& res) { resource.priorExpires = res.expires; // Do not notify the tile; when we get this message, it already has the current // version of the data. + tile.setMetadata(res.modified, res.expires); } else { resource.priorModified = res.modified; resource.priorExpires = res.expires; resource.priorEtag = res.etag; - tile.setData(res.noContent ? nullptr : res.data, res.modified, res.expires); + tile.setMetadata(res.modified, res.expires); + tile.setData(res.noContent ? nullptr : res.data); } } template <typename T> -void TileLoader<T>::loadRequired() { +void TileLoader<T>::loadFromNetwork() { assert(!request); - resource.necessity = Resource::Required; + // Instead of using Resource::LoadingMethod::All, we're first doing a CacheOnly, and then a + // NetworkOnly request. + resource.loadingMethod = Resource::LoadingMethod::NetworkOnly; request = fileSource.request(resource, [this](Response res) { loadedData(res); }); } diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index e2e700b7b7..0756d3e526 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -12,16 +12,16 @@ VectorTile::VectorTile(const OverscaledTileID& id_, : GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) { } -void VectorTile::setNecessity(Necessity necessity) { +void VectorTile::setNecessity(TileNecessity necessity) { loader.setNecessity(necessity); } -void VectorTile::setData(std::shared_ptr<const std::string> data_, - optional<Timestamp> modified_, - optional<Timestamp> expires_) { +void VectorTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) { modified = modified_; expires = expires_; +} +void VectorTile::setData(std::shared_ptr<const std::string> data_) { GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr); } diff --git a/src/mbgl/tile/vector_tile.hpp b/src/mbgl/tile/vector_tile.hpp index 566cde4f37..7dae414fef 100644 --- a/src/mbgl/tile/vector_tile.hpp +++ b/src/mbgl/tile/vector_tile.hpp @@ -15,10 +15,9 @@ public: const TileParameters&, const Tileset&); - void setNecessity(Necessity) final; - void setData(std::shared_ptr<const std::string> data, - optional<Timestamp> modified, - optional<Timestamp> expires); + void setNecessity(TileNecessity) final; + void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); + void setData(std::shared_ptr<const std::string> data); private: TileLoader<VectorTile> loader; diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp index 9faef140ef..56f78c9885 100644 --- a/src/mbgl/util/constants.cpp +++ b/src/mbgl/util/constants.cpp @@ -11,7 +11,6 @@ const bool tileParseWarnings = false; const bool styleParseWarnings = false; const bool spriteWarnings = false; const bool renderWarnings = false; -const bool renderTree = false; const bool labelTextMissingWarning = true; const bool missingFontStackWarning = true; const bool missingFontFaceWarning = true; @@ -22,7 +21,6 @@ const bool tileParseWarnings = false; const bool styleParseWarnings = false; const bool spriteWarnings = false; const bool renderWarnings = false; -const bool renderTree = false; const bool labelTextMissingWarning = false; const bool missingFontStackWarning = false; const bool missingFontFaceWarning = false; diff --git a/src/mbgl/util/http_timeout.cpp b/src/mbgl/util/http_timeout.cpp index ca9a93498f..3456369250 100644 --- a/src/mbgl/util/http_timeout.cpp +++ b/src/mbgl/util/http_timeout.cpp @@ -1,6 +1,8 @@ #include <mbgl/util/http_timeout.hpp> #include <mbgl/util/constants.hpp> +#include <cassert> + namespace mbgl { namespace http { diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 16f1d669f3..3e3a68e248 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -554,7 +554,7 @@ std::u16string verticalizePunctuation(const std::u16string& input) { std::u16string output; for (size_t i = 0; i < input.size(); i++) { - char16_t nextCharCode = i < input.size() ? input[i + 1] : 0; + char16_t nextCharCode = i < input.size() - 1 ? input[i + 1] : 0; char16_t prevCharCode = i ? input[i - 1] : 0; bool canReplacePunctuation = diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp index 8cbc85d492..802b527a26 100644 --- a/src/mbgl/util/mapbox.cpp +++ b/src/mbgl/util/mapbox.cpp @@ -114,7 +114,7 @@ std::string normalizeTileURL(const std::string& baseURL, } std::string -canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_t tileSize) { +canonicalizeTileURL(const std::string& str, const style::SourceType type, const uint16_t tileSize) { const char* version = "/v4/"; const size_t versionLen = strlen(version); @@ -133,7 +133,7 @@ canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_ std::string result = "mapbox://tiles/"; result.append(str, path.directory.first + versionLen, path.directory.second - versionLen); result.append(str, path.filename.first, path.filename.second); - if (type == SourceType::Raster) { + if (type == style::SourceType::Raster) { result += tileSize == util::tileSize ? "@2x" : "{ratio}"; } @@ -171,7 +171,7 @@ canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_ return result; } -void canonicalizeTileset(Tileset& tileset, const std::string& sourceURL, SourceType type, uint16_t tileSize) { +void canonicalizeTileset(Tileset& tileset, const std::string& sourceURL, style::SourceType type, uint16_t tileSize) { // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with. if (isMapboxURL(sourceURL)) { for (auto& url : tileset.tiles) { diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp index f3dfdd0b01..aa128f2667 100644 --- a/src/mbgl/util/mapbox.hpp +++ b/src/mbgl/util/mapbox.hpp @@ -19,10 +19,10 @@ std::string normalizeGlyphsURL(const std::string& baseURL, const std::string& ur std::string normalizeTileURL(const std::string& baseURL, const std::string& url, const std::string& accessToken); // Return a "mapbox://tiles/..." URL (suitable for normalizeTileURL) for the given Mapbox tile URL. -std::string canonicalizeTileURL(const std::string& url, SourceType, uint16_t tileSize); +std::string canonicalizeTileURL(const std::string& url, style::SourceType, uint16_t tileSize); // Replace URL templates with "mapbox://tiles/..." URLs (suitable for normalizeTileURL). -void canonicalizeTileset(Tileset&, const std::string& url, SourceType, uint16_t tileSize); +void canonicalizeTileset(Tileset&, const std::string& url, style::SourceType, uint16_t tileSize); extern const uint64_t DEFAULT_OFFLINE_TILE_COUNT_LIMIT; diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp index fcde01c1a3..c18ce0c254 100644 --- a/src/mbgl/util/math.hpp +++ b/src/mbgl/util/math.hpp @@ -77,9 +77,9 @@ T mag(const S& a) { return std::sqrt(a.x * a.x + a.y * a.y); } -template <typename S> +template <typename T = double, typename S> S unit(const S& a) { - auto magnitude = mag(a); + auto magnitude = mag<T>(a); if (magnitude == 0) { return a; } diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index 77a1416e48..339e74b250 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -11,20 +11,22 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default; class OffscreenTexture::Impl { public: - Impl(gl::Context& context_, const Size size_, OffscreenTextureAttachment type_) - : context(context_), size(std::move(size_)), type(type_) { + Impl(gl::Context& context_, const Size size_) + : context(context_), size(std::move(size_)) { + assert(!size.isEmpty()); + } + Impl(gl::Context& context_, + const Size size_, + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_) + : context(context_), size(std::move(size_)), depth(&depth_) { assert(!size.isEmpty()); } void bind() { if (!framebuffer) { texture = context.createTexture(size, gl::TextureFormat::RGBA); - - if (type == OffscreenTextureAttachment::Depth) { - gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depth = - context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size); - framebuffer = context.createFramebuffer(*texture, depth); - + if (depth) { + framebuffer = context.createFramebuffer(*texture, *depth); } else { framebuffer = context.createFramebuffer(*texture); } @@ -32,7 +34,7 @@ public: context.bindFramebuffer = framebuffer->framebuffer; } - context.activeTexture = 0; + context.activeTextureUnit = 0; context.scissorTest = false; context.viewport = { 0, 0, size }; } @@ -53,15 +55,21 @@ public: private: gl::Context& context; const Size size; - OffscreenTextureAttachment type; optional<gl::Framebuffer> framebuffer; optional<gl::Texture> texture; + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr; }; OffscreenTexture::OffscreenTexture(gl::Context& context, + const Size size) + : impl(std::make_unique<Impl>(context, std::move(size))) { + assert(!size.isEmpty()); +} + +OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size, - OffscreenTextureAttachment type) - : impl(std::make_unique<Impl>(context, std::move(size), type)) { + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer) + : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) { assert(!size.isEmpty()); } diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index 0353f3f9c5..7f7e0f0338 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -9,16 +9,13 @@ class Context; class Texture; } // namespace gl -enum class OffscreenTextureAttachment { - None, - Depth, -}; - class OffscreenTexture { public: OffscreenTexture(gl::Context&, - Size size = { 256, 256 }, - OffscreenTextureAttachment type = OffscreenTextureAttachment::None); + Size size = { 256, 256 }); + OffscreenTexture(gl::Context&, + Size size, + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&); ~OffscreenTexture(); OffscreenTexture(OffscreenTexture&&); OffscreenTexture& operator=(OffscreenTexture&&); diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index b53e91162c..c81becb986 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -126,9 +126,9 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl, } // namespace -int32_t coveringZoomLevel(double zoom, SourceType type, uint16_t size) { +int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) { zoom += std::log(util::tileSize / size) / std::log(2); - if (type == SourceType::Raster || type == SourceType::Video) { + if (type == style::SourceType::Raster || type == style::SourceType::Video) { return ::round(zoom); } else { return std::floor(zoom); @@ -169,5 +169,26 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) { z); } +// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs +// Computes the projected tiles for the lower left and upper right points of the bounds +// and uses that to compute the tile cover count +uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){ + + auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_); + auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_); + + auto x1 = floor(sw.x/ tileSize_); + auto x2 = floor((ne.x - 1) / tileSize_); + auto y1 = floor(sw.y/ tileSize_); + auto y2 = floor((ne.y - 1) / tileSize_); + + auto minX = std::fmax(std::min(x1, x2), 0); + auto maxX = std::max(x1, x2); + auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); + auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0); + + return (maxX - minX + 1) * (maxY - minY + 1); +} + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index 2d32d8bf41..b2098b59b8 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -13,10 +13,13 @@ class LatLngBounds; namespace util { -int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize); +int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize); std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z); std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z); +// Compute only the count of tiles needed for tileCover +uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize); + } // namespace util } // namespace mbgl |