diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2018-04-23 10:44:02 -0400 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2018-04-23 10:44:02 -0400 |
commit | e08b6fe87f5824ab05a4cc67d9a76af5bb5ddd3b (patch) | |
tree | 886e10260bfa044f62943186ec837b9ccd02934c /src/mbgl | |
parent | 2bb785dad2489d04db179fa9cf65514640db0a96 (diff) | |
parent | a45670cfb5752866b9c8130024a313944684c2db (diff) | |
download | qtlocation-mapboxgl-e08b6fe87f5824ab05a4cc67d9a76af5bb5ddd3b.tar.gz |
Merge branch 'release-boba' into masterupstream/fabian-merge-v4.0.0
# Conflicts:
# circle.yml
# include/mbgl/style/expression/let.hpp
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
# platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
# platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
# platform/android/gradle/dependencies.gradle
# platform/android/src/example_custom_layer.cpp
# platform/android/src/geojson/point.cpp
# platform/darwin/src/NSPredicate+MGLAdditions.mm
# platform/darwin/test/MGLExpressionTests.mm
# platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
# platform/ios/Mapbox-iOS-SDK-symbols.podspec
# platform/ios/Mapbox-iOS-SDK.podspec
# platform/ios/app/MBXViewController.m
# src/mbgl/renderer/layers/render_custom_layer.cpp
# src/mbgl/style/conversion/filter.cpp
# src/mbgl/style/expression/interpolate.cpp
# src/mbgl/style/expression/value.cpp
# test/style/filter.test.cpp
Diffstat (limited to 'src/mbgl')
39 files changed, 411 insertions, 377 deletions
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 6fb0d5e446..c67786274a 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -17,8 +17,9 @@ namespace mbgl { -FeatureIndex::FeatureIndex() - : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) { // 16x16 grid -> 32px cell +FeatureIndex::FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_) + : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) // 16x16 grid -> 32px cell + , tileData(std::move(tileData_)) { } void FeatureIndex::insert(const GeometryCollection& geometries, @@ -47,12 +48,15 @@ void FeatureIndex::query( const double tileSize, const double scale, const RenderedQueryOptions& queryOptions, - const GeometryTileData& geometryTileData, const UnwrappedTileID& tileID, const std::string& sourceID, const std::vector<const RenderLayer*>& layers, const CollisionIndex& collisionIndex, const float additionalQueryRadius) const { + + if (!tileData) { + return; + } // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; @@ -72,13 +76,13 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits); } std::vector<IndexedSubfeature> symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID); std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits); + addFeature(result, symbolFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits); } } @@ -87,7 +91,6 @@ void FeatureIndex::addFeature( const IndexedSubfeature& indexedFeature, const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, - const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, const std::vector<const RenderLayer*>& layers, const float bearing, @@ -113,7 +116,7 @@ void FeatureIndex::addFeature( } if (!geometryTileFeature) { - sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); + sourceLayer = tileData->getLayer(indexedFeature.sourceLayerName); assert(sourceLayer); geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index e95bb94da6..9e0c172342 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -50,8 +50,10 @@ public: class FeatureIndex { public: - FeatureIndex(); + FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_); + const GeometryTileData* getData() { return tileData.get(); } + void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName); void query( @@ -61,7 +63,6 @@ public: const double tileSize, const double scale, const RenderedQueryOptions& options, - const GeometryTileData&, const UnwrappedTileID&, const std::string&, const std::vector<const RenderLayer*>&, @@ -83,7 +84,6 @@ private: const IndexedSubfeature&, const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, - const GeometryTileData&, const CanonicalTileID&, const std::vector<const RenderLayer*>&, const float bearing, @@ -93,5 +93,6 @@ private: unsigned int sortIndex = 0; std::unordered_map<std::string, std::vector<std::string>> bucketLayerIDs; + std::unique_ptr<const GeometryTileData> tileData; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 6e152349ca..7dfa8edf43 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -27,7 +27,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, anchor(anchor_), line(line_), index(index_), - hasText(shapedTextOrientations.first || shapedTextOrientations.second), + hasText(false), hasIcon(shapedIcon), // Create the collision features that will be used to check whether this symbol instance can be placed @@ -48,6 +48,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, if (shapedTextOrientations.second) { verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions); } + // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap + hasText = horizontalGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0; if (shapedTextOrientations.first && shapedTextOrientations.second) { writingModes = WritingModeType::Horizontal | WritingModeType::Vertical; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 3bf85407c6..82a9255824 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -126,7 +126,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasText) { std::string u8string = layout.evaluate<TextField>(zoom, ft); - if (layout.get<TextField>().isConstant()) { + if (layout.get<TextField>().isConstant() && !leader.layout.get<TextField>().isExpression()) { u8string = util::replaceTokens(u8string, getValue); } @@ -159,7 +159,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { std::string icon = layout.evaluate<IconImage>(zoom, ft); - if (layout.get<IconImage>().isConstant()) { + if (layout.get<IconImage>().isConstant() && !leader.layout.get<IconImage>().isExpression()) { icon = util::replaceTokens(icon, getValue); } ft.icon = icon; diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index 9e077e2532..ef669c6e19 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -240,7 +240,11 @@ namespace mbgl { const PlacedSymbol& symbol, const mat4& labelPlaneMatrix, const bool returnTileDistance) { - + if (symbol.glyphOffsets.empty()) { + assert(false); + return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); + } + const float firstGlyphOffset = symbol.glyphOffsets.front(); const float lastGlyphOffset = symbol.glyphOffsets.back();; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 525291a628..d81544eed5 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -446,7 +446,6 @@ CameraOptions Map::cameraForGeometry(const Geometry<double>& geometry, const Edg latLngs.push_back({ pt.y, pt.x }); }); return cameraForLatLngs(latLngs, padding, bearing); - } LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const { diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index a14afac702..9b5037ed9f 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -61,8 +61,8 @@ struct SymbolLayoutAttributes : gl::Attributes< {{ static_cast<int16_t>(labelAnchor.x), static_cast<int16_t>(labelAnchor.y), - static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement - static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64)) + static_cast<int16_t>(::round(o.x * 32)), // use 1/32 pixels for placement + static_cast<int16_t>(::round((o.y + glyphOffsetY) * 32)) }}, {{ tx, diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index a429b8d82e..be9f64d9eb 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -6,23 +6,24 @@ #include <mbgl/style/layers/custom_layer_impl.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/gl/gl.hpp> +#include <mbgl/util/mat4.hpp> namespace mbgl { using namespace style; RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) - : RenderLayer(LayerType::Custom, _impl) { + : RenderLayer(LayerType::Custom, _impl), host(_impl->host) { + assert(BackendScope::exists()); + host->initialize(); } RenderCustomLayer::~RenderCustomLayer() { assert(BackendScope::exists()); - if (initialized) { - if (contextDestroyed && impl().contextLostFn ) { - impl().contextLostFn(impl().context); - } else if (!contextDestroyed && impl().deinitializeFn) { - impl().deinitializeFn(impl().context); - } + if (contextDestroyed) { + host->contextLost(); + } else { + host->deinitialize(); } } @@ -44,15 +45,13 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, } void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { - if (context != impl().context || !initialized) { + if (host != impl().host) { //If the context changed, deinitialize the previous one before initializing the new one. - if (context && !contextDestroyed && impl().deinitializeFn) { - MBGL_CHECK_ERROR(impl().deinitializeFn(context)); + if (host && !contextDestroyed) { + MBGL_CHECK_ERROR(host->deinitialize()); } - context = impl().context; - assert(impl().initializeFn); - MBGL_CHECK_ERROR(impl().initializeFn(impl().context)); - initialized = true; + host = impl().host; + MBGL_CHECK_ERROR(host->initialize()); } gl::Context& glContext = paintParameters.context; @@ -74,9 +73,11 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) parameters.bearing = -state.getAngle() * util::RAD2DEG; parameters.pitch = state.getPitch(); parameters.fieldOfView = state.getFieldOfView(); + mat4 projMatrix; + state.getProjMatrix(projMatrix); + parameters.projectionMatrix = projMatrix; - assert(impl().renderFn); - MBGL_CHECK_ERROR(impl().renderFn(context, parameters)); + MBGL_CHECK_ERROR(host->render(parameters)); // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index 6d1fea99d3..971d8d8f42 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -24,9 +24,8 @@ public: }; private: - bool initialized = false; bool contextDestroyed = false; - void * context = nullptr; + std::shared_ptr<style::CustomLayerHost> host; }; template <> diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp index 9d11640bf4..bc5d9bc6f9 100644 --- a/src/mbgl/shaders/collision_box.cpp +++ b/src/mbgl/shaders/collision_box.cpp @@ -22,7 +22,10 @@ varying float v_notUsed; void main() { vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1); highp float camera_to_anchor_distance = projectedPoint.w; - highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance); + highp float collision_perspective_ratio = clamp( + 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance), + 0.0, // Prevents oversized near-field boxes in pitched/overzoomed tiles + 4.0); gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0); gl_Position.xy += a_extrude * u_extrude_scale * gl_Position.w * collision_perspective_ratio; diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp index f8d785ade9..be88255e3c 100644 --- a/src/mbgl/shaders/line_pattern.cpp +++ b/src/mbgl/shaders/line_pattern.cpp @@ -215,8 +215,14 @@ void main() { float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0); float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0); - float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y); - float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y); + + // v_normal.y is 0 at the midpoint of the line, -1 at the lower edge, 1 at the upper edge + // we clamp the line width outset to be between 0 and half the pattern height plus padding (2.0) + // to ensure we don't sample outside the designated symbol on the sprite sheet. + // 0.5 is added to shift the component to be bounded between 0 and 1 for interpolation of + // the texture coordinate + float y_a = 0.5 + (v_normal.y * clamp(v_width2.s, 0.0, (u_pattern_size_a.y + 2.0) / 2.0) / u_pattern_size_a.y); + float y_b = 0.5 + (v_normal.y * clamp(v_width2.s, 0.0, (u_pattern_size_b.y + 2.0) / 2.0) / u_pattern_size_b.y); vec2 pos_a = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, vec2(x_a, y_a)); vec2 pos_b = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, vec2(x_b, y_b)); diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index f5c2bbe22d..c037c81005 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -80,7 +80,10 @@ void main() { highp float distance_ratio = u_pitch_with_map ? camera_to_anchor_distance / u_camera_to_center_distance : u_camera_to_center_distance / camera_to_anchor_distance; - highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + highp float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); size *= perspective_ratio; @@ -102,7 +105,7 @@ void main() { mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); - gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0); v_tex = a_tex / u_texsize; vec2 fade_opacity = unpack_opacity(a_fade_opacity); diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 441eaf7aac..b584c00315 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -156,7 +156,10 @@ void main() { highp float distance_ratio = u_pitch_with_map ? camera_to_anchor_distance / u_camera_to_center_distance : u_camera_to_center_distance / camera_to_anchor_distance; - highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + highp float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); size *= perspective_ratio; @@ -180,7 +183,7 @@ void main() { mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); - gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0); float gamma_scale = gl_Position.w; vec2 tex = a_tex / u_texsize; diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index fba149da12..3c941945fd 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -2,7 +2,7 @@ #include <mbgl/util/geometry.hpp> #include <mbgl/style/expression/expression.hpp> #include <mbgl/style/expression/type.hpp> -#include <mbgl/style/conversion/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> namespace mbgl { namespace style { @@ -236,10 +236,13 @@ optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) { } optional<Filter> convertExpressionFilter(const Convertible& value, Error& error) { - optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, expression::type::Boolean); + expression::ParsingContext ctx(expression::type::Boolean); + expression::ParseResult expression = ctx.parseExpression(value); if (!expression) { + error = { ctx.getCombinedErrors() }; return {}; } + return { ExpressionFilter { std::move(*expression) } }; } diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 7924a442c4..7b7727d7c4 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -290,138 +290,19 @@ void stringify(Writer& writer, const Undefined&) { writer.Null(); } -template <class Writer> -void stringify(Writer& writer, const CategoricalValue& v) { - CategoricalValue::visit(v, [&] (const auto& v_) { stringify(writer, v_); }); -} - -template <class Writer> -class StringifyStops { -public: - Writer& writer; - - template <class T> - void operator()(const ExponentialStops<T>& f) { - writer.Key("type"); - writer.String("exponential"); - writer.Key("base"); - writer.Double(f.base); - writer.Key("stops"); - stringifyStops(f.stops); - } - - template <class T> - void operator()(const IntervalStops<T>& f) { - writer.Key("type"); - writer.String("interval"); - writer.Key("stops"); - stringifyStops(f.stops); - } - - template <class T> - void operator()(const CategoricalStops<T>& f) { - writer.Key("type"); - writer.String("categorical"); - writer.Key("stops"); - stringifyStops(f.stops); - } - - template <class T> - void operator()(const IdentityStops<T>&) { - writer.Key("type"); - writer.String("identity"); - } - - template <class T> - void operator()(const CompositeExponentialStops<T>& f) { - writer.Key("type"); - writer.String("exponential"); - writer.Key("base"); - writer.Double(f.base); - writer.Key("stops"); - stringifyCompositeStops(f.stops); - } - - template <class T> - void operator()(const CompositeIntervalStops<T>& f) { - writer.Key("type"); - writer.String("interval"); - writer.Key("stops"); - stringifyCompositeStops(f.stops); - } - - template <class T> - void operator()(const CompositeCategoricalStops<T>& f) { - writer.Key("type"); - writer.String("categorical"); - writer.Key("stops"); - stringifyCompositeStops(f.stops); - } - -private: - template <class K, class V> - void stringifyStops(const std::map<K, V>& stops) { - writer.StartArray(); - for (const auto& stop : stops) { - writer.StartArray(); - stringify(writer, stop.first); - stringify(writer, stop.second); - writer.EndArray(); - } - writer.EndArray(); - } - - template <class InnerStops> - void stringifyCompositeStops(const std::map<float, InnerStops>& stops) { - writer.StartArray(); - for (const auto& outer : stops) { - for (const auto& inner : outer.second) { - writer.StartArray(); - writer.StartObject(); - writer.Key("zoom"); - writer.Double(outer.first); - writer.Key("value"); - stringify(writer, inner.first); - writer.EndObject(); - stringify(writer, inner.second); - writer.EndArray(); - } - } - writer.EndArray(); - } -}; - template <class Writer, class T> -void stringify(Writer& writer, const CameraFunction<T>& f) { - writer.StartObject(); - CameraFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer }); - writer.EndObject(); +void stringify(Writer& writer, const CameraFunction<T>& fn) { + stringify(writer, fn.getExpression().serialize()); } template <class Writer, class T> -void stringify(Writer& writer, const SourceFunction<T>& f) { - writer.StartObject(); - writer.Key("property"); - writer.String(f.property); - SourceFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer }); - if (f.defaultValue) { - writer.Key("default"); - stringify(writer, *f.defaultValue); - } - writer.EndObject(); +void stringify(Writer& writer, const SourceFunction<T>& fn) { + stringify(writer, fn.getExpression().serialize()); } template <class Writer, class T> -void stringify(Writer& writer, const CompositeFunction<T>& f) { - writer.StartObject(); - writer.Key("property"); - writer.String(f.property); - CompositeFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer }); - if (f.defaultValue) { - writer.Key("default"); - stringify(writer, *f.defaultValue); - } - writer.EndObject(); +void stringify(Writer& writer, const CompositeFunction<T>& fn) { + stringify(writer, fn.getExpression().serialize()); } template <class Writer, class T> diff --git a/src/mbgl/style/expression/at.cpp b/src/mbgl/style/expression/at.cpp index e447d33bc7..725e5ddb51 100644 --- a/src/mbgl/style/expression/at.cpp +++ b/src/mbgl/style/expression/at.cpp @@ -19,15 +19,21 @@ EvaluationResult At::evaluate(const EvaluationContext& params) const { const auto i = evaluatedIndex->get<double>(); const auto inputArray = evaluatedInput->get<std::vector<Value>>(); - if (i < 0 || i >= inputArray.size()) { + if (i < 0) { return EvaluationError { - "Array index out of bounds: " + stringify(i) + - " > " + util::toString(inputArray.size()) + "." + "Array index out of bounds: " + util::toString(i) + " < 0." + }; + } + + if (i >= inputArray.size()) { + return EvaluationError { + "Array index out of bounds: " + util::toString(i) + + " > " + util::toString(inputArray.size() - 1) + "." }; } if (i != std::floor(i)) { return EvaluationError { - "Array index must be an integer, but found " + stringify(i) + " instead." + "Array index must be an integer, but found " + util::toString(i) + " instead." }; } return inputArray[static_cast<std::size_t>(i)]; diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 86d968c521..c36ffa33e3 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -6,6 +6,7 @@ #include <mbgl/util/ignore.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/platform.hpp> +#include <cmath> namespace mbgl { namespace style { @@ -209,12 +210,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali ); }); define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> { - return std::array<double, 4> {{ - 255 * color.r / color.a, - 255 * color.g / color.a, - 255 * color.b / color.a, - color.a - }}; + return color.toArray(); }); define("rgba", rgba); @@ -271,13 +267,6 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali return object.at(key); }); - define("length", [](const std::vector<Value>& arr) -> Result<double> { - return arr.size(); - }); - define("length", [] (const std::string s) -> Result<double> { - return s.size(); - }); - define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> { if (!params.feature) { return EvaluationError { @@ -374,6 +363,11 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali return result; }); + define("round", [](double x) -> Result<double> { return std::round(x); }); + define("floor", [](double x) -> Result<double> { return std::floor(x); }); + define("ceil", [](double x) -> Result<double> { return std::ceil(x); }); + define("abs", [](double x) -> Result<double> { return std::abs(x); }); + define(">", [](double lhs, double rhs) -> Result<bool> { return lhs > rhs; }); define(">", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs > rhs; }); define(">=", [](double lhs, double rhs) -> Result<bool> { return lhs >= rhs; }); diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp index 30b2cba81b..daad8523f2 100644 --- a/src/mbgl/style/expression/interpolate.cpp +++ b/src/mbgl/style/expression/interpolate.cpp @@ -223,7 +223,11 @@ mbgl::Value Interpolate<T>::serialize() const { interpolator.match( [&](const ExponentialInterpolator& exponential) { - serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("exponential"), exponential.base }}); + if (exponential.base == 1) { + serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("linear") }}); + } else { + serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("exponential"), exponential.base }}); + } }, [&](const CubicBezierInterpolator& cubicBezier) { static const std::string cubicBezierTag("cubic-bezier"); diff --git a/src/mbgl/style/expression/length.cpp b/src/mbgl/style/expression/length.cpp new file mode 100644 index 0000000000..258353ae4e --- /dev/null +++ b/src/mbgl/style/expression/length.cpp @@ -0,0 +1,66 @@ +#include <mbgl/style/expression/length.hpp> +#include <mbgl/util/string.hpp> + +namespace mbgl { +namespace style { +namespace expression { + +Length::Length(std::unique_ptr<Expression> input_) + : Expression(type::Number), + input(std::move(input_)) { +} + +EvaluationResult Length::evaluate(const EvaluationContext& params) const { + const EvaluationResult value = input->evaluate(params); + if (!value) return value; + return value->match( + [] (const std::string& s) { + return EvaluationResult { double(s.size()) }; + }, + [] (const std::vector<Value>& v) { + return EvaluationResult { double(v.size()) }; + }, + [&] (const auto&) -> EvaluationResult { + return EvaluationError { "Expected value to be of type string or array, but found " + toString(typeOf(*value)) + " instead." }; + }); +} + +void Length::eachChild(const std::function<void(const Expression&)>& visit) const { + visit(*input); +} + +bool Length::operator==(const Expression& e) const { + if (auto eq = dynamic_cast<const Length*>(&e)) { + return *eq->input == *input; + } + return false; +} + +std::vector<optional<Value>> Length::possibleOutputs() const { + return { nullopt }; +} + +using namespace mbgl::style::conversion; +ParseResult Length::parse(const Convertible& value, ParsingContext& ctx) { + std::size_t length = arrayLength(value); + + if (length != 2) { + ctx.error("Expected one argument, but found " + util::toString(length) + " instead."); + return ParseResult(); + } + + ParseResult input = ctx.parse(arrayMember(value, 1), 1); + if (!input) return ParseResult(); + + type::Type type = (*input)->getType(); + if (!type.is<type::Array>() && !type.is<type::StringType>() && !type.is<type::ValueType>()) { + ctx.error("Expected argument of type string or array, but found " + toString(type) + " instead."); + return ParseResult(); + } + + return ParseResult(std::make_unique<Length>(std::move(*input))); +} + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index 0215982209..b522aeff9a 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -15,6 +15,7 @@ #include <mbgl/style/expression/compound_expression.hpp> #include <mbgl/style/expression/equals.hpp> #include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/length.hpp> #include <mbgl/style/expression/let.hpp> #include <mbgl/style/expression/literal.hpp> #include <mbgl/style/expression/match.hpp> @@ -31,23 +32,36 @@ namespace style { namespace expression { bool isConstant(const Expression& expression) { - if (dynamic_cast<const Var*>(&expression)) { - return false; + if (auto varExpression = dynamic_cast<const Var*>(&expression)) { + return isConstant(*varExpression->getBoundExpression()); } - + if (auto compound = dynamic_cast<const CompoundExpressionBase*>(&expression)) { if (compound->getName() == "error") { return false; } } + + bool isTypeAnnotation = dynamic_cast<const Coercion*>(&expression) || + dynamic_cast<const Assertion*>(&expression) || + dynamic_cast<const ArrayAssertion*>(&expression); - bool literalArgs = true; + bool childrenConstant = true; expression.eachChild([&](const Expression& child) { - if (!dynamic_cast<const Literal*>(&child)) { - literalArgs = false; + // We can _almost_ assume that if `expressions` children are constant, + // they would already have been evaluated to Literal values when they + // were parsed. Type annotations are the exception, because they might + // have been inferred and added after a child was parsed. + + // So we recurse into isConstant() for the children of type annotations, + // but otherwise simply check whether they are Literals. + if (isTypeAnnotation) { + childrenConstant = childrenConstant && isConstant(child); + } else { + childrenConstant = childrenConstant && dynamic_cast<const Literal*>(&child); } }); - if (!literalArgs) { + if (!childrenConstant) { return false; } @@ -89,6 +103,7 @@ const ExpressionRegistry& getExpressionRegistry() { {"case", Case::parse}, {"coalesce", Coalesce::parse}, {"interpolate", parseInterpolate}, + {"length", Length::parse}, {"let", Let::parse}, {"literal", Literal::parse}, {"match", parseMatch}, @@ -139,15 +154,15 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption return parsed; } - if (expected) { - auto array = [&](std::unique_ptr<Expression> expression) { - std::vector<std::unique_ptr<Expression>> args; - args.push_back(std::move(expression)); - return args; - }; + auto array = [&](std::unique_ptr<Expression> expression) { + std::vector<std::unique_ptr<Expression>> args; + args.push_back(std::move(expression)); + return args; + }; + if (expected) { const type::Type actual = (*parsed)->getType(); - if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) { + if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean || *expected == type::Object) && actual == type::Value) { if (typeAnnotationOption == includeTypeAnnotations) { parsed = { std::make_unique<Assertion>(*expected, array(std::move(*parsed))) }; } @@ -167,7 +182,7 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption } } - // If an expression's arguments are all literals, we can evaluate + // If an expression's arguments are all constant, we can evaluate // it immediately and replace it with a literal value in the // parsed result. if (!dynamic_cast<Literal *>(parsed->get()) && isConstant(**parsed)) { @@ -191,8 +206,16 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption } } - // if this is the root expression, enforce constraints on the use ["zoom"]. - if (key.size() == 0 && !isZoomConstant(**parsed)) { + return parsed; +} + +ParseResult ParsingContext::parseExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) { + return parse(value, typeAnnotationOption); +} + +ParseResult ParsingContext::parseLayerPropertyExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) { + ParseResult parsed = parse(value, typeAnnotationOption); + if (parsed && !isZoomConstant(**parsed)) { optional<variant<const InterpolateBase*, const Step*, ParsingError>> zoomCurve = findZoomCurve(parsed->get()); if (!zoomCurve) { error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)"); @@ -202,10 +225,23 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption return ParseResult(); } } - return parsed; } +const std::string ParsingContext::getCombinedErrors() const { + std::string combinedError; + for (const ParsingError& parsingError : *errors) { + if (combinedError.size() > 0) { + combinedError += "\n"; + } + if (parsingError.key.size() > 0) { + combinedError += parsingError.key + ": "; + } + combinedError += parsingError.message; + } + return combinedError; +} + optional<std::string> ParsingContext::checkType(const type::Type& t) { assert(expected); optional<std::string> err = type::checkSubtype(*expected, t); diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index 72779d4956..1b3257c755 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -106,12 +106,13 @@ Value ValueConverter<mbgl::Value>::toExpressionValue(const mbgl::Value& value) { mbgl::Value ValueConverter<mbgl::Value>::fromExpressionValue(const Value& value) { return value.match( [&](const Color& color)->mbgl::Value { + std::array<double, 4> array = color.toArray(); return std::vector<mbgl::Value>{ std::string("rgba"), - double(255 * color.r / color.a), - double(255 * color.g / color.a), - double(255 * color.b / color.a), - double(color.a) + array[0], + array[1], + array[2], + array[3], }; }, [&](const std::vector<Value>& values)->mbgl::Value { diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp index d4ead18816..66ab46c078 100644 --- a/src/mbgl/style/layers/background_layer.cpp +++ b/src/mbgl/style/layers/background_layer.cpp @@ -53,12 +53,14 @@ void BackgroundLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void BackgroundLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp index 9854932699..6dd744df1f 100644 --- a/src/mbgl/style/layers/circle_layer.cpp +++ b/src/mbgl/style/layers/circle_layer.cpp @@ -81,12 +81,14 @@ void CircleLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void CircleLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp index 854c771847..0e51a70e50 100644 --- a/src/mbgl/style/layers/custom_layer.cpp +++ b/src/mbgl/style/layers/custom_layer.cpp @@ -6,20 +6,8 @@ namespace mbgl { namespace style { CustomLayer::CustomLayer(const std::string& layerID, - CustomLayerInitializeFunction init, - CustomLayerRenderFunction render, - CustomLayerContextLostFunction contextLost, - CustomLayerDeinitializeFunction deinit, - void* 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)) { + std::unique_ptr<CustomLayerHost> host) + : Layer(makeMutable<Impl>(layerID, std::move(host))) { } CustomLayer::~CustomLayer() = default; diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp index 1de268d2e2..05c41623c4 100644 --- a/src/mbgl/style/layers/custom_layer_impl.cpp +++ b/src/mbgl/style/layers/custom_layer_impl.cpp @@ -4,17 +4,9 @@ namespace mbgl { namespace style { CustomLayer::Impl::Impl(const std::string& id_, - CustomLayerInitializeFunction initializeFn_, - CustomLayerRenderFunction renderFn_, - CustomLayerContextLostFunction contextLostFn_, - CustomLayerDeinitializeFunction deinitializeFn_, - void* context_) + std::unique_ptr<CustomLayerHost> host_) : Layer::Impl(LayerType::Custom, id_, std::string()) { - initializeFn = initializeFn_; - renderFn = renderFn_; - deinitializeFn = deinitializeFn_; - contextLostFn = contextLostFn_; - context = context_; + host = std::move(host_); } bool CustomLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp index 62efbbe15b..a41962c276 100644 --- a/src/mbgl/style/layers/custom_layer_impl.hpp +++ b/src/mbgl/style/layers/custom_layer_impl.hpp @@ -3,6 +3,8 @@ #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/layers/custom_layer.hpp> +#include <memory> + namespace mbgl { class TransformState; @@ -12,20 +14,12 @@ namespace style { class CustomLayer::Impl : public Layer::Impl { public: Impl(const std::string& id, - CustomLayerInitializeFunction, - CustomLayerRenderFunction, - CustomLayerContextLostFunction, - CustomLayerDeinitializeFunction, - void* context); + std::unique_ptr<CustomLayerHost> host); bool hasLayoutDifference(const Layer::Impl&) const override; void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - CustomLayerInitializeFunction initializeFn = nullptr; - CustomLayerRenderFunction renderFn = nullptr; - CustomLayerContextLostFunction contextLostFn = nullptr; - CustomLayerDeinitializeFunction deinitializeFn = nullptr; - void* context = nullptr; + std::shared_ptr<CustomLayerHost> host; }; } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp index 62f92cef75..c5b4ef0ef3 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp @@ -81,12 +81,14 @@ void FillExtrusionLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void FillExtrusionLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp index 65975752db..99a2a51ed0 100644 --- a/src/mbgl/style/layers/fill_layer.cpp +++ b/src/mbgl/style/layers/fill_layer.cpp @@ -81,12 +81,14 @@ void FillLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void FillLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp index 4989ff15f1..3f7881ddd3 100644 --- a/src/mbgl/style/layers/heatmap_layer.cpp +++ b/src/mbgl/style/layers/heatmap_layer.cpp @@ -85,12 +85,14 @@ void HeatmapLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void HeatmapLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/hillshade_layer.cpp b/src/mbgl/style/layers/hillshade_layer.cpp index ea736af1ad..e352ae090c 100644 --- a/src/mbgl/style/layers/hillshade_layer.cpp +++ b/src/mbgl/style/layers/hillshade_layer.cpp @@ -59,12 +59,14 @@ void HillshadeLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void HillshadeLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index 657a7f5a8a..6d748311bf 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -108,12 +108,14 @@ void <%- camelize(type) %>Layer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void <%- camelize(type) %>Layer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 1c7f0d28ee..56eac34c00 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -82,12 +82,14 @@ void LineLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void LineLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp index a9a8d273fa..36b2e3e027 100644 --- a/src/mbgl/style/layers/raster_layer.cpp +++ b/src/mbgl/style/layers/raster_layer.cpp @@ -59,12 +59,14 @@ void RasterLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void RasterLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index d1a1ba246e..c940f3b00a 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -82,12 +82,14 @@ void SymbolLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void SymbolLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 82d0c91806..a99cb91d26 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -125,23 +125,16 @@ void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) { } void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) { - // Don't mark ourselves loaded or renderable until the first successful placement - // TODO: Ideally we'd render this tile without symbols as long as this tile wasn't - // replacing a tile at a different zoom that _did_ have symbols. - (void)resultCorrelationID; - nonSymbolBuckets = std::move(result.nonSymbolBuckets); - pendingFeatureIndex = std::move(result.featureIndex); - pendingData = std::move(result.tileData); - observer->onTileChanged(*this); -} - -void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) { loaded = true; renderable = true; if (resultCorrelationID == correlationID) { pending = false; } - symbolBuckets = std::move(result.symbolBuckets); + + buckets = std::move(result.buckets); + + featureIndexPendingCommit = { std::move(result.featureIndex) }; + if (result.glyphAtlasImage) { glyphAtlasImage = std::move(*result.glyphAtlasImage); } @@ -183,11 +176,7 @@ void GeometryTile::upload(gl::Context& context) { } }; - for (auto& entry : nonSymbolBuckets) { - uploadFn(*entry.second); - } - - for (auto& entry : symbolBuckets) { + for (auto& entry : buckets) { uploadFn(*entry.second); } @@ -203,7 +192,6 @@ void GeometryTile::upload(gl::Context& context) { } Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { - const auto& buckets = layer.type == LayerType::Symbol ? symbolBuckets : nonSymbolBuckets; const auto it = buckets.find(layer.id); if (it == buckets.end()) { return nullptr; @@ -214,11 +202,11 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { } void GeometryTile::commitFeatureIndex() { - if (pendingFeatureIndex) { - featureIndex = std::move(pendingFeatureIndex); - } - if (pendingData) { - data = std::move(pendingData); + // We commit our pending FeatureIndex when a global placement has run, + // synchronizing the global CollisionIndex with the latest buckets/FeatureIndex + if (featureIndexPendingCommit) { + featureIndex = std::move(*featureIndexPendingCommit); + featureIndexPendingCommit = nullopt; } } @@ -230,7 +218,7 @@ void GeometryTile::queryRenderedFeatures( const RenderedQueryOptions& options, const CollisionIndex& collisionIndex) { - if (!featureIndex || !data) return; + if (!getData()) return; // Determine the additional radius needed factoring in property functions float additionalRadius = 0; @@ -247,7 +235,6 @@ void GeometryTile::queryRenderedFeatures( util::tileSize * id.overscaleFactor(), std::pow(2, transformState.getZoom() - id.overscaledZ), options, - *data, id.toUnwrapped(), sourceID, layers, @@ -259,8 +246,8 @@ void GeometryTile::querySourceFeatures( std::vector<Feature>& result, const SourceQueryOptions& options) { - // Data not yet available - if (!data) { + // Data not yet available, or tile is empty + if (!getData()) { return; } @@ -273,7 +260,7 @@ void GeometryTile::querySourceFeatures( for (auto sourceLayer : *options.sourceLayers) { // Go throught all sourceLayers, if any // to gather all the features - auto layer = data->getLayer(sourceLayer); + auto layer = getData()->getLayer(sourceLayer); if (layer) { auto featureCount = layer->featureCount(); diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 00a4aafadf..418db4a0b2 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -65,33 +65,21 @@ public: class LayoutResult { public: - std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets; + std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; std::unique_ptr<FeatureIndex> featureIndex; - std::unique_ptr<GeometryTileData> tileData; - - LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_, - std::unique_ptr<FeatureIndex> featureIndex_, - std::unique_ptr<GeometryTileData> tileData_) - : nonSymbolBuckets(std::move(nonSymbolBuckets_)), - featureIndex(std::move(featureIndex_)), - tileData(std::move(tileData_)) {} - }; - void onLayout(LayoutResult, uint64_t correlationID); - - class PlacementResult { - public: - std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; - PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_, - optional<AlphaImage> glyphAtlasImage_, - optional<PremultipliedImage> iconAtlasImage_) - : symbolBuckets(std::move(symbolBuckets_)), + LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets_, + std::unique_ptr<FeatureIndex> featureIndex_, + optional<AlphaImage> glyphAtlasImage_, + optional<PremultipliedImage> iconAtlasImage_) + : buckets(std::move(buckets_)), + featureIndex(std::move(featureIndex_)), glyphAtlasImage(std::move(glyphAtlasImage_)), iconAtlasImage(std::move(iconAtlasImage_)) {} }; - void onPlacement(PlacementResult, uint64_t correlationID); + void onLayout(LayoutResult, uint64_t correlationID); void onError(std::exception_ptr, uint64_t correlationID); @@ -104,7 +92,7 @@ public: protected: const GeometryTileData* getData() { - return data.get(); + return featureIndex ? featureIndex->getData() : nullptr; } private: @@ -123,17 +111,14 @@ private: uint64_t correlationID = 0; - std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets; + std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; + + optional<std::unique_ptr<FeatureIndex>> featureIndexPendingCommit; std::unique_ptr<FeatureIndex> featureIndex; - std::unique_ptr<FeatureIndex> pendingFeatureIndex; - std::unique_ptr<const GeometryTileData> data; - std::unique_ptr<const GeometryTileData> pendingData; optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; - std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; - const MapMode mode; bool showCollisionBoxes; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 8b6f1f63bf..1378ad5d3a 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -41,48 +41,73 @@ GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_, GeometryTileWorker::~GeometryTileWorker() = default; /* - NOTE: The comments below are technically correct, but currently - conceptually misleading. The change to foreground label placement - means that: - (1) "placement" here is a misnomer: the remaining role of - "attemptPlacement" is symbol buffer generation - (2) Once a tile has completed layout, we will only run - "attemptPlacement" once - (3) Tiles won't be rendered until "attemptPlacement" has run once - - TODO: Simplify GeometryTileWorker to fit its new role - https://github.com/mapbox/mapbox-gl-native/issues/10457 - GeometryTileWorker is a state machine. This is its transition diagram. States are indicated by [state], lines are transitions triggered by messages, (parentheses) are actions taken on transition. - [idle] <----------------------------. - | | - set{Data,Layers,Placement}, symbolDependenciesChanged | - | | - (do layout/placement; self-send "coalesced") | - v | - [coalescing] --- coalesced ------------. - | | - .-----------------. .---------------. + [Idle] <-------------------------. + | | + set{Data,Layers}, symbolDependenciesChanged, | + setShowCollisionBoxes | + | | + (do parse and/or symbol layout; self-send "coalesced") | + v | + [Coalescing] --- coalesced ---------. + | | + .-----------. .---------------------. | | - .--- set{Data,Layers} setPlacement -----. - | | | | - | v v | - .-- [need layout] <-- set{Data,Layers} -- [need placement] --. + .--- set{Data,Layers} setShowCollisionBoxes, + | | symbolDependenciesChanged --. + | | | | + | v v | + .-- [NeedsParse] <-- set{Data,Layers} -- [NeedsSymbolLayout] ---. | | coalesced coalesced | | v v - (do layout or placement; self-send "coalesced"; goto [coalescing]) - - The idea is that in the [idle] state, layout or placement happens immediately - in response to a "set" message. During this processing, multiple "set" messages - might get queued in the mailbox. At the end of processing, we self-send "coalesced", - read all the queued messages until we get to "coalesced", and then redo either - layout or placement if there were one or more "set"s (with layout taking priority, - since it will trigger placement when complete), or return to the [idle] state if not. + (do parse or symbol layout; self-send "coalesced"; goto [coalescing]) + + The idea is that in the [idle] state, parsing happens immediately in response to + a "set" message, and symbol layout happens once all symbol dependencies are met. + During this processing, multiple "set" messages might get queued in the mailbox. + At the end of processing, we self-send "coalesced", read all the queued messages + until we get to "coalesced", and then re-parse if there were one or more "set"s or + return to the [idle] state if not. + + One important goal of the design is to prevent starvation. Under heavy load new + requests for tiles should not prevent in progress request from completing. + It is nevertheless possible to restart an in-progress request: + + - [Idle] setData -> parse() + sends getGlyphs, hasPendingSymbolDependencies() is true + enters [Coalescing], sends coalesced + - [Coalescing] coalesced -> [Idle] + - [Idle] setData -> new parse(), interrupts old parse() + sends getGlyphs, hasPendingSymbolDependencies() is true + enters [Coalescing], sends coalesced + - [Coalescing] onGlyphsAvailable -> [NeedsSymbolLayout] + hasPendingSymbolDependencies() may or may not be true + - [NeedsSymbolLayout] coalesced -> performSymbolLayout() + Generates result depending on whether dependencies are met + -> [Idle] + + In this situation, we are counting on the idea that even with rapid changes to + the tile's data, the set of glyphs/images it requires will not keep growing without + limit. + + Although parsing (which populates all non-symbol buckets and requests dependencies + for symbol buckets) is internally separate from symbol layout, we only return + results to the foreground when we have completed both steps. Because we _move_ + the result buckets to the foreground, it is necessary to re-generate all buckets from + scratch for `setShowCollisionBoxes`, even though it only affects symbol layers. + + The GL JS equivalent (in worker_tile.js and vector_tile_worker_source.js) + is somewhat simpler because it relies on getGlyphs/getImages calls that transfer + an entire set of glyphs/images on every tile load, while the native logic + maintains a local state that can be incrementally updated. Because each tile load + call becomes self-contained, the equivalent of the coalescing logic is handled by + 'reloadTile' queueing a single extra 'reloadTile' callback to run after the next + completed parse. */ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, uint64_t correlationID_) { @@ -92,14 +117,14 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, switch (state) { case Idle: - redoLayout(); + parse(); coalesce(); break; case Coalescing: - case NeedLayout: - case NeedPlacement: - state = NeedLayout; + case NeedsParse: + case NeedsSymbolLayout: + state = NeedsParse; break; } } catch (...) { @@ -114,16 +139,16 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_, switch (state) { case Idle: - redoLayout(); + parse(); coalesce(); break; case Coalescing: - case NeedPlacement: - state = NeedLayout; + case NeedsSymbolLayout: + state = NeedsParse; break; - case NeedLayout: + case NeedsParse: break; } } catch (...) { @@ -138,16 +163,20 @@ void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_ switch (state) { case Idle: - attemptPlacement(); - coalesce(); + if (!hasPendingParseResult()) { + // Trigger parse if nothing is in flight, otherwise symbol layout will automatically + // pick up the change + parse(); + coalesce(); + } break; case Coalescing: - state = NeedPlacement; + state = NeedsSymbolLayout; break; - case NeedPlacement: - case NeedLayout: + case NeedsSymbolLayout: + case NeedsParse: break; } } catch (...) { @@ -160,19 +189,23 @@ void GeometryTileWorker::symbolDependenciesChanged() { switch (state) { case Idle: if (symbolLayoutsNeedPreparation) { - attemptPlacement(); + // symbolLayoutsNeedPreparation can only be set true by parsing + // and the parse result can only be cleared by performSymbolLayout + // which also clears symbolLayoutsNeedPreparation + assert(hasPendingParseResult()); + performSymbolLayout(); coalesce(); } break; case Coalescing: if (symbolLayoutsNeedPreparation) { - state = NeedPlacement; + state = NeedsSymbolLayout; } break; - case NeedPlacement: - case NeedLayout: + case NeedsSymbolLayout: + case NeedsParse: break; } } catch (...) { @@ -191,13 +224,16 @@ void GeometryTileWorker::coalesced() { state = Idle; break; - case NeedLayout: - redoLayout(); + case NeedsParse: + parse(); coalesce(); break; - case NeedPlacement: - attemptPlacement(); + case NeedsSymbolLayout: + // We may have entered NeedsSymbolLayout while coalescing + // after a performSymbolLayout. In that case, we need to + // start over with parsing in order to do another layout. + hasPendingParseResult() ? performSymbolLayout() : parse(); coalesce(); break; } @@ -279,7 +315,7 @@ static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vecto return renderLayers; } -void GeometryTileWorker::redoLayout() { +void GeometryTileWorker::parse() { if (!data || !layers) { return; } @@ -292,8 +328,8 @@ void GeometryTileWorker::redoLayout() { } std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap; - std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; - auto featureIndex = std::make_unique<FeatureIndex>(); + buckets.clear(); + featureIndex = std::make_unique<FeatureIndex>(*data ? (*data)->clone() : nullptr); BucketParameters parameters { id, mode, pixelRatio }; GlyphDependencies glyphDependencies; @@ -368,13 +404,7 @@ void GeometryTileWorker::redoLayout() { requestNewGlyphs(glyphDependencies); requestNewImages(imageDependencies); - parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult { - std::move(buckets), - std::move(featureIndex), - *data ? (*data)->clone() : nullptr, - }, correlationID); - - attemptPlacement(); + performSymbolLayout(); } bool GeometryTileWorker::hasPendingSymbolDependencies() const { @@ -385,9 +415,13 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const { } return !pendingImageDependencies.empty(); } + +bool GeometryTileWorker::hasPendingParseResult() const { + return bool(featureIndex); +} -void GeometryTileWorker::attemptPlacement() { - if (!data || !layers || hasPendingSymbolDependencies()) { +void GeometryTileWorker::performSymbolLayout() { + if (!data || !layers || !hasPendingParseResult() || hasPendingSymbolDependencies()) { return; } @@ -414,8 +448,6 @@ void GeometryTileWorker::attemptPlacement() { symbolLayoutsNeedPreparation = false; } - std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; - for (auto& symbolLayout : symbolLayouts) { if (obsolete) { return; @@ -435,11 +467,12 @@ void GeometryTileWorker::attemptPlacement() { } firstLoad = false; - - parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult { + + parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult { std::move(buckets), + std::move(featureIndex), std::move(glyphAtlasImage), - std::move(iconAtlasImage), + std::move(iconAtlasImage) }, correlationID); } diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp index 0276392679..b5417c8114 100644 --- a/src/mbgl/tile/geometry_tile_worker.hpp +++ b/src/mbgl/tile/geometry_tile_worker.hpp @@ -8,6 +8,8 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/immutable.hpp> #include <mbgl/style/layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/renderer/bucket.hpp> #include <atomic> #include <memory> @@ -43,8 +45,8 @@ public: private: void coalesced(); - void redoLayout(); - void attemptPlacement(); + void parse(); + void performSymbolLayout(); void coalesce(); @@ -53,6 +55,7 @@ private: void symbolDependenciesChanged(); bool hasPendingSymbolDependencies() const; + bool hasPendingParseResult() const; ActorRef<GeometryTileWorker> self; ActorRef<GeometryTile> parent; @@ -62,12 +65,15 @@ private: const std::atomic<bool>& obsolete; const MapMode mode; const float pixelRatio; + + std::unique_ptr<FeatureIndex> featureIndex; + std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; enum State { Idle, Coalescing, - NeedLayout, - NeedPlacement + NeedsParse, + NeedsSymbolLayout }; State state = Idle; diff --git a/src/mbgl/util/color.cpp b/src/mbgl/util/color.cpp index c8145d36e7..55f1b65436 100644 --- a/src/mbgl/util/color.cpp +++ b/src/mbgl/util/color.cpp @@ -23,11 +23,25 @@ optional<Color> Color::parse(const std::string& s) { } std::string Color::stringify() const { + std::array<double, 4> array = toArray(); return "rgba(" + - util::toString(r * 255 / a) + "," + - util::toString(g * 255 / a) + "," + - util::toString(b * 255 / a) + "," + - util::toString(a) + ")"; + util::toString(array[0]) + "," + + util::toString(array[1]) + "," + + util::toString(array[2]) + "," + + util::toString(array[3]) + ")"; +} + +std::array<double, 4> Color::toArray() const { + if (a == 0) { + return {{ 0, 0, 0, 0 }}; + } else { + return {{ + r * 255 / a, + g * 255 / a, + b * 255 / a, + a, + }}; + } } } // namespace mbgl |