summaryrefslogtreecommitdiff
path: root/src/mbgl
diff options
context:
space:
mode:
authorRandall Lee <randall.lee@mapbox.com>2018-05-22 14:09:36 -0400
committerGitHub <noreply@github.com>2018-05-22 14:09:36 -0400
commitd858cb783b499a1cc77b48a0faee137ca5e6a423 (patch)
tree5c36c353730e3050d44e472545519c0429723266 /src/mbgl
parentf93d722458be62d567aa152711a014ef51a90193 (diff)
parent60505b03174b5ec02ae723beafa7683f6ed54a62 (diff)
downloadqtlocation-mapboxgl-upstream/rclee-async-setup.tar.gz
Merge branch 'master' into rclee-async-setupupstream/rclee-async-setup
Diffstat (limited to 'src/mbgl')
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp4
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp2
-rw-r--r--src/mbgl/geometry/feature_index.cpp95
-rw-r--r--src/mbgl/geometry/feature_index.hpp52
-rw-r--r--src/mbgl/gl/attribute.cpp14
-rw-r--r--src/mbgl/gl/attribute.hpp18
-rw-r--r--src/mbgl/gl/context.cpp10
-rw-r--r--src/mbgl/gl/context.hpp4
-rw-r--r--src/mbgl/gl/program.hpp2
-rw-r--r--src/mbgl/gl/vertex_array.cpp6
-rw-r--r--src/mbgl/gl/vertex_array.hpp13
-rw-r--r--src/mbgl/layout/symbol_layout.cpp17
-rw-r--r--src/mbgl/layout/symbol_layout.hpp9
-rw-r--r--src/mbgl/map/map.cpp5
-rw-r--r--src/mbgl/map/transform.cpp57
-rw-r--r--src/mbgl/map/transform.hpp2
-rw-r--r--src/mbgl/map/transform_state.cpp13
-rw-r--r--src/mbgl/map/transform_state.hpp1
-rw-r--r--src/mbgl/programs/program.hpp34
-rw-r--r--src/mbgl/programs/symbol_program.hpp50
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp4
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp6
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp5
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp64
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp103
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp98
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp73
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.cpp74
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_hillshade_layer.cpp65
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp34
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp33
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp31
-rw-r--r--src/mbgl/renderer/render_layer.cpp29
-rw-r--r--src/mbgl/renderer/render_layer.hpp16
-rw-r--r--src/mbgl/renderer/render_source.hpp2
-rw-r--r--src/mbgl/renderer/render_tile.cpp70
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp82
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp8
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp30
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_dem_source.cpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_dem_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp2
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp13
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp2
-rw-r--r--src/mbgl/shaders/line.cpp3
-rw-r--r--src/mbgl/storage/resource.cpp8
-rw-r--r--src/mbgl/style/conversion/filter.cpp343
-rw-r--r--src/mbgl/style/conversion/stringify.hpp159
-rw-r--r--src/mbgl/style/conversion/tileset.cpp17
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp332
-rw-r--r--src/mbgl/style/expression/literal.cpp8
-rw-r--r--src/mbgl/style/expression/match.cpp18
-rw-r--r--src/mbgl/style/filter.cpp12
-rw-r--r--src/mbgl/style/filter_evaluator.cpp225
-rw-r--r--src/mbgl/text/collision_index.cpp61
-rw-r--r--src/mbgl/text/collision_index.hpp5
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp6
-rw-r--r--src/mbgl/text/placement.cpp45
-rw-r--r--src/mbgl/text/placement.hpp20
-rw-r--r--src/mbgl/tile/custom_geometry_tile.cpp1
-rw-r--r--src/mbgl/tile/geojson_tile.cpp1
-rw-r--r--src/mbgl/tile/geometry_tile.cpp55
-rw-r--r--src/mbgl/tile/geometry_tile.hpp12
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp4
-rw-r--r--src/mbgl/tile/tile.cpp6
-rw-r--r--src/mbgl/tile/tile.hpp9
-rw-r--r--src/mbgl/util/compression.cpp2
-rw-r--r--src/mbgl/util/http_header.cpp1
-rw-r--r--src/mbgl/util/i18n.cpp37
-rw-r--r--src/mbgl/util/i18n.hpp2
-rw-r--r--src/mbgl/util/intersection_tests.cpp9
-rw-r--r--src/mbgl/util/intersection_tests.hpp1
-rw-r--r--src/mbgl/util/io.cpp4
-rw-r--r--src/mbgl/util/throttler.cpp36
-rw-r--r--src/mbgl/util/throttler.hpp22
-rw-r--r--src/mbgl/util/tile_cover.cpp104
-rw-r--r--src/mbgl/util/tile_cover.hpp24
-rw-r--r--src/mbgl/util/tile_cover_impl.cpp365
-rw-r--r--src/mbgl/util/tile_cover_impl.hpp90
-rw-r--r--src/mbgl/util/tiny_sdf.cpp2
-rw-r--r--src/mbgl/util/token.hpp11
-rw-r--r--src/mbgl/util/url.cpp5
95 files changed, 2115 insertions, 1243 deletions
diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp
index a237100d13..7d776f21c4 100644
--- a/src/mbgl/annotation/render_annotation_source.cpp
+++ b/src/mbgl/annotation/render_annotation_source.cpp
@@ -65,8 +65,8 @@ RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
+ const mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
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 e812ec2883..da87d13814 100644
--- a/src/mbgl/annotation/render_annotation_source.hpp
+++ b/src/mbgl/annotation/render_annotation_source.hpp
@@ -28,7 +28,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index c67786274a..cc69a60ca0 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -7,7 +7,6 @@
#include <mbgl/util/math.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/style/filter.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mapbox/geometry/envelope.hpp>
@@ -25,34 +24,30 @@ FeatureIndex::FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_)
void FeatureIndex::insert(const GeometryCollection& geometries,
std::size_t index,
const std::string& sourceLayerName,
- const std::string& bucketName) {
+ const std::string& bucketLeaderID) {
for (const auto& ring : geometries) {
auto envelope = mapbox::geometry::envelope(ring);
- grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++),
- {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
+ if (envelope.min.x < util::EXTENT &&
+ envelope.min.y < util::EXTENT &&
+ envelope.max.x >= 0 &&
+ envelope.max.y >= 0) {
+ grid.insert(IndexedSubfeature(index, sourceLayerName, bucketLeaderID, sortIndex++),
+ {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
+ }
}
}
-static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) {
- return a.sortIndex > b.sortIndex;
-}
-
-static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) {
- return a.sortIndex < b.sortIndex;
-}
-
void FeatureIndex::query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
- const float bearing,
+ const TransformState& transformState,
+ const mat4& posMatrix,
const double tileSize,
const double scale,
const RenderedQueryOptions& queryOptions,
const UnwrappedTileID& tileID,
- const std::string& sourceID,
const std::vector<const RenderLayer*>& layers,
- const CollisionIndex& collisionIndex,
- const float additionalQueryRadius) const {
+ const float additionalQueryPadding) const {
if (!tileData) {
return;
@@ -60,15 +55,17 @@ void FeatureIndex::query(
// Determine query radius
const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
- const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits);
+ const int16_t additionalPadding = std::min<int16_t>(util::EXTENT, additionalQueryPadding * pixelsToTileUnits);
// Query the grid index
mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry);
- std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalRadius),
- convertPoint<float>(box.max + additionalRadius) });
+ std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalPadding),
+ convertPoint<float>(box.max + additionalPadding) });
- std::sort(features.begin(), features.end(), topDown);
+ std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) {
+ return a.sortIndex > b.sortIndex;
+ });
size_t previousSortIndex = std::numeric_limits<size_t>::max();
for (const auto& indexedFeature : features) {
@@ -76,25 +73,59 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, transformState, pixelsToTileUnits, posMatrix);
}
+}
+
+std::unordered_map<std::string, std::vector<Feature>> FeatureIndex::lookupSymbolFeatures(const std::vector<IndexedSubfeature>& symbolFeatures,
+ const RenderedQueryOptions& queryOptions,
+ const std::vector<const RenderLayer*>& layers,
+ const OverscaledTileID& tileID,
+ const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const {
+ std::unordered_map<std::string, std::vector<Feature>> result;
+ if (!tileData) {
+ return result;
+ }
+ std::vector<IndexedSubfeature> sortedFeatures(symbolFeatures.begin(), symbolFeatures.end());
+
+ std::sort(sortedFeatures.begin(), sortedFeatures.end(), [featureSortOrder](const IndexedSubfeature& a, const IndexedSubfeature& b) {
+ // Same idea as the non-symbol sort order, but symbol features may have changed their sort order
+ // since their corresponding IndexedSubfeature was added to the CollisionIndex
+ // The 'featureSortOrder' is relatively inefficient for querying but cheap to build on every bucket sort
+ if (featureSortOrder) {
+ // queryRenderedSymbols documentation says we'll return features in
+ // "top-to-bottom" rendering order (aka last-to-first).
+ // Actually there can be multiple symbol instances per feature, so
+ // we sort each feature based on the first matching symbol instance.
+ auto sortedA = std::find(featureSortOrder->begin(), featureSortOrder->end(), a.index);
+ auto sortedB = std::find(featureSortOrder->begin(), featureSortOrder->end(), b.index);
+ assert(sortedA != featureSortOrder->end());
+ assert(sortedB != featureSortOrder->end());
+ return sortedA > sortedB;
+ } else {
+ // Bucket hasn't been re-sorted based on angle, so use same "reverse of appearance in source data"
+ // logic as non-symboles
+ return a.sortIndex > b.sortIndex;
+ }
+ });
- 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, tileID.canonical, layers, bearing, pixelsToTileUnits);
+ for (const auto& symbolFeature : sortedFeatures) {
+ mat4 unusedMatrix;
+ addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, unusedMatrix);
}
+ return result;
}
void FeatureIndex::addFeature(
std::unordered_map<std::string, std::vector<Feature>>& result,
const IndexedSubfeature& indexedFeature,
- const GeometryCoordinates& queryGeometry,
const RenderedQueryOptions& options,
const CanonicalTileID& tileID,
const std::vector<const RenderLayer*>& layers,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const GeometryCoordinates& queryGeometry,
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const {
auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* {
for (const auto& layer : layers) {
@@ -109,7 +140,7 @@ void FeatureIndex::addFeature(
std::unique_ptr<GeometryTileLayer> sourceLayer;
std::unique_ptr<GeometryTileFeature> geometryTileFeature;
- for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) {
+ for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketLeaderID)) {
const RenderLayer* renderLayer = getRenderLayer(layerID);
if (!renderLayer) {
continue;
@@ -124,7 +155,7 @@ void FeatureIndex::addFeature(
}
if (!renderLayer->is<RenderSymbolLayer>() &&
- !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) {
+ !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, transformState, pixelsToTileUnits, posMatrix)) {
continue;
}
@@ -158,8 +189,8 @@ optional<GeometryCoordinates> FeatureIndex::translateQueryGeometry(
return translated;
}
-void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs) {
- bucketLayerIDs[bucketName] = layerIDs;
+void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs) {
+ bucketLayerIDs[bucketLeaderID] = layerIDs;
}
} // namespace mbgl
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index 9e0c172342..739c1f282f 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -5,6 +5,7 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/grid_index.hpp>
#include <mbgl/util/feature.hpp>
+#include <mbgl/util/mat4.hpp>
#include <vector>
#include <string>
@@ -14,6 +15,7 @@ namespace mbgl {
class RenderedQueryOptions;
class RenderLayer;
+class TransformState;
class CollisionIndex;
@@ -23,29 +25,26 @@ public:
IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_)
: index(index_)
, sourceLayerName(std::move(sourceLayerName_))
- , bucketName(std::move(bucketName_))
+ , bucketLeaderID(std::move(bucketName_))
, sortIndex(sortIndex_)
- , tileID(0, 0, 0)
+ , bucketInstanceId(0)
{}
- IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_,
- std::string sourceID_, CanonicalTileID tileID_)
- : index(index_)
- , sourceLayerName(std::move(sourceLayerName_))
- , bucketName(std::move(bucketName_))
- , sortIndex(std::move(sortIndex_))
- , sourceID(std::move(sourceID_))
- , tileID(std::move(tileID_))
- {}
+ IndexedSubfeature(const IndexedSubfeature& other, uint32_t bucketInstanceId_)
+ : index(other.index)
+ , sourceLayerName(other.sourceLayerName)
+ , bucketLeaderID(other.bucketLeaderID)
+ , sortIndex(other.sortIndex)
+ , bucketInstanceId(bucketInstanceId_)
+ {}
size_t index;
std::string sourceLayerName;
- std::string bucketName;
+ std::string bucketLeaderID;
size_t sortIndex;
// Only used for symbol features
- std::string sourceID;
- CanonicalTileID tileID;
+ uint32_t bucketInstanceId;
};
class FeatureIndex {
@@ -54,20 +53,19 @@ public:
const GeometryTileData* getData() { return tileData.get(); }
- void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName);
+ void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketLeaderID);
void query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
- const float bearing,
+ const TransformState&,
+ const mat4& posMatrix,
const double tileSize,
const double scale,
const RenderedQueryOptions& options,
const UnwrappedTileID&,
- const std::string&,
const std::vector<const RenderLayer*>&,
- const CollisionIndex&,
- const float additionalQueryRadius) const;
+ const float additionalQueryPadding) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -76,18 +74,26 @@ public:
const float bearing,
const float pixelsToTileUnits);
- void setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs);
+ void setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs);
+
+ std::unordered_map<std::string, std::vector<Feature>> lookupSymbolFeatures(
+ const std::vector<IndexedSubfeature>& symbolFeatures,
+ const RenderedQueryOptions& options,
+ const std::vector<const RenderLayer*>& layers,
+ const OverscaledTileID& tileID,
+ const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const;
private:
void addFeature(
std::unordered_map<std::string, std::vector<Feature>>& result,
const IndexedSubfeature&,
- const GeometryCoordinates& queryGeometry,
const RenderedQueryOptions& options,
const CanonicalTileID&,
const std::vector<const RenderLayer*>&,
- const float bearing,
- const float pixelsToTileUnits) const;
+ const GeometryCoordinates& queryGeometry,
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const;
GridIndex<IndexedSubfeature> grid;
unsigned int sortIndex = 0;
diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp
index bb5b2ddc34..b2d05fe665 100644
--- a/src/mbgl/gl/attribute.cpp
+++ b/src/mbgl/gl/attribute.cpp
@@ -1,14 +1,20 @@
#include <mbgl/gl/attribute.hpp>
+#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
namespace mbgl {
namespace gl {
-void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) {
- if (location >= MAX_ATTRIBUTES) {
- throw gl::Error("too many vertex attributes");
+void bindAttributeLocation(Context& context, ProgramID id, AttributeLocation location, const char* name) {
+ // We're using sequentially numberered attribute locations starting with 0. Therefore we can use
+ // the location as a proxy for the number of attributes.
+ if (location >= context.maximumVertexBindingCount) {
+ // Don't bind the location on this hardware since it exceeds the limit (otherwise we'd get
+ // an OpenGL error). This means we'll see rendering errors, and possibly slow rendering due
+ // to unbound attributes.
+ } else {
+ MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name));
}
- MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name));
}
std::set<std::string> getActiveAttributes(ProgramID id) {
diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp
index fa6c2ddeab..3763f0a583 100644
--- a/src/mbgl/gl/attribute.hpp
+++ b/src/mbgl/gl/attribute.hpp
@@ -17,8 +17,6 @@
namespace mbgl {
namespace gl {
-static constexpr std::size_t MAX_ATTRIBUTES = 8;
-
template <class> struct DataTypeOf;
template <> struct DataTypeOf< int8_t> : std::integral_constant<DataType, DataType::Byte> {};
template <> struct DataTypeOf<uint8_t> : std::integral_constant<DataType, DataType::UnsignedByte> {};
@@ -45,7 +43,7 @@ public:
}
};
-using AttributeBindingArray = std::array<optional<AttributeBinding>, MAX_ATTRIBUTES>;
+using AttributeBindingArray = std::vector<optional<AttributeBinding>>;
/*
gl::Attribute<T,N> manages the binding of a vertex buffer to a GL program attribute.
@@ -214,7 +212,8 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = {
} // namespace detail
-void bindAttributeLocation(ProgramID, AttributeLocation, const char * name);
+class Context;
+void bindAttributeLocation(Context&, ProgramID, AttributeLocation, const char * name);
std::set<std::string> getActiveAttributes(ProgramID);
template <class... As>
@@ -231,13 +230,13 @@ public:
using Vertex = detail::Vertex<typename As::Type...>;
- static Locations bindLocations(const ProgramID& id) {
+ static Locations bindLocations(Context& context, const ProgramID& id) {
std::set<std::string> activeAttributes = getActiveAttributes(id);
AttributeLocation location = 0;
auto maybeBindLocation = [&](const char* name) -> optional<AttributeLocation> {
if (activeAttributes.count(name)) {
- bindAttributeLocation(id, location, name);
+ bindAttributeLocation(context, id, location, name);
return location++;
} else {
return {};
@@ -277,6 +276,7 @@ public:
static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) {
AttributeBindingArray result;
+ result.resize(sizeof...(As));
auto maybeAddBinding = [&] (const optional<AttributeLocation>& location,
const optional<AttributeBinding>& binding) {
@@ -289,6 +289,12 @@ public:
return result;
}
+
+ static uint32_t activeBindingCount(const Bindings& bindings) {
+ uint32_t result = 0;
+ util::ignore({ ((result += bool(bindings.template get<As>())), 0)... });
+ return result;
+ }
};
namespace detail {
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index ba44adb42b..c6352d3e84 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -94,7 +94,13 @@ static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "Ope
static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch");
-Context::Context() = default;
+Context::Context()
+ : maximumVertexBindingCount([] {
+ GLint value;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value));
+ return value;
+ }()) {
+}
Context::~Context() {
if (cleanupOnDestruction) {
@@ -351,7 +357,7 @@ VertexArray Context::createVertexArray() {
VertexArrayID id = 0;
MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id));
UniqueVertexArray vao(std::move(id), { this });
- return { UniqueVertexArrayState(new VertexArrayState(std::move(vao), *this), VertexArrayStateDeleter { true })};
+ return { UniqueVertexArrayState(new VertexArrayState(std::move(vao)), VertexArrayStateDeleter { true })};
} else {
// On GL implementations which do not support vertex arrays, attribute bindings are global state.
// So return a VertexArray which shares our global state tracking and whose deleter is a no-op.
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 67624288e2..d95311115e 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -226,7 +226,7 @@ public:
State<value::BindVertexBuffer> vertexBuffer;
State<value::BindVertexArray, const Context&> bindVertexArray { *this };
- VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this };
+ VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }) };
State<value::PixelStorePack> pixelStorePack;
State<value::PixelStoreUnpack> pixelStoreUnpack;
@@ -239,6 +239,8 @@ public:
#endif // MBGL_USE_GLES2
bool supportsHalfFloatTextures = false;
+ const uint32_t maximumVertexBindingCount;
+ static constexpr const uint32_t minimumRequiredVertexBindingCount = 8;
private:
State<value::StencilFunc> stencilFunc;
diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp
index af02ad3d54..f33501cd11 100644
--- a/src/mbgl/gl/program.hpp
+++ b/src/mbgl/gl/program.hpp
@@ -35,7 +35,7 @@ public:
context.createProgram(context.createShader(ShaderType::Vertex, vertexSource),
context.createShader(ShaderType::Fragment, fragmentSource))),
uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))),
- attributeLocations(Attributes::bindLocations(program)) {
+ attributeLocations(Attributes::bindLocations(context, program)) {
// Re-link program after manually binding only active attributes in Attributes::bindLocations
context.linkProgram(program);
diff --git a/src/mbgl/gl/vertex_array.cpp b/src/mbgl/gl/vertex_array.cpp
index 68a500ac45..d552a8292e 100644
--- a/src/mbgl/gl/vertex_array.cpp
+++ b/src/mbgl/gl/vertex_array.cpp
@@ -9,7 +9,11 @@ void VertexArray::bind(Context& context, BufferID indexBuffer, const AttributeBi
context.bindVertexArray = state->vertexArray;
state->indexBuffer = indexBuffer;
- for (AttributeLocation location = 0; location < MAX_ATTRIBUTES; ++location) {
+ state->bindings.reserve(bindings.size());
+ for (AttributeLocation location = 0; location < bindings.size(); ++location) {
+ if (state->bindings.size() <= location) {
+ state->bindings.emplace_back(context, AttributeLocation(location));
+ }
state->bindings[location] = bindings[location];
}
}
diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp
index 46c67017bb..604754f672 100644
--- a/src/mbgl/gl/vertex_array.hpp
+++ b/src/mbgl/gl/vertex_array.hpp
@@ -15,9 +15,8 @@ class Context;
class VertexArrayState {
public:
- VertexArrayState(UniqueVertexArray vertexArray_, Context& context)
- : vertexArray(std::move(vertexArray_)),
- bindings(makeBindings(context, std::make_index_sequence<MAX_ATTRIBUTES>())) {
+ VertexArrayState(UniqueVertexArray vertexArray_)
+ : vertexArray(std::move(vertexArray_)) {
}
void setDirty() {
@@ -31,13 +30,7 @@ public:
State<value::BindElementBuffer> indexBuffer;
using AttributeState = State<value::VertexAttribute, Context&, AttributeLocation>;
- std::array<AttributeState, MAX_ATTRIBUTES> bindings;
-
-private:
- template <std::size_t... I>
- std::array<AttributeState, MAX_ATTRIBUTES> makeBindings(Context& context, std::index_sequence<I...>) {
- return {{ AttributeState { context, I }... }};
- }
+ std::vector<AttributeState> bindings;
};
class VertexArrayStateDeleter {
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 82a9255824..4249ffc24e 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -2,7 +2,6 @@
#include <mbgl/layout/merge_lines.hpp>
#include <mbgl/layout/clip_lines.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/image_atlas.hpp>
@@ -42,7 +41,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
std::unique_ptr<GeometryTileLayer> sourceLayer_,
ImageDependencies& imageDependencies,
GlyphDependencies& glyphDependencies)
- : bucketName(layers.at(0)->getID()),
+ : bucketLeaderID(layers.at(0)->getID()),
sourceLayer(std::move(sourceLayer_)),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
@@ -181,8 +180,7 @@ bool SymbolLayout::hasSymbolInstances() const {
}
void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
- const ImageMap& imageMap, const ImagePositions& imagePositions,
- const OverscaledTileID& tileID, const std::string& sourceID) {
+ const ImageMap& imageMap, const ImagePositions& imagePositions) {
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
@@ -253,7 +251,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
// if either shapedText or icon position is present, add the feature
if (shapedTextOrientations.first || shapedIcon) {
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID);
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap);
}
feature.geometry.clear();
@@ -266,9 +264,7 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolFeature& feature,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap& glyphPositionMap,
- const OverscaledTileID& tileID,
- const std::string& sourceID) {
+ const GlyphPositionMap& glyphPositionMap) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
@@ -297,8 +293,7 @@ void SymbolLayout::addFeature(const std::size_t index,
: layout.get<SymbolPlacement>();
const float textRepeatDistance = symbolSpacing / 2;
- IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(),
- sourceID, tileID.canonical);
+ IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size());
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
@@ -424,7 +419,7 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes)
const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() ||
layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>();
- auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances));
+ auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, bucketLeaderID, std::move(symbolInstances));
for (SymbolInstance &symbolInstance : bucket->symbolInstances) {
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 6951c29ada..43b577859f 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -34,8 +34,7 @@ public:
GlyphDependencies&);
void prepare(const GlyphMap&, const GlyphPositions&,
- const ImageMap&, const ImagePositions&,
- const OverscaledTileID&, const std::string&);
+ const ImageMap&, const ImagePositions&);
std::unique_ptr<SymbolBucket> place(const bool showCollisionBoxes);
@@ -44,7 +43,7 @@ public:
std::map<std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties;
- const std::string bucketName;
+ const std::string bucketLeaderID;
std::vector<SymbolInstance> symbolInstances;
private:
@@ -52,9 +51,7 @@ private:
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap&,
- const OverscaledTileID&,
- const std::string&);
+ const GlyphPositionMap&);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index a7beb0f1e2..d81544eed5 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -747,6 +747,11 @@ void Map::Impl::onInvalidate() {
}
void Map::Impl::onUpdate() {
+ // Don't load/render anything in still mode until explicitly requested.
+ if (mode != MapMode::Continuous && !stillImageRequest) {
+ return;
+ }
+
TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
transform.updateTransitions(timePoint);
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 105adf0400..da8e243d91 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -594,13 +594,10 @@ void Transform::startTransition(const CameraOptions& camera,
animation.transitionFrameFn(t);
}
observer.onCameraIsChanging();
+ return false;
} else {
- transitionFinishFn();
- transitionFinishFn = nullptr;
-
- // This callback gets destroyed here,
- // we can only return after this point.
- transitionFrameFn = nullptr;
+ // Indicate that we need to terminate this transition
+ return true;
}
};
@@ -615,7 +612,14 @@ void Transform::startTransition(const CameraOptions& camera,
};
if (!isAnimated) {
- transitionFrameFn(Clock::now());
+ auto update = std::move(transitionFrameFn);
+ auto finish = std::move(transitionFinishFn);
+
+ transitionFrameFn = nullptr;
+ transitionFinishFn = nullptr;
+
+ update(Clock::now());
+ finish();
}
}
@@ -624,8 +628,43 @@ bool Transform::inTransition() const {
}
void Transform::updateTransitions(const TimePoint& now) {
- if (transitionFrameFn) {
- transitionFrameFn(now);
+
+ // Use a temporary function to ensure that the transitionFrameFn lambda is
+ // called only once per update.
+
+ // This addresses the symptoms of https://github.com/mapbox/mapbox-gl-native/issues/11180
+ // where setting a shape source to nil (or similar) in the `onCameraIsChanging`
+ // observer function causes `Map::Impl::onUpdate()` to be called which
+ // in turn calls this function (before the current iteration has completed),
+ // leading to an infinite loop. See https://github.com/mapbox/mapbox-gl-native/issues/5833
+ // for a similar, related, issue.
+ //
+ // By temporarily nulling the `transitionFrameFn` (and then restoring it
+ // after the temporary has been called) we stop this recursion.
+ //
+ // It's important to note that the scope of this change is stop the above
+ // crashes. It doesn't address any potential deeper issue (for example
+ // user error, how often and when transition callbacks are called).
+
+ auto transition = std::move(transitionFrameFn);
+ transitionFrameFn = nullptr;
+
+ if (transition && transition(now)) {
+ // If the transition indicates that it is complete, then we should call
+ // the finish lambda (going via a temporary as above)
+ auto finish = std::move(transitionFinishFn);
+
+ transitionFinishFn = nullptr;
+ transitionFrameFn = nullptr;
+
+ if (finish) {
+ finish();
+ }
+ } else if (!transitionFrameFn) {
+ // We have to check `transitionFrameFn` is nil here, since a new transition
+ // may have been triggered in a user callback (from the transition call
+ // above)
+ transitionFrameFn = std::move(transition);
}
}
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index d429c57661..bff44a2dcd 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -165,7 +165,7 @@ private:
TimePoint transitionStart;
Duration transitionDuration;
- std::function<void(const TimePoint)> transitionFrameFn;
+ std::function<bool(const TimePoint)> transitionFrameFn;
std::function<void()> transitionFinishFn;
};
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 18d2c24aee..a85b251fb4 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -421,4 +421,17 @@ float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) con
return projectedCenter[3];
}
+float TransformState::maxPitchScaleFactor() const {
+ if (size.isEmpty()) {
+ return {};
+ }
+ auto latLng = screenCoordinateToLatLng({ 0, static_cast<float>(getSize().height) });
+ mat4 mat = coordinatePointMatrix(getZoom());
+ Point<double> pt = Projection::project(latLng, scale) / double(util::tileSize);
+ vec4 p = {{ pt.x, pt.y, 0, 1 }};
+ vec4 topPoint;
+ matrix::transformMat4(topPoint, p, mat);
+ return topPoint[3] / getCameraToCenterDistance();
+}
+
} // namespace mbgl
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 451802034d..b6f8ae4424 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -87,6 +87,7 @@ public:
}
float getCameraToTileDistance(const UnwrappedTileID&) const;
+ float maxPitchScaleFactor() const;
private:
bool rotatedNorth() const;
diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp
index bcdb270b9c..4d5de05337 100644
--- a/src/mbgl/programs/program.hpp
+++ b/src/mbgl/programs/program.hpp
@@ -46,26 +46,38 @@ public:
Shaders::fragmentSource)) {
}
+ static typename AllUniforms::Values computeAllUniformValues(
+ const UniformValues& uniformValues,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
+ float currentZoom) {
+ return uniformValues
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+ }
+
+ static typename Attributes::Bindings computeAllAttributeBindings(
+ const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties) {
+ return LayoutAttributes::bindings(layoutVertexBuffer)
+ .concat(paintPropertyBinders.attributeBindings(currentProperties));
+ }
+
+ static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) {
+ return Attributes::activeBindingCount(allAttributeBindings);
+ }
+
template <class DrawMode>
void draw(gl::Context& context,
DrawMode drawMode,
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
- const UniformValues& uniformValues,
- const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
const gl::IndexBuffer<DrawMode>& indexBuffer,
const SegmentVector<Attributes>& segments,
- const PaintPropertyBinders& paintPropertyBinders,
- const typename PaintProperties::PossiblyEvaluated& currentProperties,
- float currentZoom,
+ const typename AllUniforms::Values& allUniformValues,
+ const typename Attributes::Bindings& allAttributeBindings,
const std::string& layerID) {
- typename AllUniforms::Values allUniformValues = uniformValues
- .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
-
- typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(paintPropertyBinders.attributeBindings(currentProperties));
-
for (auto& segment : segments) {
auto vertexArrayIt = segment.vertexArrays.find(layerID);
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp
index 9b5037ed9f..51f9a48f7f 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -278,35 +278,45 @@ public:
Shaders::fragmentSource)) {
}
+ static typename AllUniforms::Values computeAllUniformValues(
+ const UniformValues& uniformValues,
+ const SymbolSizeBinder& symbolSizeBinder,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
+ float currentZoom) {
+ return uniformValues.concat(symbolSizeBinder.uniformValues(currentZoom))
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+ }
+
+ static typename Attributes::Bindings computeAllAttributeBindings(
+ const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
+ const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer,
+ const gl::VertexBuffer<SymbolOpacityAttributes::Vertex>& opacityVertexBuffer,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties) {
+ assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount &&
+ layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount);
+ return LayoutAttributes::bindings(layoutVertexBuffer)
+ .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer))
+ .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer))
+ .concat(paintPropertyBinders.attributeBindings(currentProperties));
+ }
+
+ static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) {
+ return Attributes::activeBindingCount(allAttributeBindings);
+ }
+
template <class DrawMode>
void draw(gl::Context& context,
DrawMode drawMode,
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
- const UniformValues& uniformValues,
- const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
- const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer,
- const gl::VertexBuffer<SymbolOpacityAttributes::Vertex>& opacityVertexBuffer,
- const SymbolSizeBinder& symbolSizeBinder,
const gl::IndexBuffer<DrawMode>& indexBuffer,
const SegmentVector<Attributes>& segments,
- const PaintPropertyBinders& paintPropertyBinders,
- const typename PaintProperties::PossiblyEvaluated& currentProperties,
- float currentZoom,
+ const typename AllUniforms::Values& allUniformValues,
+ const typename Attributes::Bindings& allAttributeBindings,
const std::string& layerID) {
- typename AllUniforms::Values allUniformValues = uniformValues
- .concat(symbolSizeBinder.uniformValues(currentZoom))
- .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
-
- typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer))
- .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer))
- .concat(paintPropertyBinders.attributeBindings(currentProperties));
-
- assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount &&
- layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount);
-
for (auto& segment : segments) {
auto vertexArrayIt = segment.vertexArrays.find(layerID);
diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
index 5e2c937091..4fcc761280 100644
--- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
@@ -84,7 +84,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
if (nVertices == 0)
continue;
- auto edgeDistance = 0;
+ std::size_t edgeDistance = 0;
for (uint32_t i = 0; i < nVertices; i++) {
const auto& p1 = ring[i];
@@ -102,7 +102,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
const Point<double> perp = util::unit(util::perp(d1 - d2));
const auto dist = util::dist<int16_t>(d1, d2);
- if (dist > std::numeric_limits<int16_t>::max()) {
+ if (edgeDistance + dist > std::numeric_limits<int16_t>::max()) {
edgeDistance = 0;
}
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index 60e8a0b504..4fe03eb453 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -18,11 +18,13 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
bool sdfIcons_,
bool iconsNeedLinear_,
bool sortFeaturesByY_,
+ const std::string bucketName_,
const std::vector<SymbolInstance>&& symbolInstances_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
sortFeaturesByY(sortFeaturesByY_),
+ bucketLeaderID(std::move(bucketName_)),
symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
@@ -200,8 +202,12 @@ void SymbolBucket::sortFeatures(const float angle) {
text.triangles.clear();
icon.triangles.clear();
+ featureSortOrder = std::make_unique<std::vector<size_t>>();
+ featureSortOrder->reserve(symbolInstanceIndexes.size());
+
for (auto i : symbolInstanceIndexes) {
const SymbolInstance& symbolInstance = symbolInstances[i];
+ featureSortOrder->push_back(symbolInstance.featureIndex);
if (symbolInstance.placedTextIndex) {
addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index ed8afb052c..e4aaf5ba30 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -47,6 +47,7 @@ public:
bool sdfIcons,
bool iconsNeedLinear,
bool sortFeaturesByY,
+ const std::string bucketLeaderID,
const std::vector<SymbolInstance>&&);
void upload(gl::Context&) override;
@@ -64,6 +65,8 @@ public:
const bool iconsNeedLinear;
const bool sortFeaturesByY;
+ const std::string bucketLeaderID;
+
optional<float> sortedAngle;
bool staticUploaded = false;
@@ -131,6 +134,8 @@ public:
uint32_t bucketInstanceId = 0;
bool justReloaded = false;
+
+ std::shared_ptr<std::vector<size_t>> featureSortOrder;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
index aebc4cc9aa..2dc5fe7339 100644
--- a/src/mbgl/renderer/layers/render_background_layer.cpp
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -7,6 +7,7 @@
#include <mbgl/programs/programs.hpp>
#include <mbgl/programs/background_program.hpp>
#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/map/transform_state.hpp>
namespace mbgl {
@@ -49,6 +50,35 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
const Properties<>::PossiblyEvaluated properties;
const BackgroundProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+ auto draw = [&](auto& program, auto&& uniformValues) {
+ const auto allUniformValues = program.computeAllUniformValues(
+ std::move(uniformValues),
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = program.computeAllAttributeBindings(
+ parameters.staticData.tileVertexBuffer,
+ paintAttributeData,
+ properties
+ );
+
+ checkRenderability(parameters, program.activeBindingCount(allAttributeBindings));
+
+ program.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ allUniformValues,
+ allAttributeBindings,
+ getID()
+ );
+ };
+
if (!evaluated.get<BackgroundPattern>().to.empty()) {
optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from);
optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to);
@@ -59,12 +89,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
parameters.imageManager.bind(parameters.context, 0);
for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
- parameters.programs.backgroundPattern.draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
+ draw(
+ parameters.programs.backgroundPattern,
BackgroundPatternUniforms::values(
parameters.matrixForTile(tileID),
evaluated.get<BackgroundOpacity>(),
@@ -74,36 +100,18 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
evaluated.get<BackgroundPattern>(),
tileID,
parameters.state
- ),
- parameters.staticData.tileVertexBuffer,
- parameters.staticData.quadTriangleIndexBuffer,
- parameters.staticData.tileTriangleSegments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
- getID()
+ )
);
}
} else {
for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
- parameters.programs.background.draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
+ draw(
+ parameters.programs.background,
BackgroundProgram::UniformValues {
uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) },
uniforms::u_color::Value{ evaluated.get<BackgroundColor>() },
uniforms::u_opacity::Value{ evaluated.get<BackgroundOpacity>() },
- },
- parameters.staticData.tileVertexBuffer,
- parameters.staticData.quadTriangleIndexBuffer,
- parameters.staticData.tileTriangleSegments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
- getID()
+ }
);
}
}
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
index 6092ff5452..b433a9d3fa 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.cpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -59,14 +59,11 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)));
CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl));
- parameters.programs.circle.get(evaluated).draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- parameters.mapMode != MapMode::Continuous
- ? parameters.stencilModeForClipping(tile.clip)
- : gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
+ const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
+
+ auto& programInstance = parameters.programs.circle.get(evaluated);
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
CircleProgram::UniformValues {
uniforms::u_matrix::Value{
tile.translatedMatrix(evaluated.get<CircleTranslate>(),
@@ -82,38 +79,104 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
uniforms::u_pitch_with_map::Value{ pitchWithMap }
},
+ paintPropertyBinders,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
*bucket.vertexBuffer,
+ paintPropertyBinders,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ parameters.mapMode != MapMode::Continuous
+ ? parameters.stencilModeForClipping(tile.clip)
+ : gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
*bucket.indexBuffer,
bucket.segments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
}
}
+GeometryCoordinate projectPoint(const GeometryCoordinate& p, const mat4& posMatrix, const Size& size) {
+ vec4 pos = {{ static_cast<double>(p.x), static_cast<double>(p.y), 0, 1 }};
+ matrix::transformMat4(pos, pos, posMatrix);
+ return {
+ static_cast<int16_t>((static_cast<float>(pos[0] / pos[3]) + 1) * size.width * 0.5),
+ static_cast<int16_t>((static_cast<float>(pos[1] / pos[3]) + 1) * size.height * 0.5)
+ };
+}
+
+GeometryCoordinates projectQueryGeometry(const GeometryCoordinates& queryGeometry, const mat4& posMatrix, const Size& size) {
+ GeometryCoordinates projectedGeometry;
+ for (auto& p : queryGeometry) {
+ projectedGeometry.push_back(projectPoint(p, posMatrix, size));
+ }
+ return projectedGeometry;
+}
+
bool RenderCircleLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const {
// Translate query geometry
- auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ const GeometryCoordinates& translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::CircleTranslate>(),
evaluated.get<style::CircleTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
+ transformState.getAngle(),
+ pixelsToTileUnits).value_or(queryGeometry);
// Evaluate functions
- auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature) * pixelsToTileUnits;
- auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature) * pixelsToTileUnits;
+ auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature);
+ auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature);
+ auto size = radius + stroke;
+
+ // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile
+ // Otherwise, compare geometry in the plane of the viewport
+ // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance
+ // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance
+ bool alignWithMap = evaluated.evaluate<style::CirclePitchAlignment>(zoom, feature) == AlignmentType::Map;
+ const GeometryCoordinates& transformedQueryGeometry = alignWithMap ?
+ translatedQueryGeometry :
+ projectQueryGeometry(translatedQueryGeometry, posMatrix, transformState.getSize());
+ auto transformedSize = alignWithMap ? size * pixelsToTileUnits : size;
+
+ auto geometry = feature.getGeometries();
+ for (auto& ring : geometry) {
+ for (auto& point : ring) {
+ const GeometryCoordinate& transformedPoint = alignWithMap ? point : projectPoint(point, posMatrix, transformState.getSize());
+
+ float adjustedSize = transformedSize;
+ vec4 center = {{ static_cast<double>(point.x), static_cast<double>(point.y), 0, 1 }};
+ matrix::transformMat4(center, center, posMatrix);
+ auto pitchScale = evaluated.evaluate<style::CirclePitchScale>(zoom, feature);
+ auto pitchAlignment = evaluated.evaluate<style::CirclePitchAlignment>(zoom, feature);
+ if (pitchScale == CirclePitchScaleType::Viewport && pitchAlignment == AlignmentType::Map) {
+ adjustedSize *= center[3] / transformState.getCameraToCenterDistance();
+ } else if (pitchScale == CirclePitchScaleType::Map && pitchAlignment == AlignmentType::Viewport) {
+ adjustedSize *= transformState.getCameraToCenterDistance() / center[3];
+ }
+
+ if (util::polygonIntersectsBufferedPoint(transformedQueryGeometry, transformedPoint, adjustedSize)) return true;
+ }
+ }
- // Test intersection
- return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), radius + stroke);
+ return false;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp
index f31715f98f..c9eeae4652 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.hpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.hpp
@@ -20,8 +20,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
index fbd6160e8a..d5282c9b0d 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -68,24 +68,53 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
parameters.context.setStencilMode(gl::StencilMode::disabled());
parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
+ auto draw = [&](auto& programInstance, const auto& tileBucket, auto&& uniformValues) {
+ const auto& paintPropertyBinders = tileBucket.paintPropertyBinders.at(getID());
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
+ std::move(uniformValues),
+ paintPropertyBinders,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
+ *tileBucket.vertexBuffer,
+ paintPropertyBinders,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ *tileBucket.indexBuffer,
+ tileBucket.triangleSegments,
+ allUniformValues,
+ allAttributeBindings,
+ getID());
+ };
+
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(),
+ draw(
+ parameters.programs.fillExtrusion.get(evaluated),
+ bucket,
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());
+ parameters.state,
+ parameters.evaluatedLight
+ )
+ );
}
} else {
optional<ImagePosition> imagePosA =
@@ -104,10 +133,9 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
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(),
+ draw(
+ parameters.programs.fillExtrusionPattern.get(evaluated),
+ bucket,
FillExtrusionPatternUniforms::values(
tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
evaluated.get<FillExtrusionTranslateAnchor>(),
@@ -115,10 +143,9 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
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.evaluatedLight
+ )
+ );
}
}
@@ -131,19 +158,39 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
const Properties<>::PossiblyEvaluated properties;
+ const ExtrusionTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 };
+
+ auto& programInstance = parameters.programs.extrusionTexture;
- parameters.programs.extrusionTexture.draw(
- parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
- gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ const auto allUniformValues = programInstance.computeAllUniformValues(
ExtrusionTextureProgram::UniformValues{
uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
uniforms::u_image::Value{ 0 },
- uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } },
+ uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() }
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
parameters.staticData.extrusionTextureVertexBuffer,
+ paintAttributeData,
+ properties
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.extrusionTextureSegments,
- ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
- parameters.state.getZoom(), getID());
+ allUniformValues,
+ allAttributeBindings,
+ getID());
}
}
@@ -151,14 +198,15 @@ bool RenderFillExtrusionLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4&) const {
auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::FillExtrusionTranslate>(),
evaluated.get<style::FillExtrusionTranslateAnchor>(),
- bearing,
+ transformState.getAngle(),
pixelsToTileUnits);
return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
index 838494cf91..f7ba13c267 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -22,8 +22,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
index 22cb9563c1..c59ca6f906 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -69,12 +69,11 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
const auto& depthMode,
const auto& indexBuffer,
const auto& segments) {
- program.get(evaluated).draw(
- parameters.context,
- drawMode,
- depthMode,
- parameters.stencilModeForClipping(tile.clip),
- parameters.colorModeForRenderPass(),
+ auto& programInstance = program.get(evaluated);
+
+ const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
FillProgram::UniformValues {
uniforms::u_matrix::Value{
tile.translatedMatrix(evaluated.get<FillTranslate>(),
@@ -83,12 +82,28 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
},
uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
},
+ paintPropertyBinders,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
*bucket.vertexBuffer,
+ paintPropertyBinders,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
indexBuffer,
segments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
};
@@ -139,12 +154,11 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
const auto& depthMode,
const auto& indexBuffer,
const auto& segments) {
- program.get(evaluated).draw(
- parameters.context,
- drawMode,
- depthMode,
- parameters.stencilModeForClipping(tile.clip),
- parameters.colorModeForRenderPass(),
+ auto& programInstance = program.get(evaluated);
+
+ const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
FillPatternUniforms::values(
tile.translatedMatrix(evaluated.get<FillTranslate>(),
evaluated.get<FillTranslateAnchor>(),
@@ -157,12 +171,28 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
tile.id,
parameters.state
),
+ paintPropertyBinders,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
*bucket.vertexBuffer,
+ paintPropertyBinders,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
indexBuffer,
segments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
};
@@ -188,14 +218,15 @@ bool RenderFillLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4&) const {
auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::FillTranslate>(),
evaluated.get<style::FillTranslateAnchor>(),
- bearing,
+ transformState.getAngle(),
pixelsToTileUnits);
return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp
index a51865698f..bd195fb828 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.hpp
@@ -20,8 +20,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
index 4f2e899220..dec9edb318 100644
--- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
@@ -92,23 +92,38 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) {
? parameters.stencilModeForClipping(tile.clip)
: gl::StencilMode::disabled();
- parameters.programs.heatmap.get(evaluated).draw(
+ const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
+
+ auto& programInstance = parameters.programs.heatmap.get(evaluated);
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
+ HeatmapProgram::UniformValues {
+ uniforms::u_intensity::Value{ evaluated.get<style::HeatmapIntensity>() },
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::heatmap::u_extrude_scale::Value{ extrudeScale }
+ },
+ paintPropertyBinders,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
+ *bucket.vertexBuffer,
+ paintPropertyBinders,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
stencilMode,
gl::ColorMode::additive(),
- HeatmapProgram::UniformValues {
- uniforms::u_intensity::Value{evaluated.get<style::HeatmapIntensity>()},
- uniforms::u_matrix::Value{tile.matrix},
- uniforms::heatmap::u_extrude_scale::Value{extrudeScale}
- },
- *bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.segments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
}
@@ -123,20 +138,41 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) {
matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
const Properties<>::PossiblyEvaluated properties;
+ const HeatmapTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 };
+
+ auto& programInstance = parameters.programs.heatmapTexture;
- parameters.programs.heatmapTexture.draw(
- parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
- gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ const auto allUniformValues = programInstance.computeAllUniformValues(
HeatmapTextureProgram::UniformValues{
uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
uniforms::u_image::Value{ 0 },
uniforms::u_color_ramp::Value{ 1 },
- uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() } },
+ uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() }
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
parameters.staticData.extrusionTextureVertexBuffer,
+ paintAttributeData,
+ properties
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.extrusionTextureSegments,
- HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
- parameters.state.getZoom(), getID());
+ allUniformValues,
+ allAttributeBindings,
+ getID()
+ );
}
}
@@ -165,12 +201,12 @@ bool RenderHeatmapLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState&,
+ const float pixelsToTileUnits,
+ const mat4&) const {
(void) queryGeometry;
(void) feature;
(void) zoom;
- (void) bearing;
(void) pixelsToTileUnits;
return false;
}
diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp
index 3f0b1f91b4..29fad7d8b8 100644
--- a/src/mbgl/renderer/layers/render_heatmap_layer.hpp
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp
@@ -22,8 +22,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
void updateColorRamp();
diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp
index bcfd4ffe99..411305edf4 100644
--- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp
+++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp
@@ -69,12 +69,11 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src
const auto& indexBuffer,
const auto& segments,
const UnwrappedTileID& id) {
- parameters.programs.hillshade.draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
+ auto& programInstance = parameters.programs.hillshade;
+
+ const HillshadeProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 };
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
HillshadeProgram::UniformValues {
uniforms::u_matrix::Value{ matrix },
uniforms::u_image::Value{ 0 },
@@ -84,12 +83,28 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src
uniforms::u_light::Value{ getLight(parameters) },
uniforms::u_latrange::Value{ getLatRange(id) },
},
+ paintAttributeData,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
vertexBuffer,
+ paintAttributeData,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
indexBuffer,
segments,
- HillshadeProgram::PaintPropertyBinders { evaluated, 0 },
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
};
@@ -112,13 +127,11 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src
parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Nearest, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp);
const Properties<>::PossiblyEvaluated properties;
+ const HillshadePrepareProgram::PaintPropertyBinders paintAttributeData{ properties, 0 };
- parameters.programs.hillshadePrepare.draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
+ auto& programInstance = parameters.programs.hillshadePrepare;
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
HillshadePrepareProgram::UniformValues {
uniforms::u_matrix::Value { mat },
uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} },
@@ -126,12 +139,28 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src
uniforms::u_maxzoom::Value{ float(maxzoom) },
uniforms::u_image::Value{ 0 }
},
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
parameters.staticData.rasterVertexBuffer,
+ paintAttributeData,
+ properties
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.rasterSegments,
- HillshadePrepareProgram::PaintPropertyBinders { properties, 0 },
- properties,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
bucket.texture = std::move(view.getTexture());
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index 1b4a1c0ff7..361ad0c76b 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -62,19 +62,34 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) {
LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl));
auto draw = [&] (auto& program, auto&& uniformValues) {
- program.get(evaluated).draw(
+ auto& programInstance = program.get(evaluated);
+
+ const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
+ std::move(uniformValues),
+ paintPropertyBinders,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
+ *bucket.vertexBuffer,
+ paintPropertyBinders,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
parameters.stencilModeForClipping(tile.clip),
parameters.colorModeForRenderPass(),
- std::move(uniformValues),
- *bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.segments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
};
@@ -162,15 +177,16 @@ bool RenderLineLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4&) const {
// Translate query geometry
auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::LineTranslate>(),
evaluated.get<style::LineTranslateAnchor>(),
- bearing,
+ transformState.getAngle(),
pixelsToTileUnits);
// Evaluate function
diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp
index 8bf7e2329d..5d5d79c044 100644
--- a/src/mbgl/renderer/layers/render_line_layer.hpp
+++ b/src/mbgl/renderer/layers/render_line_layer.hpp
@@ -29,8 +29,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
index b41b2ac560..f202ed4ebb 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.cpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -73,16 +73,15 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source
if (parameters.pass != RenderPass::Translucent)
return;
+ RasterProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 };
+
auto draw = [&] (const mat4& matrix,
const auto& vertexBuffer,
const auto& indexBuffer,
const auto& segments) {
- parameters.programs.raster.draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
+ auto& programInstance = parameters.programs.raster;
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
RasterProgram::UniformValues {
uniforms::u_matrix::Value{ matrix },
uniforms::u_image0::Value{ 0 },
@@ -98,12 +97,28 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source
uniforms::u_scale_parent::Value{ 1.0f },
uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
},
+ paintAttributeData,
+ evaluated,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
vertexBuffer,
+ paintAttributeData,
+ evaluated
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
indexBuffer,
segments,
- RasterProgram::PaintPropertyBinders { evaluated, 0 },
- evaluated,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
};
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
index 9e493003c0..e48c0e2f92 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.cpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -88,7 +88,26 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
const auto& binders,
const auto& paintProperties)
{
- program.get(paintProperties).draw(
+ auto& programInstance = program.get(paintProperties);
+
+ const auto allUniformValues = programInstance.computeAllUniformValues(
+ std::move(uniformValues),
+ *symbolSizeBinder,
+ binders,
+ paintProperties,
+ parameters.state.getZoom()
+ );
+ const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
+ *buffers.vertexBuffer,
+ *buffers.dynamicVertexBuffer,
+ *buffers.opacityVertexBuffer,
+ binders,
+ paintProperties
+ );
+
+ checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
+
+ programInstance.draw(
parameters.context,
gl::Triangles(),
values_.pitchAlignment == AlignmentType::Map
@@ -96,16 +115,10 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
: gl::DepthMode::disabled(),
gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
- std::move(uniformValues),
- *buffers.vertexBuffer,
- *buffers.dynamicVertexBuffer,
- *buffers.opacityVertexBuffer,
- *symbolSizeBinder,
*buffers.indexBuffer,
buffers.segments,
- binders,
- paintProperties,
- parameters.state.getZoom(),
+ allUniformValues,
+ allAttributeBindings,
getID()
);
};
diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp
index bcdc175f14..a667d5837e 100644
--- a/src/mbgl/renderer/render_layer.cpp
+++ b/src/mbgl/renderer/render_layer.cpp
@@ -9,8 +9,10 @@
#include <mbgl/renderer/layers/render_raster_layer.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/util/logging.hpp>
namespace mbgl {
@@ -73,5 +75,32 @@ void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>>
renderTiles = std::move(tiles);
}
+void RenderLayer::checkRenderability(const PaintParameters& parameters,
+ const uint32_t activeBindingCount) {
+ // Only warn once for every layer.
+ if (hasRenderFailures) {
+ return;
+ }
+
+ if (activeBindingCount > parameters.context.maximumVertexBindingCount) {
+ Log::Error(Event::OpenGL,
+ "The layer '%s' uses more data-driven properties than the current device "
+ "supports, and will have rendering errors. To ensure compatibility with this "
+ "device, use %d fewer data driven properties in this layer.",
+ getID().c_str(),
+ activeBindingCount - parameters.context.minimumRequiredVertexBindingCount);
+ hasRenderFailures = true;
+ } else if (activeBindingCount > parameters.context.minimumRequiredVertexBindingCount) {
+ Log::Error(Event::OpenGL,
+ "The layer '%s' uses more data-driven properties than some devices may support. "
+ "Though it will render correctly on this device, it may have rendering errors "
+ "on other devices. To ensure compatibility with all devices, use %d fewer "
+ "data-driven properties in this layer.",
+ getID().c_str(),
+ activeBindingCount - parameters.context.minimumRequiredVertexBindingCount);
+ hasRenderFailures = true;
+ }
+}
+
} //namespace mbgl
diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp
index 55831cb72c..3e2f1d7525 100644
--- a/src/mbgl/renderer/render_layer.hpp
+++ b/src/mbgl/renderer/render_layer.hpp
@@ -4,6 +4,7 @@
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/util/mat4.hpp>
#include <memory>
#include <string>
@@ -17,6 +18,7 @@ class PropertyEvaluationParameters;
class PaintParameters;
class RenderSource;
class RenderTile;
+class TransformState;
class RenderLayer {
protected:
@@ -69,8 +71,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const { return false; };
+ const mat4&) const { return false; };
virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const = 0;
@@ -82,6 +85,11 @@ public:
friend std::string layoutKey(const RenderLayer&);
protected:
+ // Checks whether the current hardware can render this layer. If it can't, we'll show a warning
+ // in the console to inform the developer.
+ void checkRenderability(const PaintParameters&, uint32_t activeBindingCount);
+
+protected:
// renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they
// can update opacities in the symbol buckets immediately before rendering
friend class CrossTileSymbolIndex;
@@ -92,6 +100,12 @@ protected:
// Stores what render passes this layer is currently enabled for. This depends on the
// evaluated StyleProperties object and is updated accordingly.
RenderPass passes = RenderPass::None;
+
+private:
+ // Some layers may not render correctly on some hardware when the vertex attribute limit of
+ // that GPU is exceeded. More attributes are used when adding many data driven paint properties
+ // to a layer.
+ bool hasRenderFailures = false;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp
index 53519c763e..dc80cb1dc6 100644
--- a/src/mbgl/renderer/render_source.hpp
+++ b/src/mbgl/renderer/render_source.hpp
@@ -65,7 +65,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const = 0;
+ const mat4& projMatrix) const = 0;
virtual std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const = 0;
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index 35b34833e4..64790938ef 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -74,6 +74,8 @@ void RenderTile::finishRender(PaintParameters& parameters) {
static const style::Properties<>::PossiblyEvaluated properties {};
static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+ auto& program = parameters.programs.debug;
+
if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
tile.debugBucket->complete != tile.isComplete() ||
@@ -85,41 +87,51 @@ void RenderTile::finishRender(PaintParameters& parameters) {
tile.expires, parameters.debugOptions, parameters.context);
}
- parameters.programs.debug.draw(
+ const auto allAttributeBindings = program.computeAllAttributeBindings(
+ *tile.debugBucket->vertexBuffer,
+ paintAttributeData,
+ properties
+ );
+
+ program.draw(
parameters.context,
gl::Lines { 4.0f * parameters.pixelRatio },
gl::DepthMode::disabled(),
parameters.stencilModeForClipping(clip),
gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ Color::white() }
- },
- *tile.debugBucket->vertexBuffer,
*tile.debugBucket->indexBuffer,
tile.debugBucket->segments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
+ program.computeAllUniformValues(
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::white() }
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ ),
+ allAttributeBindings,
"debug"
);
- parameters.programs.debug.draw(
+ program.draw(
parameters.context,
gl::Lines { 2.0f * parameters.pixelRatio },
gl::DepthMode::disabled(),
parameters.stencilModeForClipping(clip),
gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ Color::black() }
- },
- *tile.debugBucket->vertexBuffer,
*tile.debugBucket->indexBuffer,
tile.debugBucket->segments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
+ program.computeAllUniformValues(
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::black() }
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ ),
+ allAttributeBindings,
"debug"
);
}
@@ -131,16 +143,22 @@ void RenderTile::finishRender(PaintParameters& parameters) {
gl::DepthMode::disabled(),
parameters.stencilModeForClipping(clip),
gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ Color::red() }
- },
- parameters.staticData.tileVertexBuffer,
parameters.staticData.tileBorderIndexBuffer,
parameters.staticData.tileBorderSegments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
+ program.computeAllUniformValues(
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::red() }
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ ),
+ program.computeAllAttributeBindings(
+ parameters.staticData.tileVertexBuffer,
+ paintAttributeData,
+ properties
+ ),
"debug"
);
}
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
index 2ac714e122..4380ef24a1 100644
--- a/src/mbgl/renderer/renderer_impl.cpp
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -85,11 +85,6 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
void Renderer::Impl::render(const UpdateParameters& updateParameters) {
if (updateParameters.mode != MapMode::Continuous) {
- // Don't load/render anyting in still mode until explicitly requested.
- if (!updateParameters.stillImageRequest) {
- return;
- }
-
// Reset zoom history state.
zoomHistory.first = true;
}
@@ -401,10 +396,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
}
placementChanged = newPlacement->commit(*placement, parameters.timePoint);
- // commitFeatureIndexes depends on the assumption that no new FeatureIndex has been loaded since placement
- // started. If we violate this assumption, then we need to either make CollisionIndex completely independendent of
- // FeatureIndex, or find a way for its entries to point to multiple FeatureIndexes.
- commitFeatureIndexes();
crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers);
if (placementChanged || symbolBucketsChanged) {
placement = std::move(newPlacement);
@@ -495,7 +486,9 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
static const ClippingMaskProgram::PaintPropertyBinders paintAttributeData(properties, 0);
for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) {
- parameters.staticData.programs.clippingMask.draw(
+ auto& program = parameters.staticData.programs.clippingMask;
+
+ program.draw(
parameters.context,
gl::Triangles(),
gl::DepthMode::disabled(),
@@ -508,15 +501,21 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
gl::StencilMode::Replace
},
gl::ColorMode::disabled(),
- ClippingMaskProgram::UniformValues {
- uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) },
- },
- parameters.staticData.tileVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.tileTriangleSegments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
+ program.computeAllUniformValues(
+ ClippingMaskProgram::UniformValues {
+ uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) },
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ ),
+ program.computeAllAttributeBindings(
+ parameters.staticData.tileVertexBuffer,
+ paintAttributeData,
+ properties
+ ),
"clipping"
);
}
@@ -673,6 +672,39 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
return queryRenderedFeatures(geometry, options, layers);
}
+
+void Renderer::Impl::queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer,
+ const ScreenLineString& geometry,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const {
+
+ auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry);
+ std::vector<std::reference_wrapper<const RetainedQueryData>> bucketQueryData;
+ for (auto entry : renderedSymbols) {
+ bucketQueryData.push_back(placement->getQueryData(entry.first));
+ }
+ // Although symbol query is global, symbol results are only sortable within a bucket
+ // For a predictable global sort order, we sort the buckets based on their corresponding tile position
+ std::sort(bucketQueryData.begin(), bucketQueryData.end(), [](const RetainedQueryData& a, const RetainedQueryData& b) {
+ return
+ std::tie(a.tileID.canonical.z, a.tileID.canonical.y, a.tileID.wrap, a.tileID.canonical.x) <
+ std::tie(b.tileID.canonical.z, b.tileID.canonical.y, b.tileID.wrap, b.tileID.canonical.x);
+ });
+
+ for (auto wrappedQueryData : bucketQueryData) {
+ auto& queryData = wrappedQueryData.get();
+ auto bucketSymbols = queryData.featureIndex->lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId],
+ options,
+ layers,
+ queryData.tileID,
+ queryData.featureSortOrder);
+
+ for (auto layer : bucketSymbols) {
+ auto& resultFeatures = resultsByLayer[layer.first];
+ std::move(layer.second.begin(), layer.second.end(), std::inserter(resultFeatures, resultFeatures.end()));
+ }
+ }
+}
std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector<const RenderLayer*>& layers) const {
std::unordered_set<std::string> sourceIDs;
@@ -680,13 +712,18 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
sourceIDs.emplace(layer->baseImpl->source);
}
+ mat4 projMatrix;
+ transformState.getProjMatrix(projMatrix);
+
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, placement->getCollisionIndex());
+ auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
}
}
+
+ queryRenderedSymbols(resultsByLayer, geometry, layers, options);
std::vector<Feature> result;
@@ -785,15 +822,6 @@ bool Renderer::Impl::hasTransitions(TimePoint timePoint) const {
return false;
}
-void Renderer::Impl::commitFeatureIndexes() {
- for (auto& source : renderSources) {
- for (auto& renderTile : source.second->getRenderTiles()) {
- Tile& tile = renderTile.get().tile;
- tile.commitFeatureIndex();
- }
- }
-}
-
void Renderer::Impl::updateFadingTiles() {
fadingTiles = false;
for (auto& source : renderSources) {
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
index 4675ac79ea..4124f6f416 100644
--- a/src/mbgl/renderer/renderer_impl.hpp
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -64,7 +64,12 @@ private:
RenderLayer* getRenderLayer(const std::string& id);
const RenderLayer* getRenderLayer(const std::string& id) const;
-
+
+ void queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer,
+ const ScreenLineString& geometry,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const;
+
std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) const;
// GlyphManagerObserver implementation.
@@ -74,7 +79,6 @@ private:
void onTileChanged(RenderSource&, const OverscaledTileID&) override;
void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
- void commitFeatureIndexes();
void updateFadingTiles();
friend class Renderer;
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
index 057ad5a4a7..2d28b8dd84 100644
--- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
@@ -68,8 +68,8 @@ RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geomet
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
+ const mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const {
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
index 033d731029..5533fe2b83 100644
--- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
@@ -28,7 +28,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp
index cbf4db70b5..0e265efff4 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -86,8 +86,8 @@ RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
+ const mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
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 72fccbd043..297fa09a29 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.hpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.hpp
@@ -32,7 +32,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex&) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp
index 31a5916a34..dce5e40185 100644
--- a/src/mbgl/renderer/sources/render_image_source.cpp
+++ b/src/mbgl/renderer/sources/render_image_source.cpp
@@ -58,24 +58,32 @@ void RenderImageSource::finishRender(PaintParameters& parameters) {
static const style::Properties<>::PossiblyEvaluated properties {};
static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+ auto& programInstance = parameters.programs.debug;
+
for (auto matrix : matrices) {
- parameters.programs.debug.draw(
+ programInstance.draw(
parameters.context,
gl::LineStrip { 4.0f * parameters.pixelRatio },
gl::DepthMode::disabled(),
gl::StencilMode::disabled(),
gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ Color::red() }
- },
- parameters.staticData.tileVertexBuffer,
parameters.staticData.tileBorderIndexBuffer,
parameters.staticData.tileBorderSegments,
- paintAttributeData,
- properties,
- parameters.state.getZoom(),
- "debug"
+ programInstance.computeAllUniformValues(
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::red() }
+ },
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom()
+ ),
+ programInstance.computeAllAttributeBindings(
+ parameters.staticData.tileVertexBuffer,
+ paintAttributeData,
+ properties
+ ),
+ "image"
);
}
}
@@ -85,7 +93,7 @@ RenderImageSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex&) const {
+ const mat4&) 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 85ee0ace11..cf14e180fd 100644
--- a/src/mbgl/renderer/sources/render_image_source.hpp
+++ b/src/mbgl/renderer/sources/render_image_source.hpp
@@ -33,7 +33,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp
index b3153622c3..fbf2c09d19 100644
--- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp
@@ -147,7 +147,7 @@ RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex& ) const {
+ const mat4&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp
index 741214a14d..48c7803e92 100644
--- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp
@@ -28,7 +28,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp
index 60b3fa9a3b..f97ce4e65b 100644
--- a/src/mbgl/renderer/sources/render_raster_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_source.cpp
@@ -77,7 +77,7 @@ RenderRasterSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex& ) const {
+ const mat4&) 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 78eda199ac..32539a046d 100644
--- a/src/mbgl/renderer/sources/render_raster_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_source.hpp
@@ -28,7 +28,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp
index e87bea5dcd..4de4f01e3f 100644
--- a/src/mbgl/renderer/sources/render_vector_source.cpp
+++ b/src/mbgl/renderer/sources/render_vector_source.cpp
@@ -80,8 +80,8 @@ RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
+ const mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
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 592160dc16..6fd2425aa3 100644
--- a/src/mbgl/renderer/sources/render_vector_source.hpp
+++ b/src/mbgl/renderer/sources/render_vector_source.hpp
@@ -28,7 +28,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp
index 8f83a0f982..d28e95181b 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -242,7 +242,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const {
+ const mat4& projMatrix) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
return result;
@@ -264,14 +264,19 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x);
});
+ auto maxPitchScaleFactor = transformState.maxPitchScaleFactor();
+
for (const RenderTile& renderTile : sortedTiles) {
+ const float scale = std::pow(2, transformState.getZoom() - renderTile.id.canonical.z);
+ auto queryPadding = maxPitchScaleFactor * renderTile.tile.getQueryPadding(layers) * util::EXTENT / util::tileSize / scale;
+
GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min);
- if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) {
+ if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) {
continue;
}
GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max);
- if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) {
+ if (tileSpaceBoundsMax.x + queryPadding < 0 || tileSpaceBoundsMax.y + queryPadding < 0) {
continue;
}
@@ -286,7 +291,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
transformState,
layers,
options,
- collisionIndex);
+ projMatrix);
}
return result;
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index 2638599c38..0cef9e2c40 100644
--- a/src/mbgl/renderer/tile_pyramid.hpp
+++ b/src/mbgl/renderer/tile_pyramid.hpp
@@ -54,7 +54,7 @@ public:
const TransformState& transformState,
const std::vector<const RenderLayer*>&,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const;
+ const mat4& projMatrix) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp
index c700295a15..68d2dcc468 100644
--- a/src/mbgl/shaders/line.cpp
+++ b/src/mbgl/shaders/line.cpp
@@ -31,6 +31,7 @@ uniform vec2 u_gl_units_to_pixels;
varying vec2 v_normal;
varying vec2 v_width2;
varying float v_gamma_scale;
+varying highp float v_linesofar;
#ifndef HAS_UNIFORM_u_color
@@ -131,6 +132,8 @@ void main() {
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
+ v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0;
+
vec2 pos = a_pos_normal.xy;
// x is 1 if it's a round cap, 0 otherwise
diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp
index 207dd2ee69..ba85f87dea 100644
--- a/src/mbgl/storage/resource.cpp
+++ b/src/mbgl/storage/resource.cpp
@@ -79,13 +79,13 @@ Resource Resource::spriteJSON(const std::string& base, float pixelRatio) {
Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange) {
return Resource {
Resource::Kind::Glyphs,
- util::replaceTokens(urlTemplate, [&](const std::string& token) {
+ util::replaceTokens(urlTemplate, [&](const std::string& token) -> optional<std::string> {
if (token == "fontstack") {
return util::percentEncode(fontStackToString(fontStack));
} else if (token == "range") {
return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second);
} else {
- return std::string();
+ return {};
}
})
};
@@ -104,7 +104,7 @@ Resource Resource::tile(const std::string& urlTemplate,
}
return Resource {
Resource::Kind::Tile,
- util::replaceTokens(urlTemplate, [&](const std::string& token) {
+ util::replaceTokens(urlTemplate, [&](const std::string& token) -> optional<std::string> {
if (token == "z") {
return util::toString(z);
} else if (token == "x") {
@@ -123,7 +123,7 @@ Resource Resource::tile(const std::string& urlTemplate,
} else if (token == "ratio") {
return std::string(pixelRatio > 1.0 ? "@2x" : "");
} else {
- return std::string();
+ return {};
}
}),
Resource::TileData {
diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp
index 3c941945fd..386d85e921 100644
--- a/src/mbgl/style/conversion/filter.cpp
+++ b/src/mbgl/style/conversion/filter.cpp
@@ -1,17 +1,39 @@
#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/expression/literal.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/expression/type.hpp>
-#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/boolean_operator.hpp>
namespace mbgl {
namespace style {
namespace conversion {
-using GeometryValue = mapbox::geometry::value;
+using namespace mbgl::style::expression;
+
+static bool isExpression(const Convertible& filter);
+std::unique_ptr<Expression> convertLegacyFilter(const Convertible& values, Error& error);
+
+optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
+ if (isExpression(value)) {
+ ParsingContext parsingContext(type::Boolean);
+ ParseResult parseResult = parsingContext.parseExpression(value);
+ if (!parseResult) {
+ error = { parsingContext.getCombinedErrors() };
+ return {};
+ } else {
+ return { Filter(std::move(parseResult)) };
+ }
+ } else {
+ std::unique_ptr<Expression> expression = convertLegacyFilter(value, error);
+ if (!expression) return {};
+ return Filter(optional<std::unique_ptr<Expression>>(std::move(expression)));
+ }
+}
// This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js
-static bool isExpressionFilter(const Convertible& filter) {
+bool isExpression(const Convertible& filter) {
if (!isArray(filter) || arrayLength(filter) == 0) {
return false;
}
@@ -20,7 +42,7 @@ static bool isExpressionFilter(const Convertible& filter) {
if (!op) {
return false;
-
+
} else if (*op == "has") {
if (arrayLength(filter) < 2) return false;
optional<std::string> operand = toString(arrayMember(filter, 1));
@@ -35,7 +57,7 @@ static bool isExpressionFilter(const Convertible& filter) {
} else if (*op == "any" || *op == "all") {
for (std::size_t i = 1; i < arrayLength(filter); i++) {
Convertible f = arrayMember(filter, i);
- if (!isExpressionFilter(f) && !toBool(f)) {
+ if (!isExpression(f) && !toBool(f)) {
return false;
}
}
@@ -46,257 +68,136 @@ static bool isExpressionFilter(const Convertible& filter) {
}
}
-static optional<GeometryValue> normalizeValue(const optional<GeometryValue>& value, Error& error) {
- if (!value) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return *value;
- }
-}
+std::unique_ptr<Expression> createExpression(std::string op, std::vector<std::unique_ptr<Expression>> args, Error& error) {
+ if (op == "any") {
+ return std::make_unique<Any>(std::move(args));
+
+ } else if (op == "all") {
+ return std::make_unique<All>(std::move(args));
-static optional<FeatureType> toFeatureType(const Convertible& value, Error& error) {
- optional<std::string> type = toString(value);
- if (!type) {
- error = { "value for $type filter must be a string" };
- return {};
- } else if (*type == "Point") {
- return FeatureType::Point;
- } else if (*type == "LineString") {
- return FeatureType::LineString;
- } else if (*type == "Polygon") {
- return FeatureType::Polygon;
} else {
- error = { "value for $type filter must be Point, LineString, or Polygon" };
- return {};
+ ParsingContext parsingContext(type::Boolean);
+ ParseResult parseResult = createCompoundExpression(op, std::move(args), parsingContext);
+ if (!parseResult) {
+ error = { parsingContext.getCombinedErrors() };
+ return {};
+ } else {
+ return std::move(*parseResult);
+ }
}
}
-static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
- optional<GeometryValue> identifier = toValue(value);
- if (!identifier) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return (*identifier).match(
- [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( double t) -> optional<FeatureIdentifier> { return { t }; },
- [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
- [&] (const auto&) -> optional<FeatureIdentifier> {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- });
- }
+std::unique_ptr<Expression> createExpression(std::string op, std::unique_ptr<Expression> expression, Error& error) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(expression));
+ return createExpression(op, std::move(args), error);
}
-template <class FilterType, class IdentifierFilterType>
-optional<Filter> convertUnaryFilter(const Convertible& value, Error& error) {
- if (arrayLength(value) < 2) {
- error = { "filter expression must have 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$id") {
- return { IdentifierFilterType {} };
+std::unique_ptr<Expression> convertLiteral(const Convertible& convertible, Error& error) {
+ ParsingContext parsingContext;
+ ParseResult parseResult = Literal::parse(convertible, parsingContext);
+ if (parseResult) {
+ return std::move(*parseResult);
} else {
- return { FilterType { *key } };
+ error = { parsingContext.getCombinedErrors() };
+ return {};
}
}
-template <class FilterType, class TypeFilterType, class IdentifierFilterType>
-optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
+std::vector<std::unique_ptr<Expression>> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) {
+ std::vector<std::unique_ptr<Expression>> output;
+ for (std::size_t i = startIndex; i < arrayLength(input); i++) {
+ output.push_back(convertLiteral(arrayMember(input, i), error));
}
+ return output;
+}
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
+std::unique_ptr<Expression> convertLegacyComparisonFilter(const Convertible& values, Error& error, optional<std::string> opOverride = {}) {
+ optional<std::string> op = opOverride ? opOverride : toString(arrayMember(values, 0));
+ optional<std::string> property = toString(arrayMember(values, 1));
+
+ if (!property) {
+ error = { "filter property must be a string" };
return {};
- }
-
- if (*key == "$type") {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { TypeFilterType { *filterValue } };
-
- } else if (*key == "$id") {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { IdentifierFilterType { *filterValue } };
-
+ } else if (*property == "$type") {
+ return createExpression("filter-type-" + *op, convertLiteralArray(values, error, 2), error);
+ } else if (*property == "$id") {
+ return createExpression("filter-id-" + *op, convertLiteralArray(values, error, 2), error);
} else {
- optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
+ return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error);
}
}
-
-template <class FilterType>
-optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
+
+std::unique_ptr<Expression> convertLegacyHasFilter(const Convertible& values, Error& error) {
+ optional<std::string> property = toString(arrayMember(values, 1));
+
+ if (!property) {
+ error = { "filter property must be a string" };
return {};
+ } else if (*property == "$type") {
+ return std::make_unique<Literal>(true);
+ } else if (*property == "$id") {
+ return createExpression("filter-has-id", std::vector<std::unique_ptr<Expression>>(), error);
+ } else {
+ return createExpression("filter-has", std::make_unique<Literal>(*property), error);
}
-
- return { FilterType { *key, *filterValue } };
}
-template <class FilterType, class TypeFilterType, class IdentifierFilterType>
-optional<Filter> convertSetFilter(const Convertible& value, Error& error) {
- if (arrayLength(value) < 2) {
- error = { "filter expression must at least 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- std::vector<FeatureType> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { TypeFilterType { std::move(values) } };
-
- } else if (*key == "$id") {
- std::vector<FeatureIdentifier> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { IdentifierFilterType { std::move(values) } };
-
+std::unique_ptr<Expression> convertLegacyInFilter(const Convertible& values, Error& error) {
+ optional<std::string> property = toString(arrayMember(values, 1));
+
+ if (!property) {
+ error = { "filter property must be a string" };
+ return {};
+ } else if (arrayLength(values) == 0) {
+ return std::make_unique<Literal>(false);
+ } else if (*property == "$type") {
+ return createExpression("filter-type-in", convertLiteralArray(values, error, 2), error);
+ } else if (*property == "$id") {
+ return createExpression("filter-id-in", convertLiteralArray(values, error, 2), error);
} else {
- std::vector<GeometryValue> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { FilterType { *key, std::move(values) } };
+ return createExpression("filter-in", convertLiteralArray(values, error, 1), error);
}
}
-template <class FilterType>
-optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) {
- std::vector<Filter> filters;
- for (std::size_t i = 1; i < arrayLength(value); ++i) {
- optional<Filter> element = convert<Filter>(arrayMember(value, i), error);
- if (!element) {
- return {};
- }
- filters.push_back(*element);
+std::vector<std::unique_ptr<Expression>> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) {
+ std::vector<std::unique_ptr<Expression>> output;
+ for (std::size_t i = startIndex; i < arrayLength(input); i++) {
+ output.push_back(convertLegacyFilter(arrayMember(input, i), error));
}
-
- return { FilterType { std::move(filters) } };
+ return output;
}
-optional<Filter> convertExpressionFilter(const Convertible& value, Error& error) {
- expression::ParsingContext ctx(expression::type::Boolean);
- expression::ParseResult expression = ctx.parseExpression(value);
- if (!expression) {
- error = { ctx.getCombinedErrors() };
- return {};
- }
-
- return { ExpressionFilter { std::move(*expression) } };
-}
-
-optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
- if (isExpressionFilter(value)) {
- return convertExpressionFilter(value, error);
+std::unique_ptr<Expression> convertLegacyFilter(const Convertible& values, Error& error) {
+ if (isUndefined(values)) {
+ return std::make_unique<Literal>(true);
}
- if (!isArray(value)) {
- error = { "filter expression must be an array" };
- return {};
- }
-
- if (arrayLength(value) < 1) {
- error = { "filter expression must have at least 1 element" };
- return {};
- }
+ optional<std::string> op = toString(arrayMember(values, 0));
- optional<std::string> op = toString(arrayMember(value, 0));
if (!op) {
error = { "filter operator must be a string" };
return {};
+ } else if (arrayLength(values) <= 1) {
+ return std::make_unique<Literal>(*op != "any");
+ } else {
+ return (
+ *op == "==" ||
+ *op == "<" ||
+ *op == ">" ||
+ *op == "<=" ||
+ *op == ">=" ? convertLegacyComparisonFilter(values, error) :
+ *op == "!=" ? createExpression("!", convertLegacyComparisonFilter(values, error, {"=="}), error) :
+ *op == "any" ? createExpression("any", convertLegacyFilterArray(values, error, 1), error) :
+ *op == "all" ? createExpression("all", convertLegacyFilterArray(values, error, 1), error) :
+ *op == "none" ? createExpression("!", createExpression("any", convertLegacyFilterArray(values, error, 1), error), error) :
+ *op == "in" ? convertLegacyInFilter(values, error) :
+ *op == "!in" ? createExpression("!", convertLegacyInFilter(values, error), error) :
+ *op == "has" ? convertLegacyHasFilter(values, error) :
+ *op == "!has" ? createExpression("!", convertLegacyHasFilter(values, error), error) :
+ std::make_unique<Literal>(true)
+ );
}
-
- if (*op == "==") {
- return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
- } else if (*op == "!=") {
- return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
- } else if (*op == ">") {
- return convertBinaryFilter<GreaterThanFilter>(value, error);
- } else if (*op == ">=") {
- return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
- } else if (*op == "<") {
- return convertBinaryFilter<LessThanFilter>(value, error);
- } else if (*op == "<=") {
- return convertBinaryFilter<LessThanEqualsFilter>(value, error);
- } else if (*op == "in") {
- return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
- } else if (*op == "!in") {
- return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
- } else if (*op == "all") {
- return convertCompoundFilter<AllFilter>(value, error);
- } else if (*op == "any") {
- return convertCompoundFilter<AnyFilter>(value, error);
- } else if (*op == "none") {
- return convertCompoundFilter<NoneFilter>(value, error);
- } else if (*op == "has") {
- return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
- } else if (*op == "!has") {
- return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
- }
-
- error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
- return {};
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp
index 7b7727d7c4..74171763a0 100644
--- a/src/mbgl/style/conversion/stringify.hpp
+++ b/src/mbgl/style/conversion/stringify.hpp
@@ -126,162 +126,9 @@ void stringify(Writer& writer, const FeatureIdentifier& id) {
}
template <class Writer>
-class StringifyFilter {
-public:
- Writer& writer;
-
- void operator()(const NullFilter&) {
- writer.Null();
- }
-
- void operator()(const EqualsFilter& f) {
- stringifyBinaryFilter(f, "==");
- }
-
- void operator()(const NotEqualsFilter& f) {
- stringifyBinaryFilter(f, "!=");
- }
-
- void operator()(const LessThanFilter& f) {
- stringifyBinaryFilter(f, "<");
- }
-
- void operator()(const LessThanEqualsFilter& f) {
- stringifyBinaryFilter(f, "<=");
- }
-
- void operator()(const GreaterThanFilter& f) {
- stringifyBinaryFilter(f, ">");
- }
-
- void operator()(const GreaterThanEqualsFilter& f) {
- stringifyBinaryFilter(f, ">=");
- }
-
- void operator()(const InFilter& f) {
- stringifySetFilter(f, "in");
- }
-
- void operator()(const NotInFilter& f) {
- stringifySetFilter(f, "!in");
- }
-
- void operator()(const AllFilter& f) {
- stringifyCompoundFilter(f, "all");
- }
-
- void operator()(const AnyFilter& f) {
- stringifyCompoundFilter(f, "any");
- }
-
- void operator()(const NoneFilter& f) {
- stringifyCompoundFilter(f, "none");
- }
-
- void operator()(const HasFilter& f) {
- stringifyUnaryFilter("has", f.key);
- }
-
- void operator()(const NotHasFilter& f) {
- stringifyUnaryFilter("!has", f.key);
- }
-
- void operator()(const TypeEqualsFilter& f) {
- stringifyBinaryFilter(f, "==", "$type");
- }
-
- void operator()(const TypeNotEqualsFilter& f) {
- stringifyBinaryFilter(f, "!=", "$type");
- }
-
- void operator()(const TypeInFilter& f) {
- stringifySetFilter(f, "in", "$type");
- }
-
- void operator()(const TypeNotInFilter& f) {
- stringifySetFilter(f, "!in", "$type");
- }
-
- void operator()(const IdentifierEqualsFilter& f) {
- stringifyBinaryFilter(f, "==", "$id");
- }
-
- void operator()(const IdentifierNotEqualsFilter& f) {
- stringifyBinaryFilter(f, "!=", "$id");
- }
-
- void operator()(const IdentifierInFilter& f) {
- stringifySetFilter(f, "in", "$id");
- }
-
- void operator()(const IdentifierNotInFilter& f) {
- stringifySetFilter(f, "!in", "$id");
- }
-
- void operator()(const HasIdentifierFilter&) {
- stringifyUnaryFilter("has", "$id");
- }
-
- void operator()(const NotHasIdentifierFilter&) {
- stringifyUnaryFilter("!has", "$id");
- }
-
- void operator()(const ExpressionFilter& filter) {
- stringify(writer, filter.expression->serialize());
- }
-
-private:
- template <class F>
- void stringifyBinaryFilter(const F& f, const char * op) {
- stringifyBinaryFilter(f, op, f.key);
- }
-
- template <class F>
- void stringifyBinaryFilter(const F& f, const char * op, const std::string& key) {
- writer.StartArray();
- writer.String(op);
- writer.String(key);
- stringify(writer, f.value);
- writer.EndArray();
- }
-
- template <class F>
- void stringifySetFilter(const F& f, const char * op) {
- stringifySetFilter(f, op, f.key);
- }
-
- template <class F>
- void stringifySetFilter(const F& f, const char * op, const std::string& key) {
- writer.StartArray();
- writer.String(op);
- writer.String(key);
- for (const auto& value : f.values) {
- stringify(writer, value);
- }
- writer.EndArray();
- }
-
- template <class F>
- void stringifyCompoundFilter(const F& f, const char * op) {
- writer.StartArray();
- writer.String(op);
- for (const auto& filter : f.filters) {
- Filter::visit(filter, *this);
- }
- writer.EndArray();
- }
-
- void stringifyUnaryFilter(const char * op, const std::string& key) {
- writer.StartArray();
- writer.String(op);
- writer.String(key);
- writer.EndArray();
- }
-};
-
-template <class Writer>
-void stringify(Writer& writer, const Filter& f) {
- Filter::visit(f, StringifyFilter<Writer> { writer });
+void stringify(Writer& writer, const Filter& filter) {
+ if (!filter.expression) writer.Null();
+ else stringify(writer, (*filter.expression)->serialize());
}
template <class Writer>
diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp
index a2c4aa80b3..15ed10a90f 100644
--- a/src/mbgl/style/conversion/tileset.cpp
+++ b/src/mbgl/style/conversion/tileset.cpp
@@ -1,14 +1,11 @@
#include <mbgl/style/conversion/tileset.hpp>
#include <mbgl/util/geo.hpp>
+#include <mbgl/math/clamp.hpp>
namespace mbgl {
namespace style {
namespace conversion {
-bool validateLatitude(const double lat) {
- return lat <= 90 && lat >= -90;
-}
-
optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const {
Tileset result;
@@ -95,16 +92,20 @@ optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error
error = { "bounds array must contain numeric longitude and latitude values" };
return {};
}
- if (!validateLatitude(*bottom) || !validateLatitude(*top) || top <= bottom){
- error = { "bounds latitude values must be between -90 and 90 with bottom less than top" };
+
+ bottom = util::clamp(*bottom, -90.0, 90.0);
+ top = util::clamp(*top, -90.0, 90.0);
+ if (top <= bottom){
+ error = { "bounds bottom latitude must be between smaller than top latitude" };
return {};
}
+
if(*left >= *right) {
error = { "bounds left longitude should be less than right longitude" };
return {};
}
- *left = util::max(-180.0, *left);
- *right = util::min(180.0, *right);
+ left = util::max(-180.0, *left);
+ right = util::min(180.0, *right);
result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right });
}
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
index c36ffa33e3..4226756fe4 100644
--- a/src/mbgl/style/expression/compound_expression.cpp
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -3,6 +3,7 @@
#include <mbgl/style/expression/util.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/math/log2.hpp>
+#include <mbgl/util/i18n.hpp>
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/platform.hpp>
@@ -18,7 +19,7 @@ namespace detail {
The Signature<Fn> structs are wrappers around an "evaluate()" function whose
purpose is to extract the necessary Type data from the evaluate function's
type. There are three key (partial) specializations:
-
+
Signature<R (Params...)>:
Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result<U>
@@ -29,9 +30,9 @@ namespace detail {
Signature<R (const EvaluationContext&, Params...)>:
Wraps an evaluate function that needs to access the expression evaluation
parameters in addition to its subexpressions, i.e.,
- (const EvaluationParams& const T0&, const T1&, ...) -> Result<U>. Needed
+ (const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>. Needed
for expressions like ["zoom"], ["get", key], etc.
-
+
In each of the above evaluate signatures, T0, T1, etc. are the types of
the successfully evaluated subexpressions.
*/
@@ -42,7 +43,7 @@ struct Signature;
template <class R, class... Params>
struct Signature<R (Params...)> : SignatureBase {
using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
-
+
Signature(R (*evaluate_)(Params...), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -54,7 +55,7 @@ struct Signature<R (Params...)> : SignatureBase {
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
}
-
+
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
typename Signature::Args argsArray;
std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
@@ -79,7 +80,7 @@ private:
template <class R, typename T>
struct Signature<R (const Varargs<T>&)> : SignatureBase {
using Args = std::vector<std::unique_ptr<Expression>>;
-
+
Signature(R (*evaluate_)(const Varargs<T>&), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -88,11 +89,11 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
),
evaluate(evaluate_)
{}
-
+
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
};
-
+
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
Varargs<T> evaluated;
evaluated.reserve(args.size());
@@ -114,7 +115,7 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
template <class R, class... Params>
struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
-
+
Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -123,17 +124,17 @@ struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
),
evaluate(evaluate_)
{}
-
+
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
typename Signature::Args argsArray;
std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray));
}
-
+
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
}
-
+
private:
template <std::size_t ...I>
EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
@@ -149,6 +150,41 @@ private:
R (*evaluate)(const EvaluationContext&, Params...);
};
+
+// Evaluate function needing EvaluationContext and Varargs
+// (const EvaluationContext&, const Varargs<T>&) -> Result<U>
+template <class R, typename T>
+struct Signature<R (const EvaluationContext&, const Varargs<T>&)> : SignatureBase {
+ using Args = std::vector<std::unique_ptr<Expression>>;
+
+ Signature(R (*evaluate_)(const EvaluationContext&, const Varargs<T>&), std::string name_) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ VarargsType { valueTypeToExpressionType<T>() },
+ std::move(name_)
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
+ return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
+ };
+
+ EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
+ Varargs<T> evaluated;
+ evaluated.reserve(args.size());
+ for (const auto& arg : args) {
+ const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters);
+ if(!evaluatedArg) return evaluatedArg.error();
+ evaluated.push_back(*fromExpressionValue<std::decay_t<T>>(*evaluatedArg));
+ }
+ const R value = evaluate(evaluationParameters, evaluated);
+ if (!value) return value.error();
+ return *value;
+ }
+
+ R (*evaluate)(const EvaluationContext&, const Varargs<T>&);
+};
// Machinery to pull out function types from class methods, lambdas, etc.
template <class R, class... Params>
@@ -180,29 +216,104 @@ static std::unique_ptr<detail::SignatureBase> makeSignature(Fn evaluateFunction,
return std::make_unique<detail::Signature<Fn>>(evaluateFunction, std::move(name));
}
+Value featureIdAsExpressionValue(EvaluationContext params) {
+ assert(params.feature);
+ auto id = params.feature->getID();
+ if (!id) return Null;
+ return id->match([](const auto& idid) {
+ return toExpressionValue(mbgl::Value(idid));
+ });
+};
+
+optional<Value> featurePropertyAsExpressionValue(EvaluationContext params, const std::string& key) {
+ assert(params.feature);
+ auto property = params.feature->getValue(key);
+ return property ? toExpressionValue(*property) : optional<Value>();
+};
+
+optional<std::string> featureTypeAsString(FeatureType type) {
+ switch(type) {
+ case FeatureType::Point:
+ return optional<std::string>("Point");
+ case FeatureType::LineString:
+ return optional<std::string>("LineString");
+ case FeatureType::Polygon:
+ return optional<std::string>("Polygon");
+ case FeatureType::Unknown:
+ return optional<std::string>("Unknown");
+ default:
+ return {};
+ }
+};
+
+optional<double> featurePropertyAsDouble(EvaluationContext params, const std::string& key) {
+ assert(params.feature);
+ auto property = params.feature->getValue(key);
+ if (!property) return {};
+ return property->match(
+ [](double value) { return value; },
+ [](uint64_t value) { return optional<double>(static_cast<double>(value)); },
+ [](int64_t value) { return optional<double>(static_cast<double>(value)); },
+ [](auto) { return optional<double>(); }
+ );
+};
+
+optional<std::string> featurePropertyAsString(EvaluationContext params, const std::string& key) {
+ assert(params.feature);
+ auto property = params.feature->getValue(key);
+ if (!property) return {};
+ return property->match(
+ [](std::string value) { return value; },
+ [](auto) { return optional<std::string>(); }
+ );
+};
+
+optional<double> featureIdAsDouble(EvaluationContext params) {
+ assert(params.feature);
+ auto id = params.feature->getID();
+ if (!id) return optional<double>();
+ return id->match(
+ [](double value) { return value; },
+ [](uint64_t value) { return optional<double>(static_cast<double>(value)); },
+ [](int64_t value) { return optional<double>(static_cast<double>(value)); },
+ [](auto) { return optional<double>(); }
+ );
+};
+
+optional<std::string> featureIdAsString(EvaluationContext params) {
+ assert(params.feature);
+ auto id = params.feature->getID();
+ if (!id) return optional<std::string>();
+ return id->match(
+ [](std::string value) { return value; },
+ [](auto) { return optional<std::string>(); }
+ );
+};
+
std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initializeDefinitions() {
std::unordered_map<std::string, CompoundExpressionRegistry::Definition> definitions;
auto define = [&](std::string name, auto fn) {
definitions[name].push_back(makeSignature(fn, name));
};
-
+
define("e", []() -> Result<double> { return 2.718281828459045; });
define("pi", []() -> Result<double> { return 3.141592653589793; });
define("ln2", []() -> Result<double> { return 0.6931471805599453; });
define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); });
-
+
define("to-string", [](const Value& value) -> Result<std::string> {
return value.match(
+ [](const NullValue&) -> Result<std::string> { return std::string(); },
[](const Color& c) -> Result<std::string> { return c.stringify(); }, // avoid quoting
[](const std::string& s) -> Result<std::string> { return s; }, // avoid quoting
[](const auto& v) -> Result<std::string> { return stringify(v); }
);
});
-
+
define("to-boolean", [](const Value& v) -> Result<bool> {
return v.match(
- [&] (double f) { return (bool)f; },
+ [&] (double f) { return static_cast<bool>(f); },
[&] (const std::string& s) { return s.length() > 0; },
[&] (bool b) { return b; },
[&] (const NullValue&) { return false; },
@@ -212,10 +323,10 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> {
return color.toArray();
});
-
+
define("rgba", rgba);
define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); });
-
+
define("zoom", [](const EvaluationContext& params) -> Result<double> {
if (!params.zoom) {
return EvaluationError {
@@ -224,7 +335,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
}
return *(params.zoom);
});
-
+
define("heatmap-density", [](const EvaluationContext& params) -> Result<double> {
if (!params.heatmapDensity) {
return EvaluationError {
@@ -240,7 +351,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
"Feature data is unavailable in the current evaluation context."
};
}
-
+
return params.feature->getValue(key) ? true : false;
});
define("has", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> {
@@ -266,7 +377,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
}
return object.at(key);
});
-
+
define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
if (!params.feature) {
return EvaluationError {
@@ -280,14 +391,14 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
}
return result;
});
-
+
define("geometry-type", [](const EvaluationContext& params) -> Result<std::string> {
if (!params.feature) {
return EvaluationError {
"Feature data is unavailable in the current evaluation context."
};
}
-
+
auto type = params.feature->getType();
if (type == FeatureType::Point) {
return "Point";
@@ -299,14 +410,14 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
return "Unknown";
}
});
-
+
define("id", [](const EvaluationContext& params) -> Result<Value> {
if (!params.feature) {
return EvaluationError {
"Feature data is unavailable in the current evaluation context."
};
}
-
+
auto id = params.feature->getID();
if (!id) {
return Null;
@@ -317,7 +428,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
}
);
});
-
+
define("+", [](const Varargs<double>& args) -> Result<double> {
double sum = 0.0f;
for (auto arg : args) {
@@ -347,7 +458,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("asin", [](double x) -> Result<double> { return asin(x); });
define("acos", [](double x) -> Result<double> { return acos(x); });
define("atan", [](double x) -> Result<double> { return atan(x); });
-
+
define("min", [](const Varargs<double>& args) -> Result<double> {
double result = std::numeric_limits<double>::infinity();
for (double arg : args) {
@@ -363,7 +474,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
return result;
});
- define("round", [](double x) -> Result<double> { return std::round(x); });
+ define("round", [](double x) -> Result<double> { return ::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); });
@@ -376,9 +487,13 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("<", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs < rhs; });
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("!", [](bool e) -> Result<bool> { return !e; });
+ define("is-supported-script", [](const std::string& x) -> Result<bool> {
+ return util::i18n::isStringInSupportedScript(x);
+ });
+
define("upcase", [](const std::string& input) -> Result<std::string> {
return platform::uppercase(input);
});
@@ -395,7 +510,130 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("error", [](const std::string& input) -> Result<type::ErrorType> {
return EvaluationError { input };
});
+
+ // Legacy Filters
+ define("filter-==", [](const EvaluationContext& params, const std::string& key, const Value &lhs) -> Result<bool> {
+ const auto rhs = featurePropertyAsExpressionValue(params, key);
+ return rhs ? lhs == *rhs : false;
+ });
+
+ define("filter-id-==", [](const EvaluationContext& params, const Value &lhs) -> Result<bool> {
+ return lhs == featureIdAsExpressionValue(params);
+ });
+
+ define("filter-type-==", [](const EvaluationContext& params, const std::string &lhs) -> Result<bool> {
+ if (!params.feature) return false;
+ return featureTypeAsString(params.feature->getType()) == lhs;
+ });
+
+ define("filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsDouble(params, key);
+ return rhs ? rhs < lhs : false;
+ });
+
+ define("filter-<", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsString(params, key);
+ return rhs ? rhs < lhs : false;
+ });
+
+ define("filter-id-<", [](const EvaluationContext& params, double lhs) -> Result<bool> {
+ auto rhs = featureIdAsDouble(params);
+ return rhs ? rhs < lhs : false;
+ });
+
+ define("filter-id-<", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
+ auto rhs = featureIdAsString(params);
+ return rhs ? rhs < lhs : false;
+ });
+
+ define("filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsDouble(params, key);
+ return rhs ? rhs > lhs : false;
+ });
+
+ define("filter->", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsString(params, key);
+ return rhs ? rhs > lhs : false;
+ });
+
+ define("filter-id->", [](const EvaluationContext& params, double lhs) -> Result<bool> {
+ auto rhs = featureIdAsDouble(params);
+ return rhs ? rhs > lhs : false;
+ });
+
+ define("filter-id->", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
+ auto rhs = featureIdAsString(params);
+ return rhs ? rhs > lhs : false;
+ });
+
+ define("filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsDouble(params, key);
+ return rhs ? rhs <= lhs : false;
+ });
+
+ define("filter-<=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsString(params, key);
+ return rhs ? rhs <= lhs : false;
+ });
+
+ define("filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result<bool> {
+ auto rhs = featureIdAsDouble(params);
+ return rhs ? rhs <= lhs : false;
+ });
+
+ define("filter-id-<=", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
+ auto rhs = featureIdAsString(params);
+ return rhs ? rhs <= lhs : false;
+ });
+
+ define("filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsDouble(params, key);
+ return rhs ? rhs >= lhs : false;
+ });
+
+ define("filter->=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
+ auto rhs = featurePropertyAsString(params, key);
+ return rhs ? rhs >= lhs : false;
+ });
+
+ define("filter-id->=", [](const EvaluationContext& params, double lhs) -> Result<bool> {
+ auto rhs = featureIdAsDouble(params);
+ return rhs ? rhs >= lhs : false;
+ });
+
+ define("filter-id->=", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
+ auto rhs = featureIdAsString(params);
+ return rhs ? rhs >= lhs : false;
+ });
+
+ define("filter-has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
+ assert(params.feature);
+ return bool(params.feature->getValue(key));
+ });
+
+ define("filter-has-id", [](const EvaluationContext& params) -> Result<bool> {
+ assert(params.feature);
+ return bool(params.feature->getID());
+ });
+
+ define("filter-type-in", [](const EvaluationContext& params, const Varargs<std::string>& types) -> Result<bool> {
+ assert(params.feature);
+ optional<std::string> type = featureTypeAsString(params.feature->getType());
+ return std::find(types.begin(), types.end(), type) != types.end();
+ });
+ define("filter-id-in", [](const EvaluationContext& params, const Varargs<Value>& ids) -> Result<bool> {
+ auto id = featureIdAsExpressionValue(params);
+ return std::find(ids.begin(), ids.end(), id) != ids.end();
+ });
+
+ define("filter-in", [](const EvaluationContext& params, const Varargs<Value>& varargs) -> Result<bool> {
+ if (varargs.size() < 2) return false;
+ assert(varargs[0].is<std::string>());
+ auto value = featurePropertyAsExpressionValue(params, varargs[0].get<std::string>());
+ return value ? std::find(varargs.begin() + 1, varargs.end(), *value) != varargs.end() : false;
+ });
+
return definitions;
}
@@ -414,7 +652,7 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v
return ParseResult();
}
const CompoundExpressionRegistry::Definition& definition = it->second;
-
+
auto length = arrayLength(value);
// Check if we have a single signature with the correct number of
@@ -440,14 +678,14 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v
args.reserve(length - 1);
for (std::size_t i = 1; i < length; i++) {
optional<type::Type> expected;
-
+
if (singleMatchingSignature) {
expected = definition[*singleMatchingSignature]->params.match(
[](const VarargsType& varargs) { return varargs.type; },
[&](const std::vector<type::Type>& params_) { return params_[i - 1]; }
);
}
-
+
auto parsed = ctx.parse(arrayMember(value, i), i, expected);
if (!parsed) {
return parsed;
@@ -474,7 +712,7 @@ ParseResult createCompoundExpression(const Definition& definition,
for (const std::unique_ptr<detail::SignatureBase>& signature : definition) {
signatureContext.clearErrors();
-
+
if (signature->params.is<std::vector<type::Type>>()) {
const std::vector<type::Type>& params = signature->params.get<std::vector<type::Type>>();
if (params.size() != args.size()) {
@@ -502,12 +740,12 @@ ParseResult createCompoundExpression(const Definition& definition,
}
}
}
-
+
if (signatureContext.getErrors().size() == 0) {
return ParseResult(signature->makeExpression(std::move(args)));
}
}
-
+
if (definition.size() == 1) {
ctx.appendErrors(std::move(signatureContext));
} else {
@@ -540,10 +778,32 @@ ParseResult createCompoundExpression(const Definition& definition,
}
ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead.");
}
-
+
return ParseResult();
}
+ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx) {
+ return createCompoundExpression(name, std::vector<std::unique_ptr<Expression>>(), ctx);
+}
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::unique_ptr<Expression> arg1,
+ ParsingContext& ctx) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(arg1));
+ return createCompoundExpression(name, std::move(args), ctx);
+}
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::unique_ptr<Expression> arg1,
+ std::unique_ptr<Expression> arg2,
+ ParsingContext& ctx) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(arg1));
+ args.push_back(std::move(arg2));
+ return createCompoundExpression(name, std::move(args), ctx);
+}
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp
index 8a63980dba..f68cfd5cf5 100644
--- a/src/mbgl/style/expression/literal.cpp
+++ b/src/mbgl/style/expression/literal.cpp
@@ -109,6 +109,14 @@ mbgl::Value Literal::serialize() const {
return *fromExpressionValue<mbgl::Value>(value);
}
}
+
+std::unique_ptr<Literal> createLiteral(const char* value) {
+ return createLiteral(std::string(value));
+}
+
+std::unique_ptr<Literal> createLiteral(Value value) {
+ return std::make_unique<Literal>(value);
+}
} // namespace expression
} // namespace style
diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp
index 3d41f0bdd3..59123c9812 100644
--- a/src/mbgl/style/expression/match.cpp
+++ b/src/mbgl/style/expression/match.cpp
@@ -83,6 +83,10 @@ template<> EvaluationResult Match<std::string>::evaluate(const EvaluationContext
return inputValue.error();
}
+ if (!inputValue->is<std::string>()) {
+ return otherwise->evaluate(params);
+ }
+
auto it = branches.find(inputValue->get<std::string>());
if (it != branches.end()) {
return (*it).second->evaluate(params);
@@ -96,7 +100,11 @@ template<> EvaluationResult Match<int64_t>::evaluate(const EvaluationContext& pa
if (!inputValue) {
return inputValue.error();
}
-
+
+ if (!inputValue->is<double>()) {
+ return otherwise->evaluate(params);
+ }
+
const auto numeric = inputValue->get<double>();
int64_t rounded = std::floor(numeric);
if (numeric == rounded) {
@@ -280,7 +288,7 @@ ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) {
branches.push_back(std::make_pair(std::move(labels), std::move(*output)));
}
- auto input = ctx.parse(arrayMember(value, 1), 1, inputType);
+ auto input = ctx.parse(arrayMember(value, 1), 1, {type::Value});
if (!input) {
return ParseResult();
}
@@ -292,6 +300,12 @@ ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) {
assert(inputType && outputType);
+ optional<std::string> err;
+ if ((*input)->getType() != type::Value && (err = type::checkSubtype(*inputType, (*input)->getType()))) {
+ ctx.error(*err, 1);
+ return ParseResult();
+ }
+
return inputType->match(
[&](const type::NumberType&) {
return create<int64_t>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx);
diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp
index 51aa6bcf82..2559eb4816 100644
--- a/src/mbgl/style/filter.cpp
+++ b/src/mbgl/style/filter.cpp
@@ -1,12 +1,20 @@
#include <mbgl/style/filter.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
namespace mbgl {
namespace style {
bool Filter::operator()(const expression::EvaluationContext &context) const {
- return FilterBase::visit(*this, FilterEvaluator { context });
+
+ if (!this->expression) return true;
+
+ const expression::EvaluationResult result = (*this->expression)->evaluate(context);
+ if (result) {
+ const optional<bool> typed = expression::fromExpressionValue<bool>(*result);
+ return typed ? *typed : false;
+ } else {
+ return true;
+ }
}
} // namespace style
diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp
deleted file mode 100644
index 72022172f4..0000000000
--- a/src/mbgl/style/filter_evaluator.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-#include <mbgl/style/filter.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-
-namespace mbgl {
-namespace style {
-
-template <class Op>
-struct Comparator {
- const Op& op;
-
- template <class T>
- bool operator()(const T& lhs, const T& rhs) const {
- return op(lhs, rhs);
- }
-
- template <class T0, class T1>
- auto operator()(const T0& lhs, const T1& rhs) const
- -> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value &&
- std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> {
- return op(double(lhs), double(rhs));
- }
-
- template <class T0, class T1>
- auto operator()(const T0&, const T1&) const
- -> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value ||
- !std::is_arithmetic<T1>::value || std::is_same<T1, bool>::value, bool> {
- return false;
- }
-
- bool operator()(const NullValue&,
- const NullValue&) const {
- // Should be unreachable; null is not currently allowed by the style specification.
- assert(false);
- return false;
- }
-
- bool operator()(const std::vector<Value>&,
- const std::vector<Value>&) const {
- // Should be unreachable; nested values are not currently allowed by the style specification.
- assert(false);
- return false;
- }
-
- bool operator()(const PropertyMap&,
- const PropertyMap&) const {
- // Should be unreachable; nested values are not currently allowed by the style specification.
- assert(false);
- return false;
- }
-};
-
-template <class Op>
-bool compare(const Value& lhs, const Value& rhs, const Op& op) {
- return Value::binary_visit(lhs, rhs, Comparator<Op> { op });
-}
-
-bool equal(const Value& lhs, const Value& rhs) {
- return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; });
-}
-
-bool FilterEvaluator::operator()(const NullFilter&) const {
- return true;
-}
-
-bool FilterEvaluator::operator()(const EqualsFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- return actual && equal(*actual, filter.value);
-}
-
-bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- return !actual || !equal(*actual, filter.value);
-}
-
-bool FilterEvaluator::operator()(const LessThanFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; });
-}
-
-bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; });
-}
-
-bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; });
-}
-
-bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; });
-}
-
-bool FilterEvaluator::operator()(const InFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- if (!actual)
- return false;
- for (const auto& v: filter.values) {
- if (equal(*actual, v)) {
- return true;
- }
- }
- return false;
-}
-
-bool FilterEvaluator::operator()(const NotInFilter& filter) const {
- optional<Value> actual = context.feature->getValue(filter.key);
- if (!actual)
- return true;
- for (const auto& v: filter.values) {
- if (equal(*actual, v)) {
- return false;
- }
- }
- return true;
-}
-
-bool FilterEvaluator::operator()(const AnyFilter& filter) const {
- for (const auto& f: filter.filters) {
- if (Filter::visit(f, *this)) {
- return true;
- }
- }
- return false;
-}
-
-bool FilterEvaluator::operator()(const AllFilter& filter) const {
- for (const auto& f: filter.filters) {
- if (!Filter::visit(f, *this)) {
- return false;
- }
- }
- return true;
-}
-
-bool FilterEvaluator::operator()(const NoneFilter& filter) const {
- for (const auto& f: filter.filters) {
- if (Filter::visit(f, *this)) {
- return false;
- }
- }
- return true;
-}
-
-bool FilterEvaluator::operator()(const HasFilter& filter) const {
- return bool(context.feature->getValue(filter.key));
-}
-
-bool FilterEvaluator::operator()(const NotHasFilter& filter) const {
- return !context.feature->getValue(filter.key);
-}
-
-bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const {
- return context.feature->getType() == filter.value;
-}
-
-bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const {
- return context.feature->getType() != filter.value;
-}
-
-bool FilterEvaluator::operator()(const TypeInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (context.feature->getType() == v) {
- return true;
- }
- }
- return false;
-}
-
-bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (context.feature->getType() == v) {
- return false;
- }
- }
- return true;
-}
-
-bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const {
- return context.feature->getID() == filter.value;
-}
-
-bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const {
- return context.feature->getID() != filter.value;
-}
-
-bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (context.feature->getID() == v) {
- return true;
- }
- }
- return false;
-}
-
-bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (context.feature->getID() == v) {
- return false;
- }
- }
- return true;
-}
-
-bool FilterEvaluator::operator()(const HasIdentifierFilter&) const {
- return bool(context.feature->getID());
-}
-
-bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const {
- return !context.feature->getID();
-}
-
-bool FilterEvaluator::operator()(const ExpressionFilter& filter) const {
- const expression::EvaluationResult result = filter.expression->evaluate(context);
- if (result) {
- const optional<bool> typed = expression::fromExpressionValue<bool>(*result);
- return typed ? *typed : false;
- }
- return false;
-}
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp
index 833658c33e..091840a371 100644
--- a/src/mbgl/text/collision_index.cpp
+++ b/src/mbgl/text/collision_index.cpp
@@ -219,7 +219,7 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
}
-void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) {
+void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId) {
if (feature.alongLine) {
for (auto& circle : feature.boxes) {
if (!circle.used) {
@@ -227,18 +227,18 @@ void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlaceme
}
if (ignorePlacement) {
- ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius});
} else {
- collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius});
}
}
} else {
assert(feature.boxes.size() == 1);
auto& box = feature.boxes[0];
if (ignorePlacement) {
- ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
} else {
- collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
}
}
}
@@ -262,66 +262,41 @@ bool polygonIntersectsBox(const LineString<float>& polygon, const GridIndex<Inde
return util::polygonIntersectsPolygon(integerPolygon, bboxPoints);
}
-std::vector<IndexedSubfeature> CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const {
- std::vector<IndexedSubfeature> result;
+std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> CollisionIndex::queryRenderedSymbols(const ScreenLineString& queryGeometry) const {
+ std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> result;
if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) {
return result;
}
-
- mat4 posMatrix;
- mat4 projMatrix;
- transformState.getProjMatrix(projMatrix);
- transformState.matrixFor(posMatrix, tileID);
- matrix::multiply(posMatrix, projMatrix, posMatrix);
-
- // queryGeometry is specified in integer tile units, but in projecting we switch to float pixels
- LineString<float> projectedQuery;
+
+ LineString<float> gridQuery;
for (const auto& point : queryGeometry) {
- auto projected = projectPoint(posMatrix, convertPoint<float>(point));
- projectedQuery.push_back(projected);
+ gridQuery.emplace_back(point.x + viewportPadding, point.y + viewportPadding);
}
- auto envelope = mapbox::geometry::envelope(projectedQuery);
+ auto envelope = mapbox::geometry::envelope(gridQuery);
using QueryResult = std::pair<IndexedSubfeature, GridIndex<IndexedSubfeature>::BBox>;
- std::vector<QueryResult> thisTileFeatures;
std::vector<QueryResult> features = collisionGrid.queryWithBoxes(envelope);
-
- for (auto& queryResult : features) {
- auto& feature = queryResult.first;
- if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) {
- // We only have to filter on the canonical ID because even if the feature is showing multiple times
- // we treat it as one feature.
- thisTileFeatures.push_back(queryResult);
- }
- }
-
std::vector<QueryResult> ignoredFeatures = ignoredGrid.queryWithBoxes(envelope);
- for (auto& queryResult : ignoredFeatures) {
- auto& feature = queryResult.first;
- if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) {
- thisTileFeatures.push_back(queryResult);
- }
- }
+ features.insert(features.end(), ignoredFeatures.begin(), ignoredFeatures.end());
- std::unordered_map<std::string, std::unordered_map<std::string, std::unordered_set<std::size_t>>> sourceLayerFeatures;
- for (auto& queryResult : thisTileFeatures) {
+ std::unordered_map<uint32_t, std::unordered_set<size_t>> seenBuckets;
+ for (auto& queryResult : features) {
auto& feature = queryResult.first;
auto& bbox = queryResult.second;
// Skip already seen features.
- auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName];
+ auto& seenFeatures = seenBuckets[feature.bucketInstanceId];
if (seenFeatures.find(feature.index) != seenFeatures.end())
continue;
-
- seenFeatures.insert(feature.index);
- if (!polygonIntersectsBox(projectedQuery, bbox)) {
+ if (!polygonIntersectsBox(gridQuery, bbox)) {
continue;
}
- result.push_back(feature);
+ seenFeatures.insert(feature.index);
+ result[feature.bucketInstanceId].push_back(feature);
}
return result;
diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp
index 8653c1d76c..b2be4c6ade 100644
--- a/src/mbgl/text/collision_index.hpp
+++ b/src/mbgl/text/collision_index.hpp
@@ -28,11 +28,10 @@ public:
const bool pitchWithMap,
const bool collisionDebug);
- void insertFeature(CollisionFeature& feature, bool ignorePlacement);
+ void insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId);
- std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const;
+ std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> queryRenderedSymbols(const ScreenLineString&) const;
-
private:
bool isOffscreen(const CollisionBox&) const;
bool isInsideGrid(const CollisionBox&) const;
diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp
index f88bab9d6f..b0c3511ce3 100644
--- a/src/mbgl/text/cross_tile_symbol_index.cpp
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -62,7 +62,7 @@ CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() {
}
bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) {
- auto thisZoomIndexes = indexes[tileID.overscaledZ];
+ const auto& thisZoomIndexes = indexes[tileID.overscaledZ];
auto previousIndex = thisZoomIndexes.find(tileID);
if (previousIndex != thisZoomIndexes.end()) {
if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId) {
@@ -153,6 +153,10 @@ bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) {
auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
assert(dynamic_cast<SymbolBucket*>(bucket));
SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+ if (symbolBucket.bucketLeaderID != symbolLayer.getID()) {
+ // Only add this layer if it's the "group leader" for the bucket
+ continue;
+ }
if (!symbolBucket.bucketInstanceId) {
symbolBucket.bucketInstanceId = ++maxBucketInstanceId;
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index 54b2b7539b..9883a1f456 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -49,17 +49,25 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri
if (!renderTile.tile.isRenderable()) {
continue;
}
-
- auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<GeometryTile*>(&renderTile.tile));
+ GeometryTile& geometryTile = static_cast<GeometryTile&>(renderTile.tile);
+
+
+ auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl);
assert(dynamic_cast<SymbolBucket*>(bucket));
SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ if (symbolBucket.bucketLeaderID != symbolLayer.getID()) {
+ // Only place this layer if it's the "group leader" for the bucket
+ continue;
+ }
auto& layout = symbolBucket.layout;
const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom());
- const float scale = std::pow(2, state.getZoom() - renderTile.tile.id.overscaledZ);
- const float textPixelRatio = (util::tileSize * renderTile.tile.id.overscaleFactor()) / util::EXTENT;
+ const float scale = std::pow(2, state.getZoom() - geometryTile.id.overscaledZ);
+ const float textPixelRatio = (util::tileSize * geometryTile.id.overscaleFactor()) / util::EXTENT;
mat4 posMatrix;
state.matrixFor(posMatrix, renderTile.id);
@@ -76,7 +84,14 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri
layout.get<style::IconRotationAlignment>() == style::AlignmentType::Map,
state,
pixelsToTileUnits);
-
+
+
+ // As long as this placement lives, we have to hold onto this bucket's
+ // matching FeatureIndex/data for querying purposes
+ retainedQueryData.emplace(std::piecewise_construct,
+ std::forward_as_tuple(symbolBucket.bucketInstanceId),
+ std::forward_as_tuple(symbolBucket.bucketInstanceId, geometryTile.getFeatureIndex(), geometryTile.id));
+
placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade());
}
}
@@ -150,11 +165,11 @@ void Placement::placeLayerBucket(
}
if (placeText) {
- collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>());
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId);
}
if (placeIcon) {
- collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>());
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId);
}
assert(symbolInstance.crossTileID != 0);
@@ -220,6 +235,10 @@ void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) {
auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
assert(dynamic_cast<SymbolBucket*>(bucket));
SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+ if (symbolBucket.bucketLeaderID != symbolLayer.getID()) {
+ // Only update opacities this layer if it's the "group leader" for the bucket
+ continue;
+ }
updateBucketOpacities(symbolBucket, seenCrossTileIDs);
}
}
@@ -305,6 +324,10 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
bucket.updateOpacity();
bucket.sortFeatures(state.getAngle());
+ auto retainedData = retainedQueryData.find(bucket.bucketInstanceId);
+ if (retainedData != retainedQueryData.end()) {
+ retainedData->second.featureSortOrder = bucket.featureSortOrder;
+ }
}
float Placement::symbolFadeChange(TimePoint now) const {
@@ -337,5 +360,13 @@ void Placement::setStale() {
const CollisionIndex& Placement::getCollisionIndex() const {
return collisionIndex;
}
+
+const RetainedQueryData& Placement::getQueryData(uint32_t bucketInstanceId) const {
+ auto it = retainedQueryData.find(bucketInstanceId);
+ if (it == retainedQueryData.end()) {
+ throw std::runtime_error("Placement::getQueryData with unrecognized bucketInstanceId");
+ }
+ return it->second;
+}
} // namespace mbgl
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
index 653ae352ed..0e1751b127 100644
--- a/src/mbgl/text/placement.hpp
+++ b/src/mbgl/text/placement.hpp
@@ -44,7 +44,21 @@ public:
// visible right away.
const bool skipFade;
};
-
+
+struct RetainedQueryData {
+ uint32_t bucketInstanceId;
+ std::shared_ptr<FeatureIndex> featureIndex;
+ OverscaledTileID tileID;
+ std::shared_ptr<std::vector<size_t>> featureSortOrder;
+
+ RetainedQueryData(uint32_t bucketInstanceId_,
+ std::shared_ptr<FeatureIndex> featureIndex_,
+ OverscaledTileID tileID_)
+ : bucketInstanceId(bucketInstanceId_)
+ , featureIndex(std::move(featureIndex_))
+ , tileID(std::move(tileID_)) {}
+};
+
class Placement {
public:
Placement(const TransformState&, MapMode mapMode);
@@ -59,6 +73,8 @@ public:
bool stillRecent(TimePoint now) const;
void setRecent(TimePoint now);
void setStale();
+
+ const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const;
private:
void placeLayerBucket(
@@ -85,6 +101,8 @@ private:
TimePoint recentUntil;
bool stale = false;
+
+ std::unordered_map<uint32_t, RetainedQueryData> retainedQueryData;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp
index a2fefcfa9f..272a1594d4 100644
--- a/src/mbgl/tile/custom_geometry_tile.cpp
+++ b/src/mbgl/tile/custom_geometry_tile.cpp
@@ -3,7 +3,6 @@
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/actor/scheduler.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/tile/tile_observer.hpp>
#include <mbgl/style/custom_tile_loader.hpp>
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index f211c03569..7a83da2267 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -2,7 +2,6 @@
#include <mbgl/tile/geojson_tile_data.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
namespace mbgl {
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index a99cb91d26..af28fe3963 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -16,7 +16,6 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/actor/scheduler.hpp>
@@ -133,7 +132,7 @@ void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelatio
buckets = std::move(result.buckets);
- featureIndexPendingCommit = { std::move(result.featureIndex) };
+ latestFeatureIndex = std::move(result.featureIndex);
if (result.glyphAtlasImage) {
glyphAtlasImage = std::move(*result.glyphAtlasImage);
@@ -201,13 +200,15 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const {
return it->second.get();
}
-void GeometryTile::commitFeatureIndex() {
- // 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;
+float GeometryTile::getQueryPadding(const std::vector<const RenderLayer*>& layers) {
+ float queryPadding = 0;
+ for (const RenderLayer* layer : layers) {
+ auto bucket = getBucket(*layer->baseImpl);
+ if (bucket && bucket->hasData()) {
+ queryPadding = std::max(queryPadding, bucket->getQueryRadius(*layer));
+ }
}
+ return queryPadding;
}
void GeometryTile::queryRenderedFeatures(
@@ -216,30 +217,26 @@ void GeometryTile::queryRenderedFeatures(
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) {
+ const mat4& projMatrix) {
if (!getData()) 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(),
- util::tileSize * id.overscaleFactor(),
- std::pow(2, transformState.getZoom() - id.overscaledZ),
- options,
- id.toUnwrapped(),
- sourceID,
- layers,
- collisionIndex,
- additionalRadius);
+ const float queryPadding = getQueryPadding(layers);
+
+ mat4 posMatrix;
+ transformState.matrixFor(posMatrix, id.toUnwrapped());
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ latestFeatureIndex->query(result,
+ queryGeometry,
+ transformState,
+ posMatrix,
+ util::tileSize * id.overscaleFactor(),
+ std::pow(2, transformState.getZoom() - id.overscaledZ),
+ options,
+ id.toUnwrapped(),
+ layers,
+ queryPadding * transformState.maxPitchScaleFactor());
}
void GeometryTile::querySourceFeatures(
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index 418db4a0b2..af122474c2 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -5,7 +5,6 @@
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/util/feature.hpp>
-#include <mbgl/util/throttler.hpp>
#include <mbgl/actor/actor.hpp>
#include <mbgl/geometry/feature_index.hpp>
@@ -55,12 +54,14 @@ public:
const TransformState&,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) override;
+ const mat4& projMatrix) override;
void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&) override;
+ float getQueryPadding(const std::vector<const RenderLayer*>&) override;
+
void cancel() override;
class LayoutResult {
@@ -88,11 +89,11 @@ public:
void markRenderedPreviously() override;
void performedFadePlacement() override;
- void commitFeatureIndex() override;
+ const std::shared_ptr<FeatureIndex> getFeatureIndex() const { return latestFeatureIndex; }
protected:
const GeometryTileData* getData() {
- return featureIndex ? featureIndex->getData() : nullptr;
+ return latestFeatureIndex ? latestFeatureIndex->getData() : nullptr;
}
private:
@@ -113,8 +114,7 @@ private:
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
- optional<std::unique_ptr<FeatureIndex>> featureIndexPendingCommit;
- std::unique_ptr<FeatureIndex> featureIndex;
+ std::shared_ptr<FeatureIndex> latestFeatureIndex;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index 1378ad5d3a..ca20c4b8ab 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -5,7 +5,6 @@
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/group_by_layout.hpp>
#include <mbgl/style/filter.hpp>
-#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
@@ -441,8 +440,7 @@ void GeometryTileWorker::performSymbolLayout() {
}
symbolLayout->prepare(glyphMap, glyphAtlas.positions,
- imageMap, imageAtlas.positions,
- id, sourceID);
+ imageMap, imageAtlas.positions);
}
symbolLayoutsNeedPreparation = false;
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 88db2ba07c..b95944f10e 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -38,7 +38,11 @@ void Tile::queryRenderedFeatures(
const TransformState&,
const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex&) {}
+ const mat4&) {}
+
+float Tile::getQueryPadding(const std::vector<const RenderLayer*>&) {
+ return 0;
+}
void Tile::querySourceFeatures(
std::vector<Feature>&,
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 23365c6ae3..23d6864205 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -58,12 +58,14 @@ public:
const TransformState&,
const std::vector<const RenderLayer*>&,
const RenderedQueryOptions& options,
- const CollisionIndex&);
+ const mat4& projMatrix);
virtual void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&);
+ virtual float getQueryPadding(const std::vector<const RenderLayer*>&);
+
void setTriedCache();
// Returns true when the tile source has received a first response, regardless of whether a load
@@ -109,11 +111,6 @@ public:
// and will have time to finish by the second placement.
virtual void performedFadePlacement() {}
- // FeatureIndexes are loaded asynchronously, but must be used with a CollisionIndex
- // generated from the same data. Calling commitFeatureIndex signals the current
- // CollisionIndex is up-to-date and allows us to start using the last loaded FeatureIndex
- virtual void commitFeatureIndex() {}
-
void dumpDebugLogs() const;
const OverscaledTileID id;
diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp
index 30e813cbb8..ee3ebe7cae 100644
--- a/src/mbgl/util/compression.cpp
+++ b/src/mbgl/util/compression.cpp
@@ -1,6 +1,6 @@
#include <mbgl/util/compression.hpp>
-#if defined(__QT__) && defined(_WINDOWS)
+#if defined(__QT__) && defined(_WINDOWS) && !defined(__GNUC__)
#include <QtZlib/zlib.h>
#else
#include <zlib.h>
diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp
index 5921edfb14..8048dfe84a 100644
--- a/src/mbgl/util/http_header.cpp
+++ b/src/mbgl/util/http_header.cpp
@@ -8,6 +8,7 @@
#pragma GCC diagnostic ignored "-Wshadow"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp
index 1fc13bfb7d..5530796915 100644
--- a/src/mbgl/util/i18n.cpp
+++ b/src/mbgl/util/i18n.cpp
@@ -1,4 +1,5 @@
-#include "i18n.hpp"
+#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/utf.hpp>
#include <algorithm>
#include <map>
@@ -65,7 +66,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F)
// DEFINE_IS_IN_UNICODE_BLOCK(Hanunoo, 0x1720, 0x173F)
// DEFINE_IS_IN_UNICODE_BLOCK(Buhid, 0x1740, 0x175F)
// DEFINE_IS_IN_UNICODE_BLOCK(Tagbanwa, 0x1760, 0x177F)
-// DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF)
+DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Mongolian, 0x1800, 0x18AF)
DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Limbu, 0x1900, 0x194F)
@@ -581,6 +582,38 @@ std::u16string verticalizePunctuation(const std::u16string& input) {
char16_t verticalizePunctuation(char16_t chr) {
return verticalPunctuation.count(chr) ? verticalPunctuation.at(chr) : 0;
}
+
+bool charInSupportedScript(char16_t chr) {
+ // This is a rough heuristic: whether we "can render" a script
+ // actually depends on the properties of the font being used
+ // and whether differences from the ideal rendering are considered
+ // semantically significant.
+
+ // Even in Latin script, we "can't render" combinations such as the fi
+ // ligature, but we don't consider that semantically significant.n false;
+ if ((chr >= 0x0900 && chr <= 0x0DFF) ||
+ // Main blocks for Indic scripts and Sinhala
+ (chr >= 0x0F00 && chr <= 0x109F) ||
+ // Main blocks for Tibetan and Myanmar
+ isInKhmer(chr)) {
+ // These blocks cover common scripts that require
+ // complex text shaping, based on unicode script metadata:
+ // http://www.unicode.org/repos/cldr/trunk/common/properties/scriptMetadata.txt
+ // where "Web Rank <= 32" "Shaping Required = YES"
+ return false;
+ }
+ return true;
+}
+
+bool isStringInSupportedScript(const std::string& input) {
+ auto u16string = util::utf8_to_utf16::convert(input);
+ for (char16_t chr : u16string) {
+ if (!charInSupportedScript(chr)) {
+ return false;
+ }
+ }
+ return true;
+}
} // namespace i18n
} // namespace util
diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp
index b3960c743c..a74215a134 100644
--- a/src/mbgl/util/i18n.hpp
+++ b/src/mbgl/util/i18n.hpp
@@ -72,6 +72,8 @@ std::u16string verticalizePunctuation(const std::u16string& input);
@return The character’s specialized vertical form; 0 if not applicable. */
char16_t verticalizePunctuation(char16_t chr);
+
+bool isStringInSupportedScript(const std::string& input);
} // namespace i18n
} // namespace util
diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp
index e6ce245c0e..780fce98f9 100644
--- a/src/mbgl/util/intersection_tests.cpp
+++ b/src/mbgl/util/intersection_tests.cpp
@@ -82,11 +82,16 @@ bool lineIntersectsBufferedLine(const GeometryCoordinates& lineA, const Geometry
return false;
}
+bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius) {
+ if (polygonContainsPoint(polygon, point)) return true;
+ if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
+ return false;
+}
+
bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates& polygon, const GeometryCollection& rings, float radius) {
for (auto& ring : rings) {
for (auto& point : ring) {
- if (polygonContainsPoint(polygon, point)) return true;
- if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
+ if (polygonIntersectsBufferedPoint(polygon, point, radius)) return true;
}
}
return false;
diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp
index 5bcb29c767..c105fe4dd0 100644
--- a/src/mbgl/util/intersection_tests.hpp
+++ b/src/mbgl/util/intersection_tests.hpp
@@ -9,6 +9,7 @@ bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates&, const Geome
bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates&, const GeometryCollection&, float radius);
bool polygonIntersectsPolygon(const GeometryCoordinates&, const GeometryCoordinates&);
bool polygonIntersectsMultiPolygon(const GeometryCoordinates&, const GeometryCollection&);
+bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius);
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
index 6a6ed7b250..058cd0d202 100644
--- a/src/mbgl/util/io.cpp
+++ b/src/mbgl/util/io.cpp
@@ -20,7 +20,7 @@ void write_file(const std::string &filename, const std::string &data) {
}
std::string read_file(const std::string &filename) {
- std::ifstream file(filename);
+ std::ifstream file(filename, std::ios::binary);
if (file.good()) {
std::stringstream data;
data << file.rdbuf();
@@ -31,7 +31,7 @@ std::string read_file(const std::string &filename) {
}
optional<std::string> readFile(const std::string &filename) {
- std::ifstream file(filename);
+ std::ifstream file(filename, std::ios::binary);
if (file.good()) {
std::stringstream data;
data << file.rdbuf();
diff --git a/src/mbgl/util/throttler.cpp b/src/mbgl/util/throttler.cpp
deleted file mode 100644
index 910810ce2f..0000000000
--- a/src/mbgl/util/throttler.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <mbgl/util/throttler.hpp>
-
-namespace mbgl {
-namespace util {
-
-Throttler::Throttler(Duration frequency_, std::function<void()>&& function_)
- : frequency(frequency_)
- , function(std::move(function_))
- , pendingInvocation(false)
- , lastInvocation(TimePoint::min())
-{}
-
-void Throttler::invoke() {
- if (pendingInvocation) {
- return;
- }
-
- Duration timeToNextInvocation = lastInvocation == TimePoint::min()
- ? Duration::zero()
- : (lastInvocation + frequency) - Clock::now();
-
- if (timeToNextInvocation <= Duration::zero()) {
- lastInvocation = Clock::now();
- function();
- } else {
- pendingInvocation = true;
- timer.start(timeToNextInvocation, Duration::zero(), [this]{
- pendingInvocation = false;
- lastInvocation = Clock::now();
- function();
- });
- }
-}
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/throttler.hpp b/src/mbgl/util/throttler.hpp
deleted file mode 100644
index 175de7ccaf..0000000000
--- a/src/mbgl/util/throttler.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/timer.hpp>
-
-namespace mbgl {
-namespace util {
-
-class Throttler {
-public:
- Throttler(Duration frequency, std::function<void()>&& function);
-
- void invoke();
-private:
- Duration frequency;
- std::function<void()> function;
-
- Timer timer;
- bool pendingInvocation;
- TimePoint lastInvocation;
-};
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index 39b562d811..488e6b88ce 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -2,13 +2,18 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/tile_cover_impl.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
#include <functional>
+#include <list>
namespace mbgl {
namespace {
+using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>;
+
// Taken from polymaps src/Layer.js
// https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383
struct edge {
@@ -27,8 +32,6 @@ struct edge {
}
};
-using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>;
-
// scan-line conversion
static void scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) {
double y0 = ::fmax(ymin, std::floor(e1.y0));
@@ -147,11 +150,11 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) {
{ std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() });
return tileCover(
- TileCoordinate::fromLatLng(z, bounds.northwest()).p,
- TileCoordinate::fromLatLng(z, bounds.northeast()).p,
- TileCoordinate::fromLatLng(z, bounds.southeast()).p,
- TileCoordinate::fromLatLng(z, bounds.southwest()).p,
- TileCoordinate::fromLatLng(z, bounds.center()).p,
+ Projection::project(bounds.northwest(), z),
+ Projection::project(bounds.northeast(), z),
+ Projection::project(bounds.southeast(), z),
+ Projection::project(bounds.southwest(), z),
+ Projection::project(bounds.center(), z),
z);
}
@@ -169,25 +172,80 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t z) {
+ std::vector<UnwrappedTileID> result;
+ TileCover tc(geometry, z, true);
+ while (tc.hasNext()) {
+ result.push_back(*tc.next());
+ };
+
+ return result;
+}
+
// 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 = ::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) - ::fmax(std::min(y1, y2), 0);
-
- return (maxX - minX + 1) * (maxY - minY + 1);
+uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom){
+ if (zoom == 0) {
+ return 1;
+ }
+ auto sw = Projection::project(bounds.southwest(), zoom);
+ auto ne = Projection::project(bounds.northeast(), zoom);
+ auto maxTile = std::pow(2.0, zoom);
+ auto x1 = floor(sw.x);
+ auto x2 = ceil(ne.x) - 1;
+ auto y1 = util::clamp(floor(sw.y), 0.0, maxTile - 1);
+ auto y2 = util::clamp(floor(ne.y), 0.0, maxTile - 1);
+
+ auto dx = x1 > x2 ? (maxTile - x1) + x2 : x2 - x1;
+ auto dy = y1 - y2;
+ return (dx + 1) * (dy + 1);
+}
+
+uint64_t tileCount(const Geometry<double>& geometry, uint8_t z) {
+ uint64_t tileCount = 0;
+
+ TileCover tc(geometry, z, true);
+ while (tc.next()) {
+ tileCount++;
+ };
+ return tileCount;
+}
+
+TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) {
+ LatLngBounds bounds = LatLngBounds::hull(
+ { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() },
+ { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() });
+
+ if (bounds.isEmpty() ||
+ bounds.south() > util::LATITUDE_MAX ||
+ bounds.north() < -util::LATITUDE_MAX) {
+ bounds = LatLngBounds::world();
+ }
+
+ auto sw = Projection::project(bounds.southwest(), z);
+ auto ne = Projection::project(bounds.northeast(), z);
+ auto se = Projection::project(bounds.southeast(), z);
+ auto nw = Projection::project(bounds.northwest(), z);
+
+ Polygon<double> p({ {sw, nw, ne, se, sw} });
+ impl = std::make_unique<TileCover::Impl>(z, p, false);
+}
+
+TileCover::TileCover(const Geometry<double>& geom, int32_t z, bool project/* = true*/)
+ : impl( std::make_unique<TileCover::Impl>(z, geom, project)) {
+}
+
+TileCover::~TileCover() {
+
+}
+
+optional<UnwrappedTileID> TileCover::next() {
+ return impl->next();
+}
+
+bool TileCover::hasNext() {
+ return impl->hasNext();
}
} // namespace util
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index b2098b59b8..c953d764d2 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -2,9 +2,11 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/types.hpp>
-#include <mbgl/util/tile_coordinate.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/optional.hpp>
#include <vector>
+#include <memory>
namespace mbgl {
@@ -13,13 +15,31 @@ class LatLngBounds;
namespace util {
+// Helper class to stream tile-cover results per row
+class TileCover {
+public:
+ TileCover(const LatLngBounds&, int32_t z);
+ // When project == true, projects the geometry points to tile coordinates
+ TileCover(const Geometry<double>&, int32_t z, bool project = true);
+ ~TileCover();
+
+ optional<UnwrappedTileID> next();
+ bool hasNext();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
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);
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, int32_t z);
// Compute only the count of tiles needed for tileCover
-uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize);
+uint64_t tileCount(const LatLngBounds&, uint8_t z);
+uint64_t tileCount(const Geometry<double>&, uint8_t z);
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp
new file mode 100644
index 0000000000..b3fc07f7dd
--- /dev/null
+++ b/src/mbgl/util/tile_cover_impl.cpp
@@ -0,0 +1,365 @@
+#include <mbgl/util/tile_cover_impl.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
+
+#include <functional>
+#include <cmath>
+#include <assert.h>
+#include <limits.h>
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+using PointList = std::vector<Point<double>>;
+
+struct TileSpan {
+ int32_t xmin, xmax;
+ bool winding;
+};
+
+
+// Find the first local minimum going forward in the list.
+void start_list_on_local_minimum(PointList& points) {
+ auto prev_pt = std::prev(points.end(), 2);
+ auto pt = points.begin();
+ auto next_pt = std::next(pt);
+ while (pt != points.end()) {
+ if ((pt->y <= prev_pt->y) &&
+ (pt->y < next_pt->y)) {
+ break;
+ }
+ prev_pt = pt;
+ pt++;
+ next_pt++;
+ if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ }
+ //Re-close linear rings with first_pt = last_pt
+ if (points.back() == points.front()) {
+ points.pop_back();
+ }
+ std::rotate(points.begin(), pt, points.end());
+ points.push_back(*points.begin());
+}
+
+//Create a bound towards a local maximum point, starting from pt.
+Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) {
+ if (std::distance(pt, points.end()) < 2) { return {}; }
+ if (std::distance(pt, points.end()) == 2) {
+ Bound bnd;
+ if (pt->y < std::next(pt)->y) {
+ std::copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = true;
+ }
+ else {
+ std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = false;
+ }
+ pt = points.end();
+ return bnd;
+ }
+ const auto begin = pt;
+ auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt);
+ auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt);
+ while (pt != points.end()) {
+ if ((pt->y >= prev_pt->y) &&
+ (pt->y > next_pt->y )) {
+ break;
+ }
+ prev_pt = pt;
+ pt++;
+ next_pt++;
+ if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ }
+
+ Bound bnd;
+ if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; };
+ bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt)));
+ std::copy(begin, next_pt, std::back_inserter(bnd.points));
+ bnd.winding = true;
+ return bnd;
+}
+
+//Create a bound towards a local minimum point, starting from pt.
+Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) {
+ if (std::distance(pt, points.end()) < 2) { return {}; }
+ if (std::distance(pt, points.end()) == 2) {
+ Bound bnd;
+ if (pt->y < std::next(pt)->y) {
+ std::copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = true;
+ }
+ else {
+ std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = false;
+ }
+ pt = points.end();
+ return bnd;
+ }
+ auto begin = pt;
+ auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt);
+ auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt);
+ while (pt != points.end()) {
+ if ((pt->y <= prev_pt->y) &&
+ (pt->y < next_pt->y)) {
+ break;
+ }
+ prev_pt = pt;
+ pt++;
+ next_pt++;
+ if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ }
+
+ Bound bnd;
+ if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; };
+ bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt)));
+ //For bounds that start at a max, reverse copy so that all bounds start at a min
+ std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points));
+ bnd.winding = false;
+ return bnd;
+}
+
+//Build a map of bounds and their starting Y tile coordinate.
+void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool closed = false) {
+ if (points.size() < 2) return;
+ //While traversing closed rings, start the bounds at a local minimum
+ if (closed) {
+ start_list_on_local_minimum(points);
+ }
+
+ auto pointsIter = points.begin();
+ while (pointsIter != points.end()) {
+ Bound to_max = create_bound_towards_maximum(points, pointsIter);
+ Bound to_min = create_bound_towards_minimum(points, pointsIter);
+
+ if (to_max.points.size() > 0) {
+ // Projections may result in values beyond the bounds, clamp to max tile coordinates
+ const auto y = static_cast<uint32_t>(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile)));
+ et[y].push_back(to_max);
+ }
+ if (to_min.points.size() > 0) {
+ const auto y = static_cast<uint32_t>(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile)));
+ et[y].push_back(to_min);
+ }
+ }
+ assert(pointsIter == points.end());
+}
+
+void update_span(TileSpan& xp, double x) {
+ xp.xmin = std::min(xp.xmin, static_cast<int32_t>(std::floor(x)));
+ xp.xmax = std::max(xp.xmax, static_cast<int32_t>(std::ceil(x)));
+}
+
+//Build a vector of X tile-coordinates spanned by each bound.
+std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) {
+ std::vector<TileSpan> tile_range;
+ tile_range.reserve(aet.size());
+
+ for(Bound& b: aet) {
+ TileSpan xp = { INT_MAX, 0, b.winding };
+ double x;
+ const auto numEdges = b.points.size() - 1;
+ assert(numEdges >= 1);
+ while (b.currentPoint < numEdges) {
+ x = b.interpolate(y);
+ update_span(xp, x);
+
+ // If this edge ends beyond the current row, find the x-intercept where
+ // it exits the row
+ auto& p1 = b.points[b.currentPoint + 1];
+ if (p1.y > y+1) {
+ x = b.interpolate(y+1);
+ update_span(xp, x);
+ break;
+ } else if(b.currentPoint == numEdges - 1) {
+ // For last edge, consider x-intercept at the end of the edge.
+ x = p1.x;
+ update_span(xp, x);
+ }
+ b.currentPoint++;
+ }
+ tile_range.push_back(xp);
+ }
+ // Erase bounds in the active table whose current edge ends inside this row,
+ // or there are no more edges
+ auto bound = aet.begin();
+ while (bound != aet.end()) {
+ if ( bound->currentPoint == bound->points.size() - 1 &&
+ bound->points[bound->currentPoint].y <= y+1) {
+ bound = aet.erase(bound);
+ } else {
+ bound++;
+ }
+ }
+ // Sort the X-extents of each crossing bound by x_min, x_max
+ std::sort(tile_range.begin(), tile_range.end(), [] (TileSpan& a, TileSpan& b) {
+ return std::tie(a.xmin, a.xmax) < std::tie(b.xmin, b.xmax);
+ });
+
+ return tile_range;
+}
+
+struct BuildBoundsMap {
+ int32_t zoom;
+ bool project = false;
+ BuildBoundsMap(int32_t z, bool p): zoom(z), project(p) {}
+
+ void buildTable(const std::vector<Point<double>>& points, BoundsMap& et, bool closed = false) const {
+ PointList projectedPoints;
+ if (project) {
+ projectedPoints.reserve(points.size());
+ for(const auto&p : points) {
+ projectedPoints.push_back(
+ Projection::project(LatLng{ p.y, p.x }, zoom));
+ }
+ } else {
+ projectedPoints.insert(projectedPoints.end(), points.begin(), points.end());
+ }
+ build_bounds_map(projectedPoints, 1 << zoom, et, closed);
+ }
+
+ void buildPolygonTable(const Polygon<double>& polygon, BoundsMap& et) const {
+ for(const auto&ring : polygon) {
+ buildTable(ring, et, true);
+ }
+ }
+ BoundsMap operator()(const Point<double>&p) const {
+ Bound bnd;
+ auto point = p;
+ if(project) {
+ point = Projection::project(LatLng{p.y, p.x}, zoom);
+ }
+ bnd.points.insert(bnd.points.end(), 2, point);
+ bnd.winding = false;
+ BoundsMap et;
+ const auto y = static_cast<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom))));
+ et[y].push_back(bnd);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiPoint<double>& points) const {
+ BoundsMap et;
+ for (const Point<double>& p: points) {
+ Bound bnd;
+ auto point = p;
+ if(project) {
+ point = Projection::project(LatLng{p.y, p.x}, zoom);
+ }
+ bnd.points.insert(bnd.points.end(), 2, point);
+ bnd.winding = false;
+ const auto y = static_cast<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom))));
+ et[y].push_back(bnd);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const LineString<double>& lines) const {
+ BoundsMap et;
+ buildTable(lines, et);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiLineString<double>& lines) const {
+ BoundsMap et;
+ for(const auto&line : lines) {
+ buildTable(line, et);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const Polygon<double>& polygon) const {
+ BoundsMap et;
+ buildPolygonTable(polygon, et);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiPolygon<double>& polygons) const {
+ BoundsMap et;
+ for(const auto& polygon: polygons) {
+ buildPolygonTable(polygon, et);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const mapbox::geometry::geometry_collection<double>&) const {
+ return {};
+ }
+};
+
+TileCover::Impl::Impl(int32_t z, const Geometry<double>& geom, bool project)
+ : zoom(z) {
+ ToFeatureType toFeatureType;
+ isClosed = apply_visitor(toFeatureType, geom) == FeatureType::Polygon;
+
+ BuildBoundsMap toBoundsMap(z, project);
+ boundsMap = apply_visitor(toBoundsMap, geom);
+ if (boundsMap.size() == 0) return;
+
+ //Iniitalize the active edge table, and current row span
+ currentBounds = boundsMap.begin();
+ tileY = 0;
+ nextRow();
+ if (tileXSpans.empty()) return;
+ tileX = tileXSpans.front().first;
+}
+
+void TileCover::Impl::nextRow() {
+ // Update AET for next row
+ if (currentBounds != boundsMap.end()) {
+ if (activeBounds.size() == 0 && currentBounds->first > tileY) {
+ //For multi-geoms: use the next row with an edge table starting point
+ tileY = currentBounds->first;
+ }
+ if (tileY == currentBounds->first) {
+
+ std::move(currentBounds->second.begin(), currentBounds->second.end(), std::back_inserter(activeBounds));
+ currentBounds++;
+ }
+ }
+ //Scan aet and update currenRange with x_min, x_max pairs
+ auto xps = util::scan_row(tileY, activeBounds);
+ if (xps.size() == 0) {
+ return;
+ }
+
+ auto x_min = xps[0].xmin;
+ auto x_max = xps[0].xmax;
+ int32_t nzRule = xps[0].winding ? 1 : -1;
+ for (size_t i = 1; i < xps.size(); i++) {
+ auto xp = xps[i];
+ if (!(isClosed && nzRule != 0)) {
+ if (xp.xmin > x_max && xp.xmax >= x_max) {
+ tileXSpans.emplace(x_min, x_max);
+ x_min = xp.xmin;
+ }
+ }
+ nzRule += xp.winding ? 1 : -1;
+ x_max = std::max(x_min, xp.xmax);
+ }
+ tileXSpans.emplace(x_min, x_max);
+}
+
+bool TileCover::Impl::hasNext() const {
+ return (!tileXSpans.empty() && tileX < tileXSpans.front().second && tileY < (1u << zoom));
+}
+
+optional<UnwrappedTileID> TileCover::Impl::next() {
+ if (!hasNext()) return {};
+
+ const auto x = tileX;
+ const auto y = tileY;
+ tileX++;
+ if (tileX >= tileXSpans.front().second) {
+ tileXSpans.pop();
+ if(tileXSpans.empty()) {
+ tileY++;
+ nextRow();
+ }
+ if (!tileXSpans.empty()) {
+ tileX = tileXSpans.front().first;
+ }
+ }
+ return UnwrappedTileID(zoom, x, y);
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp
new file mode 100644
index 0000000000..7c16718984
--- /dev/null
+++ b/src/mbgl/util/tile_cover_impl.hpp
@@ -0,0 +1,90 @@
+#pragma once
+
+#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <vector>
+#include <map>
+#include <queue>
+
+namespace mbgl {
+
+class TransformState;
+class LatLngBounds;
+
+namespace util {
+
+struct Bound;
+
+using Bounds = std::vector<Bound>;
+using BoundsMap = std::map<uint32_t, Bounds>;
+
+// A chain of points from a local minimum to a local maximum. `winding` indicates
+// the direction of the original geometry.
+struct Bound {
+ std::vector<Point<double>> points;
+ size_t currentPoint = 0;
+ bool winding = false;
+
+ Bound() = default;
+ Bound(const Bound& rhs) {
+ points = rhs.points;
+ currentPoint = rhs.currentPoint;
+ winding = rhs.winding;
+ }
+ Bound& operator=(Bound&& rhs) {
+ points = std::move(rhs.points);
+ currentPoint = rhs.currentPoint;
+ winding = rhs.winding;
+ return *this;
+ }
+
+ // Compute the interpolated x coordinate at y for the current edge
+ double interpolate(uint32_t y) {
+ const auto& p0 = points[currentPoint];
+ const auto& p1 = points[currentPoint + 1];
+
+ const auto dx = p1.x - p0.x;
+ const auto dy = p1.y - p0.y;
+ auto x = p0.x;
+ if (dx == 0) {
+ return x;
+ } else if (dy == 0){
+ return y <= p0.y ? p0.x : p1.x;
+ }
+ if (y < p0.y) return x;
+ if (y > p1.y) return p1.x;
+ x = (dx / dy) * (y - p0.y) + p0.x;
+ return x;
+ }
+};
+
+class TileCover::Impl {
+public:
+ Impl(int32_t z, const Geometry<double>& geom, bool project = true);
+ ~Impl() = default;
+
+ optional<UnwrappedTileID> next();
+ bool hasNext() const;
+
+private:
+ using TileSpans = std::queue<std::pair<int32_t, int32_t>>;
+
+ void nextRow();
+
+ const int32_t zoom;
+ bool isClosed;
+
+ BoundsMap boundsMap;
+ BoundsMap::iterator currentBounds;
+ // List of bounds that begin at or before `tileY`
+ Bounds activeBounds;
+
+ TileSpans tileXSpans;
+ uint32_t tileY;
+ int32_t tileX;
+};
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp
index 60839357d5..6edcd83bc2 100644
--- a/src/mbgl/util/tiny_sdf.cpp
+++ b/src/mbgl/util/tiny_sdf.cpp
@@ -95,7 +95,7 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do
for (uint32_t i = 0; i < size; i++) {
double distance = gridOuter[i] - gridInner[i];
- sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff))));
+ sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff))));
}
return sdf;
diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp
index 149661e47e..dea12f9412 100644
--- a/src/mbgl/util/token.hpp
+++ b/src/mbgl/util/token.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/util/optional.hpp>
+
#include <map>
#include <string>
#include <algorithm>
@@ -25,7 +27,14 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) {
if (pos != end) {
for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++);
if (brace != end && *brace == '}') {
- result.append(lookup({ pos + 1, brace }));
+ std::string key { pos + 1, brace };
+ if (optional<std::string> replacement = lookup(key)) {
+ result.append(*replacement);
+ } else {
+ result.append("{");
+ result.append(key);
+ result.append("}");
+ }
pos = brace + 1;
} else {
result.append(pos, brace);
diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp
index 1f6dab9639..a4263502ef 100644
--- a/src/mbgl/util/url.cpp
+++ b/src/mbgl/util/url.cpp
@@ -130,7 +130,7 @@ Path::Path(const std::string& str, const size_t pos, const size_t count)
}
std::string transformURL(const std::string& tpl, const std::string& str, const URL& url) {
- auto result = util::replaceTokens(tpl, [&](const std::string& token) -> std::string {
+ auto result = util::replaceTokens(tpl, [&](const std::string& token) -> optional<std::string> {
if (token == "path") {
return str.substr(url.path.first, url.path.second);
} else if (token == "domain") {
@@ -146,8 +146,9 @@ std::string transformURL(const std::string& tpl, const std::string& str, const U
} else if (token == "extension") {
const Path path(str, url.path.first, url.path.second);
return str.substr(path.extension.first, path.extension.second);
+ } else {
+ return {};
}
- return "";
});
// Append the query string if it exists.