summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLangston Smith <langston.smith@mapbox.com>2018-01-04 11:15:50 -0800
committerGitHub <noreply@github.com>2018-01-04 11:15:50 -0800
commit2ea955d2751ba6459f99a0695e53505c0a11702b (patch)
treef54450918b634a2eea1bd2c4ebc671bf1bb06106 /src
parentf2ec6ae326bad79fea2b06a21151a2835522572a (diff)
parentc62b0af24fc76b4bb2eb34100611dd3ee9ee5536 (diff)
downloadqtlocation-mapboxgl-upstream/ls-android-readme-tweaks.tar.gz
Merge branch 'master' into ls-android-readme-tweaksupstream/ls-android-readme-tweaks
Diffstat (limited to 'src')
-rw-r--r--src/mbgl/actor/scheduler.cpp19
-rw-r--r--src/mbgl/algorithm/generate_clip_ids.cpp16
-rw-r--r--src/mbgl/algorithm/generate_clip_ids.hpp14
-rw-r--r--src/mbgl/algorithm/generate_clip_ids_impl.hpp28
-rw-r--r--src/mbgl/algorithm/update_renderables.hpp34
-rw-r--r--src/mbgl/algorithm/update_tile_masks.hpp128
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp135
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp40
-rw-r--r--src/mbgl/annotation/annotation_tile.cpp3
-rw-r--r--src/mbgl/annotation/annotation_tile.hpp2
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.cpp6
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/line_annotation_impl.cpp6
-rw-r--r--src/mbgl/annotation/line_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp29
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp14
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.cpp10
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.hpp4
-rw-r--r--src/mbgl/annotation/symbol_annotation_impl.hpp2
-rw-r--r--src/mbgl/geometry/feature_index.cpp123
-rw-r--r--src/mbgl/geometry/feature_index.hpp42
-rw-r--r--src/mbgl/gl/attribute.cpp71
-rw-r--r--src/mbgl/gl/attribute.hpp163
-rw-r--r--src/mbgl/gl/color_mode.hpp2
-rw-r--r--src/mbgl/gl/context.cpp161
-rw-r--r--src/mbgl/gl/context.hpp57
-rw-r--r--src/mbgl/gl/gl.cpp3
-rw-r--r--src/mbgl/gl/gl.hpp30
-rw-r--r--src/mbgl/gl/index_buffer.hpp2
-rw-r--r--src/mbgl/gl/object.cpp4
-rw-r--r--src/mbgl/gl/program.hpp56
-rw-r--r--src/mbgl/gl/renderbuffer.hpp16
-rw-r--r--src/mbgl/gl/segment.cpp7
-rw-r--r--src/mbgl/gl/segment.hpp73
-rw-r--r--src/mbgl/gl/texture.hpp20
-rw-r--r--src/mbgl/gl/types.hpp18
-rw-r--r--src/mbgl/gl/uniform.hpp6
-rw-r--r--src/mbgl/gl/value.cpp24
-rw-r--r--src/mbgl/gl/value.hpp9
-rw-r--r--src/mbgl/gl/vertex_array.cpp18
-rw-r--r--src/mbgl/gl/vertex_array.hpp73
-rw-r--r--src/mbgl/gl/vertex_buffer.hpp1
-rw-r--r--src/mbgl/layout/symbol_instance.cpp44
-rw-r--r--src/mbgl/layout/symbol_instance.hpp25
-rw-r--r--src/mbgl/layout/symbol_layout.cpp374
-rw-r--r--src/mbgl/layout/symbol_layout.hpp37
-rw-r--r--src/mbgl/layout/symbol_projection.cpp420
-rw-r--r--src/mbgl/layout/symbol_projection.hpp63
-rw-r--r--src/mbgl/map/backend_scope.cpp47
-rw-r--r--src/mbgl/map/map.cpp524
-rw-r--r--src/mbgl/map/transform.cpp34
-rw-r--r--src/mbgl/map/transform.hpp8
-rw-r--r--src/mbgl/map/transform_state.cpp25
-rw-r--r--src/mbgl/map/transform_state.hpp9
-rw-r--r--src/mbgl/map/update.hpp26
-rw-r--r--src/mbgl/map/zoom_history.hpp40
-rw-r--r--src/mbgl/programs/attributes.hpp9
-rw-r--r--src/mbgl/programs/binary_program.cpp5
-rw-r--r--src/mbgl/programs/binary_program.hpp4
-rw-r--r--src/mbgl/programs/circle_program.hpp4
-rw-r--r--src/mbgl/programs/collision_box_program.cpp2
-rw-r--r--src/mbgl/programs/collision_box_program.hpp157
-rw-r--r--src/mbgl/programs/fill_extrusion_program.hpp10
-rw-r--r--src/mbgl/programs/line_program.cpp2
-rw-r--r--src/mbgl/programs/line_program.hpp13
-rw-r--r--src/mbgl/programs/program.hpp47
-rw-r--r--src/mbgl/programs/programs.hpp4
-rw-r--r--src/mbgl/programs/segment.hpp43
-rw-r--r--src/mbgl/programs/symbol_program.cpp60
-rw-r--r--src/mbgl/programs/symbol_program.hpp338
-rw-r--r--src/mbgl/programs/uniforms.hpp5
-rw-r--r--src/mbgl/renderer/backend_scope.cpp66
-rw-r--r--src/mbgl/renderer/bucket.hpp21
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.cpp10
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.hpp6
-rw-r--r--src/mbgl/renderer/buckets/debug_bucket.cpp2
-rw-r--r--src/mbgl/renderer/buckets/debug_bucket.hpp2
-rw-r--r--src/mbgl/renderer/buckets/fill_bucket.cpp8
-rw-r--r--src/mbgl/renderer/buckets/fill_bucket.hpp7
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp14
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp5
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.cpp28
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.hpp8
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.cpp89
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.hpp17
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp178
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp79
-rw-r--r--src/mbgl/renderer/cross_faded_property_evaluator.cpp5
-rw-r--r--src/mbgl/renderer/frame_history.cpp77
-rw-r--r--src/mbgl/renderer/frame_history.hpp40
-rw-r--r--src/mbgl/renderer/image_manager.cpp42
-rw-r--r--src/mbgl/renderer/image_manager.hpp15
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp78
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp53
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp27
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp7
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp116
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp5
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp134
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp85
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp127
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.hpp2
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp260
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.hpp4
-rw-r--r--src/mbgl/renderer/paint_parameters.cpp96
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp68
-rw-r--r--src/mbgl/renderer/paint_property_binder.hpp26
-rw-r--r--src/mbgl/renderer/painter.cpp387
-rw-r--r--src/mbgl/renderer/painter.hpp181
-rw-r--r--src/mbgl/renderer/painters/painter_background.cpp83
-rw-r--r--src/mbgl/renderer/painters/painter_circle.cpp57
-rw-r--r--src/mbgl/renderer/painters/painter_clipping.cpp37
-rw-r--r--src/mbgl/renderer/painters/painter_debug.cpp162
-rw-r--r--src/mbgl/renderer/painters/painter_fill.cpp141
-rw-r--r--src/mbgl/renderer/painters/painter_fill_extrusion.cpp86
-rw-r--r--src/mbgl/renderer/painters/painter_line.cpp91
-rw-r--r--src/mbgl/renderer/painters/painter_raster.cpp89
-rw-r--r--src/mbgl/renderer/painters/painter_symbol.cpp166
-rw-r--r--src/mbgl/renderer/possibly_evaluated_property_value.hpp2
-rw-r--r--src/mbgl/renderer/render_item.hpp36
-rw-r--r--src/mbgl/renderer/render_layer.cpp10
-rw-r--r--src/mbgl/renderer/render_layer.hpp14
-rw-r--r--src/mbgl/renderer/render_pass.hpp8
-rw-r--r--src/mbgl/renderer/render_source.cpp3
-rw-r--r--src/mbgl/renderer/render_source.hpp17
-rw-r--r--src/mbgl/renderer/render_static_data.cpp67
-rw-r--r--src/mbgl/renderer/render_static_data.hpp39
-rw-r--r--src/mbgl/renderer/render_style.cpp456
-rw-r--r--src/mbgl/renderer/render_style.hpp93
-rw-r--r--src/mbgl/renderer/render_style_observer.hpp14
-rw-r--r--src/mbgl/renderer/render_tile.cpp127
-rw-r--r--src/mbgl/renderer/render_tile.hpp14
-rw-r--r--src/mbgl/renderer/renderer.cpp102
-rw-r--r--src/mbgl/renderer/renderer_backend.cpp (renamed from src/mbgl/map/backend.cpp)28
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp815
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp121
-rw-r--r--src/mbgl/renderer/renderer_observer.hpp35
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.cpp86
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.hpp50
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp45
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp16
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp165
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp29
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp23
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp14
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp25
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp14
-rw-r--r--src/mbgl/renderer/tile_mask.hpp15
-rw-r--r--src/mbgl/renderer/tile_parameters.hpp5
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp153
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp21
-rw-r--r--src/mbgl/renderer/update_parameters.hpp15
-rw-r--r--src/mbgl/shaders/circle.cpp60
-rw-r--r--src/mbgl/shaders/collision_box.cpp45
-rw-r--r--src/mbgl/shaders/collision_circle.cpp83
-rw-r--r--src/mbgl/shaders/collision_circle.hpp16
-rw-r--r--src/mbgl/shaders/debug.cpp2
-rw-r--r--src/mbgl/shaders/fill.cpp12
-rw-r--r--src/mbgl/shaders/fill_extrusion.cpp28
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.cpp28
-rw-r--r--src/mbgl/shaders/fill_outline.cpp12
-rw-r--r--src/mbgl/shaders/fill_outline_pattern.cpp8
-rw-r--r--src/mbgl/shaders/fill_pattern.cpp8
-rw-r--r--src/mbgl/shaders/line.cpp43
-rw-r--r--src/mbgl/shaders/line_pattern.cpp38
-rw-r--r--src/mbgl/shaders/line_sdf.cpp48
-rw-r--r--src/mbgl/shaders/preludes.cpp27
-rw-r--r--src/mbgl/shaders/raster.cpp13
-rw-r--r--src/mbgl/shaders/shaders.cpp1
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp97
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp174
-rw-r--r--src/mbgl/sprite/sprite_loader.cpp4
-rw-r--r--src/mbgl/storage/file_source_request.cpp37
-rw-r--r--src/mbgl/storage/file_source_request.hpp31
-rw-r--r--src/mbgl/storage/resource.cpp22
-rw-r--r--src/mbgl/storage/response.cpp1
-rw-r--r--src/mbgl/style/conversion/constant.cpp94
-rw-r--r--src/mbgl/style/conversion/coordinate.cpp29
-rw-r--r--src/mbgl/style/conversion/filter.cpp248
-rw-r--r--src/mbgl/style/conversion/geojson.cpp18
-rw-r--r--src/mbgl/style/conversion/geojson_options.cpp85
-rw-r--r--src/mbgl/style/conversion/get_json_type.cpp34
-rw-r--r--src/mbgl/style/conversion/json.hpp2
-rw-r--r--src/mbgl/style/conversion/layer.cpp206
-rw-r--r--src/mbgl/style/conversion/light.cpp115
-rw-r--r--src/mbgl/style/conversion/make_property_setters.hpp209
-rw-r--r--src/mbgl/style/conversion/make_property_setters.hpp.ejs46
-rw-r--r--src/mbgl/style/conversion/position.cpp22
-rw-r--r--src/mbgl/style/conversion/property_setter.hpp70
-rw-r--r--src/mbgl/style/conversion/source.cpp175
-rw-r--r--src/mbgl/style/conversion/tileset.cpp73
-rw-r--r--src/mbgl/style/conversion/transition_options.cpp40
-rw-r--r--src/mbgl/style/custom_tile_loader.cpp109
-rw-r--r--src/mbgl/style/custom_tile_loader.hpp45
-rw-r--r--src/mbgl/style/expression/array_assertion.cpp86
-rw-r--r--src/mbgl/style/expression/assertion.cpp73
-rw-r--r--src/mbgl/style/expression/at.cpp64
-rw-r--r--src/mbgl/style/expression/boolean_operator.cpp87
-rw-r--r--src/mbgl/style/expression/case.cpp91
-rw-r--r--src/mbgl/style/expression/check_subtype.cpp60
-rw-r--r--src/mbgl/style/expression/coalesce.cpp62
-rw-r--r--src/mbgl/style/expression/coercion.cpp144
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp568
-rw-r--r--src/mbgl/style/expression/find_zoom_curve.cpp76
-rw-r--r--src/mbgl/style/expression/get_covering_stops.cpp26
-rw-r--r--src/mbgl/style/expression/interpolate.cpp211
-rw-r--r--src/mbgl/style/expression/is_constant.cpp40
-rw-r--r--src/mbgl/style/expression/is_expression.cpp29
-rw-r--r--src/mbgl/style/expression/let.cpp92
-rw-r--r--src/mbgl/style/expression/literal.cpp108
-rw-r--r--src/mbgl/style/expression/match.cpp263
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp208
-rw-r--r--src/mbgl/style/expression/step.cpp152
-rw-r--r--src/mbgl/style/expression/util.cpp39
-rw-r--r--src/mbgl/style/expression/util.hpp14
-rw-r--r--src/mbgl/style/expression/value.cpp322
-rw-r--r--src/mbgl/style/function/categorical_stops.cpp3
-rw-r--r--src/mbgl/style/function/expression.cpp38
-rw-r--r--src/mbgl/style/function/identity_stops.cpp29
-rw-r--r--src/mbgl/style/image_impl.hpp1
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp27
-rw-r--r--src/mbgl/style/layers/circle_layer_properties.hpp5
-rw-r--r--src/mbgl/style/layers/custom_layer.cpp11
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp2
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp2
-rw-r--r--src/mbgl/style/layers/line_layer.cpp6
-rw-r--r--src/mbgl/style/layers/line_layer_properties.hpp2
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp56
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp22
-rw-r--r--src/mbgl/style/observer.hpp3
-rw-r--r--src/mbgl/style/parser.cpp7
-rw-r--r--src/mbgl/style/rapidjson_conversion.hpp152
-rw-r--r--src/mbgl/style/sources/custom_geometry_source.cpp45
-rw-r--r--src/mbgl/style/sources/custom_geometry_source_impl.cpp40
-rw-r--r--src/mbgl/style/sources/custom_geometry_source_impl.hpp29
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.cpp9
-rw-r--r--src/mbgl/style/sources/image_source.cpp7
-rw-r--r--src/mbgl/style/sources/image_source_impl.cpp8
-rw-r--r--src/mbgl/style/sources/image_source_impl.hpp6
-rw-r--r--src/mbgl/style/style.cpp16
-rw-r--r--src/mbgl/style/style_impl.cpp48
-rw-r--r--src/mbgl/style/style_impl.hpp12
-rw-r--r--src/mbgl/style/types.cpp21
-rw-r--r--src/mbgl/text/collision_feature.cpp84
-rw-r--r--src/mbgl/text/collision_feature.hpp49
-rw-r--r--src/mbgl/text/collision_index.cpp359
-rw-r--r--src/mbgl/text/collision_index.hpp70
-rw-r--r--src/mbgl/text/collision_tile.cpp235
-rw-r--r--src/mbgl/text/collision_tile.hpp67
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp165
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.hpp64
-rw-r--r--src/mbgl/text/glyph.hpp25
-rw-r--r--src/mbgl/text/glyph_manager.cpp29
-rw-r--r--src/mbgl/text/glyph_manager.hpp11
-rw-r--r--src/mbgl/text/local_glyph_rasterizer.hpp46
-rw-r--r--src/mbgl/text/placement.cpp332
-rw-r--r--src/mbgl/text/placement.hpp91
-rw-r--r--src/mbgl/text/placement_config.hpp25
-rw-r--r--src/mbgl/text/quads.cpp320
-rw-r--r--src/mbgl/text/quads.hpp31
-rw-r--r--src/mbgl/text/shaping.cpp108
-rw-r--r--src/mbgl/text/shaping.hpp11
-rw-r--r--src/mbgl/tile/custom_geometry_tile.cpp91
-rw-r--r--src/mbgl/tile/custom_geometry_tile.hpp44
-rw-r--r--src/mbgl/tile/geojson_tile.cpp123
-rw-r--r--src/mbgl/tile/geojson_tile.hpp2
-rw-r--r--src/mbgl/tile/geojson_tile_data.hpp94
-rw-r--r--src/mbgl/tile/geometry_tile.cpp151
-rw-r--r--src/mbgl/tile/geometry_tile.hpp63
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp104
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp16
-rw-r--r--src/mbgl/tile/raster_tile.cpp36
-rw-r--r--src/mbgl/tile/raster_tile.hpp18
-rw-r--r--src/mbgl/tile/raster_tile_worker.cpp10
-rw-r--r--src/mbgl/tile/raster_tile_worker.hpp2
-rw-r--r--src/mbgl/tile/tile.cpp9
-rw-r--r--src/mbgl/tile/tile.hpp44
-rw-r--r--src/mbgl/tile/tile_id.hpp275
-rw-r--r--src/mbgl/tile/tile_id_hash.cpp29
-rw-r--r--src/mbgl/tile/tile_id_io.cpp5
-rw-r--r--src/mbgl/tile/tile_loader.hpp12
-rw-r--r--src/mbgl/tile/tile_loader_impl.hpp61
-rw-r--r--src/mbgl/tile/vector_tile.cpp8
-rw-r--r--src/mbgl/tile/vector_tile.hpp7
-rw-r--r--src/mbgl/util/chrono.cpp11
-rw-r--r--src/mbgl/util/constants.cpp2
-rw-r--r--src/mbgl/util/dtoa.cpp16
-rw-r--r--src/mbgl/util/dtoa.hpp1
-rw-r--r--src/mbgl/util/geojson_impl.cpp (renamed from src/mbgl/util/geojson.cpp)0
-rw-r--r--src/mbgl/util/grid_index.cpp292
-rw-r--r--src/mbgl/util/grid_index.hpp96
-rw-r--r--src/mbgl/util/http_header.cpp2
-rw-r--r--src/mbgl/util/http_timeout.cpp2
-rw-r--r--src/mbgl/util/i18n.cpp23
-rw-r--r--src/mbgl/util/i18n.hpp4
-rw-r--r--src/mbgl/util/io.cpp6
-rw-r--r--src/mbgl/util/mapbox.cpp6
-rw-r--r--src/mbgl/util/mapbox.hpp4
-rw-r--r--src/mbgl/util/mat4.cpp9
-rw-r--r--src/mbgl/util/math.hpp17
-rw-r--r--src/mbgl/util/offscreen_texture.cpp32
-rw-r--r--src/mbgl/util/offscreen_texture.hpp18
-rw-r--r--src/mbgl/util/std.hpp8
-rw-r--r--src/mbgl/util/thread.hpp163
-rw-r--r--src/mbgl/util/thread_local.hpp44
-rw-r--r--src/mbgl/util/tile_cover.cpp25
-rw-r--r--src/mbgl/util/tile_cover.hpp5
-rw-r--r--src/mbgl/util/tiny_sdf.cpp105
-rw-r--r--src/mbgl/util/tiny_sdf.hpp20
-rw-r--r--src/parsedate/parsedate.c18
314 files changed, 13636 insertions, 6319 deletions
diff --git a/src/mbgl/actor/scheduler.cpp b/src/mbgl/actor/scheduler.cpp
new file mode 100644
index 0000000000..d7cdb2737b
--- /dev/null
+++ b/src/mbgl/actor/scheduler.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+namespace mbgl {
+
+static auto& current() {
+ static util::ThreadLocal<Scheduler> scheduler;
+ return scheduler;
+};
+
+void Scheduler::SetCurrent(Scheduler* scheduler) {
+ current().set(scheduler);
+}
+
+Scheduler* Scheduler::GetCurrent() {
+ return current().get();
+}
+
+} //namespace mbgl
diff --git a/src/mbgl/algorithm/generate_clip_ids.cpp b/src/mbgl/algorithm/generate_clip_ids.cpp
index 74e0ee242f..287d2a408e 100644
--- a/src/mbgl/algorithm/generate_clip_ids.cpp
+++ b/src/mbgl/algorithm/generate_clip_ids.cpp
@@ -31,14 +31,14 @@ bool ClipIDGenerator::Leaf::operator==(const Leaf& other) const {
return children == other.children;
}
-std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const {
- std::map<UnwrappedTileID, ClipID> stencils;
+std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getClipIDs() const {
+ std::map<UnwrappedTileID, ClipID> clipIDs;
// Merge everything.
for (auto& pair : pool) {
auto& id = pair.first;
auto& leaf = pair.second;
- auto res = stencils.emplace(id, leaf.clip);
+ auto res = clipIDs.emplace(id, leaf.clip);
if (!res.second) {
// Merge with the existing ClipID when there was already an element with the
// same tile ID.
@@ -46,14 +46,14 @@ std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const {
}
}
- for (auto it = stencils.begin(); it != stencils.end(); ++it) {
+ for (auto it = clipIDs.begin(); it != clipIDs.end(); ++it) {
auto& childId = it->first;
auto& childClip = it->second;
// Loop through all preceding stencils, and find all parents.
for (auto parentIt = std::reverse_iterator<decltype(it)>(it);
- parentIt != stencils.rend(); ++parentIt) {
+ parentIt != clipIDs.rend(); ++parentIt) {
auto& parentId = parentIt->first;
if (childId.isChildOf(parentId)) {
// Once we have a parent, we add the bits that this ID hasn't set yet.
@@ -66,11 +66,11 @@ std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const {
}
// Remove tiles that are entirely covered by children.
- util::erase_if(stencils, [&](const auto& stencil) {
- return algorithm::coveredByChildren(stencil.first, stencils);
+ util::erase_if(clipIDs, [&](const auto& stencil) {
+ return algorithm::coveredByChildren(stencil.first, clipIDs);
});
- return stencils;
+ return clipIDs;
}
} // namespace algorithm
diff --git a/src/mbgl/algorithm/generate_clip_ids.hpp b/src/mbgl/algorithm/generate_clip_ids.hpp
index d917b398af..adcf87a72a 100644
--- a/src/mbgl/algorithm/generate_clip_ids.hpp
+++ b/src/mbgl/algorithm/generate_clip_ids.hpp
@@ -3,9 +3,9 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/clip_id.hpp>
-#include <unordered_set>
+#include <set>
#include <vector>
-#include <unordered_map>
+#include <map>
namespace mbgl {
namespace algorithm {
@@ -17,18 +17,18 @@ private:
void add(const CanonicalTileID &p);
bool operator==(const Leaf &other) const;
- std::unordered_set<CanonicalTileID> children;
+ std::set<CanonicalTileID> children;
ClipID& clip;
};
uint8_t bit_offset = 0;
- std::unordered_multimap<UnwrappedTileID, Leaf> pool;
+ std::multimap<UnwrappedTileID, Leaf> pool;
public:
- template <typename Renderables>
- void update(Renderables& renderables);
+ template <typename Renderable>
+ void update(std::vector<std::reference_wrapper<Renderable>> renderables);
- std::map<UnwrappedTileID, ClipID> getStencils() const;
+ std::map<UnwrappedTileID, ClipID> getClipIDs() const;
};
} // namespace algorithm
diff --git a/src/mbgl/algorithm/generate_clip_ids_impl.hpp b/src/mbgl/algorithm/generate_clip_ids_impl.hpp
index d63ba27b6b..6a316dcdad 100644
--- a/src/mbgl/algorithm/generate_clip_ids_impl.hpp
+++ b/src/mbgl/algorithm/generate_clip_ids_impl.hpp
@@ -7,15 +7,17 @@
namespace mbgl {
namespace algorithm {
-template <typename Renderables>
-void ClipIDGenerator::update(Renderables& renderables) {
+template <typename Renderable>
+void ClipIDGenerator::update(std::vector<std::reference_wrapper<Renderable>> renderables) {
std::size_t size = 0;
+ std::sort(renderables.begin(), renderables.end(),
+ [](const auto& a, const auto& b) { return a.get().id < b.get().id; });
+
const auto end = renderables.end();
for (auto it = renderables.begin(); it != end; it++) {
- auto& tileID = it->first;
- auto& renderable = it->second;
- if (!renderable.used) {
+ auto& renderable = it->get();
+ if (!renderable.used || !renderable.needsClipping) {
continue;
}
@@ -28,17 +30,17 @@ void ClipIDGenerator::update(Renderables& renderables) {
// can never be children of the current wrap.
auto child_it = std::next(it);
const auto children_end = std::lower_bound(
- child_it, end, UnwrappedTileID{ static_cast<int16_t>(tileID.wrap + 1), { 0, 0, 0 } },
- [](auto& a, auto& b) { return a.first < b; });
+ child_it, end, UnwrappedTileID{ static_cast<int16_t>(renderable.id.wrap + 1), { 0, 0, 0 } },
+ [](auto& a, auto& b) { return a.get().id < b; });
for (; child_it != children_end; ++child_it) {
- auto& childTileID = child_it->first;
- if (childTileID.isChildOf(tileID)) {
+ auto& childTileID = child_it->get().id;
+ if (childTileID.isChildOf(it->get().id)) {
leaf.add(childTileID.canonical);
}
}
// Find a leaf with matching children.
- for (auto its = pool.equal_range(tileID); its.first != its.second; ++its.first) {
+ for (auto its = pool.equal_range(renderable.id); its.first != its.second; ++its.first) {
auto& existing = its.first->second;
if (existing == leaf) {
leaf.clip = existing.clip;
@@ -50,7 +52,7 @@ void ClipIDGenerator::update(Renderables& renderables) {
size++;
}
- pool.emplace(tileID, std::move(leaf));
+ pool.emplace(renderable.id, std::move(leaf));
}
if (size > 0) {
@@ -60,8 +62,8 @@ void ClipIDGenerator::update(Renderables& renderables) {
// We are starting our count with 1 since we need at least 1 bit set to distinguish between
// areas without any tiles whatsoever and the current area.
uint8_t count = 1;
- for (auto& pair : renderables) {
- auto& renderable = pair.second;
+ for (auto& it : renderables) {
+ auto& renderable = it.get();
if (!renderable.used) {
continue;
}
diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp
index a9c348b538..c583b6b2b6 100644
--- a/src/mbgl/algorithm/update_renderables.hpp
+++ b/src/mbgl/algorithm/update_renderables.hpp
@@ -1,8 +1,8 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/tile/tile_necessity.hpp>
#include <mbgl/util/range.hpp>
-#include <mbgl/storage/resource.hpp>
#include <unordered_set>
@@ -31,7 +31,7 @@ void updateRenderables(GetTileFn getTile,
assert(idealRenderTileID.canonical.z <= zoomRange.max);
assert(dataTileZoom >= idealRenderTileID.canonical.z);
- const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.canonical);
+ const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.wrap, idealRenderTileID.canonical);
auto tile = getTile(idealDataTileID);
if (!tile) {
tile = createTile(idealDataTileID);
@@ -40,15 +40,15 @@ void updateRenderables(GetTileFn getTile,
// if (source has the tile and bucket is loaded) {
if (tile->isRenderable()) {
- retainTile(*tile, Resource::Necessity::Required);
+ retainTile(*tile, TileNecessity::Required);
renderTile(idealRenderTileID, *tile);
} else {
// We are now attempting to load child and parent tiles.
- bool parentHasTriedOptional = tile->hasTriedOptional();
+ bool parentHasTriedOptional = tile->hasTriedCache();
bool parentIsLoaded = tile->isLoaded();
// The tile isn't loaded yet, but retain it anyway because it's an ideal tile.
- retainTile(*tile, Resource::Necessity::Required);
+ retainTile(*tile, TileNecessity::Required);
covered = true;
overscaledZ = dataTileZoom + 1;
if (overscaledZ > zoomRange.max) {
@@ -56,7 +56,7 @@ void updateRenderables(GetTileFn getTile,
const auto childDataTileID = idealDataTileID.scaledTo(overscaledZ);
tile = getTile(childDataTileID);
if (tile && tile->isRenderable()) {
- retainTile(*tile, Resource::Necessity::Optional);
+ retainTile(*tile, TileNecessity::Optional);
renderTile(idealRenderTileID, *tile);
} else {
covered = false;
@@ -64,11 +64,11 @@ void updateRenderables(GetTileFn getTile,
} else {
// Check all four actual child tiles.
for (const auto& childTileID : idealDataTileID.canonical.children()) {
- const OverscaledTileID childDataTileID(overscaledZ, childTileID);
+ const OverscaledTileID childDataTileID(overscaledZ, idealRenderTileID.wrap, childTileID);
tile = getTile(childDataTileID);
if (tile && tile->isRenderable()) {
- retainTile(*tile, Resource::Necessity::Optional);
- renderTile(childDataTileID.unwrapTo(idealRenderTileID.wrap), *tile);
+ retainTile(*tile, TileNecessity::Optional);
+ renderTile(childDataTileID.toUnwrapped(), *tile);
} else {
// At least one child tile doesn't exist, so we are going to look for
// parents as well.
@@ -81,8 +81,7 @@ void updateRenderables(GetTileFn getTile,
// We couldn't find child tiles that entirely cover the ideal tile.
for (overscaledZ = dataTileZoom - 1; overscaledZ >= zoomRange.min; --overscaledZ) {
const auto parentDataTileID = idealDataTileID.scaledTo(overscaledZ);
- const auto parentRenderTileID =
- parentDataTileID.unwrapTo(idealRenderTileID.wrap);
+ const auto parentRenderTileID = parentDataTileID.toUnwrapped();
if (checked.find(parentRenderTileID) != checked.end()) {
// Break parent tile ascent, this route has been checked by another child
@@ -98,12 +97,19 @@ void updateRenderables(GetTileFn getTile,
}
if (tile) {
- retainTile(*tile, parentIsLoaded ? Resource::Necessity::Required
- : Resource::Necessity::Optional);
+ if (!parentIsLoaded) {
+ // We haven't completed loading the child, so we only do an optional
+ // (cache) request in an attempt to quickly load data that we can show.
+ retainTile(*tile, TileNecessity::Optional);
+ } else {
+ // Now that we've checked the child and know for sure that we can't load
+ // it, we attempt to load the parent from the network.
+ retainTile(*tile, TileNecessity::Required);
+ }
// Save the current values, since they're the parent of the next iteration
// of the parent tile ascent loop.
- parentHasTriedOptional = tile->hasTriedOptional();
+ parentHasTriedOptional = tile->hasTriedCache();
parentIsLoaded = tile->isLoaded();
if (tile->isRenderable()) {
diff --git a/src/mbgl/algorithm/update_tile_masks.hpp b/src/mbgl/algorithm/update_tile_masks.hpp
new file mode 100644
index 0000000000..a7840cd163
--- /dev/null
+++ b/src/mbgl/algorithm/update_tile_masks.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include <mbgl/renderer/tile_mask.hpp>
+
+#include <vector>
+#include <functional>
+#include <algorithm>
+
+namespace mbgl {
+namespace algorithm {
+
+namespace {
+
+template <typename Renderable>
+void computeTileMasks(
+ const CanonicalTileID& root,
+ const UnwrappedTileID ref,
+ typename std::vector<std::reference_wrapper<Renderable>>::const_iterator it,
+ const typename std::vector<std::reference_wrapper<Renderable>>::const_iterator end,
+ TileMask& mask) {
+ // If the reference or any of its children is found in the list, we need to recurse.
+ for (; it != end; ++it) {
+ auto& renderable = it->get();
+ if (!renderable.used) {
+ continue;
+ }
+ if (ref == renderable.id) {
+ // The current tile is masked out, so we don't need to add them to the mask set.
+ return;
+ } else if (renderable.id.isChildOf(ref)) {
+ // There's at least one child tile that is masked out, so recursively descend.
+ for (const auto& child : ref.children()) {
+ computeTileMasks<Renderable>(root, child, it, end, mask);
+ }
+ return;
+ }
+ }
+
+ // We couldn't find a child, so it's definitely a masked part.
+ // Compute the difference between the root tile ID and the reference tile ID, since TileMask
+ // elements are always relative (see below for explanation).
+ const uint8_t diffZ = ref.canonical.z - root.z;
+ mask.emplace(diffZ, ref.canonical.x - (root.x << diffZ), ref.canonical.y - (root.y << diffZ));
+}
+
+} // namespace
+
+// Updates the TileMasks for all renderables. Renderables are objects that have an UnwrappedTileID
+// property indicating where they should be rendered on the screen. A TileMask describes all regions
+// within that tile that are *not* covered by other Renderables.
+// Example: Renderables in our list are 2/1/3, 3/3/6, and 4/5/13. The schematic for creating the
+// TileMask for 2/1/3 looks like this:
+//
+// ┌────────┬────────┬─────────────────┐
+// │ │ │#################│
+// │ 4/4/12 │ 4/5/12 │#################│
+// │ │ │#################│
+// ├──────3/2/6──────┤#####3/3/6#######│
+// │ │########│#################│
+// │ 4/4/13 │#4/5/13#│#################│
+// │ │########│#################│
+// ├────────┴──────2/1/3───────────────┤
+// │ │ │
+// │ │ │
+// │ │ │
+// │ 3/2/7 │ 3/3/7 │
+// │ │ │
+// │ │ │
+// │ │ │
+// └─────────────────┴─────────────────┘
+//
+// The TileMask for 2/1/3 thus consists of the tiles 4/4/12, 4/5/12, 4/4/13, 3/2/7, and 3/3/7,
+// but it does *not* include 4/5/13, and 3/3/6, since these are other Renderables.
+// A TileMask always contains TileIDs *relative* to the tile it is generated for, so 2/1/3 is
+// "subtracted" from these TileIDs. The final TileMask for 2/1/3 will thus be:
+//
+// ┌────────┬────────┬─────────────────┐
+// │ │ │#################│
+// │ 2/0/0 │ 2/1/0 │#################│
+// │ │ │#################│
+// ├────────┼────────┤#################│
+// │ │########│#################│
+// │ 2/0/1 │########│#################│
+// │ │########│#################│
+// ├────────┴────────┼─────────────────┤
+// │ │ │
+// │ │ │
+// │ │ │
+// │ 1/0/1 │ 1/1/1 │
+// │ │ │
+// │ │ │
+// │ │ │
+// └─────────────────┴─────────────────┘
+//
+// Only other Renderables that are *children* of the Renderable we are generating the mask for will
+// be considered. For example, adding a Renderable with TileID 4/8/13 won't affect the TileMask for
+// 2/1/3, since it is not a descendant of it.
+template <typename Renderable>
+void updateTileMasks(std::vector<std::reference_wrapper<Renderable>> renderables) {
+ std::sort(renderables.begin(), renderables.end(),
+ [](const Renderable& a, const Renderable& b) { return a.id < b.id; });
+
+ TileMask mask;
+ const auto end = renderables.end();
+ for (auto it = renderables.begin(); it != end; it++) {
+ auto& renderable = it->get();
+ if (!renderable.used) {
+ continue;
+ }
+ // Try to add all remaining ids as children. We sorted the tile list
+ // by z earlier, so all preceding items cannot be children of the current
+ // tile. We also compute the lower bound of the next wrap, because items of the next wrap
+ // can never be children of the current wrap.
+ auto child_it = std::next(it);
+ const auto children_end = std::lower_bound(
+ child_it, end,
+ UnwrappedTileID{ static_cast<int16_t>(renderable.id.wrap + 1), { 0, 0, 0 } },
+ [](auto& a, auto& b) { return a.get().id < b; });
+
+ mask.clear();
+ computeTileMasks<Renderable>(renderable.id.canonical, renderable.id, child_it, children_end,
+ mask);
+ renderable.setMask(std::move(mask));
+ }
+}
+
+} // namespace algorithm
+} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index a69dba1bf2..b94b0a1bce 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -4,6 +4,7 @@
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/annotation/line_annotation_impl.hpp>
#include <mbgl/annotation/fill_annotation_impl.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -17,102 +18,103 @@ using namespace style;
const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
+const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape.";
+
+AnnotationManager::AnnotationManager(Style& style_)
+ : style(style_) {
+};
-AnnotationManager::AnnotationManager() = default;
AnnotationManager::~AnnotationManager() = default;
-AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::setStyle(Style& style_) {
+ style = style_;
+}
+
+void AnnotationManager::onStyleLoaded() {
+ updateStyle();
+}
+
+AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation) {
std::lock_guard<std::mutex> lock(mutex);
AnnotationID id = nextID++;
Annotation::visit(annotation, [&] (const auto& annotation_) {
- this->add(id, annotation_, maxZoom);
+ this->add(id, annotation_);
});
+ dirty = true;
return id;
}
-Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation) {
std::lock_guard<std::mutex> lock(mutex);
- return Annotation::visit(annotation, [&] (const auto& annotation_) {
- return this->update(id, annotation_, maxZoom);
+ Annotation::visit(annotation, [&] (const auto& annotation_) {
+ this->update(id, annotation_);
});
+ return dirty;
}
void AnnotationManager::removeAnnotation(const AnnotationID& id) {
std::lock_guard<std::mutex> lock(mutex);
remove(id);
+ dirty = true;
}
-void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) {
+void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation) {
auto impl = std::make_shared<SymbolAnnotationImpl>(id, annotation);
symbolTree.insert(impl);
symbolAnnotations.emplace(id, impl);
}
-void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation) {
ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
- std::make_unique<LineAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
+ std::make_unique<LineAnnotationImpl>(id, annotation)).first->second;
+ impl.updateStyle(*style.get().impl);
}
-void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation) {
ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
- std::make_unique<FillAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
+ std::make_unique<FillAnnotationImpl>(id, annotation)).first->second;
+ impl.updateStyle(*style.get().impl);
}
-Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) {
- Update result = Update::Nothing;
-
+void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation) {
auto it = symbolAnnotations.find(id);
if (it == symbolAnnotations.end()) {
assert(false); // Attempt to update a non-existent symbol annotation
- return result;
+ return;
}
const SymbolAnnotation& existing = it->second->annotation;
- if (existing.geometry != annotation.geometry) {
- result |= Update::AnnotationData;
- }
-
- if (existing.icon != annotation.icon) {
- result |= Update::AnnotationData | Update::AnnotationStyle;
- }
+ if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) {
+ dirty = true;
- if (result != Update::Nothing) {
- removeAndAdd(id, annotation, maxZoom);
+ remove(id);
+ add(id, annotation);
}
-
- return result;
}
-Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation) {
auto it = shapeAnnotations.find(id);
if (it == shapeAnnotations.end()) {
assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
+ return;
}
- removeAndAdd(id, annotation, maxZoom);
- return Update::AnnotationData | Update::AnnotationStyle;
+ shapeAnnotations.erase(it);
+ add(id, annotation);
+ dirty = true;
}
-Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation) {
auto it = shapeAnnotations.find(id);
if (it == shapeAnnotations.end()) {
assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
+ return;
}
- removeAndAdd(id, annotation, maxZoom);
- return Update::AnnotationData | Update::AnnotationStyle;
-}
-
-void AnnotationManager::removeAndAdd(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
- remove(id);
- Annotation::visit(annotation, [&] (const auto& annotation_) {
- this->add(id, annotation_, maxZoom);
- });
+ shapeAnnotations.erase(it);
+ add(id, annotation);
+ dirty = true;
}
void AnnotationManager::remove(const AnnotationID& id) {
@@ -120,8 +122,9 @@ void AnnotationManager::remove(const AnnotationID& id) {
symbolTree.remove(symbolAnnotations.at(id));
symbolAnnotations.erase(id);
} else if (shapeAnnotations.find(id) != shapeAnnotations.end()) {
- obsoleteShapeAnnotationLayers.insert(shapeAnnotations.at(id)->layerID);
- shapeAnnotations.erase(id);
+ auto it = shapeAnnotations.find(id);
+ *style.get().impl->removeLayer(it->second->layerID);
+ shapeAnnotations.erase(it);
} else {
assert(false); // Should never happen
}
@@ -149,11 +152,11 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic
return tileData;
}
-void AnnotationManager::updateStyle(Style::Impl& style) {
+void AnnotationManager::updateStyle() {
// Create annotation source, point layer, and point bucket. We do everything via Style::Impl
// because we don't want annotation mutations to trigger Style::Impl::styleMutated to be set.
- if (!style.getSource(SourceID)) {
- style.addSource(std::make_unique<AnnotationSource>());
+ if (!style.get().impl->getSource(SourceID)) {
+ style.get().impl->addSource(std::make_unique<AnnotationSource>());
std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID);
@@ -162,13 +165,13 @@ void AnnotationManager::updateStyle(Style::Impl& style) {
layer->setIconAllowOverlap(true);
layer->setIconIgnorePlacement(true);
- style.addLayer(std::move(layer));
+ style.get().impl->addLayer(std::move(layer));
}
std::lock_guard<std::mutex> lock(mutex);
for (const auto& shape : shapeAnnotations) {
- shape.second->updateStyle(style);
+ shape.second->updateStyle(*style.get().impl);
}
for (const auto& image : images) {
@@ -178,29 +181,17 @@ void AnnotationManager::updateStyle(Style::Impl& style) {
// of which images need to be added because we don't know if the style is the same
// instance as in the last updateStyle call. If it's a new style, we need to add all
// images.)
- style.addImage(std::make_unique<style::Image>(image.second));
- }
-
- for (const auto& layer : obsoleteShapeAnnotationLayers) {
- if (style.getLayer(layer)) {
- style.removeLayer(layer);
- }
- }
-
- for (const auto& image : obsoleteImages) {
- if (style.getImage(image)) {
- style.removeImage(image);
- }
+ style.get().impl->addImage(std::make_unique<style::Image>(image.second));
}
-
- obsoleteShapeAnnotationLayers.clear();
- obsoleteImages.clear();
}
void AnnotationManager::updateData() {
std::lock_guard<std::mutex> lock(mutex);
- for (auto& tile : tiles) {
- tile->setData(getTileData(tile->id.canonical));
+ if (dirty) {
+ for (auto& tile : tiles) {
+ tile->setData(getTileData(tile->id.canonical));
+ }
+ dirty = false;
}
}
@@ -225,16 +216,16 @@ void AnnotationManager::addImage(std::unique_ptr<style::Image> image) {
std::lock_guard<std::mutex> lock(mutex);
const std::string id = prefixedImageID(image->getID());
images.erase(id);
- images.emplace(id,
- style::Image(id, image->getImage().clone(), image->getPixelRatio(), image->isSdf()));
- obsoleteImages.erase(id);
+ auto inserted = images.emplace(id, style::Image(id, image->getImage().clone(),
+ image->getPixelRatio(), image->isSdf()));
+ style.get().impl->addImage(std::make_unique<style::Image>(inserted.first->second));
}
void AnnotationManager::removeImage(const std::string& id_) {
std::lock_guard<std::mutex> lock(mutex);
const std::string id = prefixedImageID(id_);
images.erase(id);
- obsoleteImages.insert(id);
+ style.get().impl->removeImage(id);
}
double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) {
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index 6906791db7..326565f8bc 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -3,8 +3,6 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/style/image.hpp>
-#include <mbgl/map/update.hpp>
-#include <mbgl/style/style.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mutex>
@@ -21,20 +19,26 @@ class AnnotationTileData;
class SymbolAnnotationImpl;
class ShapeAnnotationImpl;
+namespace style {
+class Style;
+} // namespace style
+
class AnnotationManager : private util::noncopyable {
public:
- AnnotationManager();
+ AnnotationManager(style::Style&);
~AnnotationManager();
- AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom);
- Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
+ AnnotationID addAnnotation(const Annotation&);
+ bool updateAnnotation(const AnnotationID&, const Annotation&);
void removeAnnotation(const AnnotationID&);
void addImage(std::unique_ptr<style::Image>);
void removeImage(const std::string&);
double getTopOffsetPixelsForImage(const std::string&);
- void updateStyle(style::Style::Impl&);
+ void setStyle(style::Style&);
+ void onStyleLoaded();
+
void updateData();
void addTile(AnnotationTile&);
@@ -42,24 +46,29 @@ public:
static const std::string SourceID;
static const std::string PointLayerID;
+ static const std::string ShapeLayerID;
private:
- void add(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
- void add(const AnnotationID&, const LineAnnotation&, const uint8_t);
- void add(const AnnotationID&, const FillAnnotation&, const uint8_t);
-
- Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const LineAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const FillAnnotation&, const uint8_t);
+ void add(const AnnotationID&, const SymbolAnnotation&);
+ void add(const AnnotationID&, const LineAnnotation&);
+ void add(const AnnotationID&, const FillAnnotation&);
- void removeAndAdd(const AnnotationID&, const Annotation&, const uint8_t);
+ void update(const AnnotationID&, const SymbolAnnotation&);
+ void update(const AnnotationID&, const LineAnnotation&);
+ void update(const AnnotationID&, const FillAnnotation&);
void remove(const AnnotationID&);
+ void updateStyle();
+
std::unique_ptr<AnnotationTileData> getTileData(const CanonicalTileID&);
+ std::reference_wrapper<style::Style> style;
+
std::mutex mutex;
+ bool dirty = false;
+
AnnotationID nextID = 0;
using SymbolAnnotationTree = boost::geometry::index::rtree<std::shared_ptr<const SymbolAnnotationImpl>, boost::geometry::index::rstar<16, 4>>;
@@ -73,8 +82,7 @@ private:
SymbolAnnotationMap symbolAnnotations;
ShapeAnnotationMap shapeAnnotations;
ImageMap images;
- std::unordered_set<std::string> obsoleteShapeAnnotationLayers;
- std::unordered_set<std::string> obsoleteImages;
+
std::unordered_set<AnnotationTile*> tiles;
friend class AnnotationTile;
diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp
index 0596d60f4f..d405418a45 100644
--- a/src/mbgl/annotation/annotation_tile.cpp
+++ b/src/mbgl/annotation/annotation_tile.cpp
@@ -19,9 +19,6 @@ AnnotationTile::~AnnotationTile() {
annotationManager.removeTile(*this);
}
-void AnnotationTile::setNecessity(Necessity) {
-}
-
class AnnotationTileFeatureData {
public:
AnnotationTileFeatureData(const AnnotationID id_,
diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp
index 88505c50e3..a4d1e66802 100644
--- a/src/mbgl/annotation/annotation_tile.hpp
+++ b/src/mbgl/annotation/annotation_tile.hpp
@@ -14,8 +14,6 @@ public:
AnnotationTile(const OverscaledTileID&, const TileParameters&);
~AnnotationTile() override;
- void setNecessity(Necessity) final;
-
private:
AnnotationManager& annotationManager;
};
diff --git a/src/mbgl/annotation/fill_annotation_impl.cpp b/src/mbgl/annotation/fill_annotation_impl.cpp
index 5dc36edab0..9d3e12e004 100644
--- a/src/mbgl/annotation/fill_annotation_impl.cpp
+++ b/src/mbgl/annotation/fill_annotation_impl.cpp
@@ -7,9 +7,9 @@ namespace mbgl {
using namespace style;
-FillAnnotationImpl::FillAnnotationImpl(AnnotationID id_, FillAnnotation annotation_, uint8_t maxZoom_)
- : ShapeAnnotationImpl(id_, maxZoom_),
- annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor }) {
+FillAnnotationImpl::FillAnnotationImpl(AnnotationID id_, FillAnnotation annotation_)
+ : ShapeAnnotationImpl(id_),
+ annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor) {
}
void FillAnnotationImpl::updateStyle(Style::Impl& style) const {
diff --git a/src/mbgl/annotation/fill_annotation_impl.hpp b/src/mbgl/annotation/fill_annotation_impl.hpp
index 5c49e447b8..98f9921514 100644
--- a/src/mbgl/annotation/fill_annotation_impl.hpp
+++ b/src/mbgl/annotation/fill_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class FillAnnotationImpl : public ShapeAnnotationImpl {
public:
- FillAnnotationImpl(AnnotationID, FillAnnotation, uint8_t maxZoom);
+ FillAnnotationImpl(AnnotationID, FillAnnotation);
void updateStyle(style::Style::Impl&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/line_annotation_impl.cpp b/src/mbgl/annotation/line_annotation_impl.cpp
index 8954ecfa58..74fec49af8 100644
--- a/src/mbgl/annotation/line_annotation_impl.cpp
+++ b/src/mbgl/annotation/line_annotation_impl.cpp
@@ -7,9 +7,9 @@ namespace mbgl {
using namespace style;
-LineAnnotationImpl::LineAnnotationImpl(AnnotationID id_, LineAnnotation annotation_, uint8_t maxZoom_)
- : ShapeAnnotationImpl(id_, maxZoom_),
- annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color }) {
+LineAnnotationImpl::LineAnnotationImpl(AnnotationID id_, LineAnnotation annotation_)
+ : ShapeAnnotationImpl(id_),
+ annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color) {
}
void LineAnnotationImpl::updateStyle(Style::Impl& style) const {
diff --git a/src/mbgl/annotation/line_annotation_impl.hpp b/src/mbgl/annotation/line_annotation_impl.hpp
index 548a094d53..108787c422 100644
--- a/src/mbgl/annotation/line_annotation_impl.hpp
+++ b/src/mbgl/annotation/line_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class LineAnnotationImpl : public ShapeAnnotationImpl {
public:
- LineAnnotationImpl(AnnotationID, LineAnnotation, uint8_t maxZoom);
+ LineAnnotationImpl(AnnotationID, LineAnnotation);
void updateStyle(style::Style::Impl&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp
index b8601d4ed2..a0b69af8d5 100644
--- a/src/mbgl/annotation/render_annotation_source.cpp
+++ b/src/mbgl/annotation/render_annotation_source.cpp
@@ -1,7 +1,7 @@
#include <mbgl/annotation/render_annotation_source.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -38,41 +38,40 @@ void RenderAnnotationSource::update(Immutable<style::Source::Impl> baseImpl_,
parameters,
SourceType::Annotations,
util::tileSize,
- { 0, 22 },
+ // Zoom level 16 is typically sufficient for annotations.
+ // See https://github.com/mapbox/mapbox-gl-native/issues/10197
+ { 0, 16 },
[&] (const OverscaledTileID& tileID) {
return std::make_unique<AnnotationTile>(tileID, parameters);
});
}
-void RenderAnnotationSource::startRender(Painter& painter) {
- painter.clipIDGenerator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(painter);
+void RenderAnnotationSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderAnnotationSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderAnnotationSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderAnnotationSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderAnnotationSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const {
return {};
}
-void RenderAnnotationSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderAnnotationSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp
index fe384c64ca..aa2578d4e3 100644
--- a/src/mbgl/annotation/render_annotation_source.hpp
+++ b/src/mbgl/annotation/render_annotation_source.hpp
@@ -18,21 +18,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final;
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -44,7 +44,7 @@ private:
template <>
inline bool RenderSource::is<RenderAnnotationSource>() const {
- return baseImpl->type == SourceType::Annotations;
+ return baseImpl->type == style::SourceType::Annotations;
}
} // namespace mbgl
diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp
index 0c1a631ad8..715dce484e 100644
--- a/src/mbgl/annotation/shape_annotation_impl.cpp
+++ b/src/mbgl/annotation/shape_annotation_impl.cpp
@@ -1,5 +1,6 @@
#include <mbgl/annotation/shape_annotation_impl.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/wrap.hpp>
#include <mbgl/math/clamp.hpp>
@@ -12,10 +13,9 @@ namespace mbgl {
using namespace style;
namespace geojsonvt = mapbox::geojsonvt;
-ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_, const uint8_t maxZoom_)
+ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_)
: id(id_),
- maxZoom(maxZoom_),
- layerID("com.mapbox.annotations.shape." + util::toString(id)) {
+ layerID(AnnotationManager::ShapeLayerID + util::toString(id)) {
}
void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, AnnotationTileData& data) {
@@ -27,7 +27,9 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati
return Feature { std::move(geom) };
}));
mapbox::geojsonvt::Options options;
- options.maxZoom = maxZoom;
+ // The annotation source is currently hard coded to maxzoom 16, so we're topping out at z16
+ // here as well.
+ options.maxZoom = 16;
options.buffer = 255u;
options.extent = util::EXTENT;
options.tolerance = baseTolerance;
diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp
index ed9e8d015a..3e28221f7b 100644
--- a/src/mbgl/annotation/shape_annotation_impl.hpp
+++ b/src/mbgl/annotation/shape_annotation_impl.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <mbgl/annotation/annotation.hpp>
@@ -16,7 +17,7 @@ class CanonicalTileID;
class ShapeAnnotationImpl {
public:
- ShapeAnnotationImpl(const AnnotationID, const uint8_t maxZoom);
+ ShapeAnnotationImpl(const AnnotationID);
virtual ~ShapeAnnotationImpl() = default;
virtual void updateStyle(style::Style::Impl&) const = 0;
@@ -25,7 +26,6 @@ public:
void updateTileData(const CanonicalTileID&, AnnotationTileData&);
const AnnotationID id;
- const uint8_t maxZoom;
const std::string layerID;
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> shapeTiler;
};
diff --git a/src/mbgl/annotation/symbol_annotation_impl.hpp b/src/mbgl/annotation/symbol_annotation_impl.hpp
index 9270acb857..e41ff85f33 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.hpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.hpp
@@ -17,8 +17,10 @@
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#ifndef __clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#pragma GCC diagnostic ignored "-Wmisleading-indentation"
+#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index b1594388c4..3b5e12b54a 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -1,15 +1,14 @@
#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/renderer/render_style.hpp>
#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/text/collision_index.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/math/minmax.hpp>
-#include <mbgl/map/query.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mapbox/geometry/envelope.hpp>
@@ -19,7 +18,7 @@
namespace mbgl {
FeatureIndex::FeatureIndex()
- : grid(util::EXTENT, 16, 0) {
+ : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) { // 16x16 grid -> 32px cell
}
void FeatureIndex::insert(const GeometryCollection& geometries,
@@ -27,24 +26,12 @@ void FeatureIndex::insert(const GeometryCollection& geometries,
const std::string& sourceLayerName,
const std::string& bucketName) {
for (const auto& ring : geometries) {
- grid.insert(IndexedSubfeature { index, sourceLayerName, bucketName, sortIndex++ },
- mapbox::geometry::envelope(ring));
+ auto envelope = mapbox::geometry::envelope(ring);
+ grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++),
+ {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
}
}
-static bool vectorContains(const std::vector<std::string>& vector, const std::string& s) {
- return std::find(vector.begin(), vector.end(), s) != vector.end();
-}
-
-static bool vectorsIntersect(const std::vector<std::string>& vectorA, const std::vector<std::string>& vectorB) {
- for (const auto& a : vectorA) {
- if (vectorContains(vectorB, a)) {
- return true;
- }
- }
- return false;
-}
-
static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) {
return a.sortIndex > b.sortIndex;
}
@@ -53,36 +40,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature&
return a.sortIndex < b.sortIndex;
}
-static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions,
- const RenderStyle& style,
- const GeometryTile& tile,
- const float pixelsToTileUnits) {
-
- // Determine the additional radius needed factoring in property functions
- float additionalRadius = 0;
- auto getQueryRadius = [&](const RenderLayer& layer) {
- auto bucket = tile.getBucket(*layer.baseImpl);
- if (bucket) {
- additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits);
- }
- };
-
- if (queryOptions.layerIDs) {
- for (const auto& layerID : *queryOptions.layerIDs) {
- const RenderLayer* layer = style.getRenderLayer(layerID);
- if (layer) {
- getQueryRadius(*layer);
- }
- }
- } else {
- for (const RenderLayer* layer : style.getRenderLayers()) {
- getQueryRadius(*layer);
- }
- }
-
- return std::min<int16_t>(util::EXTENT, additionalRadius);
-}
-
void FeatureIndex::query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
@@ -91,18 +48,20 @@ void FeatureIndex::query(
const double scale,
const RenderedQueryOptions& queryOptions,
const GeometryTileData& geometryTileData,
- const CanonicalTileID& tileID,
- const RenderStyle& style,
- const CollisionTile* collisionTile,
- const GeometryTile& tile) const {
+ const UnwrappedTileID& tileID,
+ const std::string& sourceID,
+ const std::vector<const RenderLayer*>& layers,
+ const CollisionIndex& collisionIndex,
+ const float additionalQueryRadius) const {
// Determine query radius
const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
- const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits);
+ const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits);
// Query the grid index
mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry);
- std::vector<IndexedSubfeature> features = grid.query({ box.min - additionalRadius, box.max + additionalRadius });
+ std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalRadius),
+ convertPoint<float>(box.max + additionalRadius) });
std::sort(features.begin(), features.end(), topDown);
@@ -113,18 +72,13 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits);
}
- // Query symbol features, if they've been placed.
- if (!collisionTile) {
- return;
- }
-
- std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale);
+ std::vector<IndexedSubfeature> symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits);
}
}
@@ -135,30 +89,39 @@ void FeatureIndex::addFeature(
const RenderedQueryOptions& options,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const float bearing,
const float pixelsToTileUnits) const {
- auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName);
- if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) {
- return;
- }
-
- auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
- assert(sourceLayer);
+ auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* {
+ for (const auto& layer : layers) {
+ if (layer->getID() == layerID) {
+ return layer;
+ }
+ }
+ return nullptr;
+ };
- auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
- assert(geometryTileFeature);
+ // Lazily calculated.
+ std::unique_ptr<GeometryTileLayer> sourceLayer;
+ std::unique_ptr<GeometryTileFeature> geometryTileFeature;
- for (const auto& layerID : layerIDs) {
- if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) {
+ for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) {
+ const RenderLayer* renderLayer = getRenderLayer(layerID);
+ if (!renderLayer) {
continue;
}
- auto renderLayer = style.getRenderLayer(layerID);
- if (!renderLayer ||
- (!renderLayer->is<RenderSymbolLayer>() &&
- !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) {
+ if (!geometryTileFeature) {
+ sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
+ assert(sourceLayer);
+
+ geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
+ assert(geometryTileFeature);
+ }
+
+ if (!renderLayer->is<RenderSymbolLayer>() &&
+ !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) {
continue;
}
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index 83f339a9de..e95bb94da6 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/grid_index.hpp>
#include <mbgl/util/feature.hpp>
@@ -11,20 +12,40 @@
namespace mbgl {
-class GeometryTile;
class RenderedQueryOptions;
-class RenderStyle;
+class RenderLayer;
-class CollisionTile;
-class CanonicalTileID;
+class CollisionIndex;
class IndexedSubfeature {
public:
IndexedSubfeature() = delete;
- std::size_t index;
+ IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_)
+ : index(index_)
+ , sourceLayerName(std::move(sourceLayerName_))
+ , bucketName(std::move(bucketName_))
+ , sortIndex(sortIndex_)
+ , tileID(0, 0, 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_))
+ {}
+
+ size_t index;
std::string sourceLayerName;
std::string bucketName;
size_t sortIndex;
+
+ // Only used for symbol features
+ std::string sourceID;
+ CanonicalTileID tileID;
};
class FeatureIndex {
@@ -41,10 +62,11 @@ public:
const double scale,
const RenderedQueryOptions& options,
const GeometryTileData&,
- const CanonicalTileID&,
- const RenderStyle&,
- const CollisionTile*,
- const GeometryTile& tile) const;
+ const UnwrappedTileID&,
+ const std::string&,
+ const std::vector<const RenderLayer*>&,
+ const CollisionIndex&,
+ const float additionalQueryRadius) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -63,7 +85,7 @@ private:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const float bearing,
const float pixelsToTileUnits) const;
diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp
index e05ca75866..bb5b2ddc34 100644
--- a/src/mbgl/gl/attribute.cpp
+++ b/src/mbgl/gl/attribute.cpp
@@ -1,60 +1,39 @@
#include <mbgl/gl/attribute.hpp>
-#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
namespace mbgl {
namespace gl {
-AttributeLocation bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) {
+void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) {
+ if (location >= MAX_ATTRIBUTES) {
+ throw gl::Error("too many vertex attributes");
+ }
MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name));
- return location;
}
-void DisabledAttribute::bind(Context&, AttributeLocation location, std::size_t) const {
- MBGL_CHECK_ERROR(glDisableVertexAttribArray(location));
-}
+std::set<std::string> getActiveAttributes(ProgramID id) {
+ std::set<std::string> activeAttributes;
-template <class T> DataType DataTypeOf = static_cast<DataType>(0);
-template <> DataType DataTypeOf< int8_t> = DataType::Byte;
-template <> DataType DataTypeOf<uint8_t> = DataType::UnsignedByte;
-template <> DataType DataTypeOf< int16_t> = DataType::Short;
-template <> DataType DataTypeOf<uint16_t> = DataType::UnsignedShort;
-template <> DataType DataTypeOf< int32_t> = DataType::Integer;
-template <> DataType DataTypeOf<uint32_t> = DataType::UnsignedInteger;
-template <> DataType DataTypeOf<float> = DataType::Float;
-
-template <class T, std::size_t N>
-void AttributeBinding<T, N>::bind(Context& context, AttributeLocation location, std::size_t vertexOffset) const {
- context.vertexBuffer = vertexBuffer;
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(location));
- MBGL_CHECK_ERROR(glVertexAttribPointer(
- location,
- static_cast<GLint>(attributeSize),
- static_cast<GLenum>(DataTypeOf<T>),
- static_cast<GLboolean>(false),
- static_cast<GLsizei>(vertexSize),
- reinterpret_cast<GLvoid*>(attributeOffset + (vertexSize * vertexOffset))));
-}
+ GLint attributeCount;
+ MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, &attributeCount));
+
+ GLint maxAttributeLength;
+ MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeLength));
+
+ std::string attributeName;
+ attributeName.resize(maxAttributeLength);
-template class AttributeBinding<uint8_t, 1>;
-template class AttributeBinding<uint8_t, 2>;
-template class AttributeBinding<uint8_t, 3>;
-template class AttributeBinding<uint8_t, 4>;
-
-template class AttributeBinding<uint16_t, 1>;
-template class AttributeBinding<uint16_t, 2>;
-template class AttributeBinding<uint16_t, 3>;
-template class AttributeBinding<uint16_t, 4>;
-
-template class AttributeBinding<int16_t, 1>;
-template class AttributeBinding<int16_t, 2>;
-template class AttributeBinding<int16_t, 3>;
-template class AttributeBinding<int16_t, 4>;
-
-template class AttributeBinding<float, 1>;
-template class AttributeBinding<float, 2>;
-template class AttributeBinding<float, 3>;
-template class AttributeBinding<float, 4>;
+ GLsizei actualLength;
+ GLint size;
+ GLenum type;
+
+ for (int32_t i = 0; i < attributeCount; i++) {
+ MBGL_CHECK_ERROR(glGetActiveAttrib(id, i, maxAttributeLength, &actualLength, &size, &type, &attributeName[0]));
+ activeAttributes.emplace(std::string(attributeName, 0, actualLength));
+ }
+
+ return activeAttributes;
+}
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp
index 48222146fa..fa6c2ddeab 100644
--- a/src/mbgl/gl/attribute.hpp
+++ b/src/mbgl/gl/attribute.hpp
@@ -1,58 +1,52 @@
#pragma once
#include <mbgl/gl/types.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/indexed_tuple.hpp>
-#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
#include <cstddef>
#include <vector>
+#include <set>
#include <functional>
+#include <string>
+#include <array>
+#include <limits>
namespace mbgl {
namespace gl {
-class DisabledAttribute {
-public:
- void bind(Context&, AttributeLocation, std::size_t vertexOffset) const;
+static constexpr std::size_t MAX_ATTRIBUTES = 8;
- friend bool operator==(const DisabledAttribute&,
- const DisabledAttribute&) {
- return true;
- }
-};
+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> {};
+template <> struct DataTypeOf< int16_t> : std::integral_constant<DataType, DataType::Short> {};
+template <> struct DataTypeOf<uint16_t> : std::integral_constant<DataType, DataType::UnsignedShort> {};
+template <> struct DataTypeOf< int32_t> : std::integral_constant<DataType, DataType::Integer> {};
+template <> struct DataTypeOf<uint32_t> : std::integral_constant<DataType, DataType::UnsignedInteger> {};
+template <> struct DataTypeOf<float> : std::integral_constant<DataType, DataType::Float> {};
-template <class T, std::size_t N>
class AttributeBinding {
public:
- AttributeBinding(BufferID vertexBuffer_,
- std::size_t vertexSize_,
- std::size_t attributeOffset_,
- std::size_t attributeSize_ = N)
- : vertexBuffer(vertexBuffer_),
- vertexSize(vertexSize_),
- attributeOffset(attributeOffset_),
- attributeSize(attributeSize_)
- {}
-
- void bind(Context&, AttributeLocation, std::size_t vertexOffset) const;
+ DataType attributeType;
+ uint8_t attributeSize;
+ uint32_t attributeOffset;
+
+ BufferID vertexBuffer;
+ uint32_t vertexSize;
+ uint32_t vertexOffset;
friend bool operator==(const AttributeBinding& lhs,
const AttributeBinding& rhs) {
- return lhs.vertexBuffer == rhs.vertexBuffer
- && lhs.vertexSize == rhs.vertexSize
- && lhs.attributeOffset == rhs.attributeOffset
- && lhs.attributeSize == rhs.attributeSize;
+ return std::tie(lhs.attributeType, lhs.attributeSize, lhs.attributeOffset, lhs.vertexBuffer, lhs.vertexSize, lhs.vertexOffset)
+ == std::tie(rhs.attributeType, rhs.attributeSize, rhs.attributeOffset, rhs.vertexBuffer, rhs.vertexSize, rhs.vertexOffset);
}
-
-private:
- BufferID vertexBuffer;
- std::size_t vertexSize;
- std::size_t attributeOffset;
- std::size_t attributeSize;
};
+using AttributeBindingArray = std::array<optional<AttributeBinding>, MAX_ATTRIBUTES>;
+
/*
gl::Attribute<T,N> manages the binding of a vertex buffer to a GL program attribute.
- T is the underlying primitive type (exposed as Attribute<T,N>::ValueType)
@@ -66,10 +60,7 @@ public:
using Value = std::array<T, N>;
using Location = AttributeLocation;
-
- using Binding = variant<
- DisabledAttribute,
- AttributeBinding<T, N>>;
+ using Binding = AttributeBinding;
/*
Create a binding for this attribute. The `attributeSize` parameter may be used to
@@ -81,28 +72,29 @@ public:
std::size_t attributeIndex,
std::size_t attributeSize = N) {
static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout");
- return AttributeBinding<T, N> {
+ assert(attributeSize >= 1);
+ assert(attributeSize <= 4);
+ assert(Vertex::attributeOffsets[attributeIndex] <= std::numeric_limits<uint32_t>::max());
+ static_assert(sizeof(Vertex) <= std::numeric_limits<uint32_t>::max(), "vertex too large");
+ return AttributeBinding {
+ DataTypeOf<T>::value,
+ static_cast<uint8_t>(attributeSize),
+ static_cast<uint32_t>(Vertex::attributeOffsets[attributeIndex]),
buffer.buffer,
- sizeof(Vertex),
- Vertex::attributeOffsets[attributeIndex],
- attributeSize
+ static_cast<uint32_t>(sizeof(Vertex)),
+ 0,
};
}
- static void bind(Context& context,
- const Location& location,
- Binding& oldBinding,
- const Binding& newBinding,
- std::size_t vertexOffset) {
- if (oldBinding == newBinding) {
- return;
+ static optional<Binding> offsetBinding(const optional<Binding>& binding, std::size_t vertexOffset) {
+ assert(vertexOffset <= std::numeric_limits<uint32_t>::max());
+ if (binding) {
+ AttributeBinding result = *binding;
+ result.vertexOffset = static_cast<uint32_t>(vertexOffset);
+ return result;
+ } else {
+ return binding;
}
-
- Binding::visit(newBinding, [&] (const auto& binding) {
- binding.bind(context, location, vertexOffset);
- });
-
- oldBinding = newBinding;
}
};
@@ -222,7 +214,8 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = {
} // namespace detail
-AttributeLocation bindAttributeLocation(ProgramID, AttributeLocation, const char * name);
+void bindAttributeLocation(ProgramID, AttributeLocation, const char * name);
+std::set<std::string> getActiveAttributes(ProgramID);
template <class... As>
class Attributes {
@@ -230,19 +223,28 @@ public:
using Types = TypeList<As...>;
using Locations = IndexedTuple<
TypeList<As...>,
- TypeList<typename As::Type::Location...>>;
+ TypeList<optional<typename As::Type::Location>...>>;
using Bindings = IndexedTuple<
TypeList<As...>,
- TypeList<typename As::Type::Binding...>>;
+ TypeList<optional<typename As::Type::Binding>...>>;
using NamedLocations = std::vector<std::pair<const std::string, AttributeLocation>>;
using Vertex = detail::Vertex<typename As::Type...>;
- template <class A>
- static constexpr std::size_t Index = TypeIndex<A, As...>::value;
-
static Locations bindLocations(const ProgramID& id) {
- return Locations { bindAttributeLocation(id, Index<As>, As::name())... };
+ 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);
+ return location++;
+ } else {
+ return {};
+ }
+ };
+
+ return Locations { maybeBindLocation(As::name())... };
}
template <class Program>
@@ -251,24 +253,41 @@ public:
}
static NamedLocations getNamedLocations(const Locations& locations) {
- return NamedLocations{ { As::name(), locations.template get<As>() }... };
+ NamedLocations result;
+
+ auto maybeAddLocation = [&] (const std::string& name, const optional<AttributeLocation>& location) {
+ if (location) {
+ result.emplace_back(name, *location);
+ }
+ };
+
+ util::ignore({ (maybeAddLocation(As::name(), locations.template get<As>()), 0)... });
+
+ return result;
}
template <class DrawMode>
static Bindings bindings(const VertexBuffer<Vertex, DrawMode>& buffer) {
- return Bindings { As::Type::binding(buffer, Index<As>)... };
+ return Bindings { As::Type::binding(buffer, TypeIndex<As, As...>::value)... };
}
- static void bind(Context& context,
- const Locations& locations,
- Bindings& oldBindings,
- const Bindings& newBindings,
- std::size_t vertexOffset) {
- util::ignore({ (As::Type::bind(context,
- locations.template get<As>(),
- oldBindings.template get<As>(),
- newBindings.template get<As>(),
- vertexOffset), 0)... });
+ static Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) {
+ return Bindings { As::Type::offsetBinding(bindings.template get<As>(), vertexOffset)... };
+ }
+
+ static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) {
+ AttributeBindingArray result;
+
+ auto maybeAddBinding = [&] (const optional<AttributeLocation>& location,
+ const optional<AttributeBinding>& binding) {
+ if (location) {
+ result.at(*location) = binding;
+ }
+ };
+
+ util::ignore({ (maybeAddBinding(locations.template get<As>(), bindings.template get<As>()), 0)... });
+
+ return result;
}
};
diff --git a/src/mbgl/gl/color_mode.hpp b/src/mbgl/gl/color_mode.hpp
index e73c8737eb..c6594a3a77 100644
--- a/src/mbgl/gl/color_mode.hpp
+++ b/src/mbgl/gl/color_mode.hpp
@@ -49,7 +49,7 @@ public:
struct Replace {
static constexpr BlendEquation equation = BlendEquation::Add;
static constexpr BlendFactor srcFactor = One;
- static constexpr BlendFactor dstFactor = One;
+ static constexpr BlendFactor dstFactor = Zero;
};
using Add = LinearBlend<BlendEquation::Add>;
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index 1b4d6fbcb7..cc5aa014ed 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -1,4 +1,3 @@
-#include <mbgl/map/view.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/debugging_extension.hpp>
@@ -16,6 +15,31 @@ namespace gl {
static_assert(underlying_type(ShaderType::Vertex) == GL_VERTEX_SHADER, "OpenGL type mismatch");
static_assert(underlying_type(ShaderType::Fragment) == GL_FRAGMENT_SHADER, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Byte) == GL_BYTE, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Short) == GL_SHORT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::UnsignedShort) == GL_UNSIGNED_SHORT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Integer) == GL_INT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::UnsignedInteger) == GL_UNSIGNED_INT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Float) == GL_FLOAT, "OpenGL type mismatch");
+
+#if not MBGL_USE_GLES2
+static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8, "OpenGL type mismatch");
+#else
+static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8_OES, "OpenGL type mismatch");
+#endif // MBGL_USE_GLES2
+#if not MBGL_USE_GLES2
+static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8, "OpenGL type mismatch");
+#else
+static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8_OES, "OpenGL type mismatch");
+#endif // MBGL_USE_GLES2
+#if not MBGL_USE_GLES2
+static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT, "OpenGL type mismatch");
+#else
+static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT16, "OpenGL type mismatch");
+#endif // MBGL_USE_GLES2
+
+
static_assert(underlying_type(PrimitiveType::Points) == GL_POINTS, "OpenGL type mismatch");
static_assert(underlying_type(PrimitiveType::Lines) == GL_LINES, "OpenGL type mismatch");
static_assert(underlying_type(PrimitiveType::LineLoop) == GL_LINE_LOOP, "OpenGL type mismatch");
@@ -36,12 +60,36 @@ static_assert(std::is_same<std::underlying_type_t<TextureFormat>, GLenum>::value
static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch");
static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatVec4) == GL_FLOAT_VEC4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Int) == GL_INT, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::IntVec2) == GL_INT_VEC2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::IntVec3) == GL_INT_VEC3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::IntVec4) == GL_INT_VEC4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Bool) == GL_BOOL, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::BoolVec2) == GL_BOOL_VEC2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::BoolVec3) == GL_BOOL_VEC3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::BoolVec4) == GL_BOOL_VEC4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatMat2) == GL_FLOAT_MAT2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatMat3) == GL_FLOAT_MAT3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatMat4) == GL_FLOAT_MAT4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Sampler2D) == GL_SAMPLER_2D, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::SamplerCube) == GL_SAMPLER_CUBE, "OpenGL type mismatch");
+
+static_assert(underlying_type(BufferUsage::StreamDraw) == GL_STREAM_DRAW, "OpenGL type mismatch");
+static_assert(underlying_type(BufferUsage::StaticDraw) == GL_STATIC_DRAW, "OpenGL type mismatch");
+static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "OpenGL type mismatch");
+
static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch");
Context::Context() = default;
Context::~Context() {
- reset();
+ if (cleanupOnDestruction) {
+ reset();
+ }
}
void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) {
@@ -164,25 +212,39 @@ void Context::verifyProgramLinkage(ProgramID program_) {
throw std::runtime_error("program failed to link");
}
-UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size) {
+UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage) {
BufferID id = 0;
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
UniqueBuffer result { std::move(id), { this } };
vertexBuffer = result;
- MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
+ MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage)));
return result;
}
-UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size) {
+void Context::updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) {
+ vertexBuffer = buffer;
+ MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data));
+}
+
+UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size, const BufferUsage usage) {
BufferID id = 0;
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
UniqueBuffer result { std::move(id), { this } };
- vertexArrayObject = 0;
- elementBuffer = result;
- MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
+ bindVertexArray = 0;
+ globalVertexArrayState.indexBuffer = result;
+ MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage)));
return result;
}
+void Context::updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) {
+ // Be sure to unbind any existing vertex array object before binding the index buffer
+ // so that we don't mess up another VAO
+ bindVertexArray = 0;
+ globalVertexArrayState.indexBuffer = buffer;
+ MBGL_CHECK_ERROR(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data));
+}
+
+
UniqueTexture Context::createTexture() {
if (pooledTextures.empty()) {
pooledTextures.resize(TextureMax);
@@ -195,7 +257,14 @@ UniqueTexture Context::createTexture() {
}
bool Context::supportsVertexArrays() const {
- return vertexArray &&
+ static bool blacklisted = []() {
+ // Blacklist Adreno 3xx as it crashes on glBuffer(Sub)Data
+ const std::string renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
+ return renderer.find("Adreno (TM) 3") != std::string::npos;
+ }();
+
+ return !blacklisted &&
+ vertexArray &&
vertexArray->genVertexArrays &&
vertexArray->bindVertexArray &&
vertexArray->deleteVertexArrays;
@@ -203,7 +272,21 @@ bool Context::supportsVertexArrays() const {
#if MBGL_HAS_BINARY_PROGRAMS
bool Context::supportsProgramBinaries() const {
- return programBinary && programBinary->programBinary && programBinary->getProgramBinary;
+ if (!programBinary || !programBinary->programBinary || !programBinary->getProgramBinary) {
+ return false;
+ }
+
+ // Blacklist Adreno 3xx, 4xx, and 5xx GPUs due to known bugs:
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=510637
+ // https://chromium.googlesource.com/chromium/src/gpu/+/master/config/gpu_driver_bug_list.json#2316
+ const std::string renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
+ if (renderer.find("Adreno (TM) 3") != std::string::npos
+ || renderer.find("Adreno (TM) 4") != std::string::npos
+ || renderer.find("Adreno (TM) 5") != std::string::npos) {
+ return false;
+ }
+
+ return true;
}
optional<std::pair<BinaryProgramFormat, std::string>>
@@ -229,11 +312,17 @@ optional<std::pair<BinaryProgramFormat, std::string>> Context::getBinaryProgram(
}
#endif
-UniqueVertexArray Context::createVertexArray() {
- assert(supportsVertexArrays());
- VertexArrayID id = 0;
- MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id));
- return UniqueVertexArray(std::move(id), { this });
+VertexArray Context::createVertexArray() {
+ if (supportsVertexArrays()) {
+ 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 })};
+ } 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.
+ return { UniqueVertexArrayState(&globalVertexArrayState, VertexArrayStateDeleter { false }) };
+ }
}
UniqueFramebuffer Context::createFramebuffer() {
@@ -386,6 +475,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) {
Framebuffer
Context::createFramebuffer(const Texture& color,
const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
+ if (color.size != depthTarget.size) {
+ throw std::runtime_error("Renderbuffer size mismatch");
+ }
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0));
@@ -410,7 +502,7 @@ Context::createTexture(const Size size, const void* data, TextureFormat format,
void Context::updateTexture(
TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) {
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = id;
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), size.width,
size.height, 0, static_cast<GLenum>(format), GL_UNSIGNED_BYTE,
@@ -424,7 +516,7 @@ void Context::bindTexture(Texture& obj,
TextureWrap wrapX,
TextureWrap wrapY) {
if (filter != obj.filter || mipmap != obj.mipmap || wrapX != obj.wrapX || wrapY != obj.wrapY) {
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = obj.texture;
if (filter != obj.filter || mipmap != obj.mipmap) {
@@ -455,7 +547,7 @@ void Context::bindTexture(Texture& obj,
} else if (texture[unit] != obj.texture) {
// We are checking first to avoid setting the active texture without a subsequent
// texture bind.
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = obj.texture;
}
}
@@ -487,7 +579,7 @@ void Context::setDirtyState() {
clearStencil.setDirty();
program.setDirty();
lineWidth.setDirty();
- activeTexture.setDirty();
+ activeTextureUnit.setDirty();
pixelStorePack.setDirty();
pixelStoreUnpack.setDirty();
#if not MBGL_USE_GLES2
@@ -501,8 +593,8 @@ void Context::setDirtyState() {
tex.setDirty();
}
vertexBuffer.setDirty();
- elementBuffer.setDirty();
- vertexArrayObject.setDirty();
+ bindVertexArray.setDirty();
+ globalVertexArrayState.setDirty();
}
void Context::clear(optional<mbgl::Color> color,
@@ -513,19 +605,19 @@ void Context::clear(optional<mbgl::Color> color,
if (color) {
mask |= GL_COLOR_BUFFER_BIT;
clearColor = *color;
- colorMask = { true, true, true, true };
+ colorMask = value::ColorMask::Default;
}
if (depth) {
mask |= GL_DEPTH_BUFFER_BIT;
clearDepth = *depth;
- depthMask = true;
+ depthMask = value::DepthMask::Default;
}
if (stencil) {
mask |= GL_STENCIL_BUFFER_BIT;
clearStencil = *stencil;
- stencilMask = 0xFF;
+ stencilMask = value::StencilMask::Default;
}
MBGL_CHECK_ERROR(glClear(mask));
@@ -557,6 +649,13 @@ void Context::setDrawMode(const TriangleStrip&) {
void Context::setDepthMode(const DepthMode& depth) {
if (depth.func == DepthMode::Always && !depth.mask) {
depthTest = false;
+
+ // Workaround for rendering errors on Adreno 2xx GPUs. Depth-related state should
+ // not matter when the depth test is disabled, but on these GPUs it apparently does.
+ // https://github.com/mapbox/mapbox-gl-native/issues/9164
+ depthFunc = depth.func;
+ depthMask = depth.mask;
+ depthRange = depth.range;
} else {
depthTest = true;
depthFunc = depth.func;
@@ -621,8 +720,8 @@ void Context::performCleanup() {
for (const auto id : abandonedBuffers) {
if (vertexBuffer == id) {
vertexBuffer.setDirty();
- } else if (elementBuffer == id) {
- elementBuffer.setDirty();
+ } else if (globalVertexArrayState.indexBuffer == id) {
+ globalVertexArrayState.indexBuffer.setDirty();
}
}
MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data()));
@@ -631,8 +730,10 @@ void Context::performCleanup() {
if (!abandonedTextures.empty()) {
for (const auto id : abandonedTextures) {
- if (activeTexture == id) {
- activeTexture.setDirty();
+ for (auto& binding : texture) {
+ if (binding == id) {
+ binding.setDirty();
+ }
}
}
MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
@@ -642,8 +743,8 @@ void Context::performCleanup() {
if (!abandonedVertexArrays.empty()) {
assert(supportsVertexArrays());
for (const auto id : abandonedVertexArrays) {
- if (vertexArrayObject == id) {
- vertexArrayObject.setDirty();
+ if (bindVertexArray == id) {
+ bindVertexArray.setDirty();
}
}
MBGL_CHECK_ERROR(vertexArray->deleteVertexArrays(int(abandonedVertexArrays.size()),
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 4f5b4c797c..14f078367f 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -9,6 +9,7 @@
#include <mbgl/gl/framebuffer.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/vertex_array.hpp>
#include <mbgl/gl/types.hpp>
#include <mbgl/gl/draw_mode.hpp>
#include <mbgl/gl/depth_mode.hpp>
@@ -24,9 +25,6 @@
#include <string>
namespace mbgl {
-
-class View;
-
namespace gl {
constexpr size_t TextureMax = 64;
@@ -53,9 +51,7 @@ public:
void verifyProgramLinkage(ProgramID);
void linkProgram(ProgramID);
UniqueTexture createTexture();
-
- bool supportsVertexArrays() const;
- UniqueVertexArray createVertexArray();
+ VertexArray createVertexArray();
#if MBGL_HAS_BINARY_PROGRAMS
bool supportsProgramBinaries() const;
@@ -65,19 +61,32 @@ public:
optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const;
template <class Vertex, class DrawMode>
- VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) {
+ VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsage usage = BufferUsage::StaticDraw) {
return VertexBuffer<Vertex, DrawMode> {
v.vertexSize(),
- createVertexBuffer(v.data(), v.byteSize())
+ createVertexBuffer(v.data(), v.byteSize(), usage)
};
}
+ template <class Vertex, class DrawMode>
+ void updateVertexBuffer(VertexBuffer<Vertex, DrawMode>& buffer, VertexVector<Vertex, DrawMode>&& v) {
+ assert(v.vertexSize() == buffer.vertexCount);
+ updateVertexBuffer(buffer.buffer, v.data(), v.byteSize());
+ }
+
template <class DrawMode>
- IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v) {
+ IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v, const BufferUsage usage = BufferUsage::StaticDraw) {
return IndexBuffer<DrawMode> {
- createIndexBuffer(v.data(), v.byteSize())
+ v.indexSize(),
+ createIndexBuffer(v.data(), v.byteSize(), usage)
};
}
+
+ template <class DrawMode>
+ void updateIndexBuffer(IndexBuffer<DrawMode>& buffer, IndexVector<DrawMode>&& v) {
+ assert(v.indexSize() == buffer.indexCount);
+ updateIndexBuffer(buffer.buffer, v.data(), v.byteSize());
+ }
template <RenderbufferType type>
Renderbuffer<type> createRenderbuffer(const Size size) {
@@ -188,7 +197,13 @@ public:
return vertexArray.get();
}
+ void setCleanupOnDestruction(bool cleanup) {
+ cleanupOnDestruction = cleanup;
+ }
+
private:
+ bool cleanupOnDestruction = true;
+
std::unique_ptr<extension::Debugging> debugging;
std::unique_ptr<extension::VertexArray> vertexArray;
#if MBGL_HAS_BINARY_PROGRAMS
@@ -196,15 +211,16 @@ private:
#endif
public:
- State<value::ActiveTexture> activeTexture;
+ State<value::ActiveTextureUnit> activeTextureUnit;
State<value::BindFramebuffer> bindFramebuffer;
State<value::Viewport> viewport;
State<value::ScissorTest> scissorTest;
std::array<State<value::BindTexture>, 2> texture;
- State<value::BindVertexArray, const Context&> vertexArrayObject { *this };
State<value::Program> program;
State<value::BindVertexBuffer> vertexBuffer;
- State<value::BindElementBuffer> elementBuffer;
+
+ State<value::BindVertexArray, const Context&> bindVertexArray { *this };
+ VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this };
State<value::PixelStorePack> pixelStorePack;
State<value::PixelStoreUnpack> pixelStoreUnpack;
@@ -239,8 +255,10 @@ private:
State<value::PointSize> pointSize;
#endif // MBGL_USE_GLES2
- UniqueBuffer createVertexBuffer(const void* data, std::size_t size);
- UniqueBuffer createIndexBuffer(const void* data, std::size_t size);
+ UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage);
+ void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
+ UniqueBuffer createIndexBuffer(const void* data, std::size_t size, const BufferUsage usage);
+ void updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit);
void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit);
UniqueFramebuffer createFramebuffer();
@@ -250,6 +268,8 @@ private:
void drawPixels(Size size, const void* data, TextureFormat);
#endif // MBGL_USE_GLES2
+ bool supportsVertexArrays() const;
+
friend detail::ProgramDeleter;
friend detail::ShaderDeleter;
friend detail::BufferDeleter;
@@ -269,8 +289,13 @@ private:
std::vector<RenderbufferID> abandonedRenderbuffers;
public:
- // For testing
+ // For testing and Windows because Qt + ANGLE
+ // crashes with VAO enabled.
+#if defined(_WINDOWS)
+ bool disableVAOExtension = true;
+#else
bool disableVAOExtension = false;
+#endif
};
} // namespace gl
diff --git a/src/mbgl/gl/gl.cpp b/src/mbgl/gl/gl.cpp
index 8999468dbd..bd6d7b192d 100644
--- a/src/mbgl/gl/gl.cpp
+++ b/src/mbgl/gl/gl.cpp
@@ -1,12 +1,13 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/util/string.hpp>
+#include <mbgl/util/util.hpp>
namespace mbgl {
namespace gl {
namespace {
-constexpr const char* stringFromError(GLenum err) {
+MBGL_CONSTEXPR const char* stringFromError(GLenum err) {
switch (err) {
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
diff --git a/src/mbgl/gl/gl.hpp b/src/mbgl/gl/gl.hpp
index 3e21731330..976b7d2f74 100644
--- a/src/mbgl/gl/gl.hpp
+++ b/src/mbgl/gl/gl.hpp
@@ -1,36 +1,10 @@
#pragma once
+#include <mbgl/gl/gl_impl.hpp>
+
#include <stdexcept>
#include <limits>
-#if __APPLE__
- #include "TargetConditionals.h"
- #if TARGET_OS_IPHONE
- #include <OpenGLES/ES2/gl.h>
- #include <OpenGLES/ES2/glext.h>
- #elif TARGET_IPHONE_SIMULATOR
- #include <OpenGLES/ES2/gl.h>
- #include <OpenGLES/ES2/glext.h>
- #elif TARGET_OS_MAC
- #include <OpenGL/OpenGL.h>
- #include <OpenGL/gl.h>
- #include <OpenGL/glext.h>
- #else
- #error Unsupported Apple platform
- #endif
-#elif __ANDROID__ || MBGL_USE_GLES2
- #define GL_GLEXT_PROTOTYPES
- #include <GLES2/gl2.h>
- #include <GLES2/gl2ext.h>
-#elif __QT__ && QT_VERSION >= 0x050000
- #define GL_GLEXT_PROTOTYPES
- #include <QtGui/qopengl.h>
-#else
- #define GL_GLEXT_PROTOTYPES
- #include <GL/gl.h>
- #include <GL/glext.h>
-#endif
-
namespace mbgl {
namespace gl {
diff --git a/src/mbgl/gl/index_buffer.hpp b/src/mbgl/gl/index_buffer.hpp
index 1e57fb11f2..87bfb6068f 100644
--- a/src/mbgl/gl/index_buffer.hpp
+++ b/src/mbgl/gl/index_buffer.hpp
@@ -26,6 +26,7 @@ public:
bool empty() const { return v.empty(); }
void clear() { v.clear(); }
const uint16_t* data() const { return v.data(); }
+ const std::vector<uint16_t>& vector() const { return v; }
private:
std::vector<uint16_t> v;
@@ -34,6 +35,7 @@ private:
template <class DrawMode>
class IndexBuffer {
public:
+ std::size_t indexCount;
UniqueBuffer buffer;
};
diff --git a/src/mbgl/gl/object.cpp b/src/mbgl/gl/object.cpp
index e2d476e0c0..2c5f1bca1f 100644
--- a/src/mbgl/gl/object.cpp
+++ b/src/mbgl/gl/object.cpp
@@ -33,7 +33,9 @@ void TextureDeleter::operator()(TextureID id) const {
void VertexArrayDeleter::operator()(VertexArrayID id) const {
assert(context);
- context->abandonedVertexArrays.push_back(id);
+ if (id != 0) {
+ context->abandonedVertexArrays.push_back(id);
+ }
}
void FramebufferDeleter::operator()(FramebufferID id) const {
diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp
index 47ad39de7c..3b54ec194a 100644
--- a/src/mbgl/gl/program.hpp
+++ b/src/mbgl/gl/program.hpp
@@ -5,15 +5,16 @@
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/vertex_array.hpp>
#include <mbgl/gl/attribute.hpp>
#include <mbgl/gl/uniform.hpp>
#include <mbgl/util/io.hpp>
+#include <mbgl/util/logging.hpp>
#include <mbgl/programs/binary_program.hpp>
#include <mbgl/programs/program_parameters.hpp>
#include <mbgl/shaders/shaders.hpp>
-
#include <string>
namespace mbgl {
@@ -33,15 +34,17 @@ public:
: program(
context.createProgram(context.createShader(ShaderType::Vertex, vertexSource),
context.createShader(ShaderType::Fragment, fragmentSource))),
- attributeLocations(Attributes::bindLocations(program)),
- uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))) {
+ uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))),
+ attributeLocations(Attributes::bindLocations(program)) {
+ // Re-link program after manually binding only active attributes in Attributes::bindLocations
+ context.linkProgram(program);
}
template <class BinaryProgram>
Program(Context& context, const BinaryProgram& binaryProgram)
: program(context.createProgram(binaryProgram.format(), binaryProgram.code())),
- attributeLocations(Attributes::loadNamedLocations(binaryProgram)),
- uniformsState(Uniforms::loadNamedLocations(binaryProgram)) {
+ uniformsState(Uniforms::loadNamedLocations(binaryProgram)),
+ attributeLocations(Attributes::loadNamedLocations(binaryProgram)) {
}
static Program createProgram(gl::Context& context,
@@ -49,15 +52,13 @@ public:
const char* name,
const char* vertexSource_,
const char* fragmentSource_) {
+ const std::string vertexSource = shaders::vertexSource(programParameters, vertexSource_);
+ const std::string fragmentSource = shaders::fragmentSource(programParameters, fragmentSource_);
+
#if MBGL_HAS_BINARY_PROGRAMS
optional<std::string> cachePath = programParameters.cachePath(name);
if (cachePath && context.supportsProgramBinaries()) {
- const std::string vertexSource =
- shaders::vertexSource(programParameters, vertexSource_);
- const std::string fragmentSource =
- shaders::fragmentSource(programParameters, fragmentSource_);
- const std::string identifier =
- shaders::programIdentifier(vertexSource, fragmentSource_);
+ const std::string identifier = shaders::programIdentifier(vertexSource, fragmentSource);
try {
if (auto cachedBinaryProgram = util::readFile(*cachePath)) {
@@ -91,11 +92,9 @@ public:
return std::move(result);
}
#endif
+
(void)name;
- return Program {
- context, shaders::vertexSource(programParameters, vertexSource_),
- shaders::fragmentSource(programParameters, fragmentSource_)
- };
+ return Program { context, vertexSource, fragmentSource };
}
template <class BinaryProgram>
@@ -114,10 +113,12 @@ public:
DepthMode depthMode,
StencilMode stencilMode,
ColorMode colorMode,
- UniformValues&& uniformValues,
- AttributeBindings&& attributeBindings,
+ const UniformValues& uniformValues,
+ VertexArray& vertexArray,
+ const AttributeBindings& attributeBindings,
const IndexBuffer<DrawMode>& indexBuffer,
- const SegmentVector<Attributes>& segments) {
+ std::size_t indexOffset,
+ std::size_t indexLength) {
static_assert(std::is_same<Primitive, typename DrawMode::Primitive>::value, "incompatible draw mode");
context.setDrawMode(drawMode);
@@ -127,25 +128,22 @@ public:
context.program = program;
- Uniforms::bind(uniformsState, std::move(uniformValues));
+ Uniforms::bind(uniformsState, uniformValues);
- for (const auto& segment : segments) {
- segment.bind(context,
- indexBuffer.buffer,
- attributeLocations,
- attributeBindings);
+ vertexArray.bind(context,
+ indexBuffer.buffer,
+ Attributes::toBindingArray(attributeLocations, attributeBindings));
- context.draw(drawMode.primitiveType,
- segment.indexOffset,
- segment.indexLength);
- }
+ context.draw(drawMode.primitiveType,
+ indexOffset,
+ indexLength);
}
private:
UniqueProgram program;
- typename Attributes::Locations attributeLocations;
typename Uniforms::State uniformsState;
+ typename Attributes::Locations attributeLocations;
};
} // namespace gl
diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp
index cc8ff13268..0592557a7f 100644
--- a/src/mbgl/gl/renderbuffer.hpp
+++ b/src/mbgl/gl/renderbuffer.hpp
@@ -9,9 +9,23 @@ namespace gl {
template <RenderbufferType renderbufferType>
class Renderbuffer {
public:
+ Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false)
+ : size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) {
+ }
+
using type = std::integral_constant<RenderbufferType, renderbufferType>;
Size size;
- gl::UniqueRenderbuffer renderbuffer;
+ UniqueRenderbuffer renderbuffer;
+
+ void shouldClear(bool clear) {
+ dirty = clear;
+ }
+ bool needsClearing() {
+ return dirty;
+ }
+
+private:
+ bool dirty;
};
} // namespace gl
diff --git a/src/mbgl/gl/segment.cpp b/src/mbgl/gl/segment.cpp
deleted file mode 100644
index aabdc83cd4..0000000000
--- a/src/mbgl/gl/segment.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <mbgl/gl/segment.hpp>
-
-namespace mbgl {
-namespace gl {
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/gl/segment.hpp b/src/mbgl/gl/segment.hpp
deleted file mode 100644
index fe0658bf8e..0000000000
--- a/src/mbgl/gl/segment.hpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-#include <mbgl/gl/context.hpp>
-#include <mbgl/gl/vertex_buffer.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/logging.hpp>
-
-#include <cstddef>
-#include <vector>
-
-namespace mbgl {
-namespace gl {
-
-template <class Attributes>
-class Segment {
-public:
- Segment(std::size_t vertexOffset_,
- std::size_t indexOffset_,
- std::size_t vertexLength_ = 0,
- std::size_t indexLength_ = 0)
- : vertexOffset(vertexOffset_),
- indexOffset(indexOffset_),
- vertexLength(vertexLength_),
- indexLength(indexLength_) {}
-
- const std::size_t vertexOffset;
- const std::size_t indexOffset;
-
- std::size_t vertexLength;
- std::size_t indexLength;
-
- void bind(Context& context,
- BufferID indexBuffer_,
- const typename Attributes::Locations& attributeLocations,
- const typename Attributes::Bindings& attributeBindings_) const {
- if (context.supportsVertexArrays()) {
- if (!vao) {
- vao = context.createVertexArray();
- context.vertexBuffer.setDirty();
- }
- context.vertexArrayObject = *vao;
- if (indexBuffer != indexBuffer_) {
- indexBuffer = indexBuffer_;
- context.elementBuffer.setDirty();
- context.elementBuffer = indexBuffer_;
- }
- } else {
- // No VAO support. Force attributes to be rebound.
- context.elementBuffer = indexBuffer_;
- attributeBindings = {};
- }
-
- Attributes::bind(context,
- attributeLocations,
- attributeBindings,
- attributeBindings_,
- vertexOffset);
- }
-
-private:
- mutable optional<UniqueVertexArray> vao;
- mutable optional<BufferID> indexBuffer;
- mutable typename Attributes::Bindings attributeBindings;
-};
-
-template <class Attributes>
-class SegmentVector : public std::vector<Segment<Attributes>> {
-public:
- SegmentVector() = default;
-};
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/gl/texture.hpp b/src/mbgl/gl/texture.hpp
index 5330689ac2..625e69233a 100644
--- a/src/mbgl/gl/texture.hpp
+++ b/src/mbgl/gl/texture.hpp
@@ -8,12 +8,24 @@ namespace gl {
class Texture {
public:
+ Texture(Size size_, UniqueTexture texture_,
+ TextureFilter filter_ = TextureFilter::Nearest,
+ TextureMipMap mipmap_ = TextureMipMap::No,
+ TextureWrap wrapX_ = TextureWrap::Clamp,
+ TextureWrap wrapY_ = TextureWrap::Clamp)
+ : size(std::move(size_)),
+ texture(std::move(texture_)),
+ filter(filter_),
+ mipmap(mipmap_),
+ wrapX(wrapX_),
+ wrapY(wrapY_) {}
+
Size size;
UniqueTexture texture;
- TextureFilter filter = TextureFilter::Nearest;
- TextureMipMap mipmap = TextureMipMap::No;
- TextureWrap wrapX = TextureWrap::Clamp;
- TextureWrap wrapY = TextureWrap::Clamp;
+ TextureFilter filter;
+ TextureMipMap mipmap;
+ TextureWrap wrapX;
+ TextureWrap wrapY;
};
} // namespace gl
diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp
index 74ce67fba6..da08195e58 100644
--- a/src/mbgl/gl/types.hpp
+++ b/src/mbgl/gl/types.hpp
@@ -15,8 +15,16 @@ using VertexArrayID = uint32_t;
using FramebufferID = uint32_t;
using RenderbufferID = uint32_t;
-using AttributeLocation = int32_t;
+// OpenGL does not formally define a type for attribute locations, but most APIs use
+// GLuint. The exception is glGetAttribLocation, which returns GLint so that -1 can
+// be used as an error indicator.
+using AttributeLocation = uint32_t;
+
+// OpenGL does not formally define a type for uniform locations, but all APIs use GLint.
+// The value -1 is special, typically used as a placeholder for an unused uniform and
+// "silently ignored".
using UniformLocation = int32_t;
+
using TextureUnit = uint8_t;
enum class ShaderType : uint32_t {
@@ -24,7 +32,7 @@ enum class ShaderType : uint32_t {
Fragment = 0x8B30
};
-enum class DataType : uint32_t {
+enum class DataType : uint16_t {
Byte = 0x1400,
UnsignedByte = 0x1401,
Short = 0x1402,
@@ -96,5 +104,11 @@ enum class UniformDataType : uint32_t {
SamplerCube = 0x8B60,
};
+enum class BufferUsage : uint32_t {
+ StreamDraw = 0x88E0,
+ StaticDraw = 0x88E4,
+ DynamicDraw = 0x88E8,
+};
+
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp
index 829192bcca..5a78068fc8 100644
--- a/src/mbgl/gl/uniform.hpp
+++ b/src/mbgl/gl/uniform.hpp
@@ -48,6 +48,8 @@ public:
class State {
public:
+ State(UniformLocation location_) : location(std::move(location_)) {}
+
void operator=(const Value& value) {
if (location >= 0 && (!current || *current != value.t)) {
current = value.t;
@@ -106,14 +108,14 @@ public:
template <class Program>
static State loadNamedLocations(const Program& program) {
- return State{ { program.uniformLocation(Us::name()) }... };
+ return State(typename Us::State(program.uniformLocation(Us::name()))...);
}
static NamedLocations getNamedLocations(const State& state) {
return NamedLocations{ { Us::name(), state.template get<Us>().location }... };
}
- static void bind(State& state, Values&& values) {
+ static void bind(State& state, const Values& values) {
util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... });
}
};
diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp
index 2b825fb2bd..092403af0d 100644
--- a/src/mbgl/gl/value.cpp
+++ b/src/mbgl/gl/value.cpp
@@ -242,13 +242,13 @@ LineWidth::Type LineWidth::Get() {
return lineWidth;
}
-const constexpr ActiveTexture::Type ActiveTexture::Default;
+const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default;
-void ActiveTexture::Set(const Type& value) {
+void ActiveTextureUnit::Set(const Type& value) {
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value));
}
-ActiveTexture::Type ActiveTexture::Get() {
+ActiveTextureUnit::Type ActiveTextureUnit::Get() {
GLint activeTexture;
MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture));
return static_cast<Type>(activeTexture - GL_TEXTURE0);
@@ -365,6 +365,24 @@ BindVertexArray::Type BindVertexArray::Get(const Context& context) {
return binding;
}
+const optional<AttributeBinding> VertexAttribute::Default {};
+
+void VertexAttribute::Set(const optional<AttributeBinding>& binding, Context& context, AttributeLocation location) {
+ if (binding) {
+ context.vertexBuffer = binding->vertexBuffer;
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(location));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(
+ location,
+ static_cast<GLint>(binding->attributeSize),
+ static_cast<GLenum>(binding->attributeType),
+ static_cast<GLboolean>(false),
+ static_cast<GLsizei>(binding->vertexSize),
+ reinterpret_cast<GLvoid*>(binding->attributeOffset + (binding->vertexSize * binding->vertexOffset))));
+ } else {
+ MBGL_CHECK_ERROR(glDisableVertexAttribArray(location));
+ }
+}
+
const constexpr PixelStorePack::Type PixelStorePack::Default;
void PixelStorePack::Set(const Type& value) {
diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp
index d4c7b5cdc3..7b85a5ff4b 100644
--- a/src/mbgl/gl/value.hpp
+++ b/src/mbgl/gl/value.hpp
@@ -4,6 +4,7 @@
#include <mbgl/gl/depth_mode.hpp>
#include <mbgl/gl/stencil_mode.hpp>
#include <mbgl/gl/color_mode.hpp>
+#include <mbgl/gl/attribute.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/size.hpp>
#include <mbgl/util/range.hpp>
@@ -164,7 +165,7 @@ struct LineWidth {
static Type Get();
};
-struct ActiveTexture {
+struct ActiveTextureUnit {
using Type = TextureUnit;
static const constexpr Type Default = 0;
static void Set(const Type&);
@@ -239,6 +240,12 @@ struct BindVertexArray {
static Type Get(const Context&);
};
+struct VertexAttribute {
+ using Type = optional<gl::AttributeBinding>;
+ static const Type Default;
+ static void Set(const Type&, Context&, AttributeLocation);
+};
+
struct PixelStorePack {
using Type = PixelStorageType;
static const constexpr Type Default = { 4 };
diff --git a/src/mbgl/gl/vertex_array.cpp b/src/mbgl/gl/vertex_array.cpp
new file mode 100644
index 0000000000..68a500ac45
--- /dev/null
+++ b/src/mbgl/gl/vertex_array.cpp
@@ -0,0 +1,18 @@
+#include <mbgl/gl/vertex_array.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/gl.hpp>
+
+namespace mbgl {
+namespace gl {
+
+void VertexArray::bind(Context& context, BufferID indexBuffer, const AttributeBindingArray& bindings) {
+ context.bindVertexArray = state->vertexArray;
+ state->indexBuffer = indexBuffer;
+
+ for (AttributeLocation location = 0; location < MAX_ATTRIBUTES; ++location) {
+ state->bindings[location] = bindings[location];
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp
new file mode 100644
index 0000000000..46c67017bb
--- /dev/null
+++ b/src/mbgl/gl/vertex_array.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <mbgl/gl/object.hpp>
+#include <mbgl/gl/attribute.hpp>
+#include <mbgl/gl/state.hpp>
+#include <mbgl/gl/value.hpp>
+
+#include <array>
+#include <memory>
+
+namespace mbgl {
+namespace gl {
+
+class Context;
+
+class VertexArrayState {
+public:
+ VertexArrayState(UniqueVertexArray vertexArray_, Context& context)
+ : vertexArray(std::move(vertexArray_)),
+ bindings(makeBindings(context, std::make_index_sequence<MAX_ATTRIBUTES>())) {
+ }
+
+ void setDirty() {
+ indexBuffer.setDirty();
+ for (auto& binding : bindings) {
+ binding.setDirty();
+ }
+ }
+
+ UniqueVertexArray vertexArray;
+ 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 }... }};
+ }
+};
+
+class VertexArrayStateDeleter {
+public:
+ VertexArrayStateDeleter(bool destroy_)
+ : destroy(destroy_) {}
+
+ void operator()(VertexArrayState* ptr) const {
+ if (destroy) {
+ delete ptr;
+ }
+ }
+
+private:
+ bool destroy;
+};
+
+using UniqueVertexArrayState = std::unique_ptr<VertexArrayState, VertexArrayStateDeleter>;
+
+class VertexArray {
+public:
+ VertexArray(UniqueVertexArrayState state_)
+ : state(std::move(state_)) {
+ }
+
+ void bind(Context&, BufferID indexBuffer, const AttributeBindingArray&);
+
+private:
+ UniqueVertexArrayState state;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/vertex_buffer.hpp b/src/mbgl/gl/vertex_buffer.hpp
index 4808803d00..9f8b156b25 100644
--- a/src/mbgl/gl/vertex_buffer.hpp
+++ b/src/mbgl/gl/vertex_buffer.hpp
@@ -28,6 +28,7 @@ public:
bool empty() const { return v.empty(); }
void clear() { v.clear(); }
const Vertex* data() const { return v.data(); }
+ const std::vector<Vertex>& vector() const { return v; }
private:
std::vector<Vertex> v;
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index ffb70c7ca2..6e152349ca 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -5,46 +5,48 @@ namespace mbgl {
using namespace style;
-SymbolInstance::SymbolInstance(Anchor& anchor,
- const GeometryCoordinates& line,
+SymbolInstance::SymbolInstance(Anchor& anchor_,
+ GeometryCoordinates line_,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
- const bool addToBuffers,
const uint32_t index_,
const float textBoxScale,
const float textPadding,
const SymbolPlacementType textPlacement,
+ const std::array<float, 2> textOffset_,
const float iconBoxScale,
const float iconPadding,
- const SymbolPlacementType iconPlacement,
+ const std::array<float, 2> iconOffset_,
const GlyphPositionMap& positions,
const IndexedSubfeature& indexedFeature,
- const std::size_t featureIndex_) :
- point(anchor.point),
+ const std::size_t featureIndex_,
+ const std::u16string& key_,
+ const float overscaling) :
+ anchor(anchor_),
+ line(line_),
index(index_),
hasText(shapedTextOrientations.first || shapedTextOrientations.second),
hasIcon(shapedIcon),
// Create the collision features that will be used to check whether this symbol instance can be placed
- textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
- iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
- featureIndex(featureIndex_) {
+ textCollisionFeature(line_, anchor, shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature, overscaling),
+ iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature),
+ featureIndex(featureIndex_),
+ textOffset(textOffset_),
+ iconOffset(iconOffset_),
+ key(key_) {
// Create the quads used for rendering the icon and glyphs.
- if (addToBuffers) {
- if (shapedIcon) {
- iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first);
- }
- if (shapedTextOrientations.first) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, positions);
- glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
- }
- if (shapedTextOrientations.second) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, positions);
- glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
- }
+ if (shapedIcon) {
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
+ }
+ if (shapedTextOrientations.first) {
+ horizontalGlyphQuads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
+ }
+ if (shapedTextOrientations.second) {
+ verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
}
if (shapedTextOrientations.first && shapedTextOrientations.second) {
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index f199d929df..827a5dbbdb 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -5,6 +5,7 @@
#include <mbgl/text/collision_feature.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
+
namespace mbgl {
class Anchor;
@@ -13,33 +14,45 @@ class IndexedSubfeature;
class SymbolInstance {
public:
SymbolInstance(Anchor& anchor,
- const GeometryCoordinates& line,
+ GeometryCoordinates line,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
- const bool inside,
const uint32_t index,
const float textBoxScale,
const float textPadding,
style::SymbolPlacementType textPlacement,
+ const std::array<float, 2> textOffset,
const float iconBoxScale,
const float iconPadding,
- style::SymbolPlacementType iconPlacement,
+ const std::array<float, 2> iconOffset,
const GlyphPositionMap&,
const IndexedSubfeature&,
- const std::size_t featureIndex);
+ const std::size_t featureIndex,
+ const std::u16string& key,
+ const float overscaling);
- Point<float> point;
+ Anchor anchor;
+ GeometryCoordinates line;
uint32_t index;
bool hasText;
bool hasIcon;
- SymbolQuads glyphQuads;
+ SymbolQuads horizontalGlyphQuads;
+ SymbolQuads verticalGlyphQuads;
optional<SymbolQuad> iconQuad;
CollisionFeature textCollisionFeature;
CollisionFeature iconCollisionFeature;
WritingModeType writingModes;
std::size_t featureIndex;
+ std::array<float, 2> textOffset;
+ std::array<float, 2> iconOffset;
+ std::u16string key;
+ bool isDuplicate;
+ optional<size_t> placedTextIndex;
+ optional<size_t> placedVerticalTextIndex;
+ optional<size_t> placedIconIndex;
+ uint32_t crossTileID = 0;
};
} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index adc7eaaed8..fc1ede56ff 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -8,7 +8,6 @@
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/text/get_anchors.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/text/shaping.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/utf.hpp>
@@ -43,8 +42,8 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
std::unique_ptr<GeometryTileLayer> sourceLayer_,
ImageDependencies& imageDependencies,
GlyphDependencies& glyphDependencies)
- : sourceLayer(std::move(sourceLayer_)),
- bucketName(layers.at(0)->getID()),
+ : bucketName(layers.at(0)->getID()),
+ sourceLayer(std::move(sourceLayer_)),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
mode(parameters.mode),
@@ -75,10 +74,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
}
}
- // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment`
+ // If unspecified `*-pitch-alignment` inherits `*-rotation-alignment`
if (layout.get<TextPitchAlignment>() == AlignmentType::Auto) {
layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>();
}
+ if (layout.get<IconPitchAlignment>() == AlignmentType::Auto) {
+ layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>();
+ }
const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty();
const bool hasIcon = has<IconImage>(layout);
@@ -176,48 +178,8 @@ bool SymbolLayout::hasSymbolInstances() const {
}
void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
- const ImageMap& imageMap, const ImagePositions& imagePositions) {
- float horizontalAlign = 0.5;
- float verticalAlign = 0.5;
-
- switch (layout.get<TextAnchor>()) {
- case TextAnchorType::Top:
- case TextAnchorType::Bottom:
- case TextAnchorType::Center:
- break;
- case TextAnchorType::Right:
- case TextAnchorType::TopRight:
- case TextAnchorType::BottomRight:
- horizontalAlign = 1;
- break;
- case TextAnchorType::Left:
- case TextAnchorType::TopLeft:
- case TextAnchorType::BottomLeft:
- horizontalAlign = 0;
- break;
- }
-
- switch (layout.get<TextAnchor>()) {
- case TextAnchorType::Left:
- case TextAnchorType::Right:
- case TextAnchorType::Center:
- break;
- case TextAnchorType::Bottom:
- case TextAnchorType::BottomLeft:
- case TextAnchorType::BottomRight:
- verticalAlign = 1;
- break;
- case TextAnchorType::Top:
- case TextAnchorType::TopLeft:
- case TextAnchorType::TopRight:
- verticalAlign = 0;
- break;
- }
-
- const float justify = layout.get<TextJustify>() == TextJustifyType::Right ? 1 :
- layout.get<TextJustify>() == TextJustifyType::Left ? 0 :
- 0.5;
-
+ const ImageMap& imageMap, const ImagePositions& imagePositions,
+ const OverscaledTileID& tileID, const std::string& sourceID) {
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
@@ -243,12 +205,11 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
const Shaping result = getShaping(
/* string */ text,
/* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
- layout.get<TextMaxWidth>() * oneEm : 0,
+ layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0,
/* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
- /* horizontalAlign */ horizontalAlign,
- /* verticalAlign */ verticalAlign,
- /* justify */ justify,
- /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f,
+ /* anchor */ layout.evaluate<TextAnchor>(zoom, feature),
+ /* justify */ layout.evaluate<TextJustify>(zoom, feature),
+ /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f,
/* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
/* verticalHeight */ oneEm,
/* writingMode */ writingMode,
@@ -272,6 +233,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
shapedIcon = PositionedIcon::shapeIcon(
imagePositions.at(*feature.icon),
layout.evaluate<IconOffset>(zoom, feature),
+ layout.evaluate<IconAnchor>(zoom, feature),
layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
if (image->second->sdf) {
sdfIcons = true;
@@ -286,7 +248,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);
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID);
}
feature.geometry.clear();
@@ -299,12 +261,16 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolFeature& feature,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap& glyphPositionMap) {
+ const GlyphPositionMap& glyphPositionMap,
+ const OverscaledTileID& tileID,
+ const std::string& sourceID) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
+ const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature);
+ const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature);
// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
@@ -324,12 +290,10 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
? SymbolPlacementType::Point
: layout.get<SymbolPlacement>();
- const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
+
const float textRepeatDistance = symbolSpacing / 2;
- IndexedSubfeature indexedFeature = { feature.index, sourceLayer->getName(), bucketName,
- symbolInstances.size() };
+ IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(),
+ sourceID, tileID.canonical);
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
@@ -350,14 +314,14 @@ void SymbolLayout::addFeature(const std::size_t index,
if (avoidEdges && !inside) return;
- const bool addToBuffers = mode == MapMode::Still || withinPlus0;
-
- symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
- layout.evaluate(zoom, feature), layoutTextSize,
- addToBuffers, symbolInstances.size(),
- textBoxScale, textPadding, textPlacement,
- iconBoxScale, iconPadding, iconPlacement,
- glyphPositionMap, indexedFeature, index);
+ if (mode == MapMode::Tile || withinPlus0) {
+ symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
+ layout.evaluate(zoom, feature), layoutTextSize,
+ symbolInstances.size(),
+ textBoxScale, textPadding, textPlacement, textOffset,
+ iconBoxScale, iconPadding, iconOffset,
+ glyphPositionMap, indexedFeature, index, feature.text.value_or(std::u16string()), overscaling);
+ }
};
const auto& type = feature.getType();
@@ -428,94 +392,93 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe
return false;
}
-std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) {
- auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear);
-
- // Calculate which labels can be shown and when they can be shown and
- // create the bufers used for rendering.
-
- const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
- const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
+// Analog of `addToLineVertexArray` in JS. This version doesn't need to build up a line array like the
+// JS version does, but it uses the same logic to calculate tile distances.
+std::vector<float> CalculateTileDistances(const GeometryCoordinates& line, const Anchor& anchor) {
+ std::vector<float> tileDistances(line.size());
+ if (anchor.segment != -1) {
+ auto sumForwardLength = util::dist<float>(anchor.point, line[anchor.segment + 1]);
+ auto sumBackwardLength = util::dist<float>(anchor.point, line[anchor.segment]);
+ for (size_t i = anchor.segment + 1; i < line.size(); i++) {
+ tileDistances[i] = sumForwardLength;
+ if (i < line.size() - 1) {
+ sumForwardLength += util::dist<float>(line[i + 1], line[i]);
+ }
+ }
+ for (auto i = anchor.segment; i >= 0; i--) {
+ tileDistances[i] = sumBackwardLength;
+ if (i > 0) {
+ sumBackwardLength += util::dist<float>(line[i - 1], line[i]);
+ }
+ }
+ }
+ return tileDistances;
+}
+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));
- const bool keepUpright = layout.get<TextKeepUpright>();
-
- // Sort symbols by their y position on the canvas so that they lower symbols
- // are drawn on top of higher symbols.
- // Don't sort symbols that won't overlap because it isn't necessary and
- // because it causes more labels to pop in and out when rotating.
- if (mayOverlap) {
- const float sin = std::sin(collisionTile.config.angle);
- const float cos = std::cos(collisionTile.config.angle);
-
- std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
- const int32_t aRotated = sin * a.point.x + cos * a.point.y;
- const int32_t bRotated = sin * b.point.x + cos * b.point.y;
- return aRotated != bRotated ?
- aRotated < bRotated :
- a.index > b.index;
- });
- }
-
- for (SymbolInstance &symbolInstance : symbolInstances) {
+ for (SymbolInstance &symbolInstance : bucket->symbolInstances) {
const bool hasText = symbolInstance.hasText;
const bool hasIcon = symbolInstance.hasIcon;
- const bool iconWithoutText = layout.get<TextOptional>() || !hasText;
- const bool textWithoutIcon = layout.get<IconOptional>() || !hasIcon;
-
- // Calculate the scales at which the text and icon can be placed without collision.
-
- float glyphScale = hasText ?
- collisionTile.placeFeature(symbolInstance.textCollisionFeature,
- layout.get<TextAllowOverlap>(), layout.get<SymbolAvoidEdges>()) :
- collisionTile.minScale;
- float iconScale = hasIcon ?
- collisionTile.placeFeature(symbolInstance.iconCollisionFeature,
- layout.get<IconAllowOverlap>(), layout.get<SymbolAvoidEdges>()) :
- collisionTile.minScale;
-
-
- // Combine the scales for icons and text.
-
- if (!iconWithoutText && !textWithoutIcon) {
- iconScale = glyphScale = util::max(iconScale, glyphScale);
- } else if (!textWithoutIcon && glyphScale) {
- glyphScale = util::max(iconScale, glyphScale);
- } else if (!iconWithoutText && iconScale) {
- iconScale = util::max(iconScale, glyphScale);
- }
-
const auto& feature = features.at(symbolInstance.featureIndex);
// Insert final placement into collision tree and add glyphs/icons to buffers
if (hasText) {
- const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f);
- collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>());
- if (glyphScale < collisionTile.maxScale) {
- for (const auto& symbol : symbolInstance.glyphQuads) {
- addSymbol(
- bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom,
- keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes);
+ const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
+ bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.textOffset, symbolInstance.writingModes, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor));
+ symbolInstance.placedTextIndex = bucket->text.placedSymbols.size() - 1;
+ PlacedSymbol& horizontalSymbol = bucket->text.placedSymbols.back();
+
+ bool firstHorizontal = true;
+ for (const auto& symbol : symbolInstance.horizontalGlyphQuads) {
+ size_t index = addSymbol(
+ bucket->text, sizeData, symbol,
+ symbolInstance.anchor, horizontalSymbol);
+ if (firstHorizontal) {
+ horizontalSymbol.vertexStartIndex = index;
+ firstHorizontal = false;
+ }
+ }
+
+ if (symbolInstance.writingModes & WritingModeType::Vertical) {
+ bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.textOffset, WritingModeType::Vertical, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor));
+ symbolInstance.placedVerticalTextIndex = bucket->text.placedSymbols.size() - 1;
+
+ PlacedSymbol& verticalSymbol = bucket->text.placedSymbols.back();
+ bool firstVertical = true;
+
+ for (const auto& symbol : symbolInstance.verticalGlyphQuads) {
+ size_t index = addSymbol(
+ bucket->text, sizeData, symbol,
+ symbolInstance.anchor, verticalSymbol);
+
+ if (firstVertical) {
+ verticalSymbol.vertexStartIndex = index;
+ firstVertical = false;
+ }
}
}
}
if (hasIcon) {
- const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f);
- collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>());
- if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) {
- addSymbol(
- bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom,
- keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes);
+ if (symbolInstance.iconQuad) {
+ const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
+ bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.iconOffset, WritingModeType::None, symbolInstance.line, std::vector<float>());
+ symbolInstance.placedIconIndex = bucket->icon.placedSymbols.size() - 1;
+ PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back();
+ iconSymbol.vertexStartIndex = addSymbol(
+ bucket->icon, sizeData, *symbolInstance.iconQuad,
+ symbolInstance.anchor, iconSymbol);
}
}
@@ -525,23 +488,19 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
}
}
- if (collisionTile.config.debug) {
- addToDebugBuffers(collisionTile, *bucket);
+ if (showCollisionBoxes) {
+ addToDebugBuffers(*bucket);
}
return bucket;
}
template <typename Buffer>
-void SymbolLayout::addSymbol(Buffer& buffer,
- SymbolSizeBinder& sizeBinder,
+size_t SymbolLayout::addSymbol(Buffer& buffer,
+ const Range<float> sizeData,
const SymbolQuad& symbol,
- const SymbolFeature& feature,
- const float placementZoom,
- const bool keepUpright,
- const style::SymbolPlacementType placement,
- const float placementAngle,
- const WritingModeType writingModes) {
+ const Anchor& labelAnchor,
+ PlacedSymbol& placedSymbol) {
constexpr const uint16_t vertexLength = 4;
const auto &tl = symbol.tl;
@@ -550,32 +509,6 @@ void SymbolLayout::addSymbol(Buffer& buffer,
const auto &br = symbol.br;
const auto &tex = symbol.tex;
- float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom);
- float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F);
- const auto &anchorPoint = symbol.anchorPoint;
-
- // drop incorrectly oriented glyphs
- const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
- if (writingModes & WritingModeType::Vertical) {
- if (placement == style::SymbolPlacementType::Line && symbol.writingMode == WritingModeType::Vertical) {
- if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 5 / 4) || a > (M_PI * 7 / 4)))
- return;
- } else if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 3 / 4) || a > (M_PI * 5 / 4)))
- return;
- } else if (keepUpright && placement == style::SymbolPlacementType::Line &&
- (a <= M_PI / 2 || a > M_PI * 3 / 2)) {
- return;
- }
-
- if (maxZoom <= minZoom)
- return;
-
- // Lower min zoom so that while fading out the label
- // it can be shown outside of collision-free zoom levels
- if (minZoom == placementZoom) {
- minZoom = 0;
- }
-
if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
buffer.segments.emplace_back(buffer.vertices.vertexSize(), buffer.triangles.indexSize());
}
@@ -586,20 +519,25 @@ void SymbolLayout::addSymbol(Buffer& buffer,
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
uint16_t index = segment.vertexLength;
- // Encode angle of glyph
- uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);
-
// coordinates (2 triangles)
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, tex.x, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tl, symbol.glyphOffset.y, tex.x, tex.y, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tr, symbol.glyphOffset.y, tex.x + tex.w, tex.y, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData));
+
+ // Dynamic/Opacity vertices are initialized so that the vertex count always agrees with
+ // the layout vertex buffer, but they will always be updated before rendering happens
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
- sizeBinder.populateVertexVector(feature);
+ auto opacityVertex = SymbolOpacityAttributes::vertex(1.0, 1.0);
+ buffer.opacityVertices.emplace_back(opacityVertex);
+ buffer.opacityVertices.emplace_back(opacityVertex);
+ buffer.opacityVertices.emplace_back(opacityVertex);
+ buffer.opacityVertices.emplace_back(opacityVertex);
// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.emplace_back(index + 0, index + 1, index + 2);
@@ -607,54 +545,64 @@ void SymbolLayout::addSymbol(Buffer& buffer,
segment.vertexLength += vertexLength;
segment.indexLength += 6;
+
+ placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x);
+
+ return index;
}
-void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) {
+void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
if (!hasSymbolInstances()) {
return;
}
- const float yStretch = collisionTile.yStretch;
-
- auto& collisionBox = bucket.collisionBox;
-
for (const SymbolInstance &symbolInstance : symbolInstances) {
auto populateCollisionBox = [&](const auto& feature) {
+ SymbolBucket::CollisionBuffer& collisionBuffer = feature.alongLine ?
+ static_cast<SymbolBucket::CollisionBuffer&>(bucket.collisionCircle) :
+ static_cast<SymbolBucket::CollisionBuffer&>(bucket.collisionBox);
for (const CollisionBox &box : feature.boxes) {
auto& anchor = box.anchor;
- Point<float> tl{box.x1, box.y1 * yStretch};
- Point<float> tr{box.x2, box.y1 * yStretch};
- Point<float> bl{box.x1, box.y2 * yStretch};
- Point<float> br{box.x2, box.y2 * yStretch};
- tl = util::matrixMultiply(collisionTile.reverseRotationMatrix, tl);
- tr = util::matrixMultiply(collisionTile.reverseRotationMatrix, tr);
- bl = util::matrixMultiply(collisionTile.reverseRotationMatrix, bl);
- br = util::matrixMultiply(collisionTile.reverseRotationMatrix, br);
-
- const float maxZoom = util::clamp(zoom + util::log2(box.maxScale), util::MIN_ZOOM_F, util::MAX_ZOOM_F);
- const float placementZoom = util::clamp(zoom + util::log2(box.placementScale), util::MIN_ZOOM_F, util::MAX_ZOOM_F);
+ Point<float> tl{box.x1, box.y1};
+ Point<float> tr{box.x2, box.y1};
+ Point<float> bl{box.x1, box.y2};
+ Point<float> br{box.x2, box.y2};
static constexpr std::size_t vertexLength = 4;
- static constexpr std::size_t indexLength = 8;
+ const std::size_t indexLength = feature.alongLine ? 6 : 8;
- if (collisionBox.segments.empty() || collisionBox.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
- collisionBox.segments.emplace_back(collisionBox.vertices.vertexSize(), collisionBox.lines.indexSize());
+ if (collisionBuffer.segments.empty() || collisionBuffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
+ collisionBuffer.segments.emplace_back(collisionBuffer.vertices.vertexSize(),
+ feature.alongLine? bucket.collisionCircle.triangles.indexSize() : bucket.collisionBox.lines.indexSize());
}
- auto& segment = collisionBox.segments.back();
+ auto& segment = collisionBuffer.segments.back();
uint16_t index = segment.vertexLength;
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, tl, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, tr, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, br, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, bl, maxZoom, placementZoom));
-
- collisionBox.lines.emplace_back(index + 0, index + 1);
- collisionBox.lines.emplace_back(index + 1, index + 2);
- collisionBox.lines.emplace_back(index + 2, index + 3);
- collisionBox.lines.emplace_back(index + 3, index + 0);
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl));
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr));
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br));
+ collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl));
+
+ // Dynamic vertices are initialized so that the vertex count always agrees with
+ // the layout vertex buffer, but they will always be updated before rendering happens
+ auto dynamicVertex = CollisionBoxDynamicAttributes::vertex(false, false);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+ collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
+
+ if (feature.alongLine) {
+ bucket.collisionCircle.triangles.emplace_back(index, index + 1, index + 2);
+ bucket.collisionCircle.triangles.emplace_back(index, index + 2, index + 3);
+ } else {
+ bucket.collisionBox.lines.emplace_back(index + 0, index + 1);
+ bucket.collisionBox.lines.emplace_back(index + 1, index + 2);
+ bucket.collisionBox.lines.emplace_back(index + 2, index + 3);
+ bucket.collisionBox.lines.emplace_back(index + 3, index + 0);
+ }
segment.vertexLength += vertexLength;
segment.indexLength += indexLength;
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 8cdaadff00..6951c29ada 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -16,10 +16,10 @@
namespace mbgl {
class BucketParameters;
-class CollisionTile;
class SymbolBucket;
class Anchor;
class RenderLayer;
+class PlacedSymbol;
namespace style {
class Filter;
@@ -34,50 +34,44 @@ public:
GlyphDependencies&);
void prepare(const GlyphMap&, const GlyphPositions&,
- const ImageMap&, const ImagePositions&);
+ const ImageMap&, const ImagePositions&,
+ const OverscaledTileID&, const std::string&);
- std::unique_ptr<SymbolBucket> place(CollisionTile&);
+ std::unique_ptr<SymbolBucket> place(const bool showCollisionBoxes);
bool hasSymbolInstances() const;
- enum State {
- Pending, // Waiting for the necessary glyphs or icons to be available.
- Placed // The final positions have been determined, taking into account prior layers.
- };
-
- State state = Pending;
-
std::map<std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties;
+ const std::string bucketName;
+ std::vector<SymbolInstance> symbolInstances;
+
private:
void addFeature(const size_t,
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap&);
+ const GlyphPositionMap&,
+ const OverscaledTileID&,
+ const std::string&);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
- void addToDebugBuffers(CollisionTile&, SymbolBucket&);
+ void addToDebugBuffers(SymbolBucket&);
// Adds placed items to the buffer.
template <typename Buffer>
- void addSymbol(Buffer&,
- SymbolSizeBinder& sizeBinder,
+ size_t addSymbol(Buffer&,
+ const Range<float> sizeData,
const SymbolQuad&,
- const SymbolFeature& feature,
- float scale,
- const bool keepUpright,
- const style::SymbolPlacementType,
- const float placementAngle,
- WritingModeType writingModes);
+ const Anchor& labelAnchor,
+ PlacedSymbol& placedSymbol);
// Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature,
// which may reference data from this object.
const std::unique_ptr<GeometryTileLayer> sourceLayer;
- const std::string bucketName;
const float overscaling;
const float zoom;
const MapMode mode;
@@ -94,7 +88,6 @@ private:
style::TextSize::UnevaluatedType textSize;
style::IconSize::UnevaluatedType iconSize;
- std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;
BiDi bidi; // Consider moving this up to geometry tile worker to reduce reinstantiation costs; use of BiDi/ubiditransform object must be constrained to one thread
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
new file mode 100644
index 0000000000..ee6385c93c
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -0,0 +1,420 @@
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+
+ /*
+ * # Overview of coordinate spaces
+ *
+ * ## Tile coordinate spaces
+ * Each label has an anchor. Some labels have corresponding line geometries.
+ * The points for both anchors and lines are stored in tile units. Each tile has it's own
+ * coordinate space going from (0, 0) at the top left to (EXTENT, EXTENT) at the bottom right.
+ *
+ * ## GL coordinate space
+ * At the end of everything, the vertex shader needs to produce a position in GL coordinate space,
+ * which is (-1, 1) at the top left and (1, -1) in the bottom right.
+ *
+ * ## Map pixel coordinate spaces
+ * Each tile has a pixel coordinate space. It's just the tile units scaled so that one unit is
+ * whatever counts as 1 pixel at the current zoom.
+ * This space is used for pitch-alignment=map, rotation-alignment=map
+ *
+ * ## Rotated map pixel coordinate spaces
+ * Like the above, but rotated so axis of the space are aligned with the viewport instead of the tile.
+ * This space is used for pitch-alignment=map, rotation-alignment=viewport
+ *
+ * ## Viewport pixel coordinate space
+ * (0, 0) is at the top left of the canvas and (pixelWidth, pixelHeight) is at the bottom right corner
+ * of the canvas. This space is used for pitch-alignment=viewport
+ *
+ *
+ * # Vertex projection
+ * It goes roughly like this:
+ * 1. project the anchor and line from tile units into the correct label coordinate space
+ * - map pixel space pitch-alignment=map rotation-alignment=map
+ * - rotated map pixel space pitch-alignment=map rotation-alignment=viewport
+ * - viewport pixel space pitch-alignment=viewport rotation-alignment=*
+ * 2. if the label follows a line, find the point along the line that is the correct distance from the anchor.
+ * 3. add the glyph's corner offset to the point from step 3
+ * 4. convert from the label coordinate space to gl coordinates
+ *
+ * For horizontal labels we want to do step 1 in the shader for performance reasons (no cpu work).
+ * This is what `u_label_plane_matrix` is used for.
+ * For labels aligned with lines we have to steps 1 and 2 on the cpu since we need access to the line geometry.
+ * This is what `updateLineLabels(...)` does.
+ * Since the conversion is handled on the cpu we just set `u_label_plane_matrix` to an identity matrix.
+ *
+ * Steps 3 and 4 are done in the shaders for all labels.
+ */
+
+ /*
+ * Returns a matrix for converting from tile units to the correct label coordinate space.
+ */
+ mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) {
+ mat4 m;
+ matrix::identity(m);
+ if (pitchWithMap) {
+ matrix::scale(m, m, 1 / pixelsToTileUnits, 1 / pixelsToTileUnits, 1);
+ if (!rotateWithMap) {
+ matrix::rotate_z(m, m, state.getAngle());
+ }
+ } else {
+ matrix::scale(m, m, state.getSize().width / 2.0, -(state.getSize().height / 2.0), 1.0);
+ matrix::translate(m, m, 1, -1, 0);
+ matrix::multiply(m, m, posMatrix);
+ }
+ return m;
+ }
+
+ /*
+ * Returns a matrix for converting from the correct label coordinate space to gl coords.
+ */
+ mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) {
+ mat4 m;
+ matrix::identity(m);
+ if (pitchWithMap) {
+ matrix::multiply(m, m, posMatrix);
+ matrix::scale(m, m, pixelsToTileUnits, pixelsToTileUnits, 1);
+ if (!rotateWithMap) {
+ matrix::rotate_z(m, m, -state.getAngle());
+ }
+ } else {
+ matrix::scale(m, m, 1, -1, 1);
+ matrix::translate(m, m, -1, -1, 0);
+ matrix::scale(m, m, 2.0 / state.getSize().width, 2.0 / state.getSize().height, 1.0);
+ }
+ return m;
+ }
+
+ PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) {
+ vec4 pos = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(pos, pos, matrix);
+ return {{ static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }, pos[3] };
+ }
+
+ float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) {
+ if (zoomEvaluatedSize.isFeatureConstant) {
+ return zoomEvaluatedSize.size;
+ } else {
+ if (zoomEvaluatedSize.isZoomConstant) {
+ return placedSymbol.lowerSize;
+ } else {
+ return placedSymbol.lowerSize + zoomEvaluatedSize.sizeT * (placedSymbol.upperSize - placedSymbol.lowerSize);
+ }
+ }
+ }
+
+ bool isVisible(const vec4& anchorPos, const std::array<double, 2>& clippingBuffer) {
+ const float x = anchorPos[0] / anchorPos[3];
+ const float y = anchorPos[1] / anchorPos[3];
+ const bool inPaddedViewport = (
+ x >= -clippingBuffer[0] &&
+ x <= clippingBuffer[0] &&
+ y >= -clippingBuffer[1] &&
+ y <= clippingBuffer[1]);
+ return inPaddedViewport;
+ }
+
+ void addDynamicAttributes(const Point<float>& anchorPoint, const float angle,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ }
+
+ void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ const Point<float> offscreenPoint = { -INFINITY, -INFINITY };
+ for (size_t i = 0; i < numGlyphs; i++) {
+ addDynamicAttributes(offscreenPoint, 0, dynamicVertexArray);
+ }
+ }
+
+ enum PlacementResult {
+ OK,
+ NotEnoughRoom,
+ NeedsFlipping,
+ UseVertical
+ };
+
+ Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) {
+ // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane
+ // If it did, that would mean our label extended all the way out from within the viewport to a (very distant)
+ // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the
+ // plane of the camera.
+ const Point<float> projectedUnitVertex = project(previousTilePoint + util::unit<float>(previousTilePoint - currentTilePoint), projectionMatrix).first;
+ const Point<float> projectedUnitSegment = previousProjectedPoint - projectedUnitVertex;
+
+ return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag<float>(projectedUnitSegment)));
+ }
+
+ optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip,
+ const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const std::vector<float>& tileDistances, const mat4& labelPlaneMatrix, const bool returnTileDistance) {
+
+ const float combinedOffsetX = flip ?
+ offsetX - lineOffsetX :
+ offsetX + lineOffsetX;
+
+ int16_t dir = combinedOffsetX > 0 ? 1 : -1;
+
+ float angle = 0.0;
+ if (flip) {
+ // The label needs to be flipped to keep text upright.
+ // Iterate in the reverse direction.
+ dir *= -1;
+ angle = M_PI;
+ }
+
+ if (dir < 0) angle += M_PI;
+
+ int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1;
+
+ const int32_t initialIndex = currentIndex;
+ Point<float> current = projectedAnchorPoint;
+ Point<float> prev = projectedAnchorPoint;
+ float distanceToPrev = 0.0;
+ float currentSegmentDistance = 0.0;
+ const float absOffsetX = std::abs(combinedOffsetX);
+
+ while (distanceToPrev + currentSegmentDistance <= absOffsetX) {
+ currentIndex += dir;
+
+ // offset does not fit on the projected line
+ if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) {
+ return {};
+ }
+
+ prev = current;
+ PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix);
+ if (projection.second > 0) {
+ current = projection.first;
+ } else {
+ // The vertex is behind the plane of the camera, so we can't project it
+ // Instead, we'll create a vertex along the line that's far enough to include the glyph
+ const Point<float> previousTilePoint = distanceToPrev == 0 ?
+ tileAnchorPoint :
+ convertPoint<float>(line.at(currentIndex - dir));
+ const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex));
+ current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix);
+ }
+
+ distanceToPrev += currentSegmentDistance;
+ currentSegmentDistance = util::dist<float>(prev, current);
+ }
+
+ // The point is on the current segment. Interpolate to find it.
+ const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance;
+ const Point<float> prevToCurrent = current - prev;
+ Point<float> p = (prevToCurrent * segmentInterpolationT) + prev;
+
+ // offset the point from the line to text-offset and icon-offset
+ p += util::perp(prevToCurrent) * static_cast<float>(lineOffsetY * dir / util::mag(prevToCurrent));
+
+ const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x);
+
+ return {{
+ p,
+ segmentAngle,
+ returnTileDistance ?
+ TileDistance(
+ (currentIndex - dir) == initialIndex ? 0 : tileDistances[currentIndex - dir],
+ absOffsetX - distanceToPrev
+ ) :
+ optional<TileDistance>()
+ }};
+ }
+
+ optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale,
+ const float lineOffsetX,
+ const float lineOffsetY,
+ const bool flip,
+ const Point<float>& anchorPoint,
+ const Point<float>& tileAnchorPoint,
+ const PlacedSymbol& symbol,
+ const mat4& labelPlaneMatrix,
+ const bool returnTileDistance) {
+
+ const float firstGlyphOffset = symbol.glyphOffsets.front();
+ const float lastGlyphOffset = symbol.glyphOffsets.back();;
+
+ optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance);
+ if (!firstPlacedGlyph)
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+
+ optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance);
+ if (!lastPlacedGlyph)
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+
+ return std::make_pair(*firstPlacedGlyph, *lastPlacedGlyph);
+ }
+
+ optional<PlacementResult> requiresOrientationChange(const WritingModeType writingModes, const Point<float>& firstPoint, const Point<float>& lastPoint, const float aspectRatio) {
+ if (writingModes == (WritingModeType::Horizontal | WritingModeType::Vertical)) {
+ // On top of choosing whether to flip, choose whether to render this version of the glyphs or the alternate
+ // vertical glyphs. We can't just filter out vertical glyphs in the horizontal range because the horizontal
+ // and vertical versions can have slightly different projections which could lead to angles where both or
+ // neither showed.
+ auto rise = std::abs(lastPoint.y - firstPoint.y);
+ auto run = std::abs(lastPoint.x - firstPoint.x) * aspectRatio;
+ if (rise > run) {
+ return PlacementResult::UseVertical;
+ }
+ }
+
+ if ((writingModes == WritingModeType::Vertical) ?
+ (firstPoint.y < lastPoint.y) :
+ (firstPoint.x > lastPoint.x)) {
+ // Includes "horizontalOnly" case for labels without vertical glyphs
+ return PlacementResult::NeedsFlipping;
+ }
+ return {};
+ }
+
+ PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol,
+ const float fontSize,
+ const bool flip,
+ const bool keepUpright,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const mat4& glCoordMatrix,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray,
+ const Point<float>& projectedAnchorPoint,
+ const float aspectRatio) {
+ const float fontScale = fontSize / 24.0;
+ const float lineOffsetX = symbol.lineOffset[0] * fontSize;
+ const float lineOffsetY = symbol.lineOffset[1] * fontSize;
+
+ std::vector<PlacedGlyph> placedGlyphs;
+ if (symbol.glyphOffsets.size() > 1) {
+
+ const optional<std::pair<PlacedGlyph, PlacedGlyph>> firstAndLastGlyph =
+ placeFirstAndLastGlyph(fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol, labelPlaneMatrix, false);
+ if (!firstAndLastGlyph) {
+ return PlacementResult::NotEnoughRoom;
+ }
+
+ const Point<float> firstPoint = project(firstAndLastGlyph->first.point, glCoordMatrix).first;
+ const Point<float> lastPoint = project(firstAndLastGlyph->second.point, glCoordMatrix).first;
+
+ if (keepUpright && !flip) {
+ auto orientationChange = requiresOrientationChange(symbol.writingModes, firstPoint, lastPoint, aspectRatio);
+ if (orientationChange) {
+ return *orientationChange;
+ }
+ }
+
+ placedGlyphs.push_back(firstAndLastGlyph->first);
+ for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) {
+ const float glyphOffsetX = symbol.glyphOffsets[glyphIndex];
+ // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed
+ auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false);
+ placedGlyphs.push_back(*placedGlyph);
+ }
+ placedGlyphs.push_back(firstAndLastGlyph->second);
+ } else {
+ // Only a single glyph to place
+ // So, determine whether to flip based on projected angle of the line segment it's on
+ if (keepUpright && !flip) {
+ const Point<float> a = project(symbol.anchorPoint, posMatrix).first;
+ const Point<float> tileSegmentEnd = convertPoint<float>(symbol.line.at(symbol.segment + 1));
+ const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix);
+ // We know the anchor will be in the viewport, but the end of the line segment may be
+ // behind the plane of the camera, in which case we can use a point at any arbitrary (closer)
+ // point on the segment.
+ const Point<float> b = (projectedVertex.second > 0) ?
+ projectedVertex.first :
+ projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix);
+
+ auto orientationChange = requiresOrientationChange(symbol.writingModes, a, b, aspectRatio);
+ if (orientationChange) {
+ return *orientationChange;
+ }
+ }
+ assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all
+ const float glyphOffsetX = symbol.glyphOffsets.front();
+ optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment,
+ symbol.line, symbol.tileDistances, labelPlaneMatrix, false);
+ if (!singleGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ placedGlyphs.push_back(*singleGlyph);
+ }
+
+ for (auto& placedGlyph : placedGlyphs) {
+ addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray);
+ }
+
+ return PlacementResult::OK;
+ }
+
+
+ void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols,
+ const mat4& posMatrix, const style::SymbolPropertyValues& values,
+ const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state) {
+
+ const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom());
+
+ const std::array<double, 2> clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }};
+
+ const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map;
+ const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map;
+ const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom());
+
+ const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap,
+ rotateWithMap, state, pixelsToTileUnits);
+
+ const mat4 glCoordMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
+
+ dynamicVertexArray.clear();
+
+ bool useVertical = false;
+
+ for (auto& placedSymbol : placedSymbols) {
+ // Don't do calculations for vertical glyphs unless the previous symbol was horizontal
+ // and we determined that vertical glyphs were necessary.
+ // Also don't do calculations for symbols that are collided and fully faded out
+ if (placedSymbol.hidden || (placedSymbol.writingModes == WritingModeType::Vertical && !useVertical)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ continue;
+ }
+ // Awkward... but we're counting on the paired "vertical" symbol coming immediately after its horizontal counterpart
+ useVertical = false;
+
+ vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }};
+ matrix::transformMat4(anchorPos, anchorPos, posMatrix);
+
+ // Don't bother calculating the correct point for invisible labels.
+ if (!isVisible(anchorPos, clippingBuffer)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ continue;
+ }
+
+ const float cameraToAnchorDistance = anchorPos[3];
+ const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance());
+
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol);
+ const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ?
+ fontSize * perspectiveRatio :
+ fontSize / perspectiveRatio;
+
+ const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first;
+
+ PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio());
+
+ useVertical = placeUnflipped == PlacementResult::UseVertical;
+
+ if (placeUnflipped == PlacementResult::NotEnoughRoom || useVertical ||
+ (placeUnflipped == PlacementResult::NeedsFlipping &&
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ }
+ }
+ }
+} // end namespace mbgl
diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp
new file mode 100644
index 0000000000..3e57d162fd
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+
+namespace mbgl {
+
+ class TransformState;
+ class RenderTile;
+ class SymbolSizeBinder;
+ class PlacedSymbol;
+ namespace style {
+ class SymbolPropertyValues;
+ } // end namespace style
+
+ struct TileDistance {
+ TileDistance(float prevTileDistance_, float lastSegmentViewportDistance_)
+ : prevTileDistance(prevTileDistance_), lastSegmentViewportDistance(lastSegmentViewportDistance_)
+ {}
+ float prevTileDistance;
+ float lastSegmentViewportDistance;
+ };
+
+ struct PlacedGlyph {
+ PlacedGlyph() = default;
+
+ PlacedGlyph(Point<float> point_, float angle_, optional<TileDistance> tileDistance_)
+ : point(point_), angle(angle_), tileDistance(std::move(tileDistance_))
+ {}
+ PlacedGlyph(PlacedGlyph&& other) noexcept
+ : point(std::move(other.point)), angle(other.angle), tileDistance(std::move(other.tileDistance))
+ {}
+ PlacedGlyph(const PlacedGlyph& other)
+ : point(std::move(other.point)), angle(other.angle), tileDistance(std::move(other.tileDistance))
+ {}
+ Point<float> point;
+ float angle;
+ optional<TileDistance> tileDistance;
+ };
+
+ float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol);
+ mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits);
+ mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits);
+
+ using PointAndCameraDistance = std::pair<Point<float>,float>;
+ PointAndCameraDistance project(const Point<float>& point, const mat4& matrix);
+
+ void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>&, const std::vector<PlacedSymbol>&,
+ const mat4& posMatrix, const style::SymbolPropertyValues&,
+ const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&);
+
+ optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale,
+ const float lineOffsetX,
+ const float lineOffsetY,
+ const bool flip,
+ const Point<float>& anchorPoint,
+ const Point<float>& tileAnchorPoint,
+ const PlacedSymbol& symbol,
+ const mat4& labelPlaneMatrix,
+ const bool returnTileDistance);
+
+} // end namespace mbgl
diff --git a/src/mbgl/map/backend_scope.cpp b/src/mbgl/map/backend_scope.cpp
deleted file mode 100644
index 824ad4498b..0000000000
--- a/src/mbgl/map/backend_scope.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/util/thread_local.hpp>
-
-#include <cassert>
-
-namespace mbgl {
-
-static util::ThreadLocal<BackendScope> currentScope;
-
-BackendScope::BackendScope(Backend& backend_, ScopeType scopeType_)
- : priorScope(currentScope.get()),
- nextScope(nullptr),
- backend(backend_),
- scopeType(scopeType_) {
- if (priorScope) {
- assert(priorScope->nextScope == nullptr);
- priorScope->nextScope = this;
- }
- if (scopeType == ScopeType::Explicit) {
- backend.activate();
- }
-
- currentScope.set(this);
-}
-
-BackendScope::~BackendScope() {
- assert(nextScope == nullptr);
- if (priorScope) {
- priorScope->backend.activate();
- currentScope.set(priorScope);
- assert(priorScope->nextScope == this);
- priorScope->nextScope = nullptr;
- } else {
- if (scopeType == ScopeType::Explicit) {
- backend.deactivate();
- }
-
- currentScope.set(nullptr);
- }
-}
-
-bool BackendScope::exists() {
- return currentScope.get();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 37442770fa..947973415a 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,22 +1,19 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
-#include <mbgl/map/view.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/observer.hpp>
#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_style.hpp>
-#include <mbgl/renderer/render_style_observer.hpp>
-#include <mbgl/util/exception.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/exception.hpp>
-#include <mbgl/util/async_task.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/actor/scheduler.hpp>
@@ -28,151 +25,132 @@ namespace mbgl {
using namespace style;
-enum class RenderState : uint8_t {
- Never,
- Partial,
- Fully,
-};
-
struct StillImageRequest {
- StillImageRequest(View& view_, Map::StillImageCallback&& callback_)
- : view(view_), callback(std::move(callback_)) {
+ StillImageRequest(Map::StillImageCallback&& callback_)
+ : callback(std::move(callback_)) {
}
- View& view;
Map::StillImageCallback callback;
};
class Map::Impl : public style::Observer,
- public RenderStyleObserver {
+ public RendererObserver {
public:
Impl(Map&,
- Backend&,
+ RendererFrontend&,
+ MapObserver&,
float pixelRatio,
FileSource&,
Scheduler&,
MapMode,
- GLContextMode,
ConstrainMode,
- ViewportMode,
- optional<std::string> programCacheDir);
+ ViewportMode);
+
+ ~Impl();
+ // StyleObserver
void onSourceChanged(style::Source&) override;
- void onUpdate(Update) override;
- void onInvalidate() override;
+ void onUpdate() override;
void onStyleLoading() override;
void onStyleLoaded() override;
void onStyleError(std::exception_ptr) override;
- void onResourceError(std::exception_ptr) override;
- void render(View&);
- void renderStill();
+ // RendererObserver
+ void onInvalidate() override;
+ void onResourceError(std::exception_ptr) override;
+ void onWillStartRenderingFrame() override;
+ void onDidFinishRenderingFrame(RenderMode, bool) override;
+ void onWillStartRenderingMap() override;
+ void onDidFinishRenderingMap() override;
Map& map;
MapObserver& observer;
- Backend& backend;
+ RendererFrontend& rendererFrontend;
FileSource& fileSource;
Scheduler& scheduler;
- RenderState renderState = RenderState::Never;
Transform transform;
const MapMode mode;
- const GLContextMode contextMode;
const float pixelRatio;
- const optional<std::string> programCacheDir;
MapDebugOptions debugOptions { MapDebugOptions::NoDebug };
- Update updateFlags = Update::Nothing;
-
- AnnotationManager annotationManager;
- std::unique_ptr<Painter> painter;
std::unique_ptr<Style> style;
- std::unique_ptr<RenderStyle> renderStyle;
+ AnnotationManager annotationManager;
bool cameraMutated = false;
- size_t sourceCacheSize;
- bool loading = false;
+ uint8_t prefetchZoomDelta = util::DEFAULT_PREFETCH_ZOOM_DELTA;
- util::AsyncTask asyncInvalidate;
+ bool loading = false;
+ bool rendererFullyLoaded;
std::unique_ptr<StillImageRequest> stillImageRequest;
};
-Map::Map(Backend& backend,
+Map::Map(RendererFrontend& rendererFrontend,
+ MapObserver& mapObserver,
const Size size,
const float pixelRatio,
FileSource& fileSource,
Scheduler& scheduler,
MapMode mapMode,
- GLContextMode contextMode,
ConstrainMode constrainMode,
- ViewportMode viewportMode,
- const optional<std::string>& programCacheDir)
+ ViewportMode viewportMode)
: impl(std::make_unique<Impl>(*this,
- backend,
+ rendererFrontend,
+ mapObserver,
pixelRatio,
fileSource,
scheduler,
mapMode,
- contextMode,
constrainMode,
- viewportMode,
- programCacheDir)) {
+ viewportMode)) {
impl->transform.resize(size);
}
Map::Impl::Impl(Map& map_,
- Backend& backend_,
+ RendererFrontend& frontend,
+ MapObserver& mapObserver,
float pixelRatio_,
FileSource& fileSource_,
Scheduler& scheduler_,
MapMode mode_,
- GLContextMode contextMode_,
ConstrainMode constrainMode_,
- ViewportMode viewportMode_,
- optional<std::string> programCacheDir_)
+ ViewportMode viewportMode_)
: map(map_),
- observer(backend_),
- backend(backend_),
+ observer(mapObserver),
+ rendererFrontend(frontend),
fileSource(fileSource_),
scheduler(scheduler_),
transform(observer,
constrainMode_,
viewportMode_),
mode(mode_),
- contextMode(contextMode_),
pixelRatio(pixelRatio_),
- programCacheDir(std::move(programCacheDir_)),
- asyncInvalidate([this] {
- if (mode == MapMode::Continuous) {
- backend.invalidate();
- } else {
- renderStill();
- }
- }) {
- style = std::make_unique<Style>(scheduler, fileSource, pixelRatio);
+ style(std::make_unique<Style>(scheduler, fileSource, pixelRatio)),
+ annotationManager(*style) {
+
style->impl->setObserver(this);
+ rendererFrontend.setObserver(*this);
}
-Map::~Map() {
- BackendScope guard(impl->backend);
+Map::Impl::~Impl() {
+ // Explicitly reset the RendererFrontend first to ensure it releases
+ // All shared resources (AnnotationManager)
+ rendererFrontend.reset();
+};
- // Explicit resets currently necessary because these abandon resources that need to be
- // cleaned up by context.reset();
- impl->renderStyle.reset();
- impl->painter.reset();
-}
+Map::~Map() = default;
-void Map::renderStill(View& view, StillImageCallback callback) {
+void Map::renderStill(StillImageCallback callback) {
if (!callback) {
Log::Error(Event::General, "StillImageCallback not set");
return;
}
- if (impl->mode != MapMode::Still) {
- callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")));
+ if (impl->mode != MapMode::Static && impl->mode != MapMode::Tile) {
+ callback(std::make_exception_ptr(util::MisuseException("Map is not in static or tile image render modes")));
return;
}
@@ -186,131 +164,61 @@ void Map::renderStill(View& view, StillImageCallback callback) {
return;
}
- impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback));
- impl->onUpdate(Update::Repaint);
-}
-
-void Map::Impl::renderStill() {
- if (!stillImageRequest) {
- return;
- }
+ impl->stillImageRequest = std::make_unique<StillImageRequest>(std::move(callback));
- // TODO: determine whether we need activate/deactivate
- BackendScope guard(backend);
- render(stillImageRequest->view);
+ impl->onUpdate();
}
-void Map::triggerRepaint() {
- impl->backend.invalidate();
+void Map::renderStill(const CameraOptions& camera, MapDebugOptions debugOptions, StillImageCallback callback) {
+ impl->cameraMutated = true;
+ impl->debugOptions = debugOptions;
+ impl->transform.jumpTo(camera);
+ renderStill(std::move(callback));
}
-void Map::render(View& view) {
- impl->render(view);
+void Map::triggerRepaint() {
+ impl->onUpdate();
}
-void Map::Impl::render(View& view) {
- TimePoint timePoint = Clock::now();
-
- transform.updateTransitions(timePoint);
+#pragma mark - Map::Impl RendererObserver
- if (style->impl->loaded && updateFlags & Update::AnnotationStyle) {
- annotationManager.updateStyle(*style->impl);
- }
-
- if (updateFlags & Update::AnnotationData) {
- annotationManager.updateData();
+void Map::Impl::onWillStartRenderingMap() {
+ if (mode == MapMode::Continuous) {
+ observer.onWillStartRenderingMap();
}
+}
- updateFlags = Update::Nothing;
-
- gl::Context& context = backend.getContext();
- if (!painter) {
- renderStyle = std::make_unique<RenderStyle>(scheduler, fileSource);
- renderStyle->setObserver(this);
- painter = std::make_unique<Painter>(context, transform.getState(), pixelRatio, programCacheDir);
+void Map::Impl::onWillStartRenderingFrame() {
+ if (mode == MapMode::Continuous) {
+ observer.onWillStartRenderingFrame();
}
+}
- renderStyle->update({
- mode,
- pixelRatio,
- debugOptions,
- timePoint,
- transform.getState(),
- style->impl->getGlyphURL(),
- style->impl->spriteLoaded,
- style->getTransitionOptions(),
- style->getLight()->impl,
- style->impl->getImageImpls(),
- style->impl->getSourceImpls(),
- style->impl->getLayerImpls(),
- scheduler,
- fileSource,
- annotationManager
- });
-
- bool loaded = style->impl->isLoaded() && renderStyle->isLoaded();
+void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepaint) {
+ rendererFullyLoaded = renderMode == RenderMode::Full;
if (mode == MapMode::Continuous) {
- if (renderState == RenderState::Never) {
- observer.onWillStartRenderingMap();
- }
-
- observer.onWillStartRenderingFrame();
-
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*renderStyle,
- frameData,
- view);
-
- painter->cleanup();
-
- observer.onDidFinishRenderingFrame(loaded
- ? MapObserver::RenderMode::Full
- : MapObserver::RenderMode::Partial);
-
- if (!loaded) {
- renderState = RenderState::Partial;
- } else if (renderState != RenderState::Fully) {
- renderState = RenderState::Fully;
- observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full);
- if (loading) {
- loading = false;
- observer.onDidFinishLoadingMap();
- }
- }
+ observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode));
- // Schedule an update if we need to paint another frame due to transitions or
- // animations that are still in progress
- if (renderStyle->hasTransitions() || painter->needsAnimation() || transform.inTransition()) {
- onUpdate(Update::Repaint);
+ if (needsRepaint || transform.inTransition()) {
+ onUpdate();
}
- } else if (stillImageRequest && loaded) {
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*renderStyle,
- frameData,
- view);
-
+ } else if (stillImageRequest && rendererFullyLoaded) {
auto request = std::move(stillImageRequest);
request->callback(nullptr);
-
- painter->cleanup();
}
}
+void Map::Impl::onDidFinishRenderingMap() {
+ if (mode == MapMode::Continuous && loading) {
+ observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full);
+ if (loading) {
+ loading = false;
+ observer.onDidFinishLoadingMap();
+ }
+ }
+};
+
#pragma mark - Style
style::Style& Map::getStyle() {
@@ -322,20 +230,22 @@ const style::Style& Map::getStyle() const {
}
void Map::setStyle(std::unique_ptr<Style> style) {
+ assert(style);
impl->onStyleLoading();
impl->style = std::move(style);
+ impl->annotationManager.setStyle(*impl->style);
}
#pragma mark - Transitions
void Map::cancelTransitions() {
impl->transform.cancelTransitions();
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setGestureInProgress(bool inProgress) {
impl->transform.setGestureInProgress(inProgress);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
bool Map::isGestureInProgress() const {
@@ -363,19 +273,19 @@ CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const {
void Map::jumpTo(const CameraOptions& camera) {
impl->cameraMutated = true;
impl->transform.jumpTo(camera);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.easeTo(camera, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.flyTo(camera, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Position
@@ -383,7 +293,7 @@ void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation)
void Map::moveBy(const ScreenCoordinate& point, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.moveBy(point, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) {
@@ -394,13 +304,13 @@ void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) {
void Map::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLng(latLng, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLng(latLng, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
LatLng Map::getLatLng(const EdgeInsets& padding) const {
@@ -416,7 +326,7 @@ void Map::resetPosition(const EdgeInsets& padding) {
camera.padding = padding;
camera.zoom = 0;
impl->transform.jumpTo(camera);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
@@ -430,13 +340,13 @@ void Map::setZoom(double zoom, const AnimationOptions& animation) {
void Map::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setZoom(zoom, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setZoom(double zoom, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setZoom(zoom, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getZoom() const {
@@ -451,30 +361,30 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOption
void Map::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLngZoom(latLng, zoom, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
-CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding) const {
+CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding, optional<double> bearing) const {
return cameraForLatLngs({
bounds.northwest(),
bounds.southwest(),
bounds.southeast(),
bounds.northeast(),
- }, padding);
+ }, padding, bearing);
}
-CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding) const {
+CameraOptions cameraForLatLngs(const std::vector<LatLng>& latLngs, const Transform& transform, const EdgeInsets& padding) {
CameraOptions options;
if (latLngs.empty()) {
return options;
}
-
+ Size size = transform.getState().getSize();
// Calculate the bounds of the possibly rotated shape with respect to the viewport.
ScreenCoordinate nePixel = {-INFINITY, -INFINITY};
ScreenCoordinate swPixel = {INFINITY, INFINITY};
- double viewportHeight = getSize().height;
+ double viewportHeight = size.height;
for (LatLng latLng : latLngs) {
- ScreenCoordinate pixel = impl->transform.latLngToScreenCoordinate(latLng);
+ ScreenCoordinate pixel = transform.latLngToScreenCoordinate(latLng);
swPixel.x = std::min(swPixel.x, pixel.x);
nePixel.x = std::max(nePixel.x, pixel.x);
swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y);
@@ -486,14 +396,14 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
// Calculate the zoom level.
double minScale = INFINITY;
if (width > 0 || height > 0) {
- double scaleX = double(getSize().width) / width;
- double scaleY = double(getSize().height) / height;
+ double scaleX = double(size.width) / width;
+ double scaleY = double(size.height) / height;
scaleX -= (padding.left() + padding.right()) / width;
scaleY -= (padding.top() + padding.bottom()) / height;
minScale = util::min(scaleX, scaleY);
}
- double zoom = getZoom() + util::log2(minScale);
- zoom = util::clamp(zoom, getMinZoom(), getMaxZoom());
+ double zoom = transform.getZoom() + util::log2(minScale);
+ zoom = util::clamp(zoom, transform.getState().getMinZoom(), transform.getState().getMaxZoom());
// Calculate the center point of a virtual bounds that is extended in all directions by padding.
ScreenCoordinate centerPixel = nePixel + swPixel;
@@ -511,11 +421,34 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
// CameraOptions origin is at the top-left corner.
centerPixel.y = viewportHeight - centerPixel.y;
- options.center = latLngForPixel(centerPixel);
+ options.center = transform.screenCoordinateToLatLng(centerPixel);
options.zoom = zoom;
return options;
}
+CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding, optional<double> bearing) const {
+ if(bearing) {
+ double angle = -*bearing * util::DEG2RAD; // Convert to radians
+ Transform transform(impl->transform.getState());
+ transform.setAngle(angle);
+ CameraOptions options = mbgl::cameraForLatLngs(latLngs, transform, padding);
+ options.angle = angle;
+ return options;
+ } else {
+ return mbgl::cameraForLatLngs(latLngs, impl->transform, padding);
+ }
+}
+
+CameraOptions Map::cameraForGeometry(const Geometry<double>& geometry, const EdgeInsets& padding, optional<double> bearing) const {
+
+ std::vector<LatLng> latLngs;
+ forEachPoint(geometry, [&](const Point<double>& pt) {
+ latLngs.push_back({ pt.y, pt.x });
+ });
+ return cameraForLatLngs(latLngs, padding, bearing);
+
+}
+
LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const {
Transform shallow { impl->transform.getState() };
Size size = shallow.getState().getSize();
@@ -541,7 +474,7 @@ optional<LatLngBounds> Map::getLatLngBounds() const {
void Map::setLatLngBounds(optional<LatLngBounds> bounds) {
impl->cameraMutated = true;
impl->transform.setLatLngBounds(bounds);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setMinZoom(const double minZoom) {
@@ -592,7 +525,7 @@ double Map::getMaxPitch() const {
void Map::setSize(const Size size) {
impl->transform.resize(size);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
Size Map::getSize() const {
@@ -604,7 +537,7 @@ Size Map::getSize() const {
void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.rotateBy(first, second, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setBearing(double degrees, const AnimationOptions& animation) {
@@ -615,13 +548,13 @@ void Map::setBearing(double degrees, const AnimationOptions& animation) {
void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setBearing(double degrees, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(-degrees * util::DEG2RAD, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getBearing() const {
@@ -631,7 +564,7 @@ double Map::getBearing() const {
void Map::resetNorth(const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(0, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Pitch
@@ -644,7 +577,7 @@ void Map::setPitch(double pitch, const AnimationOptions& animation) {
void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setPitch(pitch * util::DEG2RAD, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getPitch() const {
@@ -655,7 +588,7 @@ double Map::getPitch() const {
void Map::setNorthOrientation(NorthOrientation orientation) {
impl->transform.setNorthOrientation(orientation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
NorthOrientation Map::getNorthOrientation() const {
@@ -666,7 +599,7 @@ NorthOrientation Map::getNorthOrientation() const {
void Map::setConstrainMode(mbgl::ConstrainMode mode) {
impl->transform.setConstrainMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
ConstrainMode Map::getConstrainMode() const {
@@ -677,13 +610,42 @@ ConstrainMode Map::getConstrainMode() const {
void Map::setViewportMode(mbgl::ViewportMode mode) {
impl->transform.setViewportMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
ViewportMode Map::getViewportMode() const {
return impl->transform.getViewportMode();
}
+#pragma mark - Projection mode
+
+void Map::setAxonometric(bool axonometric) {
+ impl->transform.setAxonometric(axonometric);
+ impl->onUpdate();
+}
+
+bool Map::getAxonometric() const {
+ return impl->transform.getAxonometric();
+}
+
+void Map::setXSkew(double xSkew) {
+ impl->transform.setXSkew(xSkew);
+ impl->onUpdate();
+}
+
+double Map::getXSkew() const {
+ return impl->transform.getXSkew();
+}
+
+void Map::setYSkew(double ySkew) {
+ impl->transform.setYSkew(ySkew);
+ impl->onUpdate();
+}
+
+double Map::getYSkew() const {
+ return impl->transform.getYSkew();
+}
+
#pragma mark - Projection
ScreenCoordinate Map::pixelForLatLng(const LatLng& latLng) const {
@@ -703,12 +665,10 @@ LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const {
void Map::addAnnotationImage(std::unique_ptr<style::Image> image) {
impl->annotationManager.addImage(std::move(image));
- impl->onUpdate(Update::AnnotationStyle);
}
void Map::removeAnnotationImage(const std::string& id) {
impl->annotationManager.removeImage(id);
- impl->onUpdate(Update::AnnotationStyle);
}
double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
@@ -716,79 +676,27 @@ double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
}
AnnotationID Map::addAnnotation(const Annotation& annotation) {
- auto result = impl->annotationManager.addAnnotation(annotation, getMaxZoom());
- impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData);
+ auto result = impl->annotationManager.addAnnotation(annotation);
+ impl->onUpdate();
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
- impl->onUpdate(impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom()));
+ if (impl->annotationManager.updateAnnotation(id, annotation)) {
+ impl->onUpdate();
+ }
}
void Map::removeAnnotation(AnnotationID annotation) {
impl->annotationManager.removeAnnotation(annotation);
- impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData);
-}
-
-#pragma mark - Feature query api
-
-std::vector<Feature> Map::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) {
- if (!impl->renderStyle) return {};
-
- return impl->renderStyle->queryRenderedFeatures(
- { point },
- impl->transform.getState(),
- options
- );
-}
-
-std::vector<Feature> Map::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) {
- if (!impl->renderStyle) return {};
-
- return impl->renderStyle->queryRenderedFeatures(
- {
- box.min,
- { box.max.x, box.min.y },
- box.max,
- { box.min.x, box.max.y },
- box.min
- },
- impl->transform.getState(),
- options
- );
-}
-
-std::vector<Feature> Map::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) {
- if (!impl->renderStyle) return {};
-
- const RenderSource* source = impl->renderStyle->getRenderSource(sourceID);
- if (!source) return {};
-
- return source->querySourceFeatures(options);
-}
-
-AnnotationIDs Map::queryPointAnnotations(const ScreenBox& box) {
- RenderedQueryOptions options;
- options.layerIDs = {{ AnnotationManager::PointLayerID }};
- auto features = queryRenderedFeatures(box, options);
- std::set<AnnotationID> set;
- for (auto &feature : features) {
- assert(feature.id);
- assert(feature.id->is<uint64_t>());
- assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max());
- set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>()));
- }
- AnnotationIDs ids;
- ids.reserve(set.size());
- std::move(set.begin(), set.end(), std::back_inserter(ids));
- return ids;
+ impl->onUpdate();
}
#pragma mark - Toggles
void Map::setDebug(MapDebugOptions debugOptions) {
impl->debugOptions = debugOptions;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::cycleDebugOptions() {
@@ -812,65 +720,72 @@ void Map::cycleDebugOptions() {
else
impl->debugOptions = MapDebugOptions::TileBorders;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
MapDebugOptions Map::getDebug() const {
return impl->debugOptions;
}
-bool Map::isFullyLoaded() const {
- return impl->style->impl->isLoaded() && impl->renderStyle && impl->renderStyle->isLoaded();
+void Map::setPrefetchZoomDelta(uint8_t delta) {
+ impl->prefetchZoomDelta = delta;
}
-void Map::setSourceTileCacheSize(size_t size) {
- if (size != impl->sourceCacheSize) {
- impl->sourceCacheSize = size;
- if (!impl->renderStyle) return;
- impl->renderStyle->setSourceTileCacheSize(size);
- impl->backend.invalidate();
- }
+uint8_t Map::getPrefetchZoomDelta() const {
+ return impl->prefetchZoomDelta;
}
-void Map::onLowMemory() {
- if (impl->painter) {
- BackendScope guard(impl->backend);
- impl->painter->cleanup();
- }
- if (impl->renderStyle) {
- impl->renderStyle->onLowMemory();
- impl->backend.invalidate();
- }
+bool Map::isFullyLoaded() const {
+ return impl->style->impl->isLoaded() && impl->rendererFullyLoaded;
}
void Map::Impl::onSourceChanged(style::Source& source) {
observer.onSourceChanged(source);
}
-void Map::Impl::onUpdate(Update flags) {
- updateFlags |= flags;
- asyncInvalidate.send();
+void Map::Impl::onInvalidate() {
+ onUpdate();
}
-void Map::Impl::onInvalidate() {
- onUpdate(Update::Repaint);
+void Map::Impl::onUpdate() {
+ TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
+
+ transform.updateTransitions(timePoint);
+
+ UpdateParameters params = {
+ style->impl->isLoaded(),
+ mode,
+ pixelRatio,
+ debugOptions,
+ timePoint,
+ transform.getState(),
+ style->impl->getGlyphURL(),
+ style->impl->spriteLoaded,
+ style->impl->getTransitionOptions(),
+ style->impl->getLight()->impl,
+ style->impl->getImageImpls(),
+ style->impl->getSourceImpls(),
+ style->impl->getLayerImpls(),
+ annotationManager,
+ prefetchZoomDelta,
+ bool(stillImageRequest)
+ };
+
+ rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params)));
}
void Map::Impl::onStyleLoading() {
loading = true;
+ rendererFullyLoaded = false;
observer.onWillStartLoadingMap();
}
void Map::Impl::onStyleLoaded() {
if (!cameraMutated) {
- // Zoom first because it may constrain subsequent operations.
- map.setZoom(style->getDefaultZoom());
- map.setLatLng(style->getDefaultLatLng());
- map.setBearing(style->getDefaultBearing());
- map.setPitch(style->getDefaultPitch());
+ map.jumpTo(style->getDefaultCamera());
}
- onUpdate(Update::AnnotationStyle);
+ annotationManager.onStyleLoaded();
observer.onDidFinishLoadingStyle();
}
@@ -879,7 +794,7 @@ void Map::Impl::onStyleError(std::exception_ptr error) {
}
void Map::Impl::onResourceError(std::exception_ptr error) {
- if (mode == MapMode::Still && stillImageRequest) {
+ if (mode != MapMode::Continuous && stillImageRequest) {
auto request = std::move(stillImageRequest);
request->callback(error);
}
@@ -888,9 +803,6 @@ void Map::Impl::onResourceError(std::exception_ptr error) {
void Map::dumpDebugLogs() const {
Log::Info(Event::General, "--------------------------------------------------------------------------------");
impl->style->impl->dumpDebugLogs();
- if (impl->renderStyle) {
- impl->renderStyle->dumpDebugLogs();
- }
Log::Info(Event::General, "--------------------------------------------------------------------------------");
}
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 8d05bc0e91..105adf0400 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -1,6 +1,5 @@
#include <mbgl/map/camera.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/map/view.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/math.hpp>
@@ -168,7 +167,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
double angle = camera.angle.value_or(getAngle());
double pitch = camera.pitch.value_or(getPitch());
- if (std::isnan(zoom)) {
+ if (std::isnan(zoom) || state.size.isEmpty()) {
return;
}
@@ -293,6 +292,11 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
Point<double> framePoint = util::interpolate(startPoint, endPoint, us);
double frameZoom = startZoom + state.scaleZoom(1 / w(s));
+ // Zoom can be NaN if size is empty.
+ if (std::isnan(frameZoom)) {
+ frameZoom = zoom;
+ }
+
// Convert to geographic coordinates and set the new viewpoint.
LatLng frameLatLng = Projection::unproject(framePoint, startScale);
state.setLatLngZoom(frameLatLng, frameZoom);
@@ -523,6 +527,32 @@ ViewportMode Transform::getViewportMode() const {
return state.getViewportMode();
}
+#pragma mark - Projection mode
+
+void Transform::setAxonometric(bool axonometric) {
+ state.axonometric = axonometric;
+}
+
+bool Transform::getAxonometric() const {
+ return state.axonometric;
+}
+
+void Transform::setXSkew(double xSkew) {
+ state.xSkew = xSkew;
+}
+
+double Transform::getXSkew() const {
+ return state.xSkew;
+}
+
+void Transform::setYSkew(double ySkew) {
+ state.ySkew = ySkew;
+}
+
+double Transform::getYSkew() const {
+ return state.ySkew;
+}
+
#pragma mark - Transition
void Transform::startTransition(const CameraOptions& camera,
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 749228bdf5..d429c57661 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -125,6 +125,14 @@ public:
void setViewportMode(ViewportMode);
ViewportMode getViewportMode() const;
+ // Projection mode
+ void setAxonometric(bool);
+ bool getAxonometric() const;
+ void setXSkew(double xSkew);
+ double getXSkew() const;
+ void setYSkew(double ySkew);
+ double getYSkew() const;
+
// Transitions
bool inTransition() const;
void updateTransitions(const TimePoint& now);
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index bbf7e22b31..d79a65c61e 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -66,6 +66,15 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {
matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f,
pixel_y() - size.height / 2.0f, 0);
+ if (axonometric) {
+ // mat[11] controls perspective
+ projMatrix[11] = 0;
+
+ // mat[8], mat[9] control x-skew, y-skew
+ projMatrix[8] = xSkew;
+ projMatrix[9] = ySkew;
+ }
+
matrix::scale(projMatrix, projMatrix, 1, 1,
1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom()));
}
@@ -132,7 +141,7 @@ double TransformState::getZoom() const {
return scaleZoom(scale);
}
-int32_t TransformState::getIntegerZoom() const {
+uint8_t TransformState::getIntegerZoom() const {
return getZoom();
}
@@ -358,7 +367,7 @@ void TransformState::setLatLngZoom(const LatLng& latLng, double zoom) {
constrained = bounds->constrain(latLng);
}
- double newScale = zoomScale(zoom);
+ double newScale = util::clamp(zoomScale(zoom), min_scale, max_scale);
const double newWorldSize = newScale * util::tileSize;
Bc = newWorldSize / util::DEGREES_MAX;
Cc = newWorldSize / util::M2PI;
@@ -385,4 +394,16 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate
Cc = Projection::worldSize(scale) / util::M2PI;
}
+float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) const {
+ mat4 projectionMatrix;
+ getProjMatrix(projectionMatrix);
+ mat4 tileProjectionMatrix;
+ matrixFor(tileProjectionMatrix, tileID);
+ matrix::multiply(tileProjectionMatrix, projectionMatrix, tileProjectionMatrix);
+ vec4 tileCenter = {{util::tileSize / 2, util::tileSize / 2, 0, 1}};
+ vec4 projectedCenter;
+ matrix::transformMat4(projectedCenter, tileCenter, tileProjectionMatrix);
+ return projectedCenter[3];
+}
+
} // namespace mbgl
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index e6464aeacc..0dd6d5a15e 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -47,7 +47,7 @@ public:
// Zoom
double getZoom() const;
- int32_t getIntegerZoom() const;
+ uint8_t getIntegerZoom() const;
double getZoomFraction() const;
// Bounds
@@ -86,6 +86,8 @@ public:
return !size.isEmpty() && (scale >= min_scale && scale <= max_scale);
}
+ float getCameraToTileDistance(const UnwrappedTileID&) const;
+
private:
bool rotatedNorth() const;
void constrain(double& scale, double& x, double& y) const;
@@ -94,7 +96,7 @@ private:
// Limit the amount of zooming possible on the map.
double min_scale = std::pow(2, 0);
- double max_scale = std::pow(2, 20);
+ double max_scale = std::pow(2, util::DEFAULT_MAX_ZOOM);
double min_pitch = 0.0;
double max_pitch = util::PITCH_MAX;
@@ -132,6 +134,9 @@ private:
// `fov = 2 * arctan((height / 2) / (height * 1.5))`
double fov = 0.6435011087932844;
double pitch = 0.0;
+ double xSkew = 0.0;
+ double ySkew = 1.0;
+ bool axonometric = false;
// cache values for spherical mercator math
double Bc = Projection::worldSize(scale) / util::DEGREES_MAX;
diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp
deleted file mode 100644
index 82d98284f5..0000000000
--- a/src/mbgl/map/update.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <mbgl/util/traits.hpp>
-
-namespace mbgl {
-
-enum class Update {
- Nothing = 0,
- Repaint = 1 << 0,
- AnnotationStyle = 1 << 6,
- AnnotationData = 1 << 7
-};
-
-constexpr Update operator|(Update lhs, Update rhs) {
- return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
-}
-
-constexpr Update& operator|=(Update& lhs, const Update& rhs) {
- return (lhs = lhs | rhs);
-}
-
-constexpr bool operator& (Update lhs, Update rhs) {
- return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/map/zoom_history.hpp b/src/mbgl/map/zoom_history.hpp
index 308846b1e3..7821499d72 100644
--- a/src/mbgl/map/zoom_history.hpp
+++ b/src/mbgl/map/zoom_history.hpp
@@ -8,33 +8,39 @@ namespace mbgl {
struct ZoomHistory {
float lastZoom;
+ float lastFloorZoom;
float lastIntegerZoom;
TimePoint lastIntegerZoomTime;
bool first = true;
bool update(float z, const TimePoint& now) {
+ constexpr TimePoint zero = TimePoint(Duration::zero());
+ const float floorZ = std::floor(z);
+
if (first) {
first = false;
- lastIntegerZoom = std::floor(z);
- lastIntegerZoomTime = TimePoint(Duration::zero());
+ lastIntegerZoom = floorZ;
+ lastIntegerZoomTime = zero;
+ lastZoom = z;
+ lastFloorZoom = floorZ;
+ return true;
+ }
+
+ if (lastFloorZoom > floorZ) {
+ lastIntegerZoom = floorZ + 1;
+ lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
+ } else if (lastFloorZoom < floorZ) {
+ lastIntegerZoom = floorZ;
+ lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
+ }
+
+ if (z != lastZoom) {
lastZoom = z;
+ lastFloorZoom = floorZ;
return true;
- } else {
- if (std::floor(lastZoom) < std::floor(z)) {
- lastIntegerZoom = std::floor(z);
- lastIntegerZoomTime = now;
- } else if (std::floor(lastZoom) > std::floor(z)) {
- lastIntegerZoom = std::floor(z + 1);
- lastIntegerZoomTime = now;
- }
-
- if (z != lastZoom) {
- lastZoom = z;
- return true;
- }
-
- return false;
}
+
+ return false;
}
};
diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp
index 8f2751080f..b0582b0bc2 100644
--- a/src/mbgl/programs/attributes.hpp
+++ b/src/mbgl/programs/attributes.hpp
@@ -23,9 +23,14 @@ inline uint16_t packUint8Pair(T a, T b) {
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_normal);
+MBGL_DEFINE_ATTRIBUTE(float, 3, a_projected_pos);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_label_pos);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos);
-MBGL_DEFINE_ATTRIBUTE(int16_t, 3, a_normal);
-MBGL_DEFINE_ATTRIBUTE(uint16_t, 1, a_edgedistance);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_normal_ed);
+MBGL_DEFINE_ATTRIBUTE(uint8_t, 1, a_fade_opacity);
+MBGL_DEFINE_ATTRIBUTE(uint8_t, 2, a_placed);
template <typename T, std::size_t N>
struct a_data {
diff --git a/src/mbgl/programs/binary_program.cpp b/src/mbgl/programs/binary_program.cpp
index 09b9acc514..da629194b4 100644
--- a/src/mbgl/programs/binary_program.cpp
+++ b/src/mbgl/programs/binary_program.cpp
@@ -3,6 +3,7 @@
#include <protozero/pbf_reader.hpp>
#include <protozero/pbf_writer.hpp>
#include <utility>
+#include <stdexcept>
template <class Binding>
static std::pair<const std::string, Binding> parseBinding(protozero::pbf_reader&& pbf) {
@@ -97,7 +98,7 @@ std::string BinaryProgram::serialize() const {
return data;
}
-gl::AttributeLocation BinaryProgram::attributeLocation(const std::string& name) const {
+optional<gl::AttributeLocation> BinaryProgram::attributeLocation(const std::string& name) const {
for (const auto& pair : attributes) {
if (pair.first == name) {
return pair.second;
@@ -112,7 +113,7 @@ gl::UniformLocation BinaryProgram::uniformLocation(const std::string& name) cons
return pair.second;
}
}
- return {};
+ return -1;
}
} // namespace mbgl
diff --git a/src/mbgl/programs/binary_program.hpp b/src/mbgl/programs/binary_program.hpp
index b77cf1a510..8690f3fd6f 100644
--- a/src/mbgl/programs/binary_program.hpp
+++ b/src/mbgl/programs/binary_program.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/gl/types.hpp>
+#include <mbgl/util/optional.hpp>
#include <string>
#include <vector>
@@ -29,7 +30,8 @@ public:
const std::string& identifier() const {
return binaryIdentifier;
}
- gl::AttributeLocation attributeLocation(const std::string& name) const;
+
+ optional<gl::AttributeLocation> attributeLocation(const std::string& name) const;
gl::UniformLocation uniformLocation(const std::string& name) const;
private:
diff --git a/src/mbgl/programs/circle_program.hpp b/src/mbgl/programs/circle_program.hpp
index 8f056048b1..3590acbeef 100644
--- a/src/mbgl/programs/circle_program.hpp
+++ b/src/mbgl/programs/circle_program.hpp
@@ -21,7 +21,9 @@ class CircleProgram : public Program<
gl::Uniforms<
uniforms::u_matrix,
uniforms::u_scale_with_map,
- uniforms::u_extrude_scale>,
+ uniforms::u_extrude_scale,
+ uniforms::u_camera_to_center_distance,
+ uniforms::u_pitch_with_map>,
style::CirclePaintProperties>
{
public:
diff --git a/src/mbgl/programs/collision_box_program.cpp b/src/mbgl/programs/collision_box_program.cpp
index a3dc01ebe4..57107db75d 100644
--- a/src/mbgl/programs/collision_box_program.cpp
+++ b/src/mbgl/programs/collision_box_program.cpp
@@ -2,6 +2,6 @@
namespace mbgl {
-static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 10, "expected CollisionBoxVertex size");
+static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 14, "expected CollisionBoxVertex size");
} // namespace mbgl
diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp
index 160fd42814..8d712a3df3 100644
--- a/src/mbgl/programs/collision_box_program.hpp
+++ b/src/mbgl/programs/collision_box_program.hpp
@@ -4,6 +4,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/collision_box.hpp>
+#include <mbgl/shaders/collision_circle.hpp>
#include <mbgl/style/properties.hpp>
#include <mbgl/util/geometry.hpp>
@@ -11,46 +12,170 @@
namespace mbgl {
-namespace uniforms {
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom);
-} // namespace uniforms
-
-using CollisionBoxAttributes = gl::Attributes<
+using CollisionBoxLayoutAttributes = gl::Attributes<
attributes::a_pos,
- attributes::a_extrude,
- attributes::a_data<uint8_t, 2>>;
+ attributes::a_anchor_pos,
+ attributes::a_extrude>;
+
+struct CollisionBoxDynamicAttributes : gl::Attributes<attributes::a_placed> {
+ static Vertex vertex(bool placed, bool notUsed) {
+ return Vertex {
+ {{ static_cast<uint8_t>(placed), static_cast<uint8_t>(notUsed) }}
+ };
+ }
+};
class CollisionBoxProgram : public Program<
shaders::collision_box,
gl::Line,
- CollisionBoxAttributes,
+ gl::ConcatenateAttributes<CollisionBoxLayoutAttributes, CollisionBoxDynamicAttributes>,
gl::Uniforms<
uniforms::u_matrix,
- uniforms::u_scale,
- uniforms::u_zoom,
- uniforms::u_maxzoom>,
+ uniforms::u_extrude_scale,
+ uniforms::u_camera_to_center_distance>,
style::Properties<>>
{
public:
using Program::Program;
- static LayoutVertex vertex(Point<float> a, Point<float> o, float maxzoom, float placementZoom) {
- return LayoutVertex {
+ static CollisionBoxLayoutAttributes::Vertex vertex(Point<float> a, Point<float> anchor, Point<float> o) {
+ return CollisionBoxLayoutAttributes::Vertex {
{{
static_cast<int16_t>(a.x),
static_cast<int16_t>(a.y)
}},
{{
+ static_cast<int16_t>(anchor.x),
+ static_cast<int16_t>(anchor.y)
+ }},
+ {{
static_cast<int16_t>(::round(o.x)),
static_cast<int16_t>(::round(o.y))
+ }}
+ };
+ }
+
+ 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<CollisionBoxLayoutAttributes::Vertex>& layoutVertexBuffer,
+ const gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>& dynamicVertexBuffer,
+ const gl::IndexBuffer<DrawMode>& indexBuffer,
+ const SegmentVector<Attributes>& segments,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
+ float currentZoom,
+ const std::string& layerID) {
+ typename AllUniforms::Values allUniformValues = uniformValues
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+
+ typename Attributes::Bindings allAttributeBindings = CollisionBoxLayoutAttributes::bindings(layoutVertexBuffer)
+ .concat(CollisionBoxDynamicAttributes::bindings(dynamicVertexBuffer))
+ .concat(paintPropertyBinders.attributeBindings(currentProperties));
+
+ assert(layoutVertexBuffer.vertexCount == dynamicVertexBuffer.vertexCount);
+
+ for (auto& segment : segments) {
+ auto vertexArrayIt = segment.vertexArrays.find(layerID);
+
+ if (vertexArrayIt == segment.vertexArrays.end()) {
+ vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first;
+ }
+
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ allUniformValues,
+ vertexArrayIt->second,
+ Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset),
+ indexBuffer,
+ segment.indexOffset,
+ segment.indexLength);
+ }
+ }
+};
+
+
+class CollisionCircleProgram : public Program<
+ shaders::collision_circle,
+ gl::Triangle,
+ gl::ConcatenateAttributes<CollisionBoxLayoutAttributes, CollisionBoxDynamicAttributes>,
+ gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_extrude_scale,
+ uniforms::u_camera_to_center_distance>,
+ style::Properties<>>
+{
+public:
+ using Program::Program;
+
+ static CollisionBoxLayoutAttributes::Vertex vertex(Point<float> a, Point<float> anchor, Point<float> o) {
+ return CollisionBoxLayoutAttributes::Vertex {
+ {{
+ static_cast<int16_t>(a.x),
+ static_cast<int16_t>(a.y)
}},
{{
- static_cast<uint8_t>(maxzoom * 10),
- static_cast<uint8_t>(placementZoom * 10)
+ static_cast<int16_t>(anchor.x),
+ static_cast<int16_t>(anchor.y)
+ }},
+ {{
+ static_cast<int16_t>(::round(o.x)),
+ static_cast<int16_t>(::round(o.y))
}}
};
}
+
+ 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<CollisionBoxLayoutAttributes::Vertex>& layoutVertexBuffer,
+ const gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>& dynamicVertexBuffer,
+ const gl::IndexBuffer<DrawMode>& indexBuffer,
+ const SegmentVector<Attributes>& segments,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
+ float currentZoom,
+ const std::string& layerID) {
+ typename AllUniforms::Values allUniformValues = uniformValues
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+
+ typename Attributes::Bindings allAttributeBindings = CollisionBoxLayoutAttributes::bindings(layoutVertexBuffer)
+ .concat(CollisionBoxDynamicAttributes::bindings(dynamicVertexBuffer))
+ .concat(paintPropertyBinders.attributeBindings(currentProperties));
+
+ for (auto& segment : segments) {
+ auto vertexArrayIt = segment.vertexArrays.find(layerID);
+
+ if (vertexArrayIt == segment.vertexArrays.end()) {
+ vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first;
+ }
+
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ allUniformValues,
+ vertexArrayIt->second,
+ Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset),
+ indexBuffer,
+ segment.indexOffset,
+ segment.indexLength);
+ }
+ }
};
using CollisionBoxVertex = CollisionBoxProgram::LayoutVertex;
diff --git a/src/mbgl/programs/fill_extrusion_program.hpp b/src/mbgl/programs/fill_extrusion_program.hpp
index 820670068e..c499e9ef2d 100644
--- a/src/mbgl/programs/fill_extrusion_program.hpp
+++ b/src/mbgl/programs/fill_extrusion_program.hpp
@@ -30,8 +30,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_height_factor);
struct FillExtrusionLayoutAttributes : gl::Attributes<
attributes::a_pos,
- attributes::a_normal,
- attributes::a_edgedistance>
+ attributes::a_normal_ed>
{};
struct FillExtrusionUniforms : gl::Uniforms<
@@ -100,12 +99,9 @@ public:
// We pack a bool (`t`) into the x component indicating whether it is an upper or lower vertex
static_cast<int16_t>(floor(nx * factor) * 2 + t),
static_cast<int16_t>(ny * factor * 2),
- static_cast<int16_t>(nz * factor * 2)
-
- }},
- {{
+ static_cast<int16_t>(nz * factor * 2),
// The edgedistance attribute is used for wrapping fill_extrusion patterns
- e
+ static_cast<int16_t>(e)
}}
};
}
diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp
index db5c916d32..faf57ef19b 100644
--- a/src/mbgl/programs/line_program.cpp
+++ b/src/mbgl/programs/line_program.cpp
@@ -10,7 +10,7 @@ namespace mbgl {
using namespace style;
-static_assert(sizeof(LineLayoutVertex) == 8, "expected LineLayoutVertex size");
+static_assert(sizeof(LineLayoutVertex) == 12, "expected LineLayoutVertex size");
template <class Values, class...Args>
Values makeValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp
index ed4a09bf10..da9964e623 100644
--- a/src/mbgl/programs/line_program.hpp
+++ b/src/mbgl/programs/line_program.hpp
@@ -30,7 +30,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_gl_units_to_pixels);
} // namespace uniforms
struct LineLayoutAttributes : gl::Attributes<
- attributes::a_pos,
+ attributes::a_pos_normal,
attributes::a_data<uint8_t, 4>>
{};
@@ -50,14 +50,17 @@ public:
/*
* @param p vertex position
* @param e extrude normal
- * @param t texture normal
+ * @param round whether the vertex uses a round line cap
+ * @param up whether the line normal points up or down
* @param dir direction of the line cap (-1/0/1)
*/
- static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, Point<bool> t, int8_t dir, int32_t linesofar = 0) {
+ static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, bool round, bool up, int8_t dir, int32_t linesofar = 0) {
return LayoutVertex {
{{
- static_cast<int16_t>((p.x * 2) | t.x),
- static_cast<int16_t>((p.y * 2) | t.y)
+ p.x,
+ p.y,
+ static_cast<int16_t>(round ? 1 : 0),
+ static_cast<int16_t>(up ? 1 : -1)
}},
{{
// add 128 to store a byte in an unsigned byte
diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp
index 3a38f30a86..bcdb270b9c 100644
--- a/src/mbgl/programs/program.hpp
+++ b/src/mbgl/programs/program.hpp
@@ -2,6 +2,7 @@
#include <mbgl/gl/program.hpp>
#include <mbgl/gl/features.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/binary_program.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/program_parameters.hpp>
@@ -51,26 +52,40 @@ public:
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
- UniformValues&& uniformValues,
+ const UniformValues& uniformValues,
const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
const gl::IndexBuffer<DrawMode>& indexBuffer,
- const gl::SegmentVector<Attributes>& segments,
+ const SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
const typename PaintProperties::PossiblyEvaluated& currentProperties,
- float currentZoom) {
- program.draw(
- context,
- std::move(drawMode),
- std::move(depthMode),
- std::move(stencilMode),
- std::move(colorMode),
- uniformValues
- .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)),
- LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(paintPropertyBinders.attributeBindings(currentProperties)),
- indexBuffer,
- segments
- );
+ float currentZoom,
+ 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);
+
+ if (vertexArrayIt == segment.vertexArrays.end()) {
+ vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first;
+ }
+
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ allUniformValues,
+ vertexArrayIt->second,
+ Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset),
+ indexBuffer,
+ segment.indexOffset,
+ segment.indexLength);
+ }
}
};
diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp
index 37ced32745..d769defaaf 100644
--- a/src/mbgl/programs/programs.hpp
+++ b/src/mbgl/programs/programs.hpp
@@ -32,7 +32,8 @@ public:
symbolIconSDF(context, programParameters),
symbolGlyph(context, programParameters),
debug(context, programParameters),
- collisionBox(context, programParameters) {
+ collisionBox(context, programParameters),
+ collisionCircle(context, programParameters) {
}
ProgramMap<CircleProgram> circle;
@@ -53,6 +54,7 @@ public:
DebugProgram debug;
CollisionBoxProgram collisionBox;
+ CollisionCircleProgram collisionCircle;
};
} // namespace mbgl
diff --git a/src/mbgl/programs/segment.hpp b/src/mbgl/programs/segment.hpp
new file mode 100644
index 0000000000..f729683ac9
--- /dev/null
+++ b/src/mbgl/programs/segment.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/vertex_array.hpp>
+
+#include <cstddef>
+#include <vector>
+#include <map>
+
+namespace mbgl {
+
+template <class Attributes>
+class Segment {
+public:
+ Segment(std::size_t vertexOffset_,
+ std::size_t indexOffset_,
+ std::size_t vertexLength_ = 0,
+ std::size_t indexLength_ = 0)
+ : vertexOffset(vertexOffset_),
+ indexOffset(indexOffset_),
+ vertexLength(vertexLength_),
+ indexLength(indexLength_) {}
+
+ const std::size_t vertexOffset;
+ const std::size_t indexOffset;
+
+ std::size_t vertexLength;
+ std::size_t indexLength;
+
+ // One VertexArray per layer ID. This minimizes rebinding in cases where
+ // several layers share buckets but have different sets of active attributes.
+ // This can happen:
+ // * when two layers have the same layout properties, but differing
+ // data-driven paint properties
+ // * when two fill layers have the same layout properties, but one
+ // uses fill-color and the other uses fill-pattern
+ mutable std::map<std::string, gl::VertexArray> vertexArrays;
+};
+
+template <class Attributes>
+using SegmentVector = std::vector<Segment<Attributes>>;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp
index cdbd6b9713..84a7a53f1d 100644
--- a/src/mbgl/programs/symbol_program.cpp
+++ b/src/mbgl/programs/symbol_program.cpp
@@ -2,6 +2,8 @@
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/math/clamp.hpp>
@@ -32,8 +34,10 @@ Values makeValues(const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile& tile,
const TransformState& state,
+ const float symbolFadeChange,
Args&&... args) {
std::array<float, 2> extrudeScale;
@@ -45,18 +49,48 @@ Values makeValues(const bool isText,
pixelsToGLUnits[1] * state.getCameraToCenterDistance()
}};
}
+
+ const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom());
+ const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map;
+ const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map;
+
+ // Line label rotation happens in `updateLineLabels`
+ // Pitched point labels are automatically rotated by the labelPlaneMatrix projection
+ // Unpitched point labels need to have their rotation applied after projection
+ const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
+
+ mat4 labelPlaneMatrix;
+ if (alongLine) {
+ // For labels that follow lines the first part of the projection is handled on the cpu.
+ // Pass an identity matrix because no transformation needs to be done in the vertex shader.
+ matrix::identity(labelPlaneMatrix);
+ } else {
+ labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
+ }
+
+ mat4 glCoordMatrix = getGlCoordMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
return Values {
uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate,
values.translateAnchor,
state) },
+ uniforms::u_label_plane_matrix::Value{labelPlaneMatrix},
+ uniforms::u_gl_coord_matrix::Value{ tile.translateVtxMatrix(glCoordMatrix,
+ values.translate,
+ values.translateAnchor,
+ state,
+ true) },
uniforms::u_extrude_scale::Value{ extrudeScale },
uniforms::u_texsize::Value{ texsize },
- uniforms::u_zoom::Value{ float(state.getZoom()) },
- uniforms::u_rotate_with_map::Value{ values.rotationAlignment == AlignmentType::Map },
uniforms::u_texture::Value{ 0 },
- uniforms::u_fadetexture::Value{ 1 },
+ uniforms::u_fade_change::Value{ symbolFadeChange },
uniforms::u_is_text::Value{ isText },
+ uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() },
+ uniforms::u_pitch::Value{ state.getPitch() },
+ uniforms::u_pitch_with_map::Value{ pitchWithMap },
+ uniforms::u_max_camera_distance::Value{ values.maxCameraDistance },
+ uniforms::u_rotate_symbol::Value{ rotateInShader },
+ uniforms::u_aspect_ratio::Value{ state.getSize().aspectRatio() },
std::forward<Args>(args)...
};
}
@@ -66,16 +100,20 @@ SymbolIconProgram::uniformValues(const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile& tile,
- const TransformState& state)
+ const TransformState& state,
+ const float symbolFadeChange)
{
return makeValues<SymbolIconProgram::UniformValues>(
isText,
values,
texsize,
pixelsToGLUnits,
+ alongLine,
tile,
- state
+ state,
+ symbolFadeChange
);
}
@@ -85,26 +123,26 @@ typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<Paint
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile& tile,
const TransformState& state,
+ const float symbolFadeChange,
const SymbolSDFPart part)
{
const float gammaScale = (values.pitchAlignment == AlignmentType::Map
- ? std::cos(state.getPitch())
- : 1.0) * state.getCameraToCenterDistance();
+ ? std::cos(state.getPitch()) * state.getCameraToCenterDistance()
+ : 1.0);
return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>(
isText,
values,
texsize,
pixelsToGLUnits,
+ alongLine,
tile,
state,
+ symbolFadeChange,
uniforms::u_gamma_scale::Value{ gammaScale },
- uniforms::u_pitch::Value{ state.getPitch() },
- uniforms::u_bearing::Value{ -1.0f * state.getAngle() },
- uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) },
- uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map },
uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo }
);
}
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp
index e7c428034b..a14afac702 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -7,6 +7,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/shaders/symbol_icon.hpp>
#include <mbgl/shaders/symbol_sdf.hpp>
#include <mbgl/util/geometry.hpp>
@@ -29,11 +30,9 @@ class RenderTile;
class TransformState;
namespace uniforms {
-MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map);
-MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map);
+MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_gl_coord_matrix);
+MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_label_plane_matrix);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture);
-MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale);
@@ -42,50 +41,66 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_zoom_constant);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_size);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_layout_size);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_max_camera_distance);
+MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_symbol);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio);
} // namespace uniforms
struct SymbolLayoutAttributes : gl::Attributes<
attributes::a_pos_offset,
attributes::a_data<uint16_t, 4>>
{
- static Vertex vertex(Point<float> a,
+ static Vertex vertex(Point<float> labelAnchor,
Point<float> o,
+ float glyphOffsetY,
uint16_t tx,
uint16_t ty,
- float minzoom,
- float maxzoom,
- float labelminzoom,
- uint8_t labelangle) {
+ const Range<float>& sizeData) {
return Vertex {
// combining pos and offset to reduce number of vertex attributes passed to shader (8 max for some devices)
{{
- static_cast<int16_t>(a.x),
- static_cast<int16_t>(a.y),
+ static_cast<int16_t>(labelAnchor.x),
+ static_cast<int16_t>(labelAnchor.y),
static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement
- static_cast<int16_t>(::round(o.y * 64))
+ static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64))
}},
{{
tx,
ty,
- mbgl::attributes::packUint8Pair(
- static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160
- static_cast<uint8_t>(labelangle)
- ),
- mbgl::attributes::packUint8Pair(
- static_cast<uint8_t>(minzoom * 10),
- static_cast<uint8_t>(::fmin(maxzoom, 25) * 10)
- )
+ static_cast<uint16_t>(sizeData.min * 10),
+ static_cast<uint16_t>(sizeData.max * 10)
}}
};
}
};
-
-class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> {
-public:
- using Attribute = attributes::a_size::Type;
+
+struct SymbolDynamicLayoutAttributes : gl::Attributes<attributes::a_projected_pos> {
+ static Vertex vertex(Point<float> anchorPoint, float labelAngle) {
+ return Vertex {
+ {{
+ anchorPoint.x,
+ anchorPoint.y,
+ labelAngle
+ }}
+ };
+ }
};
+struct SymbolOpacityAttributes : gl::Attributes<attributes::a_fade_opacity> {
+ static Vertex vertex(bool placed, float opacity) {
+ return Vertex {
+ {{ static_cast<uint8_t>((static_cast<uint8_t>(opacity * 127) << 1) | static_cast<uint8_t>(placed)) }}
+ };
+ }
+};
+
+struct ZoomEvaluatedSize {
+ bool isZoomConstant;
+ bool isFeatureConstant;
+ float sizeT;
+ float size;
+ float layoutSize;
+};
// Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size layout properties
// in order to provide a 'custom' scheme for encoding the necessary attribute data. As with
// PaintPropertyBinder, SymbolSizeBinder is an abstract class whose implementations handle the
@@ -98,37 +113,27 @@ public:
uniforms::u_is_size_zoom_constant,
uniforms::u_is_size_feature_constant,
uniforms::u_size_t,
- uniforms::u_size,
- uniforms::u_layout_size>;
+ uniforms::u_size>;
using UniformValues = Uniforms::Values;
static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom,
const style::DataDrivenPropertyValue<float>& sizeProperty,
const float defaultValue);
- virtual SymbolSizeAttributes::Bindings attributeBindings() const = 0;
- virtual void populateVertexVector(const GeometryTileFeature& feature) = 0;
- virtual UniformValues uniformValues(float currentZoom) const = 0;
- virtual void upload(gl::Context&) = 0;
-};
+ virtual Range<float> getVertexSizeData(const GeometryTileFeature& feature) = 0;
+ virtual ZoomEvaluatedSize evaluateForZoom(float currentZoom) const = 0;
-// Return the smallest range of stops that covers the interval [lowerZoom, upperZoom]
-template <class Stops>
-Range<float> getCoveringStops(Stops s, float lowerZoom, float upperZoom) {
- assert(!s.stops.empty());
- auto minIt = s.stops.lower_bound(lowerZoom);
- auto maxIt = s.stops.lower_bound(upperZoom);
-
- // lower_bound yields first element >= lowerZoom, but we want the *last*
- // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one.
- if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > lowerZoom) {
- minIt--;
+ UniformValues uniformValues(float currentZoom) const {
+ const ZoomEvaluatedSize u = evaluateForZoom(currentZoom);
+ return UniformValues {
+ uniforms::u_is_size_zoom_constant::Value{ u.isZoomConstant },
+ uniforms::u_is_size_feature_constant::Value{ u.isFeatureConstant},
+ uniforms::u_size_t::Value{ u.sizeT },
+ uniforms::u_size::Value{ u.size }
+ };
}
- return Range<float> {
- minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
- maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
- };
-}
+};
+
class ConstantSymbolSizeBinder final : public SymbolSizeBinder {
public:
@@ -139,30 +144,18 @@ public:
: layoutSize(defaultValue) {}
ConstantSymbolSizeBinder(const float tileZoom, const style::CameraFunction<float>& function_, const float /*defaultValue*/)
- : layoutSize(function_.evaluate(tileZoom + 1)) {
- function_.stops.match(
- [&] (const style::ExponentialStops<float>& stops) {
- const auto& zoomLevels = getCoveringStops(stops, tileZoom, tileZoom + 1);
- coveringRanges = std::make_tuple(
- zoomLevels,
- Range<float> { function_.evaluate(zoomLevels.min), function_.evaluate(zoomLevels.max) }
- );
- functionInterpolationBase = stops.base;
- },
- [&] (const style::IntervalStops<float>&) {
- function = function_;
- }
+ : layoutSize(function_.evaluate(tileZoom + 1)),
+ function(function_) {
+ const Range<float> zoomLevels = function_.getCoveringStops(tileZoom, tileZoom + 1);
+ coveringRanges = std::make_tuple(
+ zoomLevels,
+ Range<float> { function_.evaluate(zoomLevels.min), function_.evaluate(zoomLevels.max) }
);
}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { gl::DisabledAttribute() };
- }
-
- void upload(gl::Context&) override {}
- void populateVertexVector(const GeometryTileFeature&) override {};
+ Range<float> getVertexSizeData(const GeometryTileFeature&) override { return { 0.0f, 0.0f }; };
- UniformValues uniformValues(float currentZoom) const override {
+ ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override {
float size = layoutSize;
bool isZoomConstant = !(coveringRanges || function);
if (coveringRanges) {
@@ -174,28 +167,20 @@ public:
const Range<float>& zoomLevels = std::get<0>(*coveringRanges);
const Range<float>& sizeLevels = std::get<1>(*coveringRanges);
float t = util::clamp(
- util::interpolationFactor(*functionInterpolationBase, zoomLevels, currentZoom),
+ function->interpolationFactor(zoomLevels, currentZoom),
0.0f, 1.0f
);
size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min);
} else if (function) {
size = function->evaluate(currentZoom);
}
-
- return UniformValues {
- uniforms::u_is_size_zoom_constant::Value{ isZoomConstant },
- uniforms::u_is_size_feature_constant::Value{ true },
- uniforms::u_size_t::Value{ 0.0f }, // unused
- uniforms::u_size::Value{ size },
- uniforms::u_layout_size::Value{ layoutSize }
- };
+
+ const float unused = 0.0f;
+ return { isZoomConstant, true, unused, size, layoutSize };
}
float layoutSize;
- // used for exponential functions
optional<std::tuple<Range<float>, Range<float>>> coveringRanges;
- optional<float> functionInterpolationBase;
- // used for interval functions
optional<style::CameraFunction<float>> function;
};
@@ -210,104 +195,51 @@ public:
defaultValue(defaultValue_) {
}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::binding(*buffer, 0, 1) };
- }
-
- void populateVertexVector(const GeometryTileFeature& feature) override {
- const auto sizeVertex = Vertex {
- {{
- static_cast<uint16_t>(function.evaluate(feature, defaultValue) * 10)
- }}
- };
-
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
+ Range<float> getVertexSizeData(const GeometryTileFeature& feature) override {
+ const float size = function.evaluate(feature, defaultValue);
+ return { size, size };
};
- UniformValues uniformValues(float) const override {
- return UniformValues {
- uniforms::u_is_size_zoom_constant::Value{ true },
- uniforms::u_is_size_feature_constant::Value{ false },
- uniforms::u_size_t::Value{ 0.0f }, // unused
- uniforms::u_size::Value{ 0.0f }, // unused
- uniforms::u_layout_size::Value{ 0.0f } // unused
- };
- }
-
- void upload(gl::Context& context) override {
- buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
+ ZoomEvaluatedSize evaluateForZoom(float) const override {
+ const float unused = 0.0f;
+ return { true, false, unused, unused, unused };
}
- const style::SourceFunction<float>& function;
+ style::SourceFunction<float> function;
const float defaultValue;
-
- VertexVector vertices;
- optional<VertexBuffer> buffer;
};
class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder {
public:
- using Vertex = SymbolSizeAttributes::Vertex;
- using VertexVector = gl::VertexVector<Vertex>;
- using VertexBuffer = gl::VertexBuffer<Vertex>;
CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction<float>& function_, const float defaultValue_)
: function(function_),
defaultValue(defaultValue_),
layoutZoom(tileZoom + 1),
- coveringZoomStops(function.stops.match(
- [&] (const auto& stops) {
- return getCoveringStops(stops, tileZoom, tileZoom + 1); }))
+ coveringZoomStops(function.getCoveringStops(tileZoom, tileZoom + 1))
{}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::binding(*buffer, 0) };
- }
-
- void populateVertexVector(const GeometryTileFeature& feature) override {
- const auto sizeVertex = Vertex {
- {{
- static_cast<uint16_t>(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10),
- static_cast<uint16_t>(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10),
- static_cast<uint16_t>(function.evaluate(layoutZoom, feature, defaultValue) * 10)
- }}
+ Range<float> getVertexSizeData(const GeometryTileFeature& feature) override {
+ return {
+ function.evaluate(coveringZoomStops.min, feature, defaultValue),
+ function.evaluate(coveringZoomStops.max, feature, defaultValue)
};
-
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
};
- UniformValues uniformValues(float currentZoom) const override {
+ ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override {
float sizeInterpolationT = util::clamp(
- util::interpolationFactor(1.0f, coveringZoomStops, currentZoom),
+ function.interpolationFactor(coveringZoomStops, currentZoom),
0.0f, 1.0f
);
- return UniformValues {
- uniforms::u_is_size_zoom_constant::Value{ false },
- uniforms::u_is_size_feature_constant::Value{ false },
- uniforms::u_size_t::Value{ sizeInterpolationT },
- uniforms::u_size::Value{ 0.0f }, // unused
- uniforms::u_layout_size::Value{ 0.0f } // unused
- };
- }
-
- void upload(gl::Context& context) override {
- buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
+ const float unused = 0.0f;
+ return { false, false, sizeInterpolationT, unused, unused };
}
- const style::CompositeFunction<float>& function;
+ style::CompositeFunction<float> function;
const float defaultValue;
float layoutZoom;
Range<float> coveringZoomStops;
-
- VertexVector vertices;
- optional<VertexBuffer> buffer;
};
@@ -321,7 +253,7 @@ public:
using LayoutAttributes = LayoutAttrs;
using LayoutVertex = typename LayoutAttributes::Vertex;
- using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>;
+ using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, gl::ConcatenateAttributes<SymbolDynamicLayoutAttributes, SymbolOpacityAttributes>>;
using PaintProperties = PaintProps;
using PaintPropertyBinders = typename PaintProperties::Binders;
@@ -352,29 +284,49 @@ public:
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
- UniformValues&& uniformValues,
+ 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 gl::SegmentVector<Attributes>& segments,
+ const SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
const typename PaintProperties::PossiblyEvaluated& currentProperties,
- float currentZoom) {
- program.draw(
- context,
- std::move(drawMode),
- std::move(depthMode),
- std::move(stencilMode),
- std::move(colorMode),
- uniformValues
- .concat(symbolSizeBinder.uniformValues(currentZoom))
- .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)),
- LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(symbolSizeBinder.attributeBindings())
- .concat(paintPropertyBinders.attributeBindings(currentProperties)),
- indexBuffer,
- segments
- );
+ float currentZoom,
+ 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);
+
+ if (vertexArrayIt == segment.vertexArrays.end()) {
+ vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first;
+ }
+
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ allUniformValues,
+ vertexArrayIt->second,
+ Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset),
+ indexBuffer,
+ segment.indexOffset,
+ segment.indexLength);
+ }
}
};
@@ -384,13 +336,19 @@ class SymbolIconProgram : public SymbolProgram<
SymbolLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
+ uniforms::u_label_plane_matrix,
+ uniforms::u_gl_coord_matrix,
uniforms::u_extrude_scale,
uniforms::u_texsize,
- uniforms::u_zoom,
- uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture,
- uniforms::u_is_text>,
+ uniforms::u_fade_change,
+ uniforms::u_is_text,
+ uniforms::u_camera_to_center_distance,
+ uniforms::u_pitch,
+ uniforms::u_pitch_with_map,
+ uniforms::u_max_camera_distance,
+ uniforms::u_rotate_symbol,
+ uniforms::u_aspect_ratio>,
style::IconPaintProperties>
{
public:
@@ -400,8 +358,10 @@ public:
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile&,
- const TransformState&);
+ const TransformState&,
+ const float symbolFadeChange);
};
enum class SymbolSDFPart {
@@ -416,18 +376,20 @@ class SymbolSDFProgram : public SymbolProgram<
SymbolLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
+ uniforms::u_label_plane_matrix,
+ uniforms::u_gl_coord_matrix,
uniforms::u_extrude_scale,
uniforms::u_texsize,
- uniforms::u_zoom,
- uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_gamma_scale,
+ uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
- uniforms::u_bearing,
- uniforms::u_aspect_ratio,
uniforms::u_pitch_with_map,
+ uniforms::u_max_camera_distance,
+ uniforms::u_rotate_symbol,
+ uniforms::u_aspect_ratio,
+ uniforms::u_gamma_scale,
uniforms::u_is_halo>,
PaintProperties>
{
@@ -437,18 +399,20 @@ public:
SymbolLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
+ uniforms::u_label_plane_matrix,
+ uniforms::u_gl_coord_matrix,
uniforms::u_extrude_scale,
uniforms::u_texsize,
- uniforms::u_zoom,
- uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_gamma_scale,
+ uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
- uniforms::u_bearing,
+ uniforms::u_pitch_with_map,
+ uniforms::u_max_camera_distance,
+ uniforms::u_rotate_symbol,
uniforms::u_aspect_ratio,
- uniforms::u_pitch_with_map,
+ uniforms::u_gamma_scale,
uniforms::u_is_halo>,
PaintProperties>;
@@ -462,8 +426,10 @@ public:
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile&,
const TransformState&,
+ const float SymbolFadeChange,
const SymbolSDFPart);
};
diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp
index f1b2c2fb54..184f42e504 100644
--- a/src/mbgl/programs/uniforms.hpp
+++ b/src/mbgl/programs/uniforms.hpp
@@ -14,6 +14,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(Color, u_color);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_blur);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_zoom);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_collision_y_stretch);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius);
@@ -33,6 +34,9 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_gapwidth);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_offset);
MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world);
MBGL_DEFINE_UNIFORM_SCALAR(Size, u_texsize);
+MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_camera_to_center_distance);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_fade_change);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale);
@@ -47,6 +51,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_mix);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_image);
+MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels);
diff --git a/src/mbgl/renderer/backend_scope.cpp b/src/mbgl/renderer/backend_scope.cpp
new file mode 100644
index 0000000000..fafeaabb39
--- /dev/null
+++ b/src/mbgl/renderer/backend_scope.cpp
@@ -0,0 +1,66 @@
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+static util::ThreadLocal<BackendScope> currentScope;
+
+BackendScope::BackendScope(RendererBackend& backend_, ScopeType scopeType_)
+ : priorScope(currentScope.get()),
+ nextScope(nullptr),
+ backend(backend_),
+ scopeType(scopeType_) {
+ if (priorScope) {
+ assert(priorScope->nextScope == nullptr);
+ priorScope->nextScope = this;
+ priorScope->deactivate();
+ }
+
+ activate();
+
+ currentScope.set(this);
+}
+
+BackendScope::~BackendScope() {
+ assert(nextScope == nullptr);
+ deactivate();
+
+ if (priorScope) {
+ priorScope->activate();
+ currentScope.set(priorScope);
+ assert(priorScope->nextScope == this);
+ priorScope->nextScope = nullptr;
+ } else {
+ currentScope.set(nullptr);
+ }
+}
+
+void BackendScope::activate() {
+ if (scopeType == ScopeType::Explicit &&
+ !(priorScope && this->backend == priorScope->backend) &&
+ !(nextScope && this->backend == nextScope->backend)) {
+ // Only activate when set to Explicit and
+ // only once per RenderBackend
+ backend.activate();
+ activated = true;
+ }
+}
+
+void BackendScope::deactivate() {
+ if (activated &&
+ !(nextScope && this->backend == nextScope->backend)) {
+ // Only deactivate when set to Explicit and
+ // only once per RenderBackend
+ backend.deactivate();
+ activated = false;
+ }
+}
+
+bool BackendScope::exists() {
+ return currentScope.get();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index 6c391cf014..9af511a03e 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -1,33 +1,26 @@
#pragma once
-#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/renderer/render_layer.hpp>
#include <atomic>
-#include <string>
-#include <unordered_map>
namespace mbgl {
-class Painter;
-class PaintParameters;
-class RenderTile;
-
namespace gl {
class Context;
} // namespace gl
-namespace style {
-class Layer;
-} // namespace style
+class RenderLayer;
class Bucket : private util::noncopyable {
public:
Bucket() = default;
virtual ~Bucket() = default;
+ // Feature geometries are also used to populate the feature index.
+ // Obtaining these is a costly operation, so we do it only once, and
+ // pass-by-const-ref the geometries as a second parameter.
virtual void addFeature(const GeometryTileFeature&,
const GeometryCollection&) {};
@@ -35,10 +28,6 @@ public:
// this only happens once when the bucket is being rendered for the first time.
virtual void upload(gl::Context&) = 0;
- // Every time this bucket is getting rendered, this function is called. This happens either
- // once or twice (for Opaque and Transparent render passes).
- virtual void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) = 0;
-
virtual bool hasData() const = 0;
virtual float getQueryRadius(const RenderLayer&) const {
@@ -46,7 +35,7 @@ public:
};
bool needsUpload() const {
- return !uploaded;
+ return hasData() && !uploaded;
}
protected:
diff --git a/src/mbgl/renderer/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp
index 8b5743d500..d23f0861f4 100644
--- a/src/mbgl/renderer/buckets/circle_bucket.cpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.cpp
@@ -1,6 +1,5 @@
#include <mbgl/renderer/buckets/circle_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/circle_program.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/renderer/layers/render_circle_layer.hpp>
@@ -34,13 +33,6 @@ void CircleBucket::upload(gl::Context& context) {
uploaded = true;
}
-void CircleBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderCircle(parameters, *this, *layer.as<RenderCircleLayer>(), tile);
-}
-
bool CircleBucket::hasData() const {
return !segments.empty();
}
@@ -57,7 +49,7 @@ void CircleBucket::addFeature(const GeometryTileFeature& feature,
// Do not include points that are outside the tile boundaries.
// Include all points in Still mode. You need to include points from
// neighbouring tiles so that they are not clipped at tile boundaries.
- if ((mode != MapMode::Still) &&
+ if ((mode == MapMode::Continuous) &&
(x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue;
if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
diff --git a/src/mbgl/renderer/buckets/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp
index b048fd7675..78b6351bcb 100644
--- a/src/mbgl/renderer/buckets/circle_bucket.hpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.hpp
@@ -5,7 +5,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/circle_program.hpp>
#include <mbgl/style/layers/circle_layer_properties.hpp>
@@ -23,13 +23,11 @@ public:
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
-
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<CircleLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<CircleAttributes> segments;
+ SegmentVector<CircleAttributes> segments;
optional<gl::VertexBuffer<CircleLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
diff --git a/src/mbgl/renderer/buckets/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp
index acfe15d2fb..53c751c443 100644
--- a/src/mbgl/renderer/buckets/debug_bucket.cpp
+++ b/src/mbgl/renderer/buckets/debug_bucket.cpp
@@ -1,7 +1,7 @@
#include <mbgl/renderer/buckets/debug_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/geometry/debug_font_data.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/string.hpp>
#include <cmath>
diff --git a/src/mbgl/renderer/buckets/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp
index 756e58a6de..fc3128e944 100644
--- a/src/mbgl/renderer/buckets/debug_bucket.hpp
+++ b/src/mbgl/renderer/buckets/debug_bucket.hpp
@@ -33,7 +33,7 @@ public:
const optional<Timestamp> expires;
const MapDebugOptions debugMode;
- gl::SegmentVector<DebugAttributes> segments;
+ SegmentVector<DebugAttributes> segments;
optional<gl::VertexBuffer<DebugLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Lines>> indexBuffer;
};
diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp
index 042d7b7506..110db887a1 100644
--- a/src/mbgl/renderer/buckets/fill_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/fill_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
@@ -121,13 +120,6 @@ void FillBucket::upload(gl::Context& context) {
uploaded = true;
}
-void FillBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderFill(parameters, *this, *layer.as<RenderFillLayer>(), tile);
-}
-
bool FillBucket::hasData() const {
return !triangleSegments.empty() || !lineSegments.empty();
}
diff --git a/src/mbgl/renderer/buckets/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp
index 421d8b332b..a50e1971f5 100644
--- a/src/mbgl/renderer/buckets/fill_bucket.hpp
+++ b/src/mbgl/renderer/buckets/fill_bucket.hpp
@@ -4,7 +4,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/style/layers/fill_layer_properties.hpp>
@@ -23,15 +23,14 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<FillLayoutVertex> vertices;
gl::IndexVector<gl::Lines> lines;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<FillAttributes> lineSegments;
- gl::SegmentVector<FillAttributes> triangleSegments;
+ SegmentVector<FillAttributes> lineSegments;
+ SegmentVector<FillAttributes> triangleSegments;
optional<gl::VertexBuffer<FillLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Lines>> lineIndexBuffer;
diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
index f61f1d1549..5e2c937091 100644
--- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
@@ -102,13 +101,17 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
const auto d2 = convertPoint<double>(p2);
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()) {
+ edgeDistance = 0;
+ }
vertices.emplace_back(
FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance));
vertices.emplace_back(
FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance));
- edgeDistance += util::dist<int16_t>(d1, d2);
+ edgeDistance += dist;
vertices.emplace_back(
FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance));
@@ -154,13 +157,6 @@ void FillExtrusionBucket::upload(gl::Context& context) {
uploaded = true;
}
-void FillExtrusionBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile);
-}
-
bool FillExtrusionBucket::hasData() const {
return !triangleSegments.empty();
}
diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
index c54805d743..d57265ab16 100644
--- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
@@ -4,7 +4,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
@@ -21,13 +21,12 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<FillExtrusionLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<FillExtrusionAttributes> triangleSegments;
+ SegmentVector<FillExtrusionAttributes> triangleSegments;
optional<gl::VertexBuffer<FillExtrusionLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp
index 3af3cd63d3..a96518df38 100644
--- a/src/mbgl/renderer/buckets/line_bucket.cpp
+++ b/src/mbgl/renderer/buckets/line_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/line_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
@@ -16,7 +15,8 @@ LineBucket::LineBucket(const BucketParameters& parameters,
const std::vector<const RenderLayer*>& layers,
const style::LineLayoutProperties::Unevaluated& layout_)
: layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))),
- overscaling(parameters.tileID.overscaleFactor()) {
+ overscaling(parameters.tileID.overscaleFactor()),
+ zoom(parameters.tileID.overscaledZ) {
for (const auto& layer : layers) {
paintPropertyBinders.emplace(
std::piecewise_construct,
@@ -30,7 +30,7 @@ LineBucket::LineBucket(const BucketParameters& parameters,
void LineBucket::addFeature(const GeometryTileFeature& feature,
const GeometryCollection& geometryCollection) {
for (auto& line : geometryCollection) {
- addGeometry(line, feature.getType());
+ addGeometry(line, feature);
}
for (auto& pair : paintPropertyBinders) {
@@ -63,7 +63,8 @@ const float LINE_DISTANCE_SCALE = 1.0 / 2.0;
// The maximum line distance, in tile units, that fits in the buffer.
const float MAX_LINE_DISTANCE = std::pow(2, LINE_DISTANCE_BUFFER_BITS) / LINE_DISTANCE_SCALE;
-void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType type) {
+void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const GeometryTileFeature& feature) {
+ const FeatureType type = feature.getType();
const std::size_t len = [&coordinates] {
std::size_t l = coordinates.size();
// If the line has duplicate vertices at the end, adjust length to remove them.
@@ -87,7 +88,9 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType
return;
}
- const float miterLimit = layout.get<LineJoin>() == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>());
+ const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature);
+
+ const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>());
const double sharpCornerOffset = SHARP_CORNER_OFFSET * (float(util::EXTENT) / (util::tileSize * overscaling));
@@ -194,7 +197,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType
// The join if a middle vertex, otherwise the cap
const bool middleVertex = prevCoordinate && nextCoordinate;
- LineJoinType currentJoin = layout.get<LineJoin>();
+ LineJoinType currentJoin = joinType;
const LineCapType currentCap = nextCoordinate ? beginCap : endCap;
if (middleVertex) {
@@ -398,7 +401,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate,
Point<double> extrude = normal;
if (endLeft)
extrude = extrude - (util::perp(normal) * endLeft);
- vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -409,7 +412,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate,
extrude = normal * -1.0;
if (endRight)
extrude = extrude - (util::perp(normal) * endRight);
- vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, true, -endRight, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -434,7 +437,7 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex,
std::size_t startVertex,
std::vector<TriangleElement>& triangleStore) {
Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0);
- vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, false, lineTurnsLeft, 0, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -458,13 +461,6 @@ void LineBucket::upload(gl::Context& context) {
uploaded = true;
}
-void LineBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderLine(parameters, *this, *layer.as<RenderLineLayer>(), tile);
-}
-
bool LineBucket::hasData() const {
return !segments.empty();
}
diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp
index 34d8935953..4fb77c377e 100644
--- a/src/mbgl/renderer/buckets/line_bucket.hpp
+++ b/src/mbgl/renderer/buckets/line_bucket.hpp
@@ -4,7 +4,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/line_program.hpp>
#include <mbgl/style/layers/line_layer_properties.hpp>
@@ -26,7 +26,6 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
@@ -34,7 +33,7 @@ public:
gl::VertexVector<LineLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<LineAttributes> segments;
+ SegmentVector<LineAttributes> segments;
optional<gl::VertexBuffer<LineLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
@@ -42,7 +41,7 @@ public:
std::map<std::string, LineProgram::PaintPropertyBinders> paintPropertyBinders;
private:
- void addGeometry(const GeometryCoordinates&, FeatureType);
+ void addGeometry(const GeometryCoordinates&, const GeometryTileFeature&);
struct TriangleElement {
TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
@@ -60,6 +59,7 @@ private:
std::ptrdiff_t e3;
const uint32_t overscaling;
+ const float zoom;
float getLineWidth(const RenderLineLayer& layer) const;
};
diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp
index 49ec0065c3..a66dd42d74 100644
--- a/src/mbgl/renderer/buckets/raster_bucket.cpp
+++ b/src/mbgl/renderer/buckets/raster_bucket.cpp
@@ -1,22 +1,28 @@
#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/renderer/layers/render_raster_layer.hpp>
#include <mbgl/programs/raster_program.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/gl/context.hpp>
-#include <mbgl/renderer/render_tile.hpp>
namespace mbgl {
using namespace style;
-RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) {
+RasterBucket::RasterBucket(PremultipliedImage&& image_) {
+ image = std::make_shared<PremultipliedImage>(std::move(image_));
+}
+
+RasterBucket::RasterBucket(std::shared_ptr<PremultipliedImage> image_): image(image_) {
+
}
void RasterBucket::upload(gl::Context& context) {
+ if (!hasData()) {
+ return;
+ }
if (!texture) {
- texture = context.createTexture(image);
+ texture = context.createTexture(*image);
}
- if (!vertices.empty()) {
+ if (!segments.empty()) {
vertexBuffer = context.createVertexBuffer(std::move(vertices));
indexBuffer = context.createIndexBuffer(std::move(indices));
}
@@ -32,22 +38,73 @@ void RasterBucket::clear() {
uploaded = false;
}
-void RasterBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile.matrix, false);
+
+void RasterBucket::setImage(std::shared_ptr<PremultipliedImage> image_) {
+ image = std::move(image_);
+ texture = {};
+ uploaded = false;
}
-void RasterBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const mat4& matrix) {
- painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), matrix, true);
+void RasterBucket::setMask(TileMask&& mask_) {
+ if (mask == mask_) {
+ return;
+ }
+
+ mask = std::move(mask_);
+ clear();
+
+ if (mask == TileMask{ { 0, 0, 0 } }) {
+ // We want to render the full tile, and keeping the segments/vertices/indices empty means
+ // using the global shared buffers for covering the entire tile.
+ return;
+ }
+
+ // Create a new segment so that we will upload (empty) buffers even when there is nothing to
+ // draw for this tile.
+ segments.emplace_back(0, 0);
+
+ constexpr const uint16_t vertexLength = 4;
+
+ // Create the vertex buffer for the specified tile mask.
+ for (const auto& id : mask) {
+ // Create a quad for every masked tile.
+ const int32_t vertexExtent = util::EXTENT >> id.z;
+
+ const Point<int16_t> tlVertex = { static_cast<int16_t>(id.x * vertexExtent),
+ static_cast<int16_t>(id.y * vertexExtent) };
+ const Point<int16_t> brVertex = { static_cast<int16_t>(tlVertex.x + vertexExtent),
+ static_cast<int16_t>(tlVertex.y + vertexExtent) };
+
+ if (segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
+ // Move to a new segments because the old one can't hold the geometry.
+ segments.emplace_back(vertices.vertexSize(), indices.indexSize());
+ }
+
+ vertices.emplace_back(
+ RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) }));
+ vertices.emplace_back(
+ RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) }));
+ vertices.emplace_back(
+ RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) }));
+ vertices.emplace_back(
+ RasterProgram::layoutVertex({ brVertex.x, brVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(brVertex.y) }));
+
+ auto& segment = segments.back();
+ assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
+ const uint16_t offset = segment.vertexLength;
+
+ // 0, 1, 2
+ // 1, 2, 3
+ indices.emplace_back(offset, offset + 1, offset + 2);
+ indices.emplace_back(offset + 1, offset + 2, offset + 3);
+
+ segment.vertexLength += vertexLength;
+ segment.indexLength += 6;
+ }
}
bool RasterBucket::hasData() const {
- return true;
+ return !!image;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp
index b5cf7997d5..3800eadec8 100644
--- a/src/mbgl/renderer/buckets/raster_bucket.hpp
+++ b/src/mbgl/renderer/buckets/raster_bucket.hpp
@@ -5,6 +5,7 @@
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/programs/raster_program.hpp>
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/optional.hpp>
@@ -13,25 +14,25 @@ namespace mbgl {
class RasterBucket : public Bucket {
public:
- RasterBucket(UnassociatedImage&&);
+ RasterBucket(PremultipliedImage&&);
+ RasterBucket(std::shared_ptr<PremultipliedImage>);
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
- void render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const mat4& matrix);
bool hasData() const override;
void clear();
- UnassociatedImage image;
+ void setImage(std::shared_ptr<PremultipliedImage>);
+ void setMask(TileMask&&);
+
+ std::shared_ptr<PremultipliedImage> image;
optional<gl::Texture> texture;
+ TileMask mask{ { 0, 0, 0 } };
// Bucket specific vertices are used for Image Sources only
// Raster Tile Sources use the default buffers from Painter
gl::VertexVector<RasterLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> indices;
- gl::SegmentVector<RasterAttributes> segments;
+ SegmentVector<RasterAttributes> segments;
optional<gl::VertexBuffer<RasterLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index cbddade899..60e8a0b504 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -17,10 +16,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons_,
- bool iconsNeedLinear_)
+ bool iconsNeedLinear_,
+ bool sortFeaturesByY_,
+ const std::vector<SymbolInstance>&& symbolInstances_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
+ sortFeaturesByY(sortFeaturesByY_),
+ symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
@@ -37,40 +40,88 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
void SymbolBucket::upload(gl::Context& context) {
if (hasTextData()) {
- text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
- text.indexBuffer = context.createIndexBuffer(std::move(text.triangles));
- textSizeBinder->upload(context);
+ if (!staticUploaded) {
+ text.indexBuffer = context.createIndexBuffer(std::move(text.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw);
+ text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
+ } else if (!sortUploaded) {
+ context.updateIndexBuffer(*text.indexBuffer, std::move(text.triangles));
+ }
+
+ if (!dynamicUploaded) {
+ text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw);
+ }
+ if (!placementChangesUploaded) {
+ if (!text.opacityVertexBuffer) {
+ text.opacityVertexBuffer = context.createVertexBuffer(std::move(text.opacityVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*text.opacityVertexBuffer, std::move(text.opacityVertices));
+ }
+ }
}
if (hasIconData()) {
- icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
- icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles));
- iconSizeBinder->upload(context);
+ if (!staticUploaded) {
+ icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw);
+ icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
+ } else if (!sortUploaded) {
+ context.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles));
+ }
+ if (!dynamicUploaded) {
+ icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw);
+ }
+ if (!placementChangesUploaded) {
+ if (!icon.opacityVertexBuffer) {
+ icon.opacityVertexBuffer = context.createVertexBuffer(std::move(icon.opacityVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*icon.opacityVertexBuffer, std::move(icon.opacityVertices));
+ }
+ }
}
- if (!collisionBox.vertices.empty()) {
- collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices));
- collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines));
+ if (hasCollisionBoxData()) {
+ if (!staticUploaded) {
+ collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines));
+ collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices));
+ }
+ if (!placementChangesUploaded) {
+ if (!collisionBox.dynamicVertexBuffer) {
+ collisionBox.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionBox.dynamicVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*collisionBox.dynamicVertexBuffer, std::move(collisionBox.dynamicVertices));
+ }
+ }
}
-
- for (auto& pair : paintPropertyBinders) {
- pair.second.first.upload(context);
- pair.second.second.upload(context);
+
+ if (hasCollisionCircleData()) {
+ if (!staticUploaded) {
+ collisionCircle.indexBuffer = context.createIndexBuffer(std::move(collisionCircle.triangles));
+ collisionCircle.vertexBuffer = context.createVertexBuffer(std::move(collisionCircle.vertices));
+ }
+ if (!placementChangesUploaded) {
+ if (!collisionCircle.dynamicVertexBuffer) {
+ collisionCircle.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionCircle.dynamicVertices), gl::BufferUsage::StreamDraw);
+ } else {
+ context.updateVertexBuffer(*collisionCircle.dynamicVertexBuffer, std::move(collisionCircle.dynamicVertices));
+ }
+ }
}
+ if (!staticUploaded) {
+ for (auto& pair : paintPropertyBinders) {
+ pair.second.first.upload(context);
+ pair.second.second.upload(context);
+ }
+ }
+
uploaded = true;
-}
-
-void SymbolBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile);
+ staticUploaded = true;
+ placementChangesUploaded = true;
+ dynamicUploaded = true;
+ sortUploaded = true;
}
bool SymbolBucket::hasData() const {
- assert(false); // Should be calling SymbolLayout::has{Text,Icon,CollisonBox}Data() instead.
- return false;
+ return hasTextData() || hasIconData() || hasCollisionBoxData();
}
bool SymbolBucket::hasTextData() const {
@@ -85,4 +136,83 @@ bool SymbolBucket::hasCollisionBoxData() const {
return !collisionBox.segments.empty();
}
+bool SymbolBucket::hasCollisionCircleData() const {
+ return !collisionCircle.segments.empty();
+}
+
+void SymbolBucket::updateOpacity() {
+ placementChangesUploaded = false;
+ uploaded = false;
+}
+
+void addPlacedSymbol(gl::IndexVector<gl::Triangles>& triangles, const PlacedSymbol& placedSymbol) {
+ auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4;
+ for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
+ triangles.emplace_back(vertexIndex + 0, vertexIndex + 1, vertexIndex + 2);
+ triangles.emplace_back(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
+ }
+}
+
+void SymbolBucket::sortFeatures(const float angle) {
+ if (!sortFeaturesByY) {
+ return;
+ }
+
+ if (sortedAngle && *sortedAngle == angle) {
+ return;
+ }
+
+ sortedAngle = angle;
+
+ // The current approach to sorting doesn't sort across segments so don't try.
+ // Sorting within segments separately seemed not to be worth the complexity.
+ if (text.segments.size() > 1 || icon.segments.size() > 1) {
+ return;
+ }
+
+ sortUploaded = false;
+ uploaded = false;
+
+ // If the symbols are allowed to overlap sort them by their vertical screen position.
+ // The index array buffer is rewritten to reference the (unchanged) vertices in the
+ // sorted order.
+
+ // To avoid sorting the actual symbolInstance array we sort an array of indexes.
+ std::vector<size_t> symbolInstanceIndexes;
+ symbolInstanceIndexes.reserve(symbolInstances.size());
+ for (size_t i = 0; i < symbolInstances.size(); i++) {
+ symbolInstanceIndexes.push_back(i);
+ }
+
+ const float sin = std::sin(angle);
+ const float cos = std::cos(angle);
+
+ std::sort(symbolInstanceIndexes.begin(), symbolInstanceIndexes.end(), [sin, cos, this](size_t &aIndex, size_t &bIndex) {
+ const SymbolInstance& a = symbolInstances[aIndex];
+ const SymbolInstance& b = symbolInstances[bIndex];
+ const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y;
+ const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y;
+ return aRotated != bRotated ?
+ aRotated < bRotated :
+ a.index > b.index;
+ });
+
+ text.triangles.clear();
+ icon.triangles.clear();
+
+ for (auto i : symbolInstanceIndexes) {
+ const SymbolInstance& symbolInstance = symbolInstances[i];
+
+ if (symbolInstance.placedTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
+ }
+ if (symbolInstance.placedVerticalTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
+ }
+ if (symbolInstance.placedIconIndex) {
+ addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
+ }
+ }
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index 002b6e28b3..4abea90508 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -4,17 +4,39 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/symbol_program.hpp>
#include <mbgl/programs/collision_box_program.hpp>
#include <mbgl/text/glyph_range.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/layout/symbol_feature.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
#include <vector>
namespace mbgl {
+class PlacedSymbol {
+public:
+ PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
+ std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
+ anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
+ lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0)
+ {
+ }
+ Point<float> anchorPoint;
+ uint16_t segment;
+ float lowerSize;
+ float upperSize;
+ std::array<float, 2> lineOffset;
+ WritingModeType writingModes;
+ GeometryCoordinates line;
+ std::vector<float> tileDistances;
+ std::vector<float> glyphOffsets;
+ bool hidden;
+ size_t vertexStartIndex;
+};
+
class SymbolBucket : public Bucket {
public:
SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated,
@@ -23,18 +45,33 @@ public:
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons,
- bool iconsNeedLinear);
+ bool iconsNeedLinear,
+ bool sortFeaturesByY,
+ const std::vector<SymbolInstance>&&);
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
bool hasCollisionBoxData() const;
+ bool hasCollisionCircleData() const;
+
+ void updateOpacity();
+ void sortFeatures(const float angle);
const style::SymbolLayoutProperties::PossiblyEvaluated layout;
const bool sdfIcons;
const bool iconsNeedLinear;
+ const bool sortFeaturesByY;
+
+ optional<float> sortedAngle;
+
+ bool staticUploaded = false;
+ bool placementChangesUploaded = false;
+ bool dynamicUploaded = false;
+ bool sortUploaded = false;
+
+ std::vector<SymbolInstance> symbolInstances;
std::map<std::string, std::pair<
SymbolIconProgram::PaintPropertyBinders,
@@ -44,10 +81,15 @@ public:
struct TextBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
+ gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<SymbolTextAttributes> segments;
+ SegmentVector<SymbolTextAttributes> segments;
+ std::vector<PlacedSymbol> placedSymbols;
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
+ optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
+ optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} text;
@@ -55,22 +97,39 @@ public:
struct IconBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
+ gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<SymbolIconAttributes> segments;
+ SegmentVector<SymbolIconAttributes> segments;
+ std::vector<PlacedSymbol> placedSymbols;
PremultipliedImage atlasImage;
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
+ optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
+ optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} icon;
- struct CollisionBoxBuffer {
- gl::VertexVector<CollisionBoxVertex> vertices;
- gl::IndexVector<gl::Lines> lines;
- gl::SegmentVector<CollisionBoxAttributes> segments;
+ struct CollisionBuffer {
+ gl::VertexVector<CollisionBoxLayoutAttributes::Vertex> vertices;
+ gl::VertexVector<CollisionBoxDynamicAttributes::Vertex> dynamicVertices;
+ SegmentVector<CollisionBoxProgram::Attributes> segments;
+
+ optional<gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>> vertexBuffer;
+ optional<gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>> dynamicVertexBuffer;
+ };
- optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer;
+ struct CollisionBoxBuffer : public CollisionBuffer {
+ gl::IndexVector<gl::Lines> lines;
optional<gl::IndexBuffer<gl::Lines>> indexBuffer;
} collisionBox;
+
+ struct CollisionCircleBuffer : public CollisionBuffer {
+ gl::IndexVector<gl::Triangles> triangles;
+ optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
+ } collisionCircle;
+
+ uint32_t bucketInstanceId = 0;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/cross_faded_property_evaluator.cpp b/src/mbgl/renderer/cross_faded_property_evaluator.cpp
index ee3c86614f..4dff9dbf12 100644
--- a/src/mbgl/renderer/cross_faded_property_evaluator.cpp
+++ b/src/mbgl/renderer/cross_faded_property_evaluator.cpp
@@ -27,7 +27,10 @@ Faded<T> CrossFadedPropertyEvaluator<T>::calculate(const T& min, const T& mid, c
const float z = parameters.z;
const float fraction = z - std::floor(z);
const std::chrono::duration<float> d = parameters.defaultFadeDuration;
- const float t = std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f);
+ const float t =
+ d != std::chrono::duration<float>::zero()
+ ? std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f)
+ : 1.0f;
return z > parameters.zoomHistory.lastIntegerZoom
? Faded<T> { min, mid, 2.0f, 1.0f, fraction + (1.0f - fraction) * t }
diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp
deleted file mode 100644
index 869222b4eb..0000000000
--- a/src/mbgl/renderer/frame_history.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#include <mbgl/renderer/frame_history.hpp>
-#include <mbgl/math/minmax.hpp>
-#include <mbgl/gl/context.hpp>
-
-#include <cassert>
-
-namespace mbgl {
-
-FrameHistory::FrameHistory() {
- changeOpacities.fill(0);
- opacities.fill(0);
-}
-
-void FrameHistory::record(const TimePoint& now, float zoom, const Duration& duration) {
-
- int16_t zoomIndex = std::floor(zoom * 10.0);
-
- if (firstFrame) {
- changeTimes.fill(now);
-
- for (int16_t z = 0; z <= zoomIndex; z++) {
- opacities.data[z] = 255u;
- }
- firstFrame = false;
- }
-
- if (zoomIndex < previousZoomIndex) {
- for (int16_t z = zoomIndex + 1; z <= previousZoomIndex; z++) {
- changeTimes[z] = now;
- changeOpacities[z] = opacities.data[z];
- }
- } else {
- for (int16_t z = zoomIndex; z > previousZoomIndex; z--) {
- changeTimes[z] = now;
- changeOpacities[z] = opacities.data[z];
- }
- }
-
- for (int16_t z = 0; z <= 255; z++) {
- const std::chrono::duration<float> timeDiff = now - changeTimes[z];
- const int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255;
- const uint8_t opacity = z <= zoomIndex
- ? util::min(255, changeOpacities[z] + opacityChange)
- : util::max(0, changeOpacities[z] - opacityChange);
- if (opacities.data[z] != opacity) {
- opacities.data[z] = opacity;
- dirty = true;
- }
- }
-
- if (zoomIndex != previousZoomIndex) {
- previousZoomIndex = zoomIndex;
- previousTime = now;
- }
-
- time = now;
-}
-
-bool FrameHistory::needsAnimation(const Duration& duration) const {
- return (time - previousTime) < duration;
-}
-
-void FrameHistory::upload(gl::Context& context, uint32_t unit) {
- if (!texture) {
- texture = context.createTexture(opacities, unit);
- } else if (dirty) {
- context.updateTexture(*texture, opacities, unit);
- }
- dirty = false;
-}
-
-void FrameHistory::bind(gl::Context& context, uint32_t unit) {
- upload(context, unit);
- context.bindTexture(*texture, unit);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp
deleted file mode 100644
index f2b11f5f41..0000000000
--- a/src/mbgl/renderer/frame_history.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include <array>
-
-#include <mbgl/util/platform.hpp>
-#include <mbgl/gl/texture.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/optional.hpp>
-
-namespace mbgl {
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class FrameHistory {
-public:
- FrameHistory();
- void record(const TimePoint&, float zoom, const Duration&);
-
- bool needsAnimation(const Duration&) const;
- void bind(gl::Context&, uint32_t);
- void upload(gl::Context&, uint32_t);
-
-private:
- std::array<TimePoint, 256> changeTimes;
- std::array<uint8_t, 256> changeOpacities;
- AlphaImage opacities{ { 256, 1 } };
-
- int16_t previousZoomIndex = 0;
- TimePoint previousTime;
- TimePoint time;
- bool firstFrame = true;
- bool dirty = true;
-
- mbgl::optional<gl::Texture> texture;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp
index d0a106ede6..2ef6be0c4f 100644
--- a/src/mbgl/renderer/image_manager.cpp
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -4,12 +4,23 @@
namespace mbgl {
-void ImageManager::onSpriteLoaded() {
- loaded = true;
- for (const auto& entry : requestors) {
- notify(*entry.first, entry.second);
+void ImageManager::setLoaded(bool loaded_) {
+ if (loaded == loaded_) {
+ return;
+ }
+
+ loaded = loaded_;
+
+ if (loaded) {
+ for (const auto& entry : requestors) {
+ notify(*entry.first, entry.second);
+ }
+ requestors.clear();
}
- requestors.clear();
+}
+
+bool ImageManager::isLoaded() const {
+ return loaded;
}
void ImageManager::addImage(Immutable<style::Image::Impl> image_) {
@@ -28,6 +39,13 @@ void ImageManager::removeImage(const std::string& id) {
auto it = patterns.find(id);
if (it != patterns.end()) {
+ // Clear pattern from the atlas image.
+ const uint32_t x = it->second.bin->x;
+ const uint32_t y = it->second.bin->y;
+ const uint32_t w = it->second.bin->w;
+ const uint32_t h = it->second.bin->h;
+ PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
+
shelfPack.unref(*it->second.bin);
patterns.erase(it);
}
@@ -41,23 +59,23 @@ const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
return nullptr;
}
-void ImageManager::getImages(ImageRequestor& requestor, ImageDependencies dependencies) {
+void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) {
// If the sprite has been loaded, or if all the icon dependencies are already present
// (i.e. if they've been addeded via runtime styling), then notify the requestor immediately.
// Otherwise, delay notification until the sprite is loaded. At that point, if any of the
// dependencies are still unavailable, we'll just assume they are permanently missing.
bool hasAllDependencies = true;
if (!isLoaded()) {
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : pair.first) {
if (images.find(dependency) == images.end()) {
hasAllDependencies = false;
}
}
}
if (isLoaded() || hasAllDependencies) {
- notify(requestor, dependencies);
+ notify(requestor, std::move(pair));
} else {
- requestors.emplace(&requestor, std::move(dependencies));
+ requestors.emplace(&requestor, std::move(pair));
}
}
@@ -65,17 +83,17 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) {
requestors.erase(&requestor);
}
-void ImageManager::notify(ImageRequestor& requestor, const ImageDependencies& dependencies) const {
+void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const {
ImageMap response;
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : pair.first) {
auto it = images.find(dependency);
if (it != images.end()) {
response.emplace(*it);
}
}
- requestor.onImagesAvailable(response);
+ requestor.onImagesAvailable(response, pair.second);
}
void ImageManager::dumpDebugLogs() const {
diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp
index 9a9a4ce997..f72ba9fb53 100644
--- a/src/mbgl/renderer/image_manager.hpp
+++ b/src/mbgl/renderer/image_manager.hpp
@@ -21,7 +21,7 @@ class Context;
class ImageRequestor {
public:
virtual ~ImageRequestor() = default;
- virtual void onImagesAvailable(ImageMap) = 0;
+ virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0;
};
/*
@@ -39,11 +39,8 @@ public:
ImageManager();
~ImageManager();
- void onSpriteLoaded();
-
- bool isLoaded() const {
- return loaded;
- }
+ void setLoaded(bool);
+ bool isLoaded() const;
void dumpDebugLogs() const;
@@ -53,15 +50,15 @@ public:
void updateImage(Immutable<style::Image::Impl>);
void removeImage(const std::string&);
- void getImages(ImageRequestor&, ImageDependencies);
+ void getImages(ImageRequestor&, ImageRequestPair&&);
void removeRequestor(ImageRequestor&);
private:
- void notify(ImageRequestor&, const ImageDependencies&) const;
+ void notify(ImageRequestor&, const ImageRequestPair&) const;
bool loaded = false;
- std::unordered_map<ImageRequestor*, ImageDependencies> requestors;
+ std::unordered_map<ImageRequestor*, ImageRequestPair> requestors;
ImageMap images;
// Pattern stuff
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
index 702977cd23..9fddba3f74 100644
--- a/src/mbgl/renderer/layers/render_background_layer.cpp
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -1,9 +1,17 @@
#include <mbgl/renderer/layers/render_background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/util/tile_cover.hpp>
namespace mbgl {
+using namespace style;
+
RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl)
: RenderLayer(style::LayerType::Background, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -34,4 +42,74 @@ bool RenderBackgroundLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
+ // Note that for bottommost layers without a pattern, the background color is drawn with
+ // glClear rather than this method.
+
+ style::FillPaintProperties::PossiblyEvaluated properties;
+ properties.get<FillPattern>() = evaluated.get<BackgroundPattern>();
+ properties.get<FillOpacity>() = { evaluated.get<BackgroundOpacity>() };
+ properties.get<FillColor>() = { evaluated.get<BackgroundColor>() };
+
+ const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
+ 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);
+
+ if (!imagePosA || !imagePosB)
+ return;
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
+ parameters.programs.fillPattern.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ FillPatternUniforms::values(
+ parameters.matrixForTile(tileID),
+ parameters.context.viewport.getCurrentValue().size,
+ parameters.imageManager.getPixelSize(),
+ *imagePosA,
+ *imagePosB,
+ evaluated.get<BackgroundPattern>(),
+ tileID,
+ parameters.state
+ ),
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ } else {
+ for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
+ parameters.programs.fill.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ }
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp
index 0fba3d2bb1..a619670ee4 100644
--- a/src/mbgl/renderer/layers/render_background_layer.hpp
+++ b/src/mbgl/renderer/layers/render_background_layer.hpp
@@ -14,6 +14,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
index 51a5ecd7d6..fe2e7cd42d 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.cpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -1,5 +1,10 @@
#include <mbgl/renderer/layers/render_circle_layer.hpp>
#include <mbgl/renderer/buckets/circle_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/circle_program.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +12,8 @@
namespace mbgl {
+using namespace style;
+
RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl)
: RenderLayer(style::LayerType::Circle, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -40,6 +47,52 @@ bool RenderCircleLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ const bool scaleWithMap = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map;
+ const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map;
+
+ for (const RenderTile& tile : renderTiles) {
+ 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(),
+ CircleProgram::UniformValues {
+ uniforms::u_matrix::Value{
+ tile.translatedMatrix(evaluated.get<CircleTranslate>(),
+ evaluated.get<CircleTranslateAnchor>(),
+ parameters.state)
+ },
+ uniforms::u_scale_with_map::Value{ scaleWithMap },
+ uniforms::u_extrude_scale::Value{ pitchWithMap
+ ? std::array<float, 2> {{
+ tile.id.pixelsToTileUnits(1, parameters.state.getZoom()),
+ tile.id.pixelsToTileUnits(1, parameters.state.getZoom()) }}
+ : parameters.pixelsToGLUnits },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
+ uniforms::u_pitch_with_map::Value{ pitchWithMap }
+ },
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+}
+
bool RenderCircleLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp
index 4ae7399ad1..f31715f98f 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.hpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.hpp
@@ -14,6 +14,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
index 4d6084075d..7ece3970da 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.cpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -1,9 +1,10 @@
#include <mbgl/renderer/layers/render_custom_layer.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/bucket.hpp>
#include <mbgl/style/layers/custom_layer_impl.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/map/backend_scope.hpp>
namespace mbgl {
@@ -15,8 +16,12 @@ RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
RenderCustomLayer::~RenderCustomLayer() {
assert(BackendScope::exists());
- if (initialized && impl().deinitializeFn) {
- impl().deinitializeFn(impl().context);
+ if (initialized) {
+ if (contextDestroyed && impl().contextLostFn ) {
+ impl().contextLostFn(impl().context);
+ } else if (!contextDestroyed && impl().deinitializeFn) {
+ impl().deinitializeFn(impl().context);
+ }
}
}
@@ -37,21 +42,21 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&,
return nullptr;
}
-void RenderCustomLayer::render(Painter& painter, PaintParameters& paintParameters, RenderSource*) {
+void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) {
if (!initialized) {
assert(impl().initializeFn);
impl().initializeFn(impl().context);
initialized = true;
}
- gl::Context& context = painter.context;
- const TransformState& state = painter.state;
+ gl::Context& context = paintParameters.context;
+ const TransformState& state = paintParameters.state;
// Reset GL state to a known state so the CustomLayer always has a clean slate.
- context.vertexArrayObject = 0;
- context.setDepthMode(painter.depthModeForSublayer(0, gl::DepthMode::ReadOnly));
+ context.bindVertexArray = 0;
+ context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly));
context.setStencilMode(gl::StencilMode::disabled());
- context.setColorMode(painter.colorModeForRenderPass());
+ context.setColorMode(paintParameters.colorModeForRenderPass());
CustomLayerRenderParameters parameters;
@@ -69,7 +74,7 @@ void RenderCustomLayer::render(Painter& painter, PaintParameters& paintParameter
// Reset the view back to our original one, just in case the CustomLayer changed
// the viewport or Framebuffer.
- paintParameters.view.bind();
+ paintParameters.backend.bind();
context.setDirtyState();
}
diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp
index dd52d315cf..32ed9da8da 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.hpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -15,12 +15,17 @@ public:
bool hasTransition() const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final;
- void render(Painter&, PaintParameters&, RenderSource*) final;
+ void render(PaintParameters&, RenderSource*) final;
const style::CustomLayer::Impl& impl() const;
+ void markContextDestroyed() {
+ contextDestroyed = true;
+ };
+
private:
bool initialized = false;
+ bool contextDestroyed = false;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
index cd69316670..fbd6160e8a 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -1,5 +1,13 @@
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +15,8 @@
namespace mbgl {
+using namespace style;
+
RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl)
: RenderLayer(style::LayerType::FillExtrusion, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -27,14 +37,116 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters
void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluated = unevaluated.evaluate(parameters);
- passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent
- : RenderPass::None;
+ passes = (evaluated.get<style::FillExtrusionOpacity>() > 0)
+ ? (RenderPass::Translucent | RenderPass::Pass3D)
+ : RenderPass::None;
}
bool RenderFillExtrusionLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ if (parameters.pass == RenderPass::Pass3D) {
+ const auto& size = parameters.staticData.backendSize;
+
+ if (!renderTexture || renderTexture->getSize() != size) {
+ renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer);
+ }
+
+ renderTexture->bind();
+
+ optional<float> depthClearValue = {};
+ if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0;
+ // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass.
+ parameters.staticData.depthRenderbuffer->shouldClear(false);
+
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
+
+ if (evaluated.get<FillExtrusionPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusion.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.state, parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ } else {
+ optional<ImagePosition> imagePosA =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
+ optional<ImagePosition> imagePosB =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusionPattern.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionPatternUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB,
+ evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state,
+ -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
+ parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ }
+
+ } else if (parameters.pass == RenderPass::Translucent) {
+ parameters.context.bindTexture(renderTexture->getTexture());
+
+ const auto& size = parameters.staticData.backendSize;
+
+ mat4 viewportMat;
+ matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
+
+ const Properties<>::PossiblyEvaluated properties;
+
+ parameters.programs.extrusionTexture.draw(
+ parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ ExtrusionTextureProgram::UniformValues{
+ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } },
+ parameters.staticData.extrusionTextureVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.extrusionTextureSegments,
+ ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
+ parameters.state.getZoom(), getID());
+ }
+}
+
bool RenderFillExtrusionLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
index 1a55b56836..838494cf91 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -3,6 +3,8 @@
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/offscreen_texture.hpp>
namespace mbgl {
@@ -14,6 +16,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
@@ -29,6 +32,8 @@ public:
style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
const style::FillExtrusionLayer::Impl& impl() const;
+
+ optional<OffscreenTexture> renderTexture;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
index f1c7e97067..22cb9563c1 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -1,5 +1,11 @@
#include <mbgl/renderer/layers/render_fill_layer.hpp>
#include <mbgl/renderer/buckets/fill_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +13,8 @@
namespace mbgl {
+using namespace style;
+
RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl)
: RenderLayer(style::LayerType::Fill, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -50,6 +58,132 @@ bool RenderFillLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (evaluated.get<FillPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program,
+ const auto& drawMode,
+ const auto& depthMode,
+ const auto& indexBuffer,
+ const auto& segments) {
+ program.get(evaluated).draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{
+ tile.translatedMatrix(evaluated.get<FillTranslate>(),
+ evaluated.get<FillTranslateAnchor>(),
+ parameters.state)
+ },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ *bucket.vertexBuffer,
+ indexBuffer,
+ segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ // Only draw the fill when it's opaque and we're drawing opaque fragments,
+ // or when it's translucent and we're drawing translucent fragments.
+ if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f
+ && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) {
+ draw(parameters.programs.fill,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque
+ ? gl::DepthMode::ReadWrite
+ : gl::DepthMode::ReadOnly),
+ *bucket.triangleIndexBuffer,
+ bucket.triangleSegments);
+ }
+
+ if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) {
+ draw(parameters.programs.fillOutline,
+ gl::Lines{ 2.0f },
+ parameters.depthModeForSublayer(
+ unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0,
+ gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
+ }
+ }
+ } else {
+ if (parameters.pass != RenderPass::Translucent) {
+ return;
+ }
+
+ optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillPattern>().from);
+ optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program,
+ const auto& drawMode,
+ const auto& depthMode,
+ const auto& indexBuffer,
+ const auto& segments) {
+ program.get(evaluated).draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ FillPatternUniforms::values(
+ tile.translatedMatrix(evaluated.get<FillTranslate>(),
+ evaluated.get<FillTranslateAnchor>(),
+ parameters.state),
+ parameters.context.viewport.getCurrentValue().size,
+ parameters.imageManager.getPixelSize(),
+ *imagePosA,
+ *imagePosB,
+ evaluated.get<FillPattern>(),
+ tile.id,
+ parameters.state
+ ),
+ *bucket.vertexBuffer,
+ indexBuffer,
+ segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ draw(parameters.programs.fillPattern,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite),
+ *bucket.triangleIndexBuffer,
+ bucket.triangleSegments);
+
+ if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) {
+ draw(parameters.programs.fillOutlinePattern,
+ gl::Lines { 2.0f },
+ parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
+ }
+ }
+ }
+}
+
bool RenderFillLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp
index 1960fb653f..a51865698f 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.hpp
@@ -14,6 +14,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index c27e5ea990..1b4a1c0ff7 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -1,5 +1,12 @@
#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/buckets/line_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/line_program.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +14,8 @@
namespace mbgl {
+using namespace style;
+
RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl)
: RenderLayer(style::LayerType::Line, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -43,6 +52,82 @@ bool RenderLineLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)));
+ LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program, auto&& uniformValues) {
+ program.get(evaluated).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(),
+ getID()
+ );
+ };
+
+ if (!evaluated.get<LineDasharray>().from.empty()) {
+ const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
+ ? LinePatternCap::Round : LinePatternCap::Square;
+ LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap);
+ LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap);
+
+ parameters.lineAtlas.bind(parameters.context, 0);
+
+ draw(parameters.programs.lineSDF,
+ LineSDFProgram::uniformValues(
+ evaluated,
+ parameters.pixelRatio,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ posA,
+ posB,
+ parameters.lineAtlas.getSize().width));
+
+ } else if (!evaluated.get<LinePattern>().from.empty()) {
+ optional<ImagePosition> posA = parameters.imageManager.getPattern(evaluated.get<LinePattern>().from);
+ optional<ImagePosition> posB = parameters.imageManager.getPattern(evaluated.get<LinePattern>().to);
+
+ if (!posA || !posB)
+ return;
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ draw(parameters.programs.linePattern,
+ LinePatternProgram::uniformValues(
+ evaluated,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ parameters.imageManager.getPixelSize(),
+ *posA,
+ *posB));
+
+ } else {
+ draw(parameters.programs.line,
+ LineProgram::uniformValues(
+ evaluated,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits));
+ }
+ }
+}
+
optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) {
if (offset == 0) return {};
diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp
index 77551b6b7c..8bf7e2329d 100644
--- a/src/mbgl/renderer/layers/render_line_layer.hpp
+++ b/src/mbgl/renderer/layers/render_line_layer.hpp
@@ -23,6 +23,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
index 84e27a3895..06616d90e5 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.cpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -1,14 +1,18 @@
#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/gl/context.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/tile/tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/raster_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/raster_layer_impl.hpp>
namespace mbgl {
+using namespace style;
+
RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl)
: RenderLayer(style::LayerType::Raster, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -37,12 +41,113 @@ bool RenderRasterLayer::hasTransition() const {
return unevaluated.hasTransition();
}
-void RenderRasterLayer::render(Painter& painter, PaintParameters& parameters, RenderSource* source) {
- RenderLayer::render(painter, parameters, source);
- if (renderTiles.empty()) {
- RenderImageSource* imageSource = source->as<RenderImageSource>();
- if (imageSource) {
- imageSource->render(painter, parameters, *this);
+static float saturationFactor(float saturation) {
+ if (saturation > 0) {
+ return 1 - 1 / (1.001 - saturation);
+ } else {
+ return -saturation;
+ }
+}
+
+static float contrastFactor(float contrast) {
+ if (contrast > 0) {
+ return 1 / (1 - contrast);
+ } else {
+ return 1 + contrast;
+ }
+}
+
+static std::array<float, 3> spinWeights(float spin) {
+ spin *= util::DEG2RAD;
+ float s = std::sin(spin);
+ float c = std::cos(spin);
+ std::array<float, 3> spin_weights = {{
+ (2 * c + 1) / 3,
+ (-std::sqrt(3.0f) * s - c + 1) / 3,
+ (std::sqrt(3.0f) * s - c + 1) / 3
+ }};
+ return spin_weights;
+}
+
+void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source) {
+ if (parameters.pass != RenderPass::Translucent)
+ return;
+
+ 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(),
+ RasterProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_image0::Value{ 0 },
+ uniforms::u_image1::Value{ 1 },
+ uniforms::u_opacity::Value{ evaluated.get<RasterOpacity>() },
+ uniforms::u_fade_t::Value{ 1 },
+ uniforms::u_brightness_low::Value{ evaluated.get<RasterBrightnessMin>() },
+ uniforms::u_brightness_high::Value{ evaluated.get<RasterBrightnessMax>() },
+ uniforms::u_saturation_factor::Value{ saturationFactor(evaluated.get<RasterSaturation>()) },
+ uniforms::u_contrast_factor::Value{ contrastFactor(evaluated.get<RasterContrast>()) },
+ uniforms::u_spin_weights::Value{ spinWeights(evaluated.get<RasterHueRotate>()) },
+ uniforms::u_buffer_scale::Value{ 1.0f },
+ uniforms::u_scale_parent::Value{ 1.0f },
+ uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
+ },
+ vertexBuffer,
+ indexBuffer,
+ segments,
+ RasterProgram::PaintPropertyBinders { evaluated, 0 },
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ if (RenderImageSource* imageSource = source->as<RenderImageSource>()) {
+ if (imageSource->isEnabled() && imageSource->isLoaded() && !imageSource->bucket->needsUpload()) {
+ RasterBucket& bucket = *imageSource->bucket;
+
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
+
+ for (auto matrix_ : imageSource->matrices) {
+ draw(matrix_,
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments);
+ }
+ }
+ } else {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl)));
+ RasterBucket& bucket = *reinterpret_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl));
+
+ if (!bucket.hasData())
+ continue;
+
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
+
+ if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) {
+ // Draw only the parts of the tile that aren't drawn by another tile in the layer.
+ draw(tile.matrix,
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments);
+ } else {
+ // Draw the full tile.
+ draw(tile.matrix,
+ parameters.staticData.rasterVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.rasterSegments);
+ }
}
}
}
diff --git a/src/mbgl/renderer/layers/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp
index ce46152a95..87de316f7c 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.hpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.hpp
@@ -15,7 +15,7 @@ public:
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
- void render(Painter&, PaintParameters&, RenderSource*) override;
+ void render(PaintParameters&, RenderSource*) override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
index 6540fc9612..04fcb2c3ab 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.cpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -1,13 +1,27 @@
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/layout/symbol_layout.hpp>
-#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+#include <mbgl/programs/collision_box_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/geometry_tile.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/util/math.hpp>
+
+#include <cmath>
namespace mbgl {
+using namespace style;
+
RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl)
: RenderLayer(style::LayerType::Symbol, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -55,6 +69,224 @@ bool RenderSymbolLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl)));
+ SymbolBucket& bucket = *reinterpret_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl));
+
+ const auto& layout = bucket.layout;
+
+ auto draw = [&] (auto& program,
+ auto&& uniformValues,
+ const auto& buffers,
+ const auto& symbolSizeBinder,
+ const SymbolPropertyValues& values_,
+ const auto& binders,
+ const auto& paintProperties)
+ {
+ program.get(paintProperties).draw(
+ parameters.context,
+ gl::Triangles(),
+ values_.pitchAlignment == AlignmentType::Map
+ ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)
+ : 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(),
+ getID()
+ );
+ };
+
+ assert(dynamic_cast<GeometryTile*>(&tile.tile));
+ GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile);
+
+ if (bucket.hasIconData()) {
+ auto values = iconPropertyValues(layout);
+ auto paintPropertyValues = iconPaintProperties();
+
+ const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line &&
+ layout.get<IconRotationAlignment>() == AlignmentType::Map;
+
+ if (alongLine) {
+ reprojectLineLabels(bucket.icon.dynamicVertices,
+ bucket.icon.placedSymbols,
+ tile.matrix,
+ values,
+ tile,
+ *bucket.iconSizeBinder,
+ parameters.state);
+
+ parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices));
+ }
+
+ const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear;
+ const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0;
+
+ parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0,
+ bucket.sdfIcons || parameters.state.isChanging() || iconScaled || iconTransformed
+ ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
+
+ const Size texsize = geometryTile.iconAtlasTexture->size;
+
+ if (bucket.sdfIcons) {
+ if (values.hasHalo) {
+ draw(parameters.programs.symbolIconSDF,
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+
+ if (values.hasFill) {
+ draw(parameters.programs.symbolIconSDF,
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+ } else {
+ draw(parameters.programs.symbolIcon,
+ SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+ }
+
+ if (bucket.hasTextData()) {
+ parameters.context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear);
+
+ auto values = textPropertyValues(layout);
+ auto paintPropertyValues = textPaintProperties();
+
+ const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line &&
+ layout.get<TextRotationAlignment>() == AlignmentType::Map;
+
+ if (alongLine) {
+ reprojectLineLabels(bucket.text.dynamicVertices,
+ bucket.text.placedSymbols,
+ tile.matrix,
+ values,
+ tile,
+ *bucket.textSizeBinder,
+ parameters.state);
+
+ parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
+ }
+
+ const Size texsize = geometryTile.glyphAtlasTexture->size;
+
+ if (values.hasHalo) {
+ draw(parameters.programs.symbolGlyph,
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
+ bucket.text,
+ bucket.textSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).second,
+ paintPropertyValues);
+ }
+
+ if (values.hasFill) {
+ draw(parameters.programs.symbolGlyph,
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
+ bucket.text,
+ bucket.textSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).second,
+ paintPropertyValues);
+ }
+ }
+
+ if (bucket.hasCollisionBoxData()) {
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+
+ auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
+ auto scale = std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ));
+ std::array<float,2> extrudeScale =
+ {{
+ parameters.pixelsToGLUnits[0] / (pixelRatio * scale),
+ parameters.pixelsToGLUnits[1] / (pixelRatio * scale)
+
+ }};
+ parameters.programs.collisionBox.draw(
+ parameters.context,
+ gl::Lines { 1.0f },
+ gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ CollisionBoxProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::u_extrude_scale::Value{ extrudeScale },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }
+ },
+ *bucket.collisionBox.vertexBuffer,
+ *bucket.collisionBox.dynamicVertexBuffer,
+ *bucket.collisionBox.indexBuffer,
+ bucket.collisionBox.segments,
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ if (bucket.hasCollisionCircleData()) {
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+
+ auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
+ auto scale = std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ));
+ std::array<float,2> extrudeScale =
+ {{
+ parameters.pixelsToGLUnits[0] / (pixelRatio * scale),
+ parameters.pixelsToGLUnits[1] / (pixelRatio * scale)
+
+ }};
+
+ parameters.programs.collisionCircle.draw(
+ parameters.context,
+ gl::Triangles(),
+ gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ CollisionBoxProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::u_extrude_scale::Value{ extrudeScale },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }
+ },
+ *bucket.collisionCircle.vertexBuffer,
+ *bucket.collisionCircle.dynamicVertexBuffer,
+ *bucket.collisionCircle.indexBuffer,
+ bucket.collisionCircle.segments,
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+
+ }
+ }
+}
+
style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const {
return style::IconPaintProperties::PossiblyEvaluated {
evaluated.get<style::IconOpacity>(),
@@ -82,25 +314,41 @@ style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProper
style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
return style::SymbolPropertyValues {
- layout_.get<style::IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment
+ layout_.get<style::IconPitchAlignment>(),
layout_.get<style::IconRotationAlignment>(),
+ layout_.get<style::IconKeepUpright>(),
evaluated.get<style::IconTranslate>(),
evaluated.get<style::IconTranslateAnchor>(),
evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 &&
evaluated.get<style::IconHaloWidth>().constantOr(1),
- evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0
+ evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0,
+ 10.0f
};
}
style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
+ // We hide line labels with viewport alignment as they move into the distance
+ // because the approximations we use for drawing their glyphs get progressively worse
+ // The "1.5" here means we start hiding them when the distance from the label
+ // to the camera is 50% greater than the distance from the center of the map
+ // to the camera. Depending on viewport properties, you might expect this to filter
+ // the top third of the screen at pitch 60, and do almost nothing at pitch 45
+ // "10" is effectively infinite at any pitch we support
+ const bool limitMaxDistance =
+ layout_.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line
+ && layout_.get<style::TextRotationAlignment>() == style::AlignmentType::Map
+ && layout_.get<style::TextPitchAlignment>() == style::AlignmentType::Viewport;
+
return style::SymbolPropertyValues {
layout_.get<style::TextPitchAlignment>(),
layout_.get<style::TextRotationAlignment>(),
+ layout_.get<style::TextKeepUpright>(),
evaluated.get<style::TextTranslate>(),
evaluated.get<style::TextTranslateAnchor>(),
evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 &&
evaluated.get<style::TextHaloWidth>().constantOr(1),
- evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0
+ evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0,
+ limitMaxDistance ? 1.5f : 10.0f
};
}
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp
index 2199103de2..83709b5122 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.hpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp
@@ -40,6 +40,7 @@ public:
// Layout
AlignmentType pitchAlignment;
AlignmentType rotationAlignment;
+ bool keepUpright;
// Paint
std::array<float, 2> translate;
@@ -47,6 +48,8 @@ public:
bool hasHalo;
bool hasFill;
+
+ float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else
};
} // namespace style
@@ -63,6 +66,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const;
style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const;
diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp
new file mode 100644
index 0000000000..58dd5597a5
--- /dev/null
+++ b/src/mbgl/renderer/paint_parameters.cpp
@@ -0,0 +1,96 @@
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+PaintParameters::PaintParameters(gl::Context& context_,
+ float pixelRatio_,
+ GLContextMode contextMode_,
+ RendererBackend& backend_,
+ const UpdateParameters& updateParameters,
+ const EvaluatedLight& evaluatedLight_,
+ RenderStaticData& staticData_,
+ ImageManager& imageManager_,
+ LineAtlas& lineAtlas_)
+ : context(context_),
+ backend(backend_),
+ state(updateParameters.transformState),
+ evaluatedLight(evaluatedLight_),
+ staticData(staticData_),
+ imageManager(imageManager_),
+ lineAtlas(lineAtlas_),
+ mapMode(updateParameters.mode),
+ debugOptions(updateParameters.debugOptions),
+ contextMode(contextMode_),
+ timePoint(updateParameters.timePoint),
+ pixelRatio(pixelRatio_),
+#ifndef NDEBUG
+ programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs)
+#else
+ programs(staticData_.programs)
+#endif
+{
+ // Update the default matrices to the current viewport dimensions.
+ state.getProjMatrix(projMatrix);
+
+ // Calculate a second projection matrix with the near plane clipped to 100 so as
+ // not to waste lots of depth buffer precision on very close empty space, for layer
+ // types (fill-extrusion) that use the depth buffer to emulate real-world space.
+ state.getProjMatrix(nearClippedProjMatrix, 100);
+
+ pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }};
+
+ if (state.getViewportMode() == ViewportMode::FlippedY) {
+ pixelsToGLUnits[1] *= -1;
+ }
+}
+
+mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) {
+ mat4 matrix;
+ state.matrixFor(matrix, tileID);
+ matrix::multiply(matrix, projMatrix, matrix);
+ return matrix;
+}
+
+gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const {
+ float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon;
+ float farDepth = nearDepth + depthRangeSize;
+ return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } };
+}
+
+gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const {
+ return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } };
+}
+
+gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const {
+ return gl::StencilMode {
+ gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) },
+ static_cast<int32_t>(id.reference.to_ulong()),
+ 0,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Replace
+ };
+}
+
+gl::ColorMode PaintParameters::colorModeForRenderPass() const {
+ if (debugOptions & MapDebugOptions::Overdraw) {
+ const float overdraw = 1.0f / 8.0f;
+ return gl::ColorMode {
+ gl::ColorMode::Add {
+ gl::ColorMode::ConstantColor,
+ gl::ColorMode::One
+ },
+ Color { overdraw, overdraw, overdraw, 0.0f },
+ gl::ColorMode::Mask { true, true, true, true }
+ };
+ } else if (pass == RenderPass::Translucent) {
+ return gl::ColorMode::alphaBlended();
+ } else {
+ return gl::ColorMode::unblended();
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp
index 213c01cfbd..5c934c2239 100644
--- a/src/mbgl/renderer/paint_parameters.hpp
+++ b/src/mbgl/renderer/paint_parameters.hpp
@@ -1,14 +1,78 @@
#pragma once
+#include <mbgl/renderer/render_pass.hpp>
+#include <mbgl/renderer/render_light.hpp>
+#include <mbgl/renderer/mode.hpp>
+#include <mbgl/map/mode.hpp>
+#include <mbgl/gl/depth_mode.hpp>
+#include <mbgl/gl/stencil_mode.hpp>
+#include <mbgl/gl/color_mode.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/algorithm/generate_clip_ids.hpp>
+
+#include <array>
+
namespace mbgl {
+class RendererBackend;
+class UpdateParameters;
+class RenderStaticData;
class Programs;
-class View;
+class TransformState;
+class ImageManager;
+class LineAtlas;
+class UnwrappedTileID;
class PaintParameters {
public:
+ PaintParameters(gl::Context&,
+ float pixelRatio,
+ GLContextMode,
+ RendererBackend&,
+ const UpdateParameters&,
+ const EvaluatedLight&,
+ RenderStaticData&,
+ ImageManager&,
+ LineAtlas&);
+
+ gl::Context& context;
+ RendererBackend& backend;
+
+ const TransformState& state;
+ const EvaluatedLight& evaluatedLight;
+
+ RenderStaticData& staticData;
+ ImageManager& imageManager;
+ LineAtlas& lineAtlas;
+
+ RenderPass pass = RenderPass::Opaque;
+ MapMode mapMode;
+ MapDebugOptions debugOptions;
+ GLContextMode contextMode;
+ TimePoint timePoint;
+
+ float pixelRatio;
+ std::array<float, 2> pixelsToGLUnits;
+ algorithm::ClipIDGenerator clipIDGenerator;
+
Programs& programs;
- View& view;
+
+ gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const;
+ gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const;
+ gl::StencilMode stencilModeForClipping(const ClipID&) const;
+ gl::ColorMode colorModeForRenderPass() const;
+
+ mat4 matrixForTile(const UnwrappedTileID&);
+
+ mat4 projMatrix;
+ mat4 nearClippedProjMatrix;
+
+ int numSublayers = 3;
+ uint32_t currentLayer;
+ float depthRangeSize;
+ const float depthEpsilon = 1.0f / (1 << 16);
+
+ float symbolFadeChange;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp
index f78147fc87..3a49882f12 100644
--- a/src/mbgl/renderer/paint_property_binder.hpp
+++ b/src/mbgl/renderer/paint_property_binder.hpp
@@ -3,6 +3,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/gl/attribute.hpp>
#include <mbgl/gl/uniform.hpp>
+#include <mbgl/gl/context.hpp>
#include <mbgl/util/type_list.hpp>
#include <mbgl/renderer/possibly_evaluated_property_value.hpp>
#include <mbgl/renderer/paint_property_statistics.hpp>
@@ -81,7 +82,7 @@ public:
virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) = 0;
virtual void upload(gl::Context& context) = 0;
- virtual AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
+ virtual optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
virtual float interpolationFactor(float currentZoom) const = 0;
virtual T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
@@ -103,8 +104,8 @@ public:
void populateVertexVector(const GeometryTileFeature&, std::size_t) override {}
void upload(gl::Context&) override {}
- AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override {
- return gl::DisabledAttribute();
+ optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override {
+ return {};
}
float interpolationFactor(float) const override {
@@ -147,9 +148,9 @@ public:
vertexBuffer = context.createVertexBuffer(std::move(vertexVector));
}
- AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
+ optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
if (currentValue.isConstant()) {
- return gl::DisabledAttribute();
+ return {};
} else {
return Attribute::binding(*vertexBuffer, 0, BaseAttribute::Dimensions);
}
@@ -189,11 +190,11 @@ public:
CompositeFunctionPaintPropertyBinder(style::CompositeFunction<T> function_, float zoom, T defaultValue_)
: function(std::move(function_)),
defaultValue(std::move(defaultValue_)),
- rangeOfCoveringRanges(function.rangeOfCoveringRanges({zoom, zoom + 1})) {
+ zoomRange({zoom, zoom + 1}) {
}
void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override {
- Range<T> range = function.evaluate(rangeOfCoveringRanges, feature, defaultValue);
+ Range<T> range = function.evaluate(zoomRange, feature, defaultValue);
this->statistics.add(range.min);
this->statistics.add(range.max);
AttributeValue value = zoomInterpolatedAttributeValue(
@@ -208,9 +209,9 @@ public:
vertexBuffer = context.createVertexBuffer(std::move(vertexVector));
}
- AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
+ optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
if (currentValue.isConstant()) {
- return gl::DisabledAttribute();
+ return {};
} else {
return Attribute::binding(*vertexBuffer, 0);
}
@@ -218,9 +219,9 @@ public:
float interpolationFactor(float currentZoom) const override {
if (function.useIntegerZoom) {
- return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom));
+ return function.interpolationFactor(zoomRange, std::floor(currentZoom));
} else {
- return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom);
+ return function.interpolationFactor(zoomRange, currentZoom);
}
}
@@ -236,8 +237,7 @@ public:
private:
style::CompositeFunction<T> function;
T defaultValue;
- using CoveringRanges = typename style::CompositeFunction<T>::CoveringRanges;
- Range<CoveringRanges> rangeOfCoveringRanges;
+ Range<float> zoomRange;
gl::VertexVector<Vertex> vertexVector;
optional<gl::VertexBuffer<Vertex>> vertexBuffer;
};
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
deleted file mode 100644
index 47db8254e2..0000000000
--- a/src/mbgl/renderer/painter.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_style.hpp>
-
-#include <mbgl/style/source.hpp>
-#include <mbgl/style/source_impl.hpp>
-
-#include <mbgl/map/view.hpp>
-
-#include <mbgl/util/logging.hpp>
-#include <mbgl/gl/debugging.hpp>
-
-#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/layers/render_custom_layer.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
-
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-
-#include <mbgl/programs/program_parameters.hpp>
-#include <mbgl/programs/programs.hpp>
-
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/mat3.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <mbgl/util/stopwatch.hpp>
-
-#include <cassert>
-#include <algorithm>
-#include <iostream>
-#include <unordered_set>
-
-namespace mbgl {
-
-using namespace style;
-
-static gl::VertexVector<FillLayoutVertex> tileVertices() {
- gl::VertexVector<FillLayoutVertex> result;
- result.emplace_back(FillProgram::layoutVertex({ 0, 0 }));
- result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 }));
- result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT }));
- result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT }));
- return result;
-}
-
-static gl::IndexVector<gl::Triangles> quadTriangleIndices() {
- gl::IndexVector<gl::Triangles> result;
- result.emplace_back(0, 1, 2);
- result.emplace_back(1, 2, 3);
- return result;
-}
-
-static gl::IndexVector<gl::LineStrip> tileLineStripIndices() {
- gl::IndexVector<gl::LineStrip> result;
- result.emplace_back(0);
- result.emplace_back(1);
- result.emplace_back(3);
- result.emplace_back(2);
- result.emplace_back(0);
- return result;
-}
-
-static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
- gl::VertexVector<RasterLayoutVertex> result;
- result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }));
- result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { 32767, 0 }));
- result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, 32767 }));
- result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 }));
- return result;
-}
-
-static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() {
- gl::VertexVector<ExtrusionTextureLayoutVertex> result;
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 }));
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 }));
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 }));
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 }));
- return result;
-}
-
-
-Painter::Painter(gl::Context& context_,
- const TransformState& state_,
- float pixelRatio,
- const optional<std::string>& programCacheDir)
- : context(context_),
- state(state_),
- tileVertexBuffer(context.createVertexBuffer(tileVertices())),
- rasterVertexBuffer(context.createVertexBuffer(rasterVertices())),
- extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())),
- quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())),
- tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())) {
-
- tileTriangleSegments.emplace_back(0, 0, 4, 6);
- tileBorderSegments.emplace_back(0, 0, 4, 5);
- rasterSegments.emplace_back(0, 0, 4, 6);
- extrusionTextureSegments.emplace_back(0, 0, 4, 6);
-
- programs = std::make_unique<Programs>(context,
- ProgramParameters{ pixelRatio, false, programCacheDir });
-#ifndef NDEBUG
- overdrawPrograms =
- std::make_unique<Programs>(context, ProgramParameters{ pixelRatio, true, programCacheDir });
-#endif
-}
-
-Painter::~Painter() = default;
-
-bool Painter::needsAnimation() const {
- return frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION);
-}
-
-void Painter::cleanup() {
- context.performCleanup();
-}
-
-void Painter::render(RenderStyle& style, const FrameData& frame_, View& view) {
- frame = frame_;
- if (frame.contextMode == GLContextMode::Shared) {
- context.setDirtyState();
- }
-
- PaintParameters parameters {
-#ifndef NDEBUG
- paintMode() == PaintMode::Overdraw ? *overdrawPrograms : *programs,
-#else
- *programs,
-#endif
- view
- };
-
- imageManager = style.imageManager.get();
- lineAtlas = style.lineAtlas.get();
-
- evaluatedLight = style.getRenderLight().getEvaluated();
-
- RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle());
- const std::vector<RenderItem>& order = renderData.order;
- const std::unordered_set<RenderSource*>& sources = renderData.sources;
-
- // Update the default matrices to the current viewport dimensions.
- state.getProjMatrix(projMatrix);
- // Calculate a second projection matrix with the near plane clipped to 100 so as
- // not to waste lots of depth buffer precision on very close empty space, for layer
- // types (fill-extrusion) that use the depth buffer to emulate real-world space.
- state.getProjMatrix(nearClippedProjMatrix, 100);
-
- pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }};
- if (state.getViewportMode() == ViewportMode::FlippedY) {
- pixelsToGLUnits[1] *= -1;
- }
-
- frameHistory.record(frame.timePoint, state.getZoom(),
- frame.mapMode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Milliseconds(0));
-
-
- // - UPLOAD PASS -------------------------------------------------------------------------------
- // Uploads all required buffers and images before we do any actual rendering.
- {
- MBGL_DEBUG_GROUP(context, "upload");
-
- imageManager->upload(context, 0);
- lineAtlas->upload(context, 0);
- frameHistory.upload(context, 0);
- }
-
- // - CLEAR -------------------------------------------------------------------------------------
- // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
- // tiles whatsoever.
- {
- MBGL_DEBUG_GROUP(context, "clear");
- view.bind();
- context.clear(paintMode() == PaintMode::Overdraw
- ? Color::black()
- : renderData.backgroundColor,
- 1.0f,
- 0);
- }
-
- // - CLIPPING MASKS ----------------------------------------------------------------------------
- // Draws the clipping masks to the stencil buffer.
- {
- MBGL_DEBUG_GROUP(context, "clip");
-
- // Update all clipping IDs.
- clipIDGenerator = algorithm::ClipIDGenerator();
- for (const auto& source : sources) {
- source->startRender(*this);
- }
-
- MBGL_DEBUG_GROUP(context, "clipping masks");
-
- for (const auto& stencil : clipIDGenerator.getStencils()) {
- MBGL_DEBUG_GROUP(context, std::string{ "mask: " } + util::toString(stencil.first));
- renderClippingMask(stencil.first, stencil.second);
- }
- }
-
-#if not MBGL_USE_GLES2 and not defined(NDEBUG)
- if (frame.debugOptions & MapDebugOptions::StencilClip) {
- renderClipMasks(parameters);
- return;
- }
-#endif
-
- // Actually render the layers
- if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
-
- depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon;
-
- // - OPAQUE PASS -------------------------------------------------------------------------------
- // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
- renderPass(parameters,
- RenderPass::Opaque,
- order.rbegin(), order.rend(),
- 0, 1);
-
- // - TRANSLUCENT PASS --------------------------------------------------------------------------
- // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
- renderPass(parameters,
- RenderPass::Translucent,
- order.begin(), order.end(),
- static_cast<uint32_t>(order.size()) - 1, -1);
-
- if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
-
- // - DEBUG PASS --------------------------------------------------------------------------------
- // Renders debug overlays.
- {
- MBGL_DEBUG_GROUP(context, "debug");
-
- // Finalize the rendering, e.g. by calling debug render calls per tile.
- // This guarantees that we have at least one function per tile called.
- // When only rendering layers via the stylesheet, it's possible that we don't
- // ever visit a tile during rendering.
- for (const auto& source : sources) {
- source->finishRender(*this);
- }
- }
-
-#if not MBGL_USE_GLES2 and not defined(NDEBUG)
- if (frame.debugOptions & MapDebugOptions::DepthBuffer) {
- renderDepthBuffer(parameters);
- }
-#endif
-
- // TODO: Find a better way to unbind VAOs after we're done with them without introducing
- // unnecessary bind(0)/bind(N) sequences.
- {
- MBGL_DEBUG_GROUP(context, "cleanup");
-
- context.activeTexture = 1;
- context.texture[1] = 0;
- context.activeTexture = 0;
- context.texture[0] = 0;
-
- context.vertexArrayObject = 0;
- }
-}
-
-template <class Iterator>
-void Painter::renderPass(PaintParameters& parameters,
- RenderPass pass_,
- Iterator it, Iterator end,
- uint32_t i, int8_t increment) {
- pass = pass_;
-
- MBGL_DEBUG_GROUP(context, pass == RenderPass::Opaque ? "opaque" : "translucent");
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s {", indent++ * 4, "",
- pass == RenderPass::Opaque ? "opaque" : "translucent");
- }
-
- for (; it != end; ++it, i += increment) {
- currentLayer = i;
-
- const auto& item = *it;
- const RenderLayer& layer = item.layer;
-
- if (!layer.hasRenderPass(pass))
- continue;
-
- if (layer.is<RenderBackgroundLayer>()) {
- MBGL_DEBUG_GROUP(context, "background");
- renderBackground(parameters, *layer.as<RenderBackgroundLayer>());
- } else if (layer.is<RenderFillExtrusionLayer>()) {
- const auto size = context.viewport.getCurrentValue().size;
-
- if (!extrusionTexture || extrusionTexture->getSize() != size) {
- extrusionTexture = OffscreenTexture(context, size, OffscreenTextureAttachment::Depth);
- }
-
- extrusionTexture->bind();
-
- context.setStencilMode(gl::StencilMode::disabled());
- context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadWrite));
- context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {});
-
- renderItem(parameters, item);
-
- parameters.view.bind();
- context.bindTexture(extrusionTexture->getTexture());
-
- mat4 viewportMat;
- matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
-
- const Properties<>::PossiblyEvaluated properties;
-
- parameters.programs.extrusionTexture.draw(
- context, gl::Triangles(), gl::DepthMode::disabled(), gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- ExtrusionTextureProgram::UniformValues{
- uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
- uniforms::u_image::Value{ 0 },
- uniforms::u_opacity::Value{ layer.as<RenderFillExtrusionLayer>()
- ->evaluated.get<FillExtrusionOpacity>() } },
- extrusionTextureVertexBuffer, quadTriangleIndexBuffer, extrusionTextureSegments,
- ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
- state.getZoom());
- } else {
- renderItem(parameters, item);
- }
- }
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
- }
-}
-
-void Painter::renderItem(PaintParameters& parameters, const RenderItem& item) {
- RenderLayer& layer = item.layer;
- MBGL_DEBUG_GROUP(context, layer.getID());
- layer.render(*this, parameters, item.source);
-}
-
-mat4 Painter::matrixForTile(const UnwrappedTileID& tileID) {
- mat4 matrix;
- state.matrixFor(matrix, tileID);
- matrix::multiply(matrix, projMatrix, matrix);
- return matrix;
-}
-
-gl::DepthMode Painter::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const {
- float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon;
- float farDepth = nearDepth + depthRangeSize;
- return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } };
-}
-
-gl::StencilMode Painter::stencilModeForClipping(const ClipID& id) const {
- return gl::StencilMode {
- gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) },
- static_cast<int32_t>(id.reference.to_ulong()),
- 0,
- gl::StencilMode::Keep,
- gl::StencilMode::Keep,
- gl::StencilMode::Replace
- };
-}
-
-gl::ColorMode Painter::colorModeForRenderPass() const {
- if (paintMode() == PaintMode::Overdraw) {
- const float overdraw = 1.0f / 8.0f;
- return gl::ColorMode {
- gl::ColorMode::Add {
- gl::ColorMode::ConstantColor,
- gl::ColorMode::One
- },
- Color { overdraw, overdraw, overdraw, 0.0f },
- gl::ColorMode::Mask { true, true, true, true }
- };
- } else if (pass == RenderPass::Translucent) {
- return gl::ColorMode::alphaBlended();
- } else {
- return gl::ColorMode::unblended();
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
deleted file mode 100644
index 7e966adefa..0000000000
--- a/src/mbgl/renderer/painter.hpp
+++ /dev/null
@@ -1,181 +0,0 @@
-#pragma once
-
-#include <mbgl/map/transform_state.hpp>
-
-#include <mbgl/tile/tile_id.hpp>
-
-#include <mbgl/renderer/frame_history.hpp>
-#include <mbgl/renderer/render_item.hpp>
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/renderer/render_light.hpp>
-
-#include <mbgl/gl/context.hpp>
-#include <mbgl/programs/debug_program.hpp>
-#include <mbgl/programs/program_parameters.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/programs/extrusion_texture_program.hpp>
-#include <mbgl/programs/raster_program.hpp>
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/offscreen_texture.hpp>
-
-#include <mbgl/algorithm/generate_clip_ids.hpp>
-
-#include <array>
-#include <vector>
-#include <set>
-#include <map>
-
-namespace mbgl {
-
-class RenderStyle;
-class RenderTile;
-class ImageManager;
-class View;
-class LineAtlas;
-struct FrameData;
-class Tile;
-
-class DebugBucket;
-class FillBucket;
-class FillExtrusionBucket;
-class LineBucket;
-class CircleBucket;
-class SymbolBucket;
-class RasterBucket;
-
-class RenderFillLayer;
-class RenderFillExtrusionLayer;
-class RenderLineLayer;
-class RenderCircleLayer;
-class RenderSymbolLayer;
-class RenderRasterLayer;
-class RenderBackgroundLayer;
-
-class Programs;
-class PaintParameters;
-class TilePyramid;
-
-struct ClipID;
-
-struct FrameData {
- TimePoint timePoint;
- float pixelRatio;
- MapMode mapMode;
- GLContextMode contextMode;
- MapDebugOptions debugOptions;
-};
-
-class Painter : private util::noncopyable {
-public:
- Painter(gl::Context&, const TransformState&, float pixelRatio, const optional<std::string>& programCacheDir);
- ~Painter();
-
- void render(RenderStyle&,
- const FrameData&,
- View&);
-
- void cleanup();
-
- void renderClippingMask(const UnwrappedTileID&, const ClipID&);
- void renderTileDebug(const RenderTile&);
- void renderTileDebug(const mat4& matrix);
- void renderFill(PaintParameters&, FillBucket&, const RenderFillLayer&, const RenderTile&);
- void renderFillExtrusion(PaintParameters&, FillExtrusionBucket&, const RenderFillExtrusionLayer&, const RenderTile&);
- void renderLine(PaintParameters&, LineBucket&, const RenderLineLayer&, const RenderTile&);
- void renderCircle(PaintParameters&, CircleBucket&, const RenderCircleLayer&, const RenderTile&);
- void renderSymbol(PaintParameters&, SymbolBucket&, const RenderSymbolLayer&, const RenderTile&);
- void renderRaster(PaintParameters&, RasterBucket&, const RenderRasterLayer&, const mat4&, bool useBucketBuffers /* = false */);
- void renderBackground(PaintParameters&, const RenderBackgroundLayer&);
-
- void renderItem(PaintParameters&, const RenderItem&);
-
-#ifndef NDEBUG
- // Renders tile clip boundaries, using stencil buffer to calculate fill color.
- void renderClipMasks(PaintParameters&);
- // Renders the depth buffer.
- void renderDepthBuffer(PaintParameters&);
-#endif
-
- bool needsAnimation() const;
-
- template <class Iterator>
- void renderPass(PaintParameters&,
- RenderPass,
- Iterator it, Iterator end,
- uint32_t i, int8_t increment);
-
- mat4 matrixForTile(const UnwrappedTileID&);
- gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const;
- gl::StencilMode stencilModeForClipping(const ClipID&) const;
- gl::ColorMode colorModeForRenderPass() const;
-
-#ifndef NDEBUG
- PaintMode paintMode() const {
- return frame.debugOptions & MapDebugOptions::Overdraw ? PaintMode::Overdraw
- : PaintMode::Regular;
- }
-#else
- PaintMode paintMode() const {
- return PaintMode::Regular;
- }
-#endif
-
- gl::Context& context;
-
- algorithm::ClipIDGenerator clipIDGenerator;
-
- mat4 projMatrix;
- mat4 nearClippedProjMatrix;
-
- std::array<float, 2> pixelsToGLUnits;
-
- const mat4 identityMatrix = []{
- mat4 identity;
- matrix::identity(identity);
- return identity;
- }();
-
- const TransformState& state;
-
- FrameData frame;
-
- int indent = 0;
-
- RenderPass pass = RenderPass::Opaque;
-
- int numSublayers = 3;
- uint32_t currentLayer;
- float depthRangeSize;
- const float depthEpsilon = 1.0f / (1 << 16);
-
- ImageManager* imageManager = nullptr;
- LineAtlas* lineAtlas = nullptr;
-
- optional<OffscreenTexture> extrusionTexture;
-
- EvaluatedLight evaluatedLight;
-
- FrameHistory frameHistory;
-
- std::unique_ptr<Programs> programs;
-#ifndef NDEBUG
- std::unique_ptr<Programs> overdrawPrograms;
-#endif
-
- gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer;
- gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer;
- gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer;
-
- gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer;
- gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer;
-
- gl::SegmentVector<FillAttributes> tileTriangleSegments;
- gl::SegmentVector<DebugAttributes> tileBorderSegments;
- gl::SegmentVector<RasterAttributes> rasterSegments;
- gl::SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_background.cpp b/src/mbgl/renderer/painters/painter_background.cpp
deleted file mode 100644
index 577d7d6cda..0000000000
--- a/src/mbgl/renderer/painters/painter_background.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/tile_cover.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderBackground(PaintParameters& parameters, const RenderBackgroundLayer& layer) {
- // Note that for bottommost layers without a pattern, the background color is drawn with
- // glClear rather than this method.
- const BackgroundPaintProperties::PossiblyEvaluated& background = layer.evaluated;
-
- style::FillPaintProperties::PossiblyEvaluated properties;
- properties.get<FillPattern>() = background.get<BackgroundPattern>();
- properties.get<FillOpacity>() = { background.get<BackgroundOpacity>() };
- properties.get<FillColor>() = { background.get<BackgroundColor>() };
-
- const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
-
- if (!background.get<BackgroundPattern>().to.empty()) {
- optional<ImagePosition> imagePosA = imageManager->getPattern(background.get<BackgroundPattern>().from);
- optional<ImagePosition> imagePosB = imageManager->getPattern(background.get<BackgroundPattern>().to);
-
- if (!imagePosA || !imagePosB)
- return;
-
- imageManager->bind(context, 0);
-
- for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
- parameters.programs.fillPattern.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillPatternUniforms::values(
- matrixForTile(tileID),
- context.viewport.getCurrentValue().size,
- imageManager->getPixelSize(),
- *imagePosA,
- *imagePosB,
- background.get<BackgroundPattern>(),
- tileID,
- state
- ),
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- }
- } else {
- for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
- parameters.programs.fill.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{ matrixForTile(tileID) },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- }
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_circle.cpp b/src/mbgl/renderer/painters/painter_circle.cpp
deleted file mode 100644
index 58e384979d..0000000000
--- a/src/mbgl/renderer/painters/painter_circle.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/circle_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_circle_layer.hpp>
-#include <mbgl/style/layers/circle_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/circle_program.hpp>
-#include <mbgl/gl/context.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderCircle(PaintParameters& parameters,
- CircleBucket& bucket,
- const RenderCircleLayer& layer,
- const RenderTile& tile) {
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- const CirclePaintProperties::PossiblyEvaluated& properties = layer.evaluated;
- const bool scaleWithMap = properties.get<CirclePitchScale>() == CirclePitchScaleType::Map;
-
- parameters.programs.circle.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- frame.mapMode == MapMode::Still
- ? stencilModeForClipping(tile.clip)
- : gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- CircleProgram::UniformValues {
- uniforms::u_matrix::Value{
- tile.translatedMatrix(properties.get<CircleTranslate>(),
- properties.get<CircleTranslateAnchor>(),
- state)
- },
- uniforms::u_scale_with_map::Value{ scaleWithMap },
- uniforms::u_extrude_scale::Value{ scaleWithMap
- ? std::array<float, 2> {{
- pixelsToGLUnits[0] * state.getCameraToCenterDistance(),
- pixelsToGLUnits[1] * state.getCameraToCenterDistance()
- }}
- : pixelsToGLUnits }
- },
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_clipping.cpp b/src/mbgl/renderer/painters/painter_clipping.cpp
deleted file mode 100644
index cad092594e..0000000000
--- a/src/mbgl/renderer/painters/painter_clipping.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/clip_id.hpp>
-
-namespace mbgl {
-
-void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& clip) {
- static const style::FillPaintProperties::PossiblyEvaluated properties {};
- static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
- programs->fill.get(properties).draw(
- context,
- gl::Triangles(),
- gl::DepthMode::disabled(),
- gl::StencilMode {
- gl::StencilMode::Always(),
- static_cast<int32_t>(clip.reference.to_ulong()),
- 0b11111111,
- gl::StencilMode::Keep,
- gl::StencilMode::Keep,
- gl::StencilMode::Replace
- },
- gl::ColorMode::disabled(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{ matrixForTile(tileID) },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_debug.cpp b/src/mbgl/renderer/painters/painter_debug.cpp
deleted file mode 100644
index ee76aee54c..0000000000
--- a/src/mbgl/renderer/painters/painter_debug.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/buckets/debug_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/map/view.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/gl/debugging.hpp>
-#include <mbgl/util/color.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderTileDebug(const RenderTile& renderTile) {
- if (frame.debugOptions == MapDebugOptions::NoDebug)
- return;
-
- MBGL_DEBUG_GROUP(context, std::string { "debug " } + util::toString(renderTile.id));
-
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
-
- auto draw = [&] (Color color, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, auto drawMode) {
- programs->debug.draw(
- context,
- drawMode,
- gl::DepthMode::disabled(),
- stencilModeForClipping(renderTile.clip),
- gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ renderTile.matrix },
- uniforms::u_color::Value{ color }
- },
- vertexBuffer,
- indexBuffer,
- segments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- };
-
- if (frame.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
- Tile& tile = renderTile.tile;
- if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
- tile.debugBucket->complete != tile.isComplete() ||
- !(tile.debugBucket->modified == tile.modified) ||
- !(tile.debugBucket->expires == tile.expires) ||
- tile.debugBucket->debugMode != frame.debugOptions) {
- tile.debugBucket = std::make_unique<DebugBucket>(
- tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
- tile.expires, frame.debugOptions, context);
- }
-
- draw(Color::white(),
- *tile.debugBucket->vertexBuffer,
- *tile.debugBucket->indexBuffer,
- tile.debugBucket->segments,
- gl::Lines { 4.0f * frame.pixelRatio });
-
- draw(Color::black(),
- *tile.debugBucket->vertexBuffer,
- *tile.debugBucket->indexBuffer,
- tile.debugBucket->segments,
- gl::Lines { 2.0f * frame.pixelRatio });
- }
-
- if (frame.debugOptions & MapDebugOptions::TileBorders) {
- draw(Color::red(),
- tileVertexBuffer,
- tileBorderIndexBuffer,
- tileBorderSegments,
- gl::LineStrip { 4.0f * frame.pixelRatio });
- }
-}
-
-void Painter::renderTileDebug(const mat4& matrix) {
- if (frame.debugOptions == MapDebugOptions::NoDebug)
- return;
-
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
-
- if (frame.debugOptions & MapDebugOptions::TileBorders) {
- programs->debug.draw(
- context,
- gl::LineStrip { 4.0f * frame.pixelRatio },
- gl::DepthMode::disabled(),
- gl::StencilMode::disabled(),
- gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ Color::red() }
- },
- tileVertexBuffer,
- tileBorderIndexBuffer,
- tileBorderSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- }
-}
-
-#ifndef NDEBUG
-void Painter::renderClipMasks(PaintParameters&) {
- context.setStencilMode(gl::StencilMode::disabled());
- context.setDepthMode(gl::DepthMode::disabled());
- context.setColorMode(gl::ColorMode::unblended());
- context.program = 0;
-
-#if not MBGL_USE_GLES2
- // Reset the value in case someone else changed it, or it's dirty.
- context.pixelTransferStencil = gl::value::PixelTransferStencil::Default;
-
- // Read the stencil buffer
- const auto viewport = context.viewport.getCurrentValue();
- auto image =
- context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false);
-
- // Scale the Stencil buffer to cover the entire color space.
- auto it = image.data.get();
- auto end = it + viewport.size.width * viewport.size.height;
- const auto factor = 255.0f / *std::max_element(it, end);
- for (; it != end; ++it) {
- *it *= factor;
- }
-
- context.pixelZoom = { 1, 1 };
- context.rasterPos = { -1, -1, 0, 1 };
- context.drawPixels(image);
-#endif // MBGL_USE_GLES2
-}
-
-void Painter::renderDepthBuffer(PaintParameters&) {
- context.setStencilMode(gl::StencilMode::disabled());
- context.setDepthMode(gl::DepthMode::disabled());
- context.setColorMode(gl::ColorMode::unblended());
- context.program = 0;
-
-#if not MBGL_USE_GLES2
- // Scales the values in the depth buffer so that they cover the entire grayscale range. This
- // makes it easier to spot tiny differences.
- const float base = 1.0f / (1.0f - depthRangeSize);
- context.pixelTransferDepth = { base, 1.0f - base };
-
- // Read the stencil buffer
- auto viewport = context.viewport.getCurrentValue();
- auto image =
- context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false);
-
- context.pixelZoom = { 1, 1 };
- context.rasterPos = { -1, -1, 0, 1 };
- context.drawPixels(image);
-#endif // MBGL_USE_GLES2
-}
-#endif // NDEBUG
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_fill.cpp b/src/mbgl/renderer/painters/painter_fill.cpp
deleted file mode 100644
index 3a0bfed454..0000000000
--- a/src/mbgl/renderer/painters/painter_fill.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/fill_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_fill_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/convert.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderFill(PaintParameters& parameters,
- FillBucket& bucket,
- const RenderFillLayer& layer,
- const RenderTile& tile) {
- const FillPaintProperties::PossiblyEvaluated& properties = layer.evaluated;
-
- if (!properties.get<FillPattern>().from.empty()) {
- if (pass != RenderPass::Translucent) {
- return;
- }
-
- optional<ImagePosition> imagePosA = imageManager->getPattern(properties.get<FillPattern>().from);
- optional<ImagePosition> imagePosB = imageManager->getPattern(properties.get<FillPattern>().to);
-
- if (!imagePosA || !imagePosB) {
- return;
- }
-
- imageManager->bind(context, 0);
-
- auto draw = [&] (uint8_t sublayer,
- auto& program,
- const auto& drawMode,
- const auto& indexBuffer,
- const auto& segments) {
- program.get(properties).draw(
- context,
- drawMode,
- depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- FillPatternUniforms::values(
- tile.translatedMatrix(properties.get<FillTranslate>(),
- properties.get<FillTranslateAnchor>(),
- state),
- context.viewport.getCurrentValue().size,
- imageManager->getPixelSize(),
- *imagePosA,
- *imagePosB,
- properties.get<FillPattern>(),
- tile.id,
- state
- ),
- *bucket.vertexBuffer,
- indexBuffer,
- segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
- };
-
- draw(0,
- parameters.programs.fillPattern,
- gl::Triangles(),
- *bucket.triangleIndexBuffer,
- bucket.triangleSegments);
-
- if (!properties.get<FillAntialias>() || !layer.unevaluated.get<FillOutlineColor>().isUndefined()) {
- return;
- }
-
- draw(2,
- parameters.programs.fillOutlinePattern,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- } else {
- auto draw = [&] (uint8_t sublayer,
- auto& program,
- const auto& drawMode,
- const auto& indexBuffer,
- const auto& segments) {
- program.get(properties).draw(
- context,
- drawMode,
- depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{
- tile.translatedMatrix(properties.get<FillTranslate>(),
- properties.get<FillTranslateAnchor>(),
- state)
- },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- *bucket.vertexBuffer,
- indexBuffer,
- segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
- };
-
- if (properties.get<FillAntialias>() && !layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) {
- draw(2,
- parameters.programs.fillOutline,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- }
-
- // Only draw the fill when it's opaque and we're drawing opaque fragments,
- // or when it's translucent and we're drawing translucent fragments.
- if ((properties.get<FillColor>().constantOr(Color()).a >= 1.0f
- && properties.get<FillOpacity>().constantOr(0) >= 1.0f) == (pass == RenderPass::Opaque)) {
- draw(1,
- parameters.programs.fill,
- gl::Triangles(),
- *bucket.triangleIndexBuffer,
- bucket.triangleSegments);
- }
-
- if (properties.get<FillAntialias>() && layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) {
- draw(2,
- parameters.programs.fillOutline,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- }
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_fill_extrusion.cpp b/src/mbgl/renderer/painters/painter_fill_extrusion.cpp
deleted file mode 100644
index 165476944b..0000000000
--- a/src/mbgl/renderer/painters/painter_fill_extrusion.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_extrusion_program.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/convert.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderFillExtrusion(PaintParameters& parameters,
- FillExtrusionBucket& bucket,
- const RenderFillExtrusionLayer& layer,
- const RenderTile& tile) {
- const FillExtrusionPaintProperties::PossiblyEvaluated& properties = layer.evaluated;
-
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- if (!properties.get<FillExtrusionPattern>().from.empty()) {
- optional<ImagePosition> imagePosA = imageManager->getPattern(properties.get<FillExtrusionPattern>().from);
- optional<ImagePosition> imagePosB = imageManager->getPattern(properties.get<FillExtrusionPattern>().to);
-
- if (!imagePosA || !imagePosB) {
- return;
- }
-
- imageManager->bind(context, 0);
-
- parameters.programs.fillExtrusionPattern.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadWrite),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillExtrusionPatternUniforms::values(
- tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(),
- properties.get<FillExtrusionTranslateAnchor>(),
- state),
- imageManager->getPixelSize(),
- *imagePosA,
- *imagePosB,
- properties.get<FillExtrusionPattern>(),
- tile.id,
- state,
- -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
- evaluatedLight
- ),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.triangleSegments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom());
-
- } else {
- parameters.programs.fillExtrusion.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadWrite),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillExtrusionUniforms::values(
- tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(),
- properties.get<FillExtrusionTranslateAnchor>(),
- state),
- state,
- evaluatedLight
- ),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.triangleSegments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom());
- };
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_line.cpp b/src/mbgl/renderer/painters/painter_line.cpp
deleted file mode 100644
index 58f4131d96..0000000000
--- a/src/mbgl/renderer/painters/painter_line.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/line_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_line_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/line_program.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderLine(PaintParameters& parameters,
- LineBucket& bucket,
- const RenderLineLayer& layer,
- const RenderTile& tile) {
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- const RenderLinePaintProperties::PossiblyEvaluated& properties = layer.evaluated;
-
- auto draw = [&] (auto& program, auto&& uniformValues) {
- program.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- std::move(uniformValues),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
- };
-
- if (!properties.get<LineDasharray>().from.empty()) {
- const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
- ? LinePatternCap::Round : LinePatternCap::Square;
- LinePatternPos posA = lineAtlas->getDashPosition(properties.get<LineDasharray>().from, cap);
- LinePatternPos posB = lineAtlas->getDashPosition(properties.get<LineDasharray>().to, cap);
-
- lineAtlas->bind(context, 0);
-
- draw(parameters.programs.lineSDF,
- LineSDFProgram::uniformValues(
- properties,
- frame.pixelRatio,
- tile,
- state,
- pixelsToGLUnits,
- posA,
- posB,
- lineAtlas->getSize().width));
-
- } else if (!properties.get<LinePattern>().from.empty()) {
- optional<ImagePosition> posA = imageManager->getPattern(properties.get<LinePattern>().from);
- optional<ImagePosition> posB = imageManager->getPattern(properties.get<LinePattern>().to);
-
- if (!posA || !posB)
- return;
-
- imageManager->bind(context, 0);
-
- draw(parameters.programs.linePattern,
- LinePatternProgram::uniformValues(
- properties,
- tile,
- state,
- pixelsToGLUnits,
- imageManager->getPixelSize(),
- *posA,
- *posB));
-
- } else {
- draw(parameters.programs.line,
- LineProgram::uniformValues(
- properties,
- tile,
- state,
- pixelsToGLUnits));
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_raster.cpp b/src/mbgl/renderer/painters/painter_raster.cpp
deleted file mode 100644
index 56e38ae8f4..0000000000
--- a/src/mbgl/renderer/painters/painter_raster.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/raster_program.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-static float saturationFactor(float saturation) {
- if (saturation > 0) {
- return 1 - 1 / (1.001 - saturation);
- } else {
- return -saturation;
- }
-}
-
-static float contrastFactor(float contrast) {
- if (contrast > 0) {
- return 1 / (1 - contrast);
- } else {
- return 1 + contrast;
- }
-}
-
-static std::array<float, 3> spinWeights(float spin) {
- spin *= util::DEG2RAD;
- float s = std::sin(spin);
- float c = std::cos(spin);
- std::array<float, 3> spin_weights = {{
- (2 * c + 1) / 3,
- (-std::sqrt(3.0f) * s - c + 1) / 3,
- (std::sqrt(3.0f) * s - c + 1) / 3
- }};
- return spin_weights;
-}
-
-void Painter::renderRaster(PaintParameters& parameters,
- RasterBucket& bucket,
- const RenderRasterLayer& layer,
- const mat4& matrix,
- bool useBucketBuffers = false) {
- if (pass != RenderPass::Translucent)
- return;
- if (!bucket.hasData())
- return;
-
- const RasterPaintProperties::PossiblyEvaluated& properties = layer.evaluated;
- const RasterProgram::PaintPropertyBinders paintAttributeData(properties, 0);
-
- assert(bucket.texture);
- context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
- context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
-
- parameters.programs.raster.draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- RasterProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_image0::Value{ 0 },
- uniforms::u_image1::Value{ 1 },
- uniforms::u_opacity::Value{ properties.get<RasterOpacity>() },
- uniforms::u_fade_t::Value{ 1 },
- uniforms::u_brightness_low::Value{ properties.get<RasterBrightnessMin>() },
- uniforms::u_brightness_high::Value{ properties.get<RasterBrightnessMax>() },
- uniforms::u_saturation_factor::Value{ saturationFactor(properties.get<RasterSaturation>()) },
- uniforms::u_contrast_factor::Value{ contrastFactor(properties.get<RasterContrast>()) },
- uniforms::u_spin_weights::Value{ spinWeights(properties.get<RasterHueRotate>()) },
- uniforms::u_buffer_scale::Value{ 1.0f },
- uniforms::u_scale_parent::Value{ 1.0f },
- uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
- },
- useBucketBuffers ? *bucket.vertexBuffer : rasterVertexBuffer,
- useBucketBuffers ? *bucket.indexBuffer : quadTriangleIndexBuffer,
- useBucketBuffers ? bucket.segments : rasterSegments,
- paintAttributeData,
- properties,
- state.getZoom()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp
deleted file mode 100644
index d3a505aa3f..0000000000
--- a/src/mbgl/renderer/painters/painter_symbol.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/symbol_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/symbol_program.hpp>
-#include <mbgl/programs/collision_box_program.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
-
-#include <cmath>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderSymbol(PaintParameters& parameters,
- SymbolBucket& bucket,
- const RenderSymbolLayer& layer,
- const RenderTile& tile) {
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- const auto& layout = bucket.layout;
-
- frameHistory.bind(context, 1);
-
- auto draw = [&] (auto& program,
- auto&& uniformValues,
- const auto& buffers,
- const auto& symbolSizeBinder,
- const SymbolPropertyValues& values_,
- const auto& binders,
- const auto& paintProperties)
- {
- // We clip symbols to their tile extent in still mode.
- const bool needsClipping = frame.mapMode == MapMode::Still;
-
- program.get(paintProperties).draw(
- context,
- gl::Triangles(),
- values_.pitchAlignment == AlignmentType::Map
- ? depthModeForSublayer(0, gl::DepthMode::ReadOnly)
- : gl::DepthMode::disabled(),
- needsClipping
- ? stencilModeForClipping(tile.clip)
- : gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- std::move(uniformValues),
- *buffers.vertexBuffer,
- *symbolSizeBinder,
- *buffers.indexBuffer,
- buffers.segments,
- binders,
- paintProperties,
- state.getZoom()
- );
- };
-
- assert(dynamic_cast<GeometryTile*>(&tile.tile));
- GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile);
-
- if (bucket.hasIconData()) {
- auto values = layer.iconPropertyValues(layout);
- auto paintPropertyValues = layer.iconPaintProperties();
-
- const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear;
- const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0;
-
- context.bindTexture(*geometryTile.iconAtlasTexture, 0,
- bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed
- ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
-
- const Size texsize = geometryTile.iconAtlasTexture->size;
-
- if (bucket.sdfIcons) {
- if (values.hasHalo) {
- draw(parameters.programs.symbolIconSDF,
- SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
- bucket.icon,
- bucket.iconSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).first,
- paintPropertyValues);
- }
-
- if (values.hasFill) {
- draw(parameters.programs.symbolIconSDF,
- SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
- bucket.icon,
- bucket.iconSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).first,
- paintPropertyValues);
- }
- } else {
- draw(parameters.programs.symbolIcon,
- SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state),
- bucket.icon,
- bucket.iconSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).first,
- paintPropertyValues);
- }
- }
-
- if (bucket.hasTextData()) {
- context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear);
-
- auto values = layer.textPropertyValues(layout);
- auto paintPropertyValues = layer.textPaintProperties();
-
- const Size texsize = geometryTile.glyphAtlasTexture->size;
-
- if (values.hasHalo) {
- draw(parameters.programs.symbolGlyph,
- SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
- bucket.text,
- bucket.textSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).second,
- paintPropertyValues);
- }
-
- if (values.hasFill) {
- draw(parameters.programs.symbolGlyph,
- SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
- bucket.text,
- bucket.textSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).second,
- paintPropertyValues);
- }
- }
-
- if (bucket.hasCollisionBoxData()) {
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
-
- programs->collisionBox.draw(
- context,
- gl::Lines { 1.0f },
- gl::DepthMode::disabled(),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- CollisionBoxProgram::UniformValues {
- uniforms::u_matrix::Value{ tile.matrix },
- uniforms::u_scale::Value{ std::pow(2.0f, float(state.getZoom() - tile.tile.id.overscaledZ)) },
- uniforms::u_zoom::Value{ float(state.getZoom() * 10) },
- uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) },
- },
- *bucket.collisionBox.vertexBuffer,
- *bucket.collisionBox.indexBuffer,
- bucket.collisionBox.segments,
- paintAttributeData,
- properties,
- state.getZoom()
- );
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp
index 8a5dfbe4ea..e662d5dfb1 100644
--- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp
+++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp
@@ -45,7 +45,7 @@ public:
template <class Feature>
T evaluate(const Feature& feature, float zoom, T defaultValue) const {
return this->match(
- [&] (const T& constant) { return constant; },
+ [&] (const T& constant_) { return constant_; },
[&] (const style::SourceFunction<T>& function) {
return function.evaluate(feature, defaultValue);
},
diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp
deleted file mode 100644
index 4bf5629263..0000000000
--- a/src/mbgl/renderer/render_item.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <mbgl/util/color.hpp>
-
-#include <unordered_set>
-#include <vector>
-
-namespace mbgl {
-
-class RenderLayer;
-class RenderSource;
-class RenderTile;
-class Bucket;
-
-namespace style {
-} // namespace style
-
-class RenderItem {
-public:
- RenderItem(RenderLayer& layer_,
- RenderSource* renderSource_)
- : layer(layer_), source(renderSource_) {
- }
-
- RenderLayer& layer;
- RenderSource* source;
-};
-
-class RenderData {
-public:
- Color backgroundColor;
- std::unordered_set<RenderSource*> sources;
- std::vector<RenderItem> order;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp
index 3f9d68003e..eb2b74ffe0 100644
--- a/src/mbgl/renderer/render_layer.cpp
+++ b/src/mbgl/renderer/render_layer.cpp
@@ -9,7 +9,6 @@
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/tile/tile.hpp>
namespace mbgl {
@@ -68,14 +67,5 @@ void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>>
renderTiles = std::move(tiles);
}
-void RenderLayer::render(Painter& painter, PaintParameters& parameters, RenderSource*) {
- for (auto& tileRef : renderTiles) {
- auto& tile = tileRef.get();
- auto bucket = tile.tile.getBucket(*baseImpl);
- bucket->render(painter, parameters, *this, tile);
- }
-}
-
-
} //namespace mbgl
diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp
index e06f479281..55831cb72c 100644
--- a/src/mbgl/renderer/render_layer.hpp
+++ b/src/mbgl/renderer/render_layer.hpp
@@ -14,7 +14,6 @@ class Bucket;
class BucketParameters;
class TransitionParameters;
class PropertyEvaluationParameters;
-class Painter;
class PaintParameters;
class RenderSource;
class RenderTile;
@@ -62,7 +61,7 @@ public:
// Checks whether this layer can be rendered.
bool needsRendering(float zoom) const;
- virtual void render(Painter&, PaintParameters&, RenderSource*);
+ virtual void render(PaintParameters&, RenderSource*) = 0;
// Check wether the given geometry intersects
// with the feature
@@ -83,13 +82,16 @@ public:
friend std::string layoutKey(const RenderLayer&);
protected:
+ // renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they
+ // can update opacities in the symbol buckets immediately before rendering
+ friend class CrossTileSymbolIndex;
+ friend class Placement;
+ // Stores current set of tiles to be rendered for this layer.
+ std::vector<std::reference_wrapper<RenderTile>> renderTiles;
+
// 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;
-
- //Stores current set of tiles to be rendered for this layer.
- std::vector<std::reference_wrapper<RenderTile>> renderTiles;
-
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
index d273e34ff7..5d18304129 100644
--- a/src/mbgl/renderer/render_pass.hpp
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/traits.hpp>
+#include <mbgl/util/util.hpp>
#include <cstdint>
@@ -10,17 +11,18 @@ enum class RenderPass : uint8_t {
None = 0,
Opaque = 1 << 0,
Translucent = 1 << 1,
+ Pass3D = 1 << 2,
};
-constexpr RenderPass operator|(RenderPass a, RenderPass b) {
+MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) {
return RenderPass(mbgl::underlying_type(a) | mbgl::underlying_type(b));
}
-constexpr RenderPass& operator|=(RenderPass& a, RenderPass b) {
+MBGL_CONSTEXPR RenderPass& operator|=(RenderPass& a, RenderPass b) {
return (a = a | b);
}
-constexpr RenderPass operator&(RenderPass a, RenderPass b) {
+MBGL_CONSTEXPR RenderPass operator&(RenderPass a, RenderPass b) {
return RenderPass(mbgl::underlying_type(a) & mbgl::underlying_type(b));
}
diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp
index 7723a1c7ca..6624bb7d96 100644
--- a/src/mbgl/renderer/render_source.cpp
+++ b/src/mbgl/renderer/render_source.cpp
@@ -6,6 +6,7 @@
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/annotation/render_annotation_source.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
+#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
#include <mbgl/tile/tile.hpp>
namespace mbgl {
@@ -27,6 +28,8 @@ std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl)
return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl));
case SourceType::Image:
return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl));
+ case SourceType::CustomVector:
+ return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl));
}
// Not reachable, but placate GCC.
diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp
index a00a6c797d..8c84af4f1e 100644
--- a/src/mbgl/renderer/render_source.hpp
+++ b/src/mbgl/renderer/render_source.hpp
@@ -15,16 +15,16 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
-class RenderStyle;
class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class Tile;
class RenderSourceObserver;
class TileParameters;
+class CollisionIndex;
class RenderSource : protected TileObserver {
public:
@@ -54,21 +54,22 @@ public:
bool needsRelayout,
const TileParameters&) = 0;
- virtual void startRender(Painter&) = 0;
- virtual void finishRender(Painter&) = 0;
+ virtual void startRender(PaintParameters&) = 0;
+ virtual void finishRender(PaintParameters&) = 0;
- virtual std::map<UnwrappedTileID, RenderTile>& getRenderTiles() = 0;
+ // Returns an unsorted list of RenderTiles.
+ virtual std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() = 0;
virtual std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const = 0;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const = 0;
virtual std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const = 0;
- virtual void setCacheSize(size_t) = 0;
virtual void onLowMemory() = 0;
virtual void dumpDebugLogs() const = 0;
diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp
new file mode 100644
index 0000000000..ccf239e643
--- /dev/null
+++ b/src/mbgl/renderer/render_static_data.cpp
@@ -0,0 +1,67 @@
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/program_parameters.hpp>
+
+namespace mbgl {
+
+static gl::VertexVector<FillLayoutVertex> tileVertices() {
+ gl::VertexVector<FillLayoutVertex> result;
+ result.emplace_back(FillProgram::layoutVertex({ 0, 0 }));
+ result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 }));
+ result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT }));
+ result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT }));
+ return result;
+}
+
+static gl::IndexVector<gl::Triangles> quadTriangleIndices() {
+ gl::IndexVector<gl::Triangles> result;
+ result.emplace_back(0, 1, 2);
+ result.emplace_back(1, 2, 3);
+ return result;
+}
+
+static gl::IndexVector<gl::LineStrip> tileLineStripIndices() {
+ gl::IndexVector<gl::LineStrip> result;
+ result.emplace_back(0);
+ result.emplace_back(1);
+ result.emplace_back(3);
+ result.emplace_back(2);
+ result.emplace_back(0);
+ return result;
+}
+
+static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
+ gl::VertexVector<RasterLayoutVertex> result;
+ result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }));
+ result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { util::EXTENT, 0 }));
+ result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, util::EXTENT }));
+ result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { util::EXTENT, util::EXTENT }));
+ return result;
+}
+
+static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() {
+ gl::VertexVector<ExtrusionTextureLayoutVertex> result;
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 }));
+ return result;
+}
+
+RenderStaticData::RenderStaticData(gl::Context& context, float pixelRatio, const optional<std::string>& programCacheDir)
+ : tileVertexBuffer(context.createVertexBuffer(tileVertices())),
+ rasterVertexBuffer(context.createVertexBuffer(rasterVertices())),
+ extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())),
+ quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())),
+ tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())),
+ programs(context, ProgramParameters { pixelRatio, false, programCacheDir })
+#ifndef NDEBUG
+ , overdrawPrograms(context, ProgramParameters { pixelRatio, true, programCacheDir })
+#endif
+{
+ tileTriangleSegments.emplace_back(0, 0, 4, 6);
+ tileBorderSegments.emplace_back(0, 0, 4, 5);
+ rasterSegments.emplace_back(0, 0, 4, 6);
+ extrusionTextureSegments.emplace_back(0, 0, 4, 6);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp
new file mode 100644
index 0000000000..cf58c31f4d
--- /dev/null
+++ b/src/mbgl/renderer/render_static_data.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <string>
+
+namespace mbgl {
+
+class RenderStaticData {
+public:
+ RenderStaticData(gl::Context&, float pixelRatio, const optional<std::string>& programCacheDir);
+
+ gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer;
+ gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer;
+ gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer;
+
+ gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer;
+ gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer;
+
+ SegmentVector<FillAttributes> tileTriangleSegments;
+ SegmentVector<DebugAttributes> tileBorderSegments;
+ SegmentVector<RasterAttributes> rasterSegments;
+ SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
+
+ optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer;
+ bool has3D = false;
+ Size backendSize;
+
+ Programs programs;
+
+#ifndef NDEBUG
+ Programs overdrawPrograms;
+#endif
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp
deleted file mode 100644
index f9c3f0ca9f..0000000000
--- a/src/mbgl/renderer/render_style.cpp
+++ /dev/null
@@ -1,456 +0,0 @@
-#include <mbgl/renderer/render_style.hpp>
-#include <mbgl/renderer/render_style_observer.hpp>
-#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/transition_parameters.hpp>
-#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_item.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/layers/render_circle_layer.hpp>
-#include <mbgl/renderer/layers/render_custom_layer.hpp>
-#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/layers/render_fill_layer.hpp>
-#include <mbgl/renderer/layers/render_line_layer.hpp>
-#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/renderer/style_diff.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/style/source_impl.hpp>
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/sprite/sprite_loader.hpp>
-#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/query.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/string.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-RenderStyleObserver nullObserver;
-
-RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_)
- : scheduler(scheduler_),
- fileSource(fileSource_),
- glyphManager(std::make_unique<GlyphManager>(fileSource)),
- imageManager(std::make_unique<ImageManager>()),
- lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
- imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()),
- sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()),
- layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()),
- renderLight(makeMutable<Light::Impl>()),
- observer(&nullObserver) {
- glyphManager->setObserver(this);
-}
-
-RenderStyle::~RenderStyle() {
- assert(BackendScope::exists()); // Required for custom layers.
-}
-
-void RenderStyle::setObserver(RenderStyleObserver* observer_) {
- observer = observer_;
-}
-
-std::vector<const RenderLayer*> RenderStyle::getRenderLayers() const {
- std::vector<const RenderLayer*> result;
- result.reserve(renderLayers.size());
- for (const auto& layer : *layerImpls) {
- result.push_back(getRenderLayer(layer->id));
- }
- return result;
-}
-
-RenderLayer* RenderStyle::getRenderLayer(const std::string& id) {
- auto it = renderLayers.find(id);
- return it != renderLayers.end() ? it->second.get() : nullptr;
-}
-
-const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const {
- auto it = renderLayers.find(id);
- return it != renderLayers.end() ? it->second.get() : nullptr;
-}
-
-const RenderLight& RenderStyle::getRenderLight() const {
- return renderLight;
-}
-
-void RenderStyle::update(const UpdateParameters& parameters) {
- assert(BackendScope::exists()); // Required for custom layers.
-
- const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint);
-
- const TransitionParameters transitionParameters {
- parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(),
- parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions()
- };
-
- const PropertyEvaluationParameters evaluationParameters {
- zoomHistory,
- parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(),
- parameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
- };
-
- const TileParameters tileParameters {
- parameters.pixelRatio,
- parameters.debugOptions,
- parameters.transformState,
- parameters.scheduler,
- parameters.fileSource,
- parameters.mode,
- parameters.annotationManager,
- *imageManager,
- *glyphManager
- };
-
- glyphManager->setURL(parameters.glyphURL);
-
- // Update light.
- const bool lightChanged = renderLight.impl != parameters.light;
-
- if (lightChanged) {
- renderLight.impl = parameters.light;
- renderLight.transition(transitionParameters);
- }
-
- if (lightChanged || zoomChanged || renderLight.hasTransition()) {
- renderLight.evaluate(evaluationParameters);
- }
-
-
- const ImageDifference imageDiff = diffImages(imageImpls, parameters.images);
- imageImpls = parameters.images;
-
- // Remove removed images from sprite atlas.
- for (const auto& entry : imageDiff.removed) {
- imageManager->removeImage(entry.first);
- }
-
- // Add added images to sprite atlas.
- for (const auto& entry : imageDiff.added) {
- imageManager->addImage(entry.second);
- }
-
- // Update changed images.
- for (const auto& entry : imageDiff.changed) {
- imageManager->updateImage(entry.second.after);
- }
-
- if (parameters.spriteLoaded && !imageManager->isLoaded()) {
- imageManager->onSpriteLoaded();
- }
-
-
- const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers);
- layerImpls = parameters.layers;
-
- // Remove render layers for removed layers.
- for (const auto& entry : layerDiff.removed) {
- renderLayers.erase(entry.first);
- }
-
- // Create render layers for newly added layers.
- for (const auto& entry : layerDiff.added) {
- renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
- }
-
- // Update render layers for changed layers.
- for (const auto& entry : layerDiff.changed) {
- renderLayers.at(entry.first)->setImpl(entry.second.after);
- }
-
- // Update layers for class and zoom changes.
- for (const auto& entry : renderLayers) {
- RenderLayer& layer = *entry.second;
- const bool layerAdded = layerDiff.added.count(entry.first);
- const bool layerChanged = layerDiff.changed.count(entry.first);
-
- if (layerAdded || layerChanged) {
- layer.transition(transitionParameters);
- }
-
- if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
- layer.evaluate(evaluationParameters);
- }
- }
-
-
- const SourceDifference sourceDiff = diffSources(sourceImpls, parameters.sources);
- sourceImpls = parameters.sources;
-
- // Remove render layers for removed sources.
- for (const auto& entry : sourceDiff.removed) {
- renderSources.erase(entry.first);
- }
-
- // Create render sources for newly added sources.
- for (const auto& entry : sourceDiff.added) {
- std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
- renderSource->setObserver(this);
- renderSources.emplace(entry.first, std::move(renderSource));
- }
-
- // Update all sources.
- for (const auto& source : *sourceImpls) {
- std::vector<Immutable<Layer::Impl>> filteredLayers;
- bool needsRendering = false;
- bool needsRelayout = false;
-
- for (const auto& layer : *layerImpls) {
- if (layer->type == LayerType::Background ||
- layer->type == LayerType::Custom ||
- layer->source != source->id) {
- continue;
- }
-
- if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
- needsRendering = true;
- }
-
- if (!needsRelayout && (
- hasLayoutDifference(layerDiff, layer->id) ||
- !imageDiff.added.empty() ||
- !imageDiff.removed.empty() ||
- !imageDiff.changed.empty())) {
- needsRelayout = true;
- }
-
- filteredLayers.push_back(layer);
- }
-
- renderSources.at(source->id)->update(source,
- filteredLayers,
- needsRendering,
- needsRelayout,
- tileParameters);
- }
-}
-
-RenderSource* RenderStyle::getRenderSource(const std::string& id) const {
- auto it = renderSources.find(id);
- return it != renderSources.end() ? it->second.get() : nullptr;
-}
-
-bool RenderStyle::hasTransitions() const {
- if (renderLight.hasTransition()) {
- return true;
- }
-
- for (const auto& entry : renderLayers) {
- if (entry.second->hasTransition()) {
- return true;
- }
- }
-
- return false;
-}
-
-bool RenderStyle::isLoaded() const {
- for (const auto& entry: renderSources) {
- if (!entry.second->isLoaded()) {
- return false;
- }
- }
-
- if (!imageManager->isLoaded()) {
- return false;
- }
-
- return true;
-}
-
-RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) {
- RenderData result;
-
- for (const auto& entry : renderSources) {
- if (entry.second->isEnabled()) {
- result.sources.insert(entry.second.get());
- }
- }
-
- for (auto& layerImpl : *layerImpls) {
- RenderLayer* layer = getRenderLayer(layerImpl->id);
- assert(layer);
-
- if (!layer->needsRendering(zoomHistory.lastZoom)) {
- continue;
- }
-
- if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
- if (debugOptions & MapDebugOptions::Overdraw) {
- // We want to skip glClear optimization in overdraw mode.
- result.order.emplace_back(*layer, nullptr);
- continue;
- }
- const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
- if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) {
- // This is a solid background. We can use glClear().
- result.backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
- } else {
- // This is a textured background, or not the bottommost layer. We need to render it with a quad.
- result.order.emplace_back(*layer, nullptr);
- }
- continue;
- }
-
- if (layer->is<RenderCustomLayer>()) {
- result.order.emplace_back(*layer, nullptr);
- continue;
- }
-
- RenderSource* source = getRenderSource(layer->baseImpl->source);
- if (!source) {
- Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
- continue;
- }
-
- auto& renderTiles = source->getRenderTiles();
- const bool symbolLayer = layer->is<RenderSymbolLayer>();
-
- // Sort symbol tiles in opposite y position, so tiles with overlapping
- // symbols are drawn on top of each other, with lower symbols being
- // drawn on top of higher symbols.
- std::vector<std::reference_wrapper<RenderTile>> sortedTiles;
- std::transform(renderTiles.begin(), renderTiles.end(), std::back_inserter(sortedTiles),
- [](auto& pair) { return std::ref(pair.second); });
- if (symbolLayer) {
- std::sort(sortedTiles.begin(), sortedTiles.end(),
- [angle](const RenderTile& a, const RenderTile& b) {
- Point<float> pa(a.id.canonical.x, a.id.canonical.y);
- Point<float> pb(b.id.canonical.x, b.id.canonical.y);
-
- auto par = util::rotate(pa, angle);
- auto pbr = util::rotate(pb, angle);
-
- return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
- });
- }
-
- std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
- for (auto& sortedTile : sortedTiles) {
- auto& tile = sortedTile.get();
- if (!tile.tile.isRenderable()) {
- continue;
- }
-
- // We're not clipping symbol layers, so when we have both parents and children of symbol
- // layers, we drop all children in favor of their parent to avoid duplicate labels.
- // See https://github.com/mapbox/mapbox-gl-native/issues/2482
- if (symbolLayer) {
- bool skip = false;
- // Look back through the buckets we decided to render to find out whether there is
- // already a bucket from this layer that is a parent of this tile. Tiles are ordered
- // by zoom level when we obtain them from getTiles().
- for (auto it = sortedTilesForInsertion.rbegin();
- it != sortedTilesForInsertion.rend(); ++it) {
- if (tile.tile.id.isChildOf(it->get().tile.id)) {
- skip = true;
- break;
- }
- }
- if (skip) {
- continue;
- }
- }
-
- auto bucket = tile.tile.getBucket(*layer->baseImpl);
- if (bucket) {
- sortedTilesForInsertion.emplace_back(tile);
- tile.used = true;
- }
- }
- layer->setRenderTiles(std::move(sortedTilesForInsertion));
- result.order.emplace_back(*layer, source);
- }
-
- return result;
-}
-
-std::vector<Feature> RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const {
- std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
-
- if (options.layerIDs) {
- std::unordered_set<std::string> sourceIDs;
- for (const auto& layerID : *options.layerIDs) {
- if (const RenderLayer* layer = getRenderLayer(layerID)) {
- sourceIDs.emplace(layer->baseImpl->source);
- }
- }
- for (const auto& sourceID : sourceIDs) {
- if (RenderSource* renderSource = getRenderSource(sourceID)) {
- auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
- } else {
- for (const auto& entry : renderSources) {
- auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
-
- std::vector<Feature> result;
-
- if (resultsByLayer.empty()) {
- return result;
- }
-
- // Combine all results based on the style layer order.
- for (const auto& layerImpl : *layerImpls) {
- const RenderLayer* layer = getRenderLayer(layerImpl->id);
- if (!layer->needsRendering(zoomHistory.lastZoom)) {
- continue;
- }
- auto it = resultsByLayer.find(layer->baseImpl->id);
- if (it != resultsByLayer.end()) {
- std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
- }
- }
-
- return result;
-}
-
-void RenderStyle::setSourceTileCacheSize(size_t size) {
- for (const auto& entry : renderSources) {
- entry.second->setCacheSize(size);
- }
-}
-
-void RenderStyle::onLowMemory() {
- for (const auto& entry : renderSources) {
- entry.second->onLowMemory();
- }
-}
-
-void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
- Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
- glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
- observer->onResourceError(error);
-}
-
-void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
- util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
- observer->onResourceError(error);
-}
-
-void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) {
- observer->onInvalidate();
-}
-
-void RenderStyle::dumpDebugLogs() const {
- for (const auto& entry : renderSources) {
- entry.second->dumpDebugLogs();
- }
-
- imageManager->dumpDebugLogs();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp
deleted file mode 100644
index dc33e7b2f4..0000000000
--- a/src/mbgl/renderer/render_style.hpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#pragma once
-
-#include <mbgl/style/image.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_source_observer.hpp>
-#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/renderer/render_light.hpp>
-#include <mbgl/text/glyph_manager_observer.hpp>
-#include <mbgl/map/zoom_history.hpp>
-#include <mbgl/map/mode.hpp>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace mbgl {
-
-class FileSource;
-class GlyphManager;
-class ImageManager;
-class LineAtlas;
-class RenderData;
-class TransformState;
-class RenderedQueryOptions;
-class Scheduler;
-class UpdateParameters;
-class RenderStyleObserver;
-
-namespace style {
-class Image;
-class Source;
-class Layer;
-} // namespace style
-
-class RenderStyle : public GlyphManagerObserver,
- public RenderSourceObserver {
-public:
- RenderStyle(Scheduler&, FileSource&);
- ~RenderStyle() final;
-
- void setObserver(RenderStyleObserver*);
- void update(const UpdateParameters&);
-
- bool isLoaded() const;
- bool hasTransitions() const;
-
- RenderSource* getRenderSource(const std::string& id) const;
-
- std::vector<const RenderLayer*> getRenderLayers() const;
-
- RenderLayer* getRenderLayer(const std::string& id);
- const RenderLayer* getRenderLayer(const std::string& id) const;
-
- const RenderLight& getRenderLight() const;
-
- RenderData getRenderData(MapDebugOptions, float angle);
-
- std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const;
-
- void setSourceTileCacheSize(size_t);
- void onLowMemory();
-
- void dumpDebugLogs() const;
-
- Scheduler& scheduler;
- FileSource& fileSource;
- std::unique_ptr<GlyphManager> glyphManager;
- std::unique_ptr<ImageManager> imageManager;
- std::unique_ptr<LineAtlas> lineAtlas;
-
-private:
- Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
- Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
- Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
-
- std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
- std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
- RenderLight renderLight;
-
- // GlyphManagerObserver implementation.
- void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
-
- // RenderSourceObserver implementation.
- void onTileChanged(RenderSource&, const OverscaledTileID&) override;
- void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
-
- RenderStyleObserver* observer;
- ZoomHistory zoomHistory;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp
deleted file mode 100644
index 5852dd68b8..0000000000
--- a/src/mbgl/renderer/render_style_observer.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <exception>
-
-namespace mbgl {
-
-class RenderStyleObserver {
-public:
- virtual ~RenderStyleObserver() = default;
- virtual void onInvalidate() {}
- virtual void onResourceError(std::exception_ptr) {}
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index 59c3ea076b..8df31f8d7c 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -1,7 +1,11 @@
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/buckets/debug_bucket.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
namespace mbgl {
@@ -10,24 +14,26 @@ using namespace style;
mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix,
const std::array<float, 2>& translation,
TranslateAnchorType anchor,
- const TransformState& state) const {
+ const TransformState& state,
+ const bool inViewportPixelUnits) const {
if (translation[0] == 0 && translation[1] == 0) {
return tileMatrix;
}
mat4 vtxMatrix;
- if (anchor == TranslateAnchorType::Viewport) {
- const double sin_a = std::sin(-state.getAngle());
- const double cos_a = std::cos(-state.getAngle());
- matrix::translate(vtxMatrix, tileMatrix,
- id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()),
- id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()),
- 0);
+ const float angle = inViewportPixelUnits ?
+ (anchor == TranslateAnchorType::Map ? state.getAngle() : 0) :
+ (anchor == TranslateAnchorType::Viewport ? -state.getAngle() : 0);
+
+ Point<float> translate = util::rotate(Point<float>{ translation[0], translation[1] }, angle);
+
+ if (inViewportPixelUnits) {
+ matrix::translate(vtxMatrix, tileMatrix, translate.x, translate.y, 0);
} else {
matrix::translate(vtxMatrix, tileMatrix,
- id.pixelsToTileUnits(translation[0], state.getZoom()),
- id.pixelsToTileUnits(translation[1], state.getZoom()),
+ id.pixelsToTileUnits(translate.x, state.getZoom()),
+ id.pixelsToTileUnits(translate.y, state.getZoom()),
0);
}
@@ -37,24 +43,107 @@ mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix,
mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation,
TranslateAnchorType anchor,
const TransformState& state) const {
- return translateVtxMatrix(matrix, translation, anchor, state);
+ return translateVtxMatrix(matrix, translation, anchor, state, false);
}
mat4 RenderTile::translatedClipMatrix(const std::array<float, 2>& translation,
TranslateAnchorType anchor,
const TransformState& state) const {
- return translateVtxMatrix(nearClippedMatrix, translation, anchor, state);
+ return translateVtxMatrix(nearClippedMatrix, translation, anchor, state, false);
}
-void RenderTile::startRender(Painter& painter) {
- tile.upload(painter.context);
+void RenderTile::setMask(TileMask&& mask) {
+ tile.setMask(std::move(mask));
+}
+
+void RenderTile::startRender(PaintParameters& parameters) {
+ tile.upload(parameters.context);
// Calculate two matrices for this tile: matrix is the standard tile matrix; nearClippedMatrix
// clips the near plane to 100 to save depth buffer precision
- painter.state.matrixFor(matrix, id);
- painter.state.matrixFor(nearClippedMatrix, id);
- matrix::multiply(matrix, painter.projMatrix, matrix);
- matrix::multiply(nearClippedMatrix, painter.nearClippedProjMatrix, nearClippedMatrix);
+ parameters.state.matrixFor(matrix, id);
+ parameters.state.matrixFor(nearClippedMatrix, id);
+ matrix::multiply(matrix, parameters.projMatrix, matrix);
+ matrix::multiply(nearClippedMatrix, parameters.nearClippedProjMatrix, nearClippedMatrix);
+}
+
+void RenderTile::finishRender(PaintParameters& parameters) {
+ if (!used || parameters.debugOptions == MapDebugOptions::NoDebug)
+ return;
+
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
+ if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
+ if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
+ tile.debugBucket->complete != tile.isComplete() ||
+ !(tile.debugBucket->modified == tile.modified) ||
+ !(tile.debugBucket->expires == tile.expires) ||
+ tile.debugBucket->debugMode != parameters.debugOptions) {
+ tile.debugBucket = std::make_unique<DebugBucket>(
+ tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
+ tile.expires, parameters.debugOptions, parameters.context);
+ }
+
+ parameters.programs.debug.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,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
+
+ parameters.programs.debug.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,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
+ }
+
+ if (parameters.debugOptions & MapDebugOptions::TileBorders) {
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::LineStrip { 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::red() }
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.tileBorderIndexBuffer,
+ parameters.staticData.tileBorderSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
+ }
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp
index 6677278873..3db02393d2 100644
--- a/src/mbgl/renderer/render_tile.hpp
+++ b/src/mbgl/renderer/render_tile.hpp
@@ -4,6 +4,7 @@
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/clip_id.hpp>
#include <mbgl/style/types.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <array>
@@ -11,9 +12,9 @@ namespace mbgl {
class Tile;
class TransformState;
-class Painter;
+class PaintParameters;
-class RenderTile {
+class RenderTile final {
public:
RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {}
RenderTile(const RenderTile&) = delete;
@@ -27,6 +28,7 @@ public:
mat4 matrix;
mat4 nearClippedMatrix;
bool used = false;
+ bool needsClipping = false;
mat4 translatedMatrix(const std::array<float, 2>& translate,
style::TranslateAnchorType anchor,
@@ -36,13 +38,15 @@ public:
style::TranslateAnchorType anchor,
const TransformState&) const;
- void startRender(Painter&);
+ void setMask(TileMask&&);
+ void startRender(PaintParameters&);
+ void finishRender(PaintParameters&);
-private:
mat4 translateVtxMatrix(const mat4& tileMatrix,
const std::array<float, 2>& translation,
style::TranslateAnchorType anchor,
- const TransformState& state) const;
+ const TransformState& state,
+ const bool inViewportPixelUnits) const;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp
new file mode 100644
index 0000000000..6d086c70b1
--- /dev/null
+++ b/src/mbgl/renderer/renderer.cpp
@@ -0,0 +1,102 @@
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_impl.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
+
+namespace mbgl {
+
+Renderer::Renderer(RendererBackend& backend,
+ float pixelRatio_,
+ FileSource& fileSource_,
+ Scheduler& scheduler_,
+ GLContextMode contextMode_,
+ const optional<std::string> programCacheDir_,
+ const optional<std::string> localFontFamily_)
+ : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_,
+ contextMode_, std::move(programCacheDir_), std::move(localFontFamily_))) {
+}
+
+Renderer::~Renderer() {
+ BackendScope guard { impl->backend };
+ impl.reset();
+}
+
+void Renderer::markContextLost() {
+ impl->markContextLost();
+}
+
+void Renderer::setObserver(RendererObserver* observer) {
+ impl->setObserver(observer);
+}
+
+void Renderer::render(const UpdateParameters& updateParameters) {
+ impl->render(updateParameters);
+}
+
+std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const {
+ return impl->queryRenderedFeatures(geometry, options);
+}
+
+std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) const {
+ return impl->queryRenderedFeatures({ point }, options);
+}
+
+std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) const {
+ return impl->queryRenderedFeatures(
+ {
+ box.min,
+ {box.max.x, box.min.y},
+ box.max,
+ {box.min.x, box.max.y},
+ box.min
+ },
+ options
+ );
+}
+
+AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const {
+ RenderedQueryOptions options;
+ options.layerIDs = {{ AnnotationManager::PointLayerID }};
+ auto features = queryRenderedFeatures(box, options);
+ return getAnnotationIDs(features);
+}
+
+AnnotationIDs Renderer::queryShapeAnnotations(const ScreenBox& box) const {
+ auto features = impl->queryShapeAnnotations({
+ box.min,
+ {box.max.x, box.min.y},
+ box.max,
+ {box.min.x, box.max.y},
+ box.min
+ });
+ return getAnnotationIDs(features);
+}
+
+AnnotationIDs Renderer::getAnnotationIDs(const std::vector<Feature>& features) const {
+ std::set<AnnotationID> set;
+ for (auto &feature : features) {
+ assert(feature.id);
+ assert(feature.id->is<uint64_t>());
+ assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max());
+ set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>()));
+ }
+ AnnotationIDs ids;
+ ids.reserve(set.size());
+ std::move(set.begin(), set.end(), std::back_inserter(ids));
+ return ids;
+}
+
+std::vector<Feature> Renderer::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
+ return impl->querySourceFeatures(sourceID, options);
+}
+
+void Renderer::dumpDebugLogs() {
+ impl->dumDebugLogs();
+}
+
+void Renderer::onLowMemory() {
+ BackendScope guard { impl->backend };
+ impl->onLowMemory();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/renderer/renderer_backend.cpp
index 83c2fed00b..22d263313c 100644
--- a/src/mbgl/map/backend.cpp
+++ b/src/mbgl/renderer/renderer_backend.cpp
@@ -1,5 +1,5 @@
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/extension.hpp>
#include <mbgl/gl/debugging.hpp>
@@ -8,62 +8,62 @@
namespace mbgl {
-Backend::Backend() = default;
+RendererBackend::RendererBackend() = default;
-gl::Context& Backend::getContext() {
+gl::Context& RendererBackend::getContext() {
assert(BackendScope::exists());
std::call_once(initialized, [this] {
context = std::make_unique<gl::Context>();
context->enableDebugging();
context->initializeExtensions(
- std::bind(&Backend::initializeExtension, this, std::placeholders::_1));
+ std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1));
});
return *context;
}
-PremultipliedImage Backend::readFramebuffer(const Size& size) const {
+PremultipliedImage RendererBackend::readFramebuffer(const Size& size) const {
assert(context);
return context->readFramebuffer<PremultipliedImage>(size);
}
-void Backend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
+void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
getContext().bindFramebuffer.setCurrentValue(fbo);
if (fbo != ImplicitFramebufferBinding) {
assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
}
}
-void Backend::assumeViewport(int32_t x, int32_t y, const Size& size) {
+void RendererBackend::assumeViewport(int32_t x, int32_t y, const Size& size) {
getContext().viewport.setCurrentValue({ x, y, size });
assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue());
}
-void Backend::assumeScissorTest(bool enabled) {
+void RendererBackend::assumeScissorTest(bool enabled) {
getContext().scissorTest.setCurrentValue(enabled);
assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue());
}
-bool Backend::implicitFramebufferBound() {
+bool RendererBackend::implicitFramebufferBound() {
return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding;
}
-void Backend::setFramebufferBinding(const gl::FramebufferID fbo) {
+void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) {
getContext().bindFramebuffer = fbo;
if (fbo != ImplicitFramebufferBinding) {
assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
}
}
-void Backend::setViewport(int32_t x, int32_t y, const Size& size) {
+void RendererBackend::setViewport(int32_t x, int32_t y, const Size& size) {
getContext().viewport = { x, y, size };
assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue());
}
-void Backend::setScissorTest(bool enabled) {
+void RendererBackend::setScissorTest(bool enabled) {
getContext().scissorTest = enabled;
assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue());
}
-Backend::~Backend() = default;
+RendererBackend::~RendererBackend() = default;
} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
new file mode 100644
index 0000000000..4bed0e251b
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -0,0 +1,815 @@
+#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/renderer/renderer_impl.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/layers/render_background_layer.hpp>
+#include <mbgl/renderer/layers/render_custom_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/style_diff.hpp>
+#include <mbgl/renderer/query.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/gl/debugging.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/logging.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+static RendererObserver& nullObserver() {
+ static RendererObserver observer;
+ return observer;
+}
+
+Renderer::Impl::Impl(RendererBackend& backend_,
+ float pixelRatio_,
+ FileSource& fileSource_,
+ Scheduler& scheduler_,
+ GLContextMode contextMode_,
+ const optional<std::string> programCacheDir_,
+ const optional<std::string> localFontFamily_)
+ : backend(backend_)
+ , scheduler(scheduler_)
+ , fileSource(fileSource_)
+ , observer(&nullObserver())
+ , contextMode(contextMode_)
+ , pixelRatio(pixelRatio_)
+ , programCacheDir(programCacheDir_)
+ , glyphManager(std::make_unique<GlyphManager>(fileSource, std::make_unique<LocalGlyphRasterizer>(localFontFamily_)))
+ , imageManager(std::make_unique<ImageManager>())
+ , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 }))
+ , imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>())
+ , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>())
+ , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>())
+ , renderLight(makeMutable<Light::Impl>())
+ , placement(std::make_unique<Placement>(TransformState{}, MapMode::Static)) {
+ glyphManager->setObserver(this);
+}
+
+Renderer::Impl::~Impl() {
+ assert(BackendScope::exists());
+
+ if (contextLost) {
+ // Signal all RenderCustomLayers that the context was lost
+ // before cleaning up
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ if (layer.is<RenderCustomLayer>()) {
+ layer.as<RenderCustomLayer>()->markContextDestroyed();
+ }
+ }
+ }
+};
+
+void Renderer::Impl::setObserver(RendererObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver();
+}
+
+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;
+ }
+
+ assert(BackendScope::exists());
+
+ updateParameters.annotationManager.updateData();
+
+ const bool zoomChanged = zoomHistory.update(updateParameters.transformState.getZoom(), updateParameters.timePoint);
+
+ const TransitionParameters transitionParameters {
+ updateParameters.timePoint,
+ updateParameters.mode == MapMode::Continuous ? updateParameters.transitionOptions : TransitionOptions()
+ };
+
+ const PropertyEvaluationParameters evaluationParameters {
+ zoomHistory,
+ updateParameters.timePoint,
+ updateParameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
+ };
+
+ const TileParameters tileParameters {
+ updateParameters.pixelRatio,
+ updateParameters.debugOptions,
+ updateParameters.transformState,
+ scheduler,
+ fileSource,
+ updateParameters.mode,
+ updateParameters.annotationManager,
+ *imageManager,
+ *glyphManager,
+ updateParameters.prefetchZoomDelta
+ };
+
+ glyphManager->setURL(updateParameters.glyphURL);
+
+ // Update light.
+ const bool lightChanged = renderLight.impl != updateParameters.light;
+
+ if (lightChanged) {
+ renderLight.impl = updateParameters.light;
+ renderLight.transition(transitionParameters);
+ }
+
+ if (lightChanged || zoomChanged || renderLight.hasTransition()) {
+ renderLight.evaluate(evaluationParameters);
+ }
+
+
+ const ImageDifference imageDiff = diffImages(imageImpls, updateParameters.images);
+ imageImpls = updateParameters.images;
+
+ // Remove removed images from sprite atlas.
+ for (const auto& entry : imageDiff.removed) {
+ imageManager->removeImage(entry.first);
+ }
+
+ // Add added images to sprite atlas.
+ for (const auto& entry : imageDiff.added) {
+ imageManager->addImage(entry.second);
+ }
+
+ // Update changed images.
+ for (const auto& entry : imageDiff.changed) {
+ imageManager->updateImage(entry.second.after);
+ }
+
+ imageManager->setLoaded(updateParameters.spriteLoaded);
+
+
+ const LayerDifference layerDiff = diffLayers(layerImpls, updateParameters.layers);
+ layerImpls = updateParameters.layers;
+
+ // Remove render layers for removed layers.
+ for (const auto& entry : layerDiff.removed) {
+ renderLayers.erase(entry.first);
+ }
+
+ // Create render layers for newly added layers.
+ for (const auto& entry : layerDiff.added) {
+ renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
+ }
+
+ // Update render layers for changed layers.
+ for (const auto& entry : layerDiff.changed) {
+ renderLayers.at(entry.first)->setImpl(entry.second.after);
+ }
+
+ // Update layers for class and zoom changes.
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ const bool layerAdded = layerDiff.added.count(entry.first);
+ const bool layerChanged = layerDiff.changed.count(entry.first);
+
+ if (layerAdded || layerChanged) {
+ layer.transition(transitionParameters);
+ }
+
+ if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
+ layer.evaluate(evaluationParameters);
+ }
+ }
+
+
+ const SourceDifference sourceDiff = diffSources(sourceImpls, updateParameters.sources);
+ sourceImpls = updateParameters.sources;
+
+ // Remove render layers for removed sources.
+ for (const auto& entry : sourceDiff.removed) {
+ renderSources.erase(entry.first);
+ }
+
+ // Create render sources for newly added sources.
+ for (const auto& entry : sourceDiff.added) {
+ std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
+ renderSource->setObserver(this);
+ renderSources.emplace(entry.first, std::move(renderSource));
+ }
+
+ const bool hasImageDiff = !(imageDiff.added.empty() && imageDiff.removed.empty() && imageDiff.changed.empty());
+
+ // Update all sources.
+ for (const auto& source : *sourceImpls) {
+ std::vector<Immutable<Layer::Impl>> filteredLayers;
+ bool needsRendering = false;
+ bool needsRelayout = false;
+
+ for (const auto& layer : *layerImpls) {
+ if (layer->type == LayerType::Background ||
+ layer->type == LayerType::Custom ||
+ layer->source != source->id) {
+ continue;
+ }
+
+ if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
+ needsRendering = true;
+ }
+
+ if (!needsRelayout && (hasImageDiff || hasLayoutDifference(layerDiff, layer->id))) {
+ needsRelayout = true;
+ }
+
+ filteredLayers.push_back(layer);
+ }
+
+ renderSources.at(source->id)->update(source,
+ filteredLayers,
+ needsRendering,
+ needsRelayout,
+ tileParameters);
+ }
+
+ transformState = updateParameters.transformState;
+
+ if (!staticData) {
+ staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio, programCacheDir);
+ }
+
+ PaintParameters parameters {
+ backend.getContext(),
+ pixelRatio,
+ contextMode,
+ backend,
+ updateParameters,
+ renderLight.getEvaluated(),
+ *staticData,
+ *imageManager,
+ *lineAtlas
+ };
+
+ bool loaded = updateParameters.styleLoaded && isLoaded();
+ if (updateParameters.mode != MapMode::Continuous && !loaded) {
+ return;
+ }
+
+ if (renderState == RenderState::Never) {
+ observer->onWillStartRenderingMap();
+ }
+
+ observer->onWillStartRenderingFrame();
+
+ backend.updateAssumedState();
+
+ if (parameters.contextMode == GLContextMode::Shared) {
+ parameters.context.setDirtyState();
+ }
+
+ Color backgroundColor;
+
+ class RenderItem {
+ public:
+ RenderLayer& layer;
+ RenderSource* source;
+ };
+
+ std::vector<RenderItem> order;
+
+ for (auto& layerImpl : *layerImpls) {
+ RenderLayer* layer = getRenderLayer(layerImpl->id);
+ assert(layer);
+
+ if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) {
+ parameters.staticData.has3D = true;
+ }
+
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+
+ if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
+ const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
+ if (parameters.contextMode == GLContextMode::Unique
+ && layerImpl.get() == layerImpls->at(0).get()
+ && paint.get<BackgroundPattern>().from.empty()) {
+ // This is a solid background. We can use glClear().
+ backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
+ } else {
+ // This is a textured background, or not the bottommost layer. We need to render it with a quad.
+ order.emplace_back(RenderItem { *layer, nullptr });
+ }
+ continue;
+ }
+
+ if (layer->is<RenderCustomLayer>()) {
+ order.emplace_back(RenderItem { *layer, nullptr });
+ continue;
+ }
+
+ RenderSource* source = getRenderSource(layer->baseImpl->source);
+ if (!source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
+ continue;
+ }
+
+ const bool symbolLayer = layer->is<RenderSymbolLayer>();
+
+ auto sortedTiles = source->getRenderTiles();
+ if (symbolLayer) {
+ // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn
+ // on top of each other, with lower symbols being drawn on top of higher symbols.
+ std::sort(sortedTiles.begin(), sortedTiles.end(), [&](const RenderTile& a, const RenderTile& b) {
+ Point<float> pa(a.id.canonical.x, a.id.canonical.y);
+ Point<float> pb(b.id.canonical.x, b.id.canonical.y);
+
+ auto par = util::rotate(pa, parameters.state.getAngle());
+ auto pbr = util::rotate(pb, parameters.state.getAngle());
+
+ return std::tie(b.id.canonical.z, par.y, par.x) < std::tie(a.id.canonical.z, pbr.y, pbr.x);
+ });
+ } else {
+ std::sort(sortedTiles.begin(), sortedTiles.end(),
+ [](const auto& a, const auto& b) { return a.get().id < b.get().id; });
+ // Don't render non-symbol layers for tiles that we're only holding on to for symbol fading
+ sortedTiles.erase(std::remove_if(sortedTiles.begin(), sortedTiles.end(),
+ [](const auto& tile) { return tile.get().tile.holdForFade(); }),
+ sortedTiles.end());
+ }
+
+ std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
+ for (auto& sortedTile : sortedTiles) {
+ auto& tile = sortedTile.get();
+ if (!tile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = tile.tile.getBucket(*layer->baseImpl);
+ if (bucket) {
+ sortedTilesForInsertion.emplace_back(tile);
+ tile.used = true;
+
+ // We only need clipping when we're _not_ drawing a symbol layer.
+ if (!symbolLayer) {
+ tile.needsClipping = true;
+ }
+ }
+ }
+ layer->setRenderTiles(std::move(sortedTilesForInsertion));
+ order.emplace_back(RenderItem { *layer, source });
+ }
+
+ bool symbolBucketsChanged = false;
+ if (parameters.mapMode != MapMode::Continuous) {
+ // TODO: Think about right way for symbol index to handle still rendering
+ crossTileSymbolIndex.reset();
+ }
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ if (crossTileSymbolIndex.addLayer(*it->layer.as<RenderSymbolLayer>())) symbolBucketsChanged = true;
+ }
+ }
+
+ bool placementChanged = false;
+ if (!placement->stillRecent(parameters.timePoint)) {
+ auto newPlacement = std::make_unique<Placement>(parameters.state, parameters.mapMode);
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ newPlacement->placeLayer(*it->layer.as<RenderSymbolLayer>(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision);
+ }
+ }
+
+ placementChanged = newPlacement->commit(*placement, parameters.timePoint);
+ if (placementChanged || symbolBucketsChanged) {
+ placement = std::move(newPlacement);
+ }
+
+ placement->setRecent(parameters.timePoint);
+
+ updateFadingTiles();
+ } else {
+ placement->setStale();
+ }
+
+ parameters.symbolFadeChange = placement->symbolFadeChange(parameters.timePoint);
+
+ if (placementChanged || symbolBucketsChanged) {
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ placement->updateLayerOpacities(*it->layer.as<RenderSymbolLayer>());
+ }
+ }
+ }
+
+ // - UPLOAD PASS -------------------------------------------------------------------------------
+ // Uploads all required buffers and images before we do any actual rendering.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "upload");
+
+ parameters.imageManager.upload(parameters.context, 0);
+ parameters.lineAtlas.upload(parameters.context, 0);
+
+ // Update all clipping IDs + upload buckets.
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ entry.second->startRender(parameters);
+ }
+ }
+ }
+
+ // - 3D PASS -------------------------------------------------------------------------------------
+ // Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same
+ // depth rbo between them.
+ if (parameters.staticData.has3D) {
+ parameters.staticData.backendSize = parameters.backend.getFramebufferSize();
+
+ MBGL_DEBUG_GROUP(parameters.context, "3d");
+ parameters.pass = RenderPass::Pass3D;
+
+ if (!parameters.staticData.depthRenderbuffer ||
+ parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) {
+ parameters.staticData.depthRenderbuffer =
+ parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize);
+ }
+ parameters.staticData.depthRenderbuffer->shouldClear(true);
+
+ uint32_t i = static_cast<uint32_t>(order.size()) - 1;
+ for (auto it = order.begin(); it != order.end(); ++it, --i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
+ }
+
+ // - CLEAR -------------------------------------------------------------------------------------
+ // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
+ // tiles whatsoever.
+ {
+ using namespace gl::value;
+
+ MBGL_DEBUG_GROUP(parameters.context, "clear");
+ parameters.backend.bind();
+ if (parameters.debugOptions & MapDebugOptions::Overdraw) {
+ parameters.context.clear(Color::black(), ClearDepth::Default, ClearStencil::Default);
+ } else if (parameters.contextMode == GLContextMode::Shared) {
+ parameters.context.clear({}, ClearDepth::Default, ClearStencil::Default);
+ } else {
+ parameters.context.clear(backgroundColor, ClearDepth::Default, ClearStencil::Default);
+ }
+ }
+
+ // - CLIPPING MASKS ----------------------------------------------------------------------------
+ // Draws the clipping masks to the stencil buffer.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "clipping masks");
+
+ static const style::FillPaintProperties::PossiblyEvaluated properties {};
+ static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
+ for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) {
+ parameters.staticData.programs.fill.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ gl::DepthMode::disabled(),
+ gl::StencilMode {
+ gl::StencilMode::Always(),
+ static_cast<int32_t>(clipID.second.reference.to_ulong()),
+ 0b11111111,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Replace
+ },
+ gl::ColorMode::disabled(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "clipping"
+ );
+ }
+ }
+
+#if not MBGL_USE_GLES2 and not defined(NDEBUG)
+ // Render tile clip boundaries, using stencil buffer to calculate fill color.
+ if (parameters.debugOptions & MapDebugOptions::StencilClip) {
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.setDepthMode(gl::DepthMode::disabled());
+ parameters.context.setColorMode(gl::ColorMode::unblended());
+ parameters.context.program = 0;
+
+ // Reset the value in case someone else changed it, or it's dirty.
+ parameters.context.pixelTransferStencil = gl::value::PixelTransferStencil::Default;
+
+ // Read the stencil buffer
+ const auto viewport = parameters.context.viewport.getCurrentValue();
+ auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false);
+
+ // Scale the Stencil buffer to cover the entire color space.
+ auto it = image.data.get();
+ auto end = it + viewport.size.width * viewport.size.height;
+ const auto factor = 255.0f / *std::max_element(it, end);
+ for (; it != end; ++it) {
+ *it *= factor;
+ }
+
+ parameters.context.pixelZoom = { 1, 1 };
+ parameters.context.rasterPos = { -1, -1, 0, 1 };
+ parameters.context.drawPixels(image);
+
+ return;
+ }
+#endif
+
+ // Actually render the layers
+
+ parameters.depthRangeSize = 1 - (order.size() + 2) * parameters.numSublayers * parameters.depthEpsilon;
+
+ // - OPAQUE PASS -------------------------------------------------------------------------------
+ // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
+ {
+ parameters.pass = RenderPass::Opaque;
+ MBGL_DEBUG_GROUP(parameters.context, "opaque");
+
+ uint32_t i = 0;
+ for (auto it = order.rbegin(); it != order.rend(); ++it, ++i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
+ }
+
+ // - TRANSLUCENT PASS --------------------------------------------------------------------------
+ // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
+ {
+ parameters.pass = RenderPass::Translucent;
+ MBGL_DEBUG_GROUP(parameters.context, "translucent");
+
+ uint32_t i = static_cast<uint32_t>(order.size()) - 1;
+ for (auto it = order.begin(); it != order.end(); ++it, --i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
+ }
+
+ // - DEBUG PASS --------------------------------------------------------------------------------
+ // Renders debug overlays.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "debug");
+
+ // Finalize the rendering, e.g. by calling debug render calls per tile.
+ // This guarantees that we have at least one function per tile called.
+ // When only rendering layers via the stylesheet, it's possible that we don't
+ // ever visit a tile during rendering.
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ entry.second->finishRender(parameters);
+ }
+ }
+ }
+
+#if not MBGL_USE_GLES2 and not defined(NDEBUG)
+ // Render the depth buffer.
+ if (parameters.debugOptions & MapDebugOptions::DepthBuffer) {
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.setDepthMode(gl::DepthMode::disabled());
+ parameters.context.setColorMode(gl::ColorMode::unblended());
+ parameters.context.program = 0;
+
+ // Scales the values in the depth buffer so that they cover the entire grayscale range. This
+ // makes it easier to spot tiny differences.
+ const float base = 1.0f / (1.0f - parameters.depthRangeSize);
+ parameters.context.pixelTransferDepth = { base, 1.0f - base };
+
+ // Read the stencil buffer
+ auto viewport = parameters.context.viewport.getCurrentValue();
+ auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false);
+
+ parameters.context.pixelZoom = { 1, 1 };
+ parameters.context.rasterPos = { -1, -1, 0, 1 };
+ parameters.context.drawPixels(image);
+ }
+#endif
+
+ // TODO: Find a better way to unbind VAOs after we're done with them without introducing
+ // unnecessary bind(0)/bind(N) sequences.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "cleanup");
+
+ parameters.context.activeTextureUnit = 1;
+ parameters.context.texture[1] = 0;
+ parameters.context.activeTextureUnit = 0;
+ parameters.context.texture[0] = 0;
+
+ parameters.context.bindVertexArray = 0;
+ }
+
+ observer->onDidFinishRenderingFrame(
+ loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
+ updateParameters.mode == MapMode::Continuous && hasTransitions(parameters.timePoint)
+ );
+
+ if (!loaded) {
+ renderState = RenderState::Partial;
+ } else if (renderState != RenderState::Fully) {
+ renderState = RenderState::Fully;
+ observer->onDidFinishRenderingMap();
+ }
+
+ // Cleanup only after signaling completion
+ parameters.context.performCleanup();
+}
+
+std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const {
+ std::vector<const RenderLayer*> layers;
+ if (options.layerIDs) {
+ for (const auto& layerID : *options.layerIDs) {
+ if (const RenderLayer* layer = getRenderLayer(layerID)) {
+ layers.emplace_back(layer);
+ }
+ }
+ } else {
+ for (const auto& entry : renderLayers) {
+ layers.emplace_back(entry.second.get());
+ }
+ }
+
+ return queryRenderedFeatures(geometry, options, layers);
+}
+
+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;
+ for (const RenderLayer* layer : layers) {
+ sourceIDs.emplace(layer->baseImpl->source);
+ }
+
+ std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
+ for (const auto& sourceID : sourceIDs) {
+ if (RenderSource* renderSource = getRenderSource(sourceID)) {
+ auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, placement->getCollisionIndex());
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+
+ std::vector<Feature> result;
+
+ if (resultsByLayer.empty()) {
+ return result;
+ }
+
+ // Combine all results based on the style layer order.
+ for (const auto& layerImpl : *layerImpls) {
+ const RenderLayer* layer = getRenderLayer(layerImpl->id);
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+ auto it = resultsByLayer.find(layer->baseImpl->id);
+ if (it != resultsByLayer.end()) {
+ std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
+ }
+ }
+
+ return result;
+}
+
+std::vector<Feature> Renderer::Impl::queryShapeAnnotations(const ScreenLineString& geometry) const {
+ std::vector<const RenderLayer*> shapeAnnotationLayers;
+ RenderedQueryOptions options;
+ for (const auto& layerImpl : *layerImpls) {
+ if (std::mismatch(layerImpl->id.begin(), layerImpl->id.end(),
+ AnnotationManager::ShapeLayerID.begin(), AnnotationManager::ShapeLayerID.end()).second == AnnotationManager::ShapeLayerID.end()) {
+ if (const RenderLayer* layer = getRenderLayer(layerImpl->id)) {
+ shapeAnnotationLayers.emplace_back(layer);
+ }
+ }
+ }
+
+ return queryRenderedFeatures(geometry, options, shapeAnnotationLayers);
+}
+
+std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
+ const RenderSource* source = getRenderSource(sourceID);
+ if (!source) return {};
+
+ return source->querySourceFeatures(options);
+}
+
+void Renderer::Impl::onLowMemory() {
+ assert(BackendScope::exists());
+ backend.getContext().performCleanup();
+ for (const auto& entry : renderSources) {
+ entry.second->onLowMemory();
+ }
+ observer->onInvalidate();
+}
+
+void Renderer::Impl::dumDebugLogs() {
+ for (const auto& entry : renderSources) {
+ entry.second->dumpDebugLogs();
+ }
+
+ imageManager->dumpDebugLogs();
+}
+
+RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+const RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) const {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const {
+ auto it = renderSources.find(id);
+ return it != renderSources.end() ? it->second.get() : nullptr;
+}
+
+bool Renderer::Impl::hasTransitions(TimePoint timePoint) const {
+ if (renderLight.hasTransition()) {
+ return true;
+ }
+
+ for (const auto& entry : renderLayers) {
+ if (entry.second->hasTransition()) {
+ return true;
+ }
+ }
+
+ if (placement->hasTransitions(timePoint)) {
+ return true;
+ }
+
+ if (fadingTiles) {
+ return true;
+ }
+
+ return false;
+}
+
+void Renderer::Impl::updateFadingTiles() {
+ fadingTiles = false;
+ for (auto& source : renderSources) {
+ for (auto& renderTile : source.second->getRenderTiles()) {
+ Tile& tile = renderTile.get().tile;
+ if (tile.holdForFade()) {
+ fadingTiles = true;
+ tile.performedFadePlacement();
+ }
+ }
+ }
+}
+
+bool Renderer::Impl::isLoaded() const {
+ for (const auto& entry: renderSources) {
+ if (!entry.second->isLoaded()) {
+ return false;
+ }
+ }
+
+ if (!imageManager->isLoaded()) {
+ return false;
+ }
+
+ return true;
+}
+
+void Renderer::Impl::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
+ glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Renderer::Impl::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
+ util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Renderer::Impl::onTileChanged(RenderSource&, const OverscaledTileID&) {
+ observer->onInvalidate();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
new file mode 100644
index 0000000000..5d0200a5df
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -0,0 +1,121 @@
+#pragma once
+
+#include <mbgl/renderer/mode.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/render_source_observer.hpp>
+#include <mbgl/renderer/render_light.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/layer.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/map/zoom_history.hpp>
+#include <mbgl/text/cross_tile_symbol_index.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/placement.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class RendererBackend;
+class RendererObserver;
+class RenderSource;
+class RenderLayer;
+class UpdateParameters;
+class RenderStaticData;
+class RenderedQueryOptions;
+class SourceQueryOptions;
+class FileSource;
+class Scheduler;
+class GlyphManager;
+class ImageManager;
+class LineAtlas;
+class CrossTileSymbolIndex;
+
+class Renderer::Impl : public GlyphManagerObserver,
+ public RenderSourceObserver{
+public:
+ Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode,
+ const optional<std::string> programCacheDir, const optional<std::string> localFontFamily);
+ ~Impl() final;
+
+ void markContextLost() {
+ contextLost = true;
+ };
+
+ void setObserver(RendererObserver*);
+
+ void render(const UpdateParameters&);
+
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&) const;
+ std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
+ std::vector<Feature> queryShapeAnnotations(const ScreenLineString&) const;
+
+ void onLowMemory();
+ void dumDebugLogs();
+
+private:
+ bool isLoaded() const;
+ bool hasTransitions(TimePoint) const;
+
+ RenderSource* getRenderSource(const std::string& id) const;
+
+ RenderLayer* getRenderLayer(const std::string& id);
+ const RenderLayer* getRenderLayer(const std::string& id) const;
+
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) const;
+
+ // GlyphManagerObserver implementation.
+ void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
+
+ // RenderSourceObserver implementation.
+ void onTileChanged(RenderSource&, const OverscaledTileID&) override;
+ void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
+
+ void updateFadingTiles();
+
+ friend class Renderer;
+
+ RendererBackend& backend;
+ Scheduler& scheduler;
+ FileSource& fileSource;
+
+ RendererObserver* observer;
+
+ const GLContextMode contextMode;
+ const float pixelRatio;
+ const optional<std::string> programCacheDir;
+
+ enum class RenderState {
+ Never,
+ Partial,
+ Fully,
+ };
+
+ RenderState renderState = RenderState::Never;
+ ZoomHistory zoomHistory;
+ TransformState transformState;
+
+ std::unique_ptr<GlyphManager> glyphManager;
+ std::unique_ptr<ImageManager> imageManager;
+ std::unique_ptr<LineAtlas> lineAtlas;
+ std::unique_ptr<RenderStaticData> staticData;
+
+ Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
+ Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
+ Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
+
+ std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
+ std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
+ RenderLight renderLight;
+
+ CrossTileSymbolIndex crossTileSymbolIndex;
+ std::unique_ptr<Placement> placement;
+
+ bool contextLost = false;
+ bool fadingTiles = false;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp
new file mode 100644
index 0000000000..551b5c803e
--- /dev/null
+++ b/src/mbgl/renderer/renderer_observer.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <exception>
+
+namespace mbgl {
+
+class RendererObserver {
+public:
+ virtual ~RendererObserver() = default;
+
+ enum class RenderMode : uint32_t {
+ Partial,
+ Full
+ };
+
+ // Signals that a repaint is required
+ virtual void onInvalidate() {}
+
+ // Resource failed to download / parse
+ virtual void onResourceError(std::exception_ptr) {}
+
+ // First frame
+ virtual void onWillStartRenderingMap() {}
+
+ // Start of frame, initial is the first frame for this map
+ virtual void onWillStartRenderingFrame() {}
+
+ // End of frame, boolean flags that a repaint is required
+ virtual void onDidFinishRenderingFrame(RenderMode, bool) {}
+
+ // Final frame
+ virtual void onDidFinishRenderingMap() {}
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
new file mode 100644
index 0000000000..111f0234ed
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
@@ -0,0 +1,86 @@
+#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/tile/custom_geometry_tile.hpp>
+
+#include <mbgl/algorithm/generate_clip_ids.hpp>
+#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_)
+ : RenderSource(impl_) {
+ tilePyramid.setObserver(this);
+}
+
+const style::CustomGeometrySource::Impl& RenderCustomGeometrySource::impl() const {
+ return static_cast<const style::CustomGeometrySource::Impl&>(*baseImpl);
+}
+
+bool RenderCustomGeometrySource::isLoaded() const {
+ return tilePyramid.isLoaded();
+}
+
+void RenderCustomGeometrySource::update(Immutable<style::Source::Impl> baseImpl_,
+ const std::vector<Immutable<Layer::Impl>>& layers,
+ const bool needsRendering,
+ const bool needsRelayout,
+ const TileParameters& parameters) {
+ std::swap(baseImpl, baseImpl_);
+
+ enabled = needsRendering;
+
+ auto tileLoader = impl().getTileLoader();
+ if (!tileLoader) {
+ return;
+ }
+
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::CustomVector,
+ util::tileSize,
+ impl().getZoomRange(),
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<CustomGeometryTile>(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader);
+ });
+}
+
+void RenderCustomGeometrySource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
+}
+
+void RenderCustomGeometrySource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
+}
+
+std::vector<std::reference_wrapper<RenderTile>> RenderCustomGeometrySource::getRenderTiles() {
+ return tilePyramid.getRenderTiles();
+}
+
+std::unordered_map<std::string, std::vector<Feature>>
+RenderCustomGeometrySource::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);
+}
+
+std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const {
+ return tilePyramid.querySourceFeatures(options);
+}
+
+void RenderCustomGeometrySource::onLowMemory() {
+ tilePyramid.onLowMemory();
+}
+
+void RenderCustomGeometrySource::dumpDebugLogs() const {
+ tilePyramid.dumpDebugLogs();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
new file mode 100644
index 0000000000..82e691d5c9
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/tile_pyramid.hpp>
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+
+namespace mbgl {
+
+class RenderCustomGeometrySource : public RenderSource {
+public:
+ RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>);
+
+ bool isLoaded() const final;
+
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
+
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
+
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
+
+ std::unordered_map<std::string, std::vector<Feature>>
+ queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
+
+ std::vector<Feature>
+ querySourceFeatures(const SourceQueryOptions&) const final;
+
+ void onLowMemory() final;
+ void dumpDebugLogs() const final;
+
+private:
+ const style::CustomGeometrySource::Impl& impl() const;
+
+ TilePyramid tilePyramid;
+};
+
+template <>
+inline bool RenderSource::is<RenderCustomGeometrySource>() const {
+ return baseImpl->type == style::SourceType::CustomVector;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp
index 337b7b8b7a..d07cfcdc41 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -1,7 +1,8 @@
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/tile/geojson_tile.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -34,19 +35,26 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
GeoJSONData* data_ = impl().getData();
- if (!data_) {
- return;
- }
-
if (data_ != data) {
data = data_;
tilePyramid.cache.clear();
- for (auto const& item : tilePyramid.tiles) {
- static_cast<GeoJSONTile*>(item.second.get())->updateData(data->getTile(item.first.canonical));
+ if (data) {
+ const uint8_t maxZ = impl().getZoomRange().max;
+ for (const auto& pair : tilePyramid.tiles) {
+ if (pair.first.canonical.z <= maxZ) {
+ static_cast<GeoJSONTile*>(pair.second.get())->updateData(data->getTile(pair.first.canonical));
+ }
+ }
}
}
+ if (!data) {
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ return;
+ }
+
tilePyramid.update(layers,
needsRendering,
needsRelayout,
@@ -59,35 +67,32 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
});
}
-void RenderGeoJSONSource::startRender(Painter& painter) {
- painter.clipIDGenerator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(painter);
+void RenderGeoJSONSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderGeoJSONSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderGeoJSONSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderGeoJSONSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const {
return tilePyramid.querySourceFeatures(options);
}
-void RenderGeoJSONSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderGeoJSONSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp
index 9b5477e1d0..55166ea901 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.hpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.hpp
@@ -22,21 +22,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final;
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex&) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -44,12 +44,12 @@ private:
const style::GeoJSONSource::Impl& impl() const;
TilePyramid tilePyramid;
- style::GeoJSONData* data;
+ style::GeoJSONData* data = nullptr;
};
template <>
inline bool RenderSource::is<RenderGeoJSONSource>() const {
- return baseImpl->type == SourceType::GeoJSON;
+ return baseImpl->type == style::SourceType::GeoJSON;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp
index f5068b9d7f..d215dc8d13 100644
--- a/src/mbgl/renderer/sources/render_image_source.cpp
+++ b/src/mbgl/renderer/sources/render_image_source.cpp
@@ -1,19 +1,23 @@
#include <mbgl/map/transform_state.hpp>
#include <mbgl/math/log2.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/constants.hpp>
namespace mbgl {
using namespace style;
RenderImageSource::RenderImageSource(Immutable<style::ImageSource::Impl> impl_)
- : RenderSource(impl_), shouldRender(false) {
+ : RenderSource(impl_) {
}
RenderImageSource::~RenderImageSource() = default;
@@ -26,7 +30,7 @@ bool RenderImageSource::isLoaded() const {
return !!bucket;
}
-void RenderImageSource::startRender(Painter& painter) {
+void RenderImageSource::startRender(PaintParameters& parameters) {
if (!isLoaded()) {
return;
}
@@ -36,31 +40,53 @@ void RenderImageSource::startRender(Painter& painter) {
for (size_t i = 0; i < tileIds.size(); i++) {
mat4 matrix;
matrix::identity(matrix);
- painter.state.matrixFor(matrix, tileIds[i]);
- matrix::multiply(matrix, painter.projMatrix, matrix);
+ parameters.state.matrixFor(matrix, tileIds[i]);
+ matrix::multiply(matrix, parameters.projMatrix, matrix);
matrices.push_back(matrix);
}
- if (bucket->needsUpload() && shouldRender) {
- bucket->upload(painter.context);
+ if (bucket->needsUpload()) {
+ bucket->upload(parameters.context);
}
}
-void RenderImageSource::finishRender(Painter& painter) {
- if (!isLoaded() || !shouldRender) {
+void RenderImageSource::finishRender(PaintParameters& parameters) {
+ if (!isLoaded() || !(parameters.debugOptions & MapDebugOptions::TileBorders)) {
return;
}
+
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
for (auto matrix : matrices) {
- painter.renderTileDebug(matrix);
+ parameters.programs.debug.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,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
}
}
std::unordered_map<std::string, std::vector<Feature>>
RenderImageSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions&) const {
- return {};
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex&) const {
+ return std::unordered_map<std::string, std::vector<Feature>> {};
}
std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const {
@@ -72,89 +98,106 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
const bool needsRendering,
const bool,
const TileParameters& parameters) {
- std::swap(baseImpl, baseImpl_);
-
enabled = needsRendering;
+ if (!needsRendering) {
+ return;
+ }
auto transformState = parameters.transformState;
- auto size = transformState.getSize();
- double viewportHeight = size.height;
+ std::swap(baseImpl, baseImpl_);
auto coords = impl().getCoordinates();
+ std::shared_ptr<PremultipliedImage> image = impl().getImage();
- // Compute the screen coordinates at wrap=0 for the given LatLng
- ScreenCoordinate nePixel = { -INFINITY, -INFINITY };
- ScreenCoordinate swPixel = { INFINITY, INFINITY };
-
- for (LatLng latLng : coords) {
- ScreenCoordinate pixel = transformState.latLngToScreenCoordinate(latLng);
- swPixel.x = std::min(swPixel.x, pixel.x);
- nePixel.x = std::max(nePixel.x, pixel.x);
- swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y);
- nePixel.y = std::max(nePixel.y, viewportHeight - pixel.y);
- }
- double width = nePixel.x - swPixel.x;
- double height = nePixel.y - swPixel.y;
-
- // Don't bother drawing the ImageSource unless it occupies >4 screen pixels
- shouldRender = (width * height > 4);
- if (!shouldRender) {
+ if (!image || !image->valid()) {
+ enabled = false;
return;
}
+ // Compute the z0 tile coordinates for the given LatLngs
+ TileCoordinatePoint nePoint = { -INFINITY, -INFINITY };
+ TileCoordinatePoint swPoint = { INFINITY, INFINITY };
+ std::vector<TileCoordinatePoint> tileCoordinates;
+ for (LatLng latLng : coords) {
+ auto point = TileCoordinate::fromLatLng(0, latLng).p;
+ tileCoordinates.push_back(point);
+ swPoint.x = std::min(swPoint.x, point.x);
+ nePoint.x = std::max(nePoint.x, point.x);
+ swPoint.y = std::min(swPoint.y, point.y);
+ nePoint.y = std::max(nePoint.y, point.y);
+ }
+
// Calculate the optimum zoom level to determine the tile ids to use for transforms
- double minScale = INFINITY;
- if (width > 0 || height > 0) {
- double scaleX = double(size.width) / width;
- double scaleY = double(size.height) / height;
- minScale = util::min(scaleX, scaleY);
+ auto dx = nePoint.x - swPoint.x;
+ auto dy = nePoint.y - swPoint.y;
+ auto dMax = std::max(dx, dy);
+ double zoom = std::max(0.0, std::floor(-util::log2(dMax)));
+
+ // Only enable if the long side of the image is > 2 pixels. Resulting in a
+ // display of at least 2 x 1 px image
+ // A tile coordinate unit represents the length of one tile (tileSize) at a given zoom.
+ // To convert a tile coordinate to pixels, multiply by tileSize.
+ // Here dMax is in z0 tile units, so we also scale by 2^z to match current zoom.
+ enabled = dMax * std::pow(2.0, transformState.getZoom()) * util::tileSize > 2.0;
+ if (!enabled) {
+ return;
}
- double zoom = transformState.getZoom() + util::log2(minScale);
- zoom = util::clamp(zoom, transformState.getMinZoom(), transformState.getMaxZoom());
auto imageBounds = LatLngBounds::hull(coords[0], coords[1]);
imageBounds.extend(coords[2]);
imageBounds.extend(coords[3]);
- auto tileCover = util::tileCover(imageBounds, ::floor(zoom));
+ auto tileCover = util::tileCover(imageBounds, zoom);
tileIds.clear();
tileIds.push_back(tileCover[0]);
+ bool hasVisibleTile = false;
// Add additional wrapped tile ids if neccessary
auto idealTiles = util::tileCover(transformState, transformState.getZoom());
for (auto tile : idealTiles) {
if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) {
tileIds.push_back({ tile.wrap, tileCover[0].canonical });
+ hasVisibleTile = true;
+ }
+ else if (!hasVisibleTile) {
+ for (auto coveringTile: tileCover) {
+ if(coveringTile.canonical == tile.canonical ||
+ coveringTile.canonical.isChildOf(tile.canonical) ||
+ tile.canonical.isChildOf(coveringTile.canonical)) {
+ hasVisibleTile = true;
+ }
+ }
}
}
+ enabled = hasVisibleTile;
+ if (!enabled) {
+ return;
+ }
+
// Calculate Geometry Coordinates based on tile cover at ideal zoom
GeometryCoordinates geomCoords;
- for (auto latLng : coords) {
- auto tc = TileCoordinate::fromLatLng(0, latLng);
- auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tc.p);
+ for (auto tileCoords : tileCoordinates) {
+ auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tileCoords);
geomCoords.push_back(gc);
}
-
- const UnassociatedImage& image = impl().getImage();
- if (!image.valid()) {
- return;
- }
-
- if (!bucket || image != bucket->image) {
- bucket = std::make_unique<RasterBucket>(image.clone());
+ if (!bucket) {
+ bucket = std::make_unique<RasterBucket>(image);
} else {
bucket->clear();
+ if (image != bucket->image) {
+ bucket->setImage(image);
+ }
}
// Set Bucket Vertices, Indices, and segments
bucket->vertices.emplace_back(
RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { 32767, 0 }));
+ RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, 32767 }));
+ RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { 32767, 32767 }));
+ RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT }));
bucket->indices.emplace_back(0, 1, 2);
bucket->indices.emplace_back(1, 2, 3);
@@ -162,16 +205,6 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
bucket->segments.emplace_back(0, 0, 4, 6);
}
-void RenderImageSource::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer) {
- if (isLoaded() && !bucket->needsUpload() && shouldRender) {
- for (auto matrix : matrices) {
- bucket->render(painter, parameters, layer, matrix);
- }
- }
-}
-
void RenderImageSource::dumpDebugLogs() const {
Log::Info(Event::General, "RenderImageSource::id: %s", impl().id.c_str());
Log::Info(Event::General, "RenderImageSource::loaded: %s", isLoaded() ? "yes" : "no");
diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp
index 5175cbf4a4..72cf4cea61 100644
--- a/src/mbgl/renderer/sources/render_image_source.hpp
+++ b/src/mbgl/renderer/sources/render_image_source.hpp
@@ -5,13 +5,8 @@
#include <mbgl/style/sources/image_source_impl.hpp>
namespace mbgl {
-class RenderLayer;
-class PaintParameters;
-class RasterBucket;
-namespace gl {
-class Context;
-} // namespace gl
+class RasterBucket;
class RenderImageSource : public RenderSource {
public:
@@ -20,9 +15,8 @@ public:
bool isLoaded() const final;
- void startRender(Painter&) final;
- void render(Painter&, PaintParameters&, const RenderLayer&);
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
void update(Immutable<style::Source::Impl>,
const std::vector<Immutable<style::Layer::Impl>>&,
@@ -30,37 +24,36 @@ public:
bool needsRelayout,
const TileParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final {
- return tiles;
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final {
+ return {};
}
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final {
- }
void onLowMemory() final {
}
void dumpDebugLogs() const final;
private:
+ friend class RenderRasterLayer;
+
const style::ImageSource::Impl& impl() const;
- std::map<UnwrappedTileID, RenderTile> tiles;
std::vector<UnwrappedTileID> tileIds;
std::unique_ptr<RasterBucket> bucket;
std::vector<mat4> matrices;
- bool shouldRender;
};
template <>
inline bool RenderSource::is<RenderImageSource>() const {
- return baseImpl->type == SourceType::Image;
+ return baseImpl->type == style::SourceType::Image;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp
index 385437af1d..f11f9b7aed 100644
--- a/src/mbgl/renderer/sources/render_raster_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_source.cpp
@@ -1,6 +1,7 @@
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/tile/raster_tile.hpp>
+#include <mbgl/algorithm/update_tile_masks.hpp>
namespace mbgl {
@@ -56,34 +57,32 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_,
});
}
-void RenderRasterSource::startRender(Painter& painter) {
- tilePyramid.startRender(painter);
+void RenderRasterSource::startRender(PaintParameters& parameters) {
+ algorithm::updateTileMasks(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderRasterSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderRasterSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderRasterSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderRasterSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderRasterSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions&) const {
- return {};
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex& ) const {
+ return std::unordered_map<std::string, std::vector<Feature>> {};
}
std::vector<Feature> RenderRasterSource::querySourceFeatures(const SourceQueryOptions&) const {
return {};
}
-void RenderRasterSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderRasterSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp
index d1e37a3099..25041fde43 100644
--- a/src/mbgl/renderer/sources/render_raster_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_source.hpp
@@ -18,21 +18,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final;
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -45,7 +45,7 @@ private:
template <>
inline bool RenderSource::is<RenderRasterSource>() const {
- return baseImpl->type == SourceType::Raster;
+ return baseImpl->type == style::SourceType::Raster;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp
index 5b266b10a5..49f8fdff2c 100644
--- a/src/mbgl/renderer/sources/render_vector_source.cpp
+++ b/src/mbgl/renderer/sources/render_vector_source.cpp
@@ -1,6 +1,6 @@
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/tile/vector_tile.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
@@ -60,35 +60,32 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_,
});
}
-void RenderVectorSource::startRender(Painter& painter) {
- painter.clipIDGenerator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(painter);
+void RenderVectorSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderVectorSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderVectorSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderVectorSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderVectorSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const {
return tilePyramid.querySourceFeatures(options);
}
-void RenderVectorSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderVectorSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp
index d5d9598a75..4a992e854f 100644
--- a/src/mbgl/renderer/sources/render_vector_source.hpp
+++ b/src/mbgl/renderer/sources/render_vector_source.hpp
@@ -18,21 +18,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final;
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -45,7 +45,7 @@ private:
template <>
inline bool RenderSource::is<RenderVectorSource>() const {
- return baseImpl->type == SourceType::Vector;
+ return baseImpl->type == style::SourceType::Vector;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/tile_mask.hpp b/src/mbgl/renderer/tile_mask.hpp
new file mode 100644
index 0000000000..5f24d63ba4
--- /dev/null
+++ b/src/mbgl/renderer/tile_mask.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <mbgl/tile/tile_id.hpp>
+
+#include <set>
+
+namespace mbgl {
+
+// A TileMask is a set of TileIDs that describe what part of a tile should be rendered. It omits
+// those parts of the tile that are covered by other/better tiles. If the entire tile should be
+// rendered, it contains the { 0, 0, 0 } tile. If it's empty, no part of the tile will be rendered.
+// TileMasks are typically generated with algorithm::updateTileMasks().
+using TileMask = std::set<CanonicalTileID>;
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/tile_parameters.hpp b/src/mbgl/renderer/tile_parameters.hpp
index cf7a5b100a..665c7490d2 100644
--- a/src/mbgl/renderer/tile_parameters.hpp
+++ b/src/mbgl/renderer/tile_parameters.hpp
@@ -13,8 +13,8 @@ class GlyphManager;
class TileParameters {
public:
- float pixelRatio;
- MapDebugOptions debugOptions;
+ const float pixelRatio;
+ const MapDebugOptions debugOptions;
const TransformState& transformState;
Scheduler& workerScheduler;
FileSource& fileSource;
@@ -22,6 +22,7 @@ public:
AnnotationManager& annotationManager;
ImageManager& imageManager;
GlyphManager& glyphManager;
+ const uint8_t prefetchZoomDelta;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp
index c2806299e3..870d9050bc 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -1,11 +1,10 @@
#include <mbgl/renderer/tile_pyramid.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/map/query.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/enum.hpp>
@@ -39,23 +38,20 @@ bool TilePyramid::isLoaded() const {
return true;
}
-void TilePyramid::startRender(Painter& painter) {
- for (auto& pair : renderTiles) {
- pair.second.startRender(painter);
+void TilePyramid::startRender(PaintParameters& parameters) {
+ for (auto& tile : renderTiles) {
+ tile.startRender(parameters);
}
}
-void TilePyramid::finishRender(Painter& painter) {
- for (auto& pair : renderTiles) {
- auto& tile = pair.second;
- if (tile.used) {
- painter.renderTileDebug(tile);
- }
+void TilePyramid::finishRender(PaintParameters& parameters) {
+ for (auto& tile : renderTiles) {
+ tile.finishRender(parameters);
}
}
-std::map<UnwrappedTileID, RenderTile>& TilePyramid::getRenderTiles() {
- return renderTiles;
+std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() {
+ return { renderTiles.begin(), renderTiles.end() };
}
void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers,
@@ -89,14 +85,27 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
// Determine the overzooming/underzooming amounts and required tiles.
int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
int32_t tileZoom = overscaledZoom;
+ int32_t panZoom = zoomRange.max;
std::vector<UnwrappedTileID> idealTiles;
+ std::vector<UnwrappedTileID> panTiles;
+
if (overscaledZoom >= zoomRange.min) {
int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
- // Make sure we're not reparsing overzoomed raster tiles.
- if (type == SourceType::Raster) {
+ // Only attempt prefetching in continuous mode.
+ if (parameters.mode == MapMode::Continuous) {
tileZoom = idealZoom;
+
+ // Request lower zoom level tiles (if configured to do so) in an attempt
+ // to show something on the screen faster at the cost of a little of bandwidth.
+ if (parameters.prefetchZoomDelta) {
+ panZoom = std::max<int32_t>(tileZoom - parameters.prefetchZoomDelta, zoomRange.min);
+ }
+
+ if (panZoom < tileZoom) {
+ panTiles = util::tileCover(parameters.transformState, panZoom);
+ }
}
idealTiles = util::tileCover(parameters.transformState, idealZoom);
@@ -107,10 +116,13 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
// use because they're still loading. In addition to that, we also need to retain all tiles that
// we're actively using, e.g. as a replacement for tile that aren't loaded yet.
std::set<OverscaledTileID> retain;
+ std::set<UnwrappedTileID> rendered;
+
+ auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void {
+ if (retain.emplace(tile.id).second) {
+ tile.setNecessity(necessity);
+ }
- auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void {
- retain.emplace(tile.id);
- tile.setNecessity(necessity);
if (needsRelayout) {
tile.setLayers(layers);
}
@@ -133,13 +145,40 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
}
return tiles.emplace(tileID, std::move(tile)).first->second.get();
};
+
+ std::map<UnwrappedTileID, Tile*> previouslyRenderedTiles;
+ for (auto& renderTile : renderTiles) {
+ previouslyRenderedTiles[renderTile.id] = &renderTile.tile;
+ }
+
auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) {
- renderTiles.emplace(tileID, RenderTile{ tileID, tile });
+ renderTiles.emplace_back(tileID, tile);
+ rendered.emplace(tileID);
+ previouslyRenderedTiles.erase(tileID); // Still rendering this tile, no need for special fading logic.
+ tile.markRenderedIdeal();
};
renderTiles.clear();
+
+ if (!panTiles.empty()) {
+ algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn,
+ [](const UnwrappedTileID&, Tile&) {}, panTiles, zoomRange, panZoom);
+ }
+
algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn,
idealTiles, zoomRange, tileZoom);
+
+ for (auto previouslyRenderedTile : previouslyRenderedTiles) {
+ Tile& tile = *previouslyRenderedTile.second;
+ tile.markRenderedPreviously();
+ if (tile.holdForFade()) {
+ // Since it was rendered in the last frame, we know we have it
+ // Don't mark the tile "Required" to avoid triggering a new network request
+ retainTileFn(tile, TileNecessity::Optional);
+ renderTiles.emplace_back(previouslyRenderedTile.first, tile);
+ rendered.emplace(previouslyRenderedTile.first);
+ }
+ }
if (type != SourceType::Annotations) {
size_t conservativeCacheSize =
@@ -150,41 +189,44 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
cache.setSize(conservativeCacheSize);
}
- removeStaleTiles(retain);
-
- const PlacementConfig config { parameters.transformState.getAngle(),
- parameters.transformState.getPitch(),
- parameters.debugOptions & MapDebugOptions::Collision };
-
- for (auto& pair : tiles) {
- pair.second->setPlacementConfig(config);
- }
-}
-
-// Moves all tiles to the cache except for those specified in the retain set.
-void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) {
// Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep
// and removes items from tiles that don't have the corresponding key in the retain set.
- auto tilesIt = tiles.begin();
- auto retainIt = retain.begin();
- while (tilesIt != tiles.end()) {
- if (retainIt == retain.end() || tilesIt->first < *retainIt) {
- tilesIt->second->setNecessity(Tile::Necessity::Optional);
- cache.add(tilesIt->first, std::move(tilesIt->second));
- tiles.erase(tilesIt++);
- } else {
- if (!(*retainIt < tilesIt->first)) {
- ++tilesIt;
+ {
+ auto tilesIt = tiles.begin();
+ auto retainIt = retain.begin();
+ while (tilesIt != tiles.end()) {
+ auto renderedIt = rendered.find(tilesIt->first.toUnwrapped());
+ if (renderedIt == rendered.end()) {
+ // Since this tile isn't in the render set, crossTileIDs won't be kept
+ // updated by CrossTileSymbolIndex. We need to reset the stored crossTileIDs
+ // so they're not reused if/when this tile is re-added to the render set
+ tilesIt->second->resetCrossTileIDs();
+ }
+ if (retainIt == retain.end() || tilesIt->first < *retainIt) {
+ if (!needsRelayout) {
+ tilesIt->second->setNecessity(TileNecessity::Optional);
+ cache.add(tilesIt->first, std::move(tilesIt->second));
+ }
+ tiles.erase(tilesIt++);
+ } else {
+ if (!(*retainIt < tilesIt->first)) {
+ ++tilesIt;
+ }
+ ++retainIt;
}
- ++retainIt;
}
}
+
+ for (auto& pair : tiles) {
+ pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision);
+ }
}
std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
return result;
@@ -199,18 +241,14 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry);
-
- auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) {
+ std::vector<std::reference_wrapper<const RenderTile>> sortedTiles{ renderTiles.begin(),
+ renderTiles.end() };
+ std::sort(sortedTiles.begin(), sortedTiles.end(), [](const RenderTile& a, const RenderTile& b) {
return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) <
std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x);
- };
- std::vector<std::reference_wrapper<const RenderTile>> sortedTiles;
- std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles),
- [](const auto& pair) { return std::ref(pair.second); });
- std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles);
+ });
- for (const auto& renderTileRef : sortedTiles) {
- const RenderTile& renderTile = renderTileRef.get();
+ for (const RenderTile& renderTile : sortedTiles) {
GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min);
if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) {
continue;
@@ -230,8 +268,9 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
renderTile.tile.queryRenderedFeatures(result,
tileSpaceQueryGeometry,
transformState,
- style,
- options);
+ layers,
+ options,
+ collisionIndex);
}
return result;
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index 5846560808..feab8a838c 100644
--- a/src/mbgl/renderer/tile_pyramid.hpp
+++ b/src/mbgl/renderer/tile_pyramid.hpp
@@ -18,10 +18,10 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
-class RenderStyle;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class TileParameters;
@@ -37,21 +37,22 @@ public:
bool needsRendering,
bool needsRelayout,
const TileParameters&,
- SourceType type,
+ style::SourceType type,
uint16_t tileSize,
Range<uint8_t> zoomRange,
std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile);
- void startRender(Painter&);
- void finishRender(Painter&);
+ void startRender(PaintParameters&);
+ void finishRender(PaintParameters&);
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles();
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles();
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const;
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
@@ -63,12 +64,10 @@ public:
bool enabled = false;
- void removeStaleTiles(const std::set<OverscaledTileID>&);
-
std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
TileCache cache;
- std::map<UnwrappedTileID, RenderTile> renderTiles;
+ std::vector<RenderTile> renderTiles;
TileObserver* observer = nullptr;
};
diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp
index ce79a4f31b..b54abc050d 100644
--- a/src/mbgl/renderer/update_parameters.hpp
+++ b/src/mbgl/renderer/update_parameters.hpp
@@ -2,20 +2,22 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/style/light.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/source.hpp>
#include <mbgl/style/layer.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/immutable.hpp>
+
+#include <vector>
namespace mbgl {
-class Scheduler;
-class FileSource;
class AnnotationManager;
class UpdateParameters {
public:
+ const bool styleLoaded;
const MapMode mode;
const float pixelRatio;
const MapDebugOptions debugOptions;
@@ -30,9 +32,12 @@ public:
const Immutable<std::vector<Immutable<style::Source::Impl>>> sources;
const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers;
- Scheduler& scheduler;
- FileSource& fileSource;
AnnotationManager& annotationManager;
+
+ const uint8_t prefetchZoomDelta;
+
+ // For still image requests, render requested
+ const bool stillImageRequest;
};
} // namespace mbgl
diff --git a/src/mbgl/shaders/circle.cpp b/src/mbgl/shaders/circle.cpp
index 2e0c76122c..c14335914b 100644
--- a/src/mbgl/shaders/circle.cpp
+++ b/src/mbgl/shaders/circle.cpp
@@ -9,7 +9,9 @@ const char* circle::name = "circle";
const char* circle::vertexSource = R"MBGL_SHADER(
uniform mat4 u_matrix;
uniform bool u_scale_with_map;
+uniform bool u_pitch_with_map;
uniform vec2 u_extrude_scale;
+uniform highp float u_camera_to_center_distance;
attribute vec2 a_pos;
@@ -22,6 +24,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
uniform lowp float a_radius_t;
attribute mediump vec2 a_radius;
@@ -30,6 +33,7 @@ varying mediump float radius;
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -38,6 +42,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -46,6 +51,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
uniform lowp float a_stroke_color_t;
attribute highp vec4 a_stroke_color;
@@ -54,6 +60,7 @@ varying highp vec4 stroke_color;
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
uniform lowp float a_stroke_width_t;
attribute mediump vec2 a_stroke_width;
@@ -62,6 +69,7 @@ varying mediump float stroke_width;
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
uniform lowp float a_stroke_opacity_t;
attribute lowp vec2 a_stroke_opacity;
@@ -70,63 +78,87 @@ varying lowp float stroke_opacity;
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main(void) {
-
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
radius = unpack_mix_vec2(a_radius, a_radius_t);
#else
mediump float radius = u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
stroke_color = unpack_mix_vec4(a_stroke_color, a_stroke_color_t);
#else
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
stroke_width = unpack_mix_vec2(a_stroke_width, a_stroke_width_t);
#else
mediump float stroke_width = u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
stroke_opacity = unpack_mix_vec2(a_stroke_opacity, a_stroke_opacity_t);
#else
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
// unencode the extrusion vector that we snuck into the a_pos vector
vec2 extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
// multiply a_pos by 0.5, since we had it * 2 in order to sneak
// in extrusion data
- gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1);
-
- if (u_scale_with_map) {
- gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale;
+ vec2 circle_center = floor(a_pos * 0.5);
+ if (u_pitch_with_map) {
+ vec2 corner_position = circle_center;
+ if (u_scale_with_map) {
+ corner_position += extrude * (radius + stroke_width) * u_extrude_scale;
+ } else {
+ // Pitching the circle with the map effectively scales it with the map
+ // To counteract the effect for pitch-scale: viewport, we rescale the
+ // whole circle based on the pitch scaling effect at its central point
+ vec4 projected_center = u_matrix * vec4(circle_center, 0, 1);
+ corner_position += extrude * (radius + stroke_width) * u_extrude_scale * (projected_center.w / u_camera_to_center_distance);
+ }
+
+ gl_Position = u_matrix * vec4(corner_position, 0, 1);
} else {
- gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w;
+ gl_Position = u_matrix * vec4(circle_center, 0, 1);
+
+ if (u_scale_with_map) {
+ gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * u_camera_to_center_distance;
+ } else {
+ gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w;
+ }
}
// This is a minimum blur distance that serves as a faux-antialiasing for
@@ -146,74 +178,88 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
varying mediump float radius;
#else
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
varying highp vec4 stroke_color;
#else
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
varying mediump float stroke_width;
#else
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
varying lowp float stroke_opacity;
#else
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_radius
mediump float radius = u_radius;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_color
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_width
mediump float stroke_width = u_stroke_width;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_opacity
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
vec2 extrude = v_data.xy;
float extrude_length = length(extrude);
diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp
index 5f733c6a1e..9d11640bf4 100644
--- a/src/mbgl/shaders/collision_box.cpp
+++ b/src/mbgl/shaders/collision_box.cpp
@@ -8,49 +8,52 @@ namespace shaders {
const char* collision_box::name = "collision_box";
const char* collision_box::vertexSource = R"MBGL_SHADER(
attribute vec2 a_pos;
+attribute vec2 a_anchor_pos;
attribute vec2 a_extrude;
-attribute vec2 a_data;
+attribute vec2 a_placed;
uniform mat4 u_matrix;
-uniform float u_scale;
+uniform vec2 u_extrude_scale;
+uniform float u_camera_to_center_distance;
-varying float v_max_zoom;
-varying float v_placement_zoom;
+varying float v_placed;
+varying float v_notUsed;
void main() {
- gl_Position = u_matrix * vec4(a_pos + a_extrude / u_scale, 0.0, 1.0);
+ vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance);
- v_max_zoom = a_data.x;
- v_placement_zoom = a_data.y;
+ gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
+ gl_Position.xy += a_extrude * u_extrude_scale * gl_Position.w * collision_perspective_ratio;
+
+ v_placed = a_placed.x;
+ v_notUsed = a_placed.y;
}
)MBGL_SHADER";
const char* collision_box::fragmentSource = R"MBGL_SHADER(
-uniform float u_zoom;
-uniform float u_maxzoom;
-varying float v_max_zoom;
-varying float v_placement_zoom;
+varying float v_placed;
+varying float v_notUsed;
void main() {
float alpha = 0.5;
- gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha;
-
- if (v_placement_zoom > u_zoom) {
- gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
- }
+ // Red = collision, hide label
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
- if (u_zoom >= v_max_zoom) {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25;
+ // Blue = no collision, label is showing
+ if (v_placed > 0.5) {
+ gl_FragColor = vec4(0.0, 0.0, 1.0, 0.5) * alpha;
}
- if (v_placement_zoom >= u_maxzoom) {
- gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0) * alpha * 0.2;
+ if (v_notUsed > 0.5) {
+ // This box not used, fade it out
+ gl_FragColor *= .1;
}
}
-
)MBGL_SHADER";
} // namespace shaders
diff --git a/src/mbgl/shaders/collision_circle.cpp b/src/mbgl/shaders/collision_circle.cpp
new file mode 100644
index 0000000000..1e85d99a33
--- /dev/null
+++ b/src/mbgl/shaders/collision_circle.cpp
@@ -0,0 +1,83 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/collision_circle.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* collision_circle::name = "collision_circle";
+const char* collision_circle::vertexSource = R"MBGL_SHADER(
+attribute vec2 a_pos;
+attribute vec2 a_anchor_pos;
+attribute vec2 a_extrude;
+attribute vec2 a_placed;
+
+uniform mat4 u_matrix;
+uniform vec2 u_extrude_scale;
+uniform float u_camera_to_center_distance;
+
+varying float v_placed;
+varying float v_notUsed;
+varying float v_radius;
+
+varying vec2 v_extrude;
+varying vec2 v_extrude_scale;
+
+void main() {
+ vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ highp float collision_perspective_ratio = 0.5 + 0.5 * (camera_to_anchor_distance / u_camera_to_center_distance);
+
+ gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
+
+ highp float padding_factor = 1.2; // Pad the vertices slightly to make room for anti-alias blur
+ gl_Position.xy += a_extrude * u_extrude_scale * padding_factor * gl_Position.w / collision_perspective_ratio;
+
+ v_placed = a_placed.x;
+ v_notUsed = a_placed.y;
+ v_radius = abs(a_extrude.y); // We don't pitch the circles, so both units of the extrusion vector are equal in magnitude to the radius
+
+ v_extrude = a_extrude * padding_factor;
+ v_extrude_scale = u_extrude_scale * u_camera_to_center_distance / collision_perspective_ratio;
+}
+
+)MBGL_SHADER";
+const char* collision_circle::fragmentSource = R"MBGL_SHADER(
+
+varying float v_placed;
+varying float v_notUsed;
+varying float v_radius;
+varying vec2 v_extrude;
+varying vec2 v_extrude_scale;
+
+void main() {
+ float alpha = 0.5;
+
+ // Red = collision, hide label
+ vec4 color = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
+
+ // Blue = no collision, label is showing
+ if (v_placed > 0.5) {
+ color = vec4(0.0, 0.0, 1.0, 0.5) * alpha;
+ }
+
+ if (v_notUsed > 0.5) {
+ // This box not used, fade it out
+ color *= .2;
+ }
+
+ float extrude_scale_length = length(v_extrude_scale);
+ float extrude_length = length(v_extrude) * extrude_scale_length;
+ float stroke_width = 3.0;
+ float radius = v_radius * extrude_scale_length;
+
+ float distance_to_edge = abs(extrude_length - radius);
+ float opacity_t = smoothstep(-stroke_width, 0.0, -distance_to_edge);
+
+ gl_FragColor = opacity_t * color;
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/collision_circle.hpp b/src/mbgl/shaders/collision_circle.hpp
new file mode 100644
index 0000000000..12b1bcd445
--- /dev/null
+++ b/src/mbgl/shaders/collision_circle.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class collision_circle {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/debug.cpp b/src/mbgl/shaders/debug.cpp
index d39dcf25be..9012cfa755 100644
--- a/src/mbgl/shaders/debug.cpp
+++ b/src/mbgl/shaders/debug.cpp
@@ -12,7 +12,7 @@ attribute vec2 a_pos;
uniform mat4 u_matrix;
void main() {
- gl_Position = u_matrix * vec4(a_pos, step(32767.0, a_pos.x), 1);
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
)MBGL_SHADER";
diff --git a/src/mbgl/shaders/fill.cpp b/src/mbgl/shaders/fill.cpp
index 8f5f304014..3ba00836a2 100644
--- a/src/mbgl/shaders/fill.cpp
+++ b/src/mbgl/shaders/fill.cpp
@@ -20,6 +20,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -28,20 +29,23 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
@@ -54,22 +58,26 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
gl_FragColor = color * opacity;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp
index ad14e4f32e..5bb2b9cd07 100644
--- a/src/mbgl/shaders/fill_extrusion.cpp
+++ b/src/mbgl/shaders/fill_extrusion.cpp
@@ -13,8 +13,7 @@ uniform lowp vec3 u_lightpos;
uniform lowp float u_lightintensity;
attribute vec2 a_pos;
-attribute vec3 a_normal;
-attribute float a_edgedistance;
+attribute vec4 a_normal_ed;
varying vec4 v_color;
@@ -27,6 +26,7 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
uniform lowp float a_height_t;
attribute lowp vec2 a_height;
@@ -36,6 +36,7 @@ uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
uniform lowp float a_color_t;
attribute highp vec4 a_color;
@@ -44,31 +45,36 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_base
base = unpack_mix_vec2(a_base, a_base_t);
#else
lowp float base = u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
height = unpack_mix_vec2(a_height, a_height_t);
#else
lowp float height = u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
+ vec3 normal = a_normal_ed.xyz;
+
base = max(0.0, base);
height = max(0.0, height);
- float ed = a_edgedistance; // use each attrib in order to not trip a VAO assert
- float t = mod(a_normal.x, 2.0);
+ float t = mod(normal.x, 2.0);
gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1);
@@ -82,7 +88,7 @@ void main() {
color += ambientlight;
// Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray
- float directional = clamp(dot(a_normal / 16384.0, u_lightpos), 0.0, 1.0);
+ float directional = clamp(dot(normal / 16384.0, u_lightpos), 0.0, 1.0);
// Adjust directional so that
// the range of values for highlight/shading is narrower
@@ -91,7 +97,7 @@ void main() {
directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional);
// Add gradient along z axis of side surfaces
- if (a_normal.y != 0.0) {
+ if (normal.y != 0.0) {
directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);
}
@@ -113,32 +119,38 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
varying lowp float height;
#else
uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
varying highp vec4 color;
#else
uniform highp vec4 u_color;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_base
lowp float base = u_base;
#endif
+
#ifdef HAS_UNIFORM_u_height
lowp float height = u_height;
#endif
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
gl_FragColor = v_color;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp
index 2681973af6..466d0e04fe 100644
--- a/src/mbgl/shaders/fill_extrusion_pattern.cpp
+++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp
@@ -22,8 +22,7 @@ uniform lowp vec3 u_lightpos;
uniform lowp float u_lightintensity;
attribute vec2 a_pos;
-attribute vec3 a_normal;
-attribute float a_edgedistance;
+attribute vec4 a_normal_ed;
varying vec2 v_pos_a;
varying vec2 v_pos_b;
@@ -39,6 +38,7 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
uniform lowp float a_height_t;
attribute lowp vec2 a_height;
@@ -47,40 +47,46 @@ varying lowp float height;
uniform lowp float u_height;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_base
base = unpack_mix_vec2(a_base, a_base_t);
#else
lowp float base = u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
height = unpack_mix_vec2(a_height, a_height_t);
#else
lowp float height = u_height;
#endif
+
+ vec3 normal = a_normal_ed.xyz;
+ float edgedistance = a_normal_ed.w;
+
base = max(0.0, base);
height = max(0.0, height);
- float t = mod(a_normal.x, 2.0);
+ float t = mod(normal.x, 2.0);
float z = t > 0.0 ? height : base;
gl_Position = u_matrix * vec4(a_pos, z, 1);
- vec2 pos = a_normal.x == 1.0 && a_normal.y == 0.0 && a_normal.z == 16384.0
+ vec2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0
? a_pos // extrusion top
- : vec2(a_edgedistance, z * u_height_factor); // extrusion side
+ : vec2(edgedistance, z * u_height_factor); // extrusion side
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, pos);
v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, pos);
v_lighting = vec4(0.0, 0.0, 0.0, 1.0);
- float directional = clamp(dot(a_normal / 16383.0, u_lightpos), 0.0, 1.0);
+ float directional = clamp(dot(normal / 16383.0, u_lightpos), 0.0, 1.0);
directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional);
- if (a_normal.y != 0.0) {
+ if (normal.y != 0.0) {
directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);
}
@@ -109,22 +115,26 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
varying lowp float height;
#else
uniform lowp float u_height;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_base
lowp float base = u_base;
#endif
+
#ifdef HAS_UNIFORM_u_height
lowp float height = u_height;
#endif
+
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
diff --git a/src/mbgl/shaders/fill_outline.cpp b/src/mbgl/shaders/fill_outline.cpp
index 18a4d8c0a8..9ade598d10 100644
--- a/src/mbgl/shaders/fill_outline.cpp
+++ b/src/mbgl/shaders/fill_outline.cpp
@@ -23,6 +23,7 @@ varying highp vec4 outline_color;
uniform highp vec4 u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -31,20 +32,23 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_outline_color
outline_color = unpack_mix_vec4(a_outline_color, a_outline_color_t);
#else
highp vec4 outline_color = u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;
}
@@ -58,24 +62,28 @@ varying highp vec4 outline_color;
uniform highp vec4 u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
varying vec2 v_pos;
void main() {
-
+
#ifdef HAS_UNIFORM_u_outline_color
highp vec4 outline_color = u_outline_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
float dist = length(v_pos - gl_FragCoord.xy);
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
gl_FragColor = outline_color * (alpha * opacity);
diff --git a/src/mbgl/shaders/fill_outline_pattern.cpp b/src/mbgl/shaders/fill_outline_pattern.cpp
index 68e69c2135..11cddb7d07 100644
--- a/src/mbgl/shaders/fill_outline_pattern.cpp
+++ b/src/mbgl/shaders/fill_outline_pattern.cpp
@@ -32,14 +32,16 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);
@@ -70,12 +72,14 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
diff --git a/src/mbgl/shaders/fill_pattern.cpp b/src/mbgl/shaders/fill_pattern.cpp
index f6f9e2fbff..a3817c4426 100644
--- a/src/mbgl/shaders/fill_pattern.cpp
+++ b/src/mbgl/shaders/fill_pattern.cpp
@@ -30,14 +30,16 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);
@@ -65,12 +67,14 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp
index 1eb92c4b71..c700295a15 100644
--- a/src/mbgl/shaders/line.cpp
+++ b/src/mbgl/shaders/line.cpp
@@ -21,7 +21,7 @@ const char* line::vertexSource = R"MBGL_SHADER(
// #define scale 63.0
#define scale 0.015873016
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -41,6 +41,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -49,6 +50,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -57,6 +59,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -64,6 +67,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -71,6 +75,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -78,61 +83,66 @@ attribute mediump vec2 a_width;
uniform mediump float u_width;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
mediump float width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
-
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
- offset = -1.0 * offset;
+ offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -149,9 +159,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -172,36 +179,42 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
varying vec2 v_width2;
varying vec2 v_normal;
varying float v_gamma_scale;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp
index 222042a13c..f8d785ade9 100644
--- a/src/mbgl/shaders/line_pattern.cpp
+++ b/src/mbgl/shaders/line_pattern.cpp
@@ -23,7 +23,7 @@ const char* line_pattern::vertexSource = R"MBGL_SHADER(
// Retina devices need a smaller distance to avoid aliasing.
#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -44,6 +44,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -52,6 +53,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -59,6 +61,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -66,6 +69,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -73,55 +77,60 @@ attribute mediump vec2 a_width;
uniform mediump float u_width;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_width
mediump float width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
- offset = -1.0 * offset;
+ offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -138,9 +147,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -178,22 +184,26 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp
index 168f4ca98d..c5d50566e8 100644
--- a/src/mbgl/shaders/line_sdf.cpp
+++ b/src/mbgl/shaders/line_sdf.cpp
@@ -23,7 +23,7 @@ const char* line_sdf::vertexSource = R"MBGL_SHADER(
// Retina devices need a smaller distance to avoid aliasing.
#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -49,6 +49,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -57,6 +58,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -65,6 +67,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -72,6 +75,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -79,6 +83,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -87,6 +92,7 @@ varying mediump float width;
uniform mediump float u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
uniform lowp float a_floorwidth_t;
attribute lowp vec2 a_floorwidth;
@@ -95,68 +101,75 @@ varying lowp float floorwidth;
uniform lowp float u_floorwidth;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
floorwidth = unpack_mix_vec2(a_floorwidth, a_floorwidth_t);
#else
lowp float floorwidth = u_floorwidth;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
offset = -1.0 * offset;
-
+
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -172,9 +185,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -209,52 +219,62 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_width
varying mediump float width;
#else
uniform mediump float u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
varying lowp float floorwidth;
#else
uniform lowp float u_floorwidth;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_width
mediump float width = u_width;
#endif
+
#ifdef HAS_UNIFORM_u_floorwidth
lowp float floorwidth = u_floorwidth;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp
index 95fa624e8d..6baa488a10 100644
--- a/src/mbgl/shaders/preludes.cpp
+++ b/src/mbgl/shaders/preludes.cpp
@@ -24,25 +24,6 @@ precision highp float;
#endif
-float evaluate_zoom_function_1(const vec4 values, const float t) {
- if (t < 1.0) {
- return mix(values[0], values[1], t);
- } else if (t < 2.0) {
- return mix(values[1], values[2], t - 1.0);
- } else {
- return mix(values[2], values[3], t - 2.0);
- }
-}
-vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) {
- if (t < 1.0) {
- return mix(value0, value1, t);
- } else if (t < 2.0) {
- return mix(value1, value2, t - 1.0);
- } else {
- return mix(value2, value3, t - 2.0);
- }
-}
-
// Unpack a pair of values that have been packed into a single float.
// The packed values are assumed to be 8-bit unsigned integers, and are
// packed like so:
@@ -53,9 +34,13 @@ vec2 unpack_float(const float packedValue) {
return vec2(v0, packedIntValue - v0 * 256);
}
+vec2 unpack_opacity(const float packedOpacity) {
+ int intOpacity = int(packedOpacity) / 2;
+ return vec2(float(intOpacity) / 127.0, mod(packedOpacity, 2.0));
+}
-// To minimize the number of attributes needed in the mapbox-gl-native shaders,
-// we encode a 4-component color into a pair of floats (i.e. a vec2) as follows:
+// To minimize the number of attributes needed, we encode a 4-component
+// color into a pair of floats (i.e. a vec2) as follows:
// [ floor(color.r * 255) * 256 + color.g * 255,
// floor(color.b * 255) * 256 + color.g * 255 ]
vec4 decode_color(const vec2 encodedColor) {
diff --git a/src/mbgl/shaders/raster.cpp b/src/mbgl/shaders/raster.cpp
index eb7a2db240..98291bfec6 100644
--- a/src/mbgl/shaders/raster.cpp
+++ b/src/mbgl/shaders/raster.cpp
@@ -20,7 +20,12 @@ varying vec2 v_pos1;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
- v_pos0 = (((a_texture_pos / 32767.0) - 0.5) / u_buffer_scale ) + 0.5;
+ // We are using Int16 for texture position coordinates to give us enough precision for
+ // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer
+ // as an arbitrarily high number to preserve adequate precision when rendering.
+ // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates,
+ // so math for modifying either is consistent.
+ v_pos0 = (((a_texture_pos / 8192.0) - 0.5) / u_buffer_scale ) + 0.5;
v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent;
}
@@ -45,6 +50,12 @@ void main() {
// read and cross-fade colors from the main and parent tiles
vec4 color0 = texture2D(u_image0, v_pos0);
vec4 color1 = texture2D(u_image1, v_pos1);
+ if (color0.a > 0.0) {
+ color0.rgb = color0.rgb / color0.a;
+ }
+ if (color1.a > 0.0) {
+ color1.rgb = color1.rgb / color1.a;
+ }
vec4 color = mix(color0, color1, u_fade_t);
color.a *= u_opacity;
vec3 rgb = color.rgb;
diff --git a/src/mbgl/shaders/shaders.cpp b/src/mbgl/shaders/shaders.cpp
index 31ff405f02..2e5a318024 100644
--- a/src/mbgl/shaders/shaders.cpp
+++ b/src/mbgl/shaders/shaders.cpp
@@ -22,6 +22,7 @@ std::string programIdentifier(const std::string& vertexSource, const std::string
ss << std::setfill('0') << std::setw(sizeof(size_t) * 2) << std::hex;
ss << std::hash<std::string>()(vertexSource);
ss << std::hash<std::string>()(fragmentSource);
+ ss << "v2";
return ss.str();
}
diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index bc570cf361..f5c2bbe22d 100644
--- a/src/mbgl/shaders/symbol_icon.cpp
+++ b/src/mbgl/shaders/symbol_icon.cpp
@@ -7,17 +7,22 @@ namespace shaders {
const char* symbol_icon::name = "symbol_icon";
const char* symbol_icon::vertexSource = R"MBGL_SHADER(
+const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec4 a_data;
+attribute vec3 a_projected_pos;
+attribute float a_fade_opacity;
-// icon-size data (see symbol_sdf.vertex.glsl for more)
-attribute vec3 a_size;
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
-uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function
-uniform mediump float u_size; // used when size is both zoom and feature constant
-uniform mediump float u_layout_size; // used when size is feature constant
+uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function
+uniform highp float u_size; // used when size is both zoom and feature constant
+uniform highp float u_camera_to_center_distance;
+uniform highp float u_pitch;
+uniform bool u_rotate_symbol;
+uniform highp float u_aspect_ratio;
+uniform float u_fade_change;
#ifndef HAS_UNIFORM_u_opacity
@@ -28,84 +33,86 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-// matrix is for the vertex position.
+
uniform mat4 u_matrix;
+uniform mat4 u_label_plane_matrix;
+uniform mat4 u_gl_coord_matrix;
uniform bool u_is_text;
-uniform mediump float u_zoom;
-uniform bool u_rotate_with_map;
-uniform vec2 u_extrude_scale;
+uniform bool u_pitch_with_map;
uniform vec2 u_texsize;
varying vec2 v_tex;
-varying vec2 v_fade_tex;
+varying float v_fade_opacity;
void main() {
-
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
vec2 a_tex = a_data.xy;
- mediump vec2 label_data = unpack_float(a_data[2]);
- mediump float a_labelminzoom = label_data[0];
- mediump vec2 a_zoom = unpack_float(a_data[3]);
- mediump float a_minzoom = a_zoom[0];
- mediump float a_maxzoom = a_zoom[1];
+ vec2 a_size = a_data.zw;
+
+ highp float segment_angle = -a_projected_pos[2];
float size;
- // In order to accommodate placing labels around corners in
- // symbol-placement: line, each glyph in a label could have multiple
- // "quad"s only one of which should be shown at a given zoom level.
- // The min/max zoom assigned to each quad is based on the font size at
- // the vector tile's zoom level, which might be different than at the
- // currently rendered zoom level if text-size is zoom-dependent.
- // Thus, we compensate for this difference by calculating an adjustment
- // based on the scale of rendered text size relative to layout text size.
- mediump float layoutSize;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = mix(a_size[0], a_size[1], u_size_t) / 10.0;
- layoutSize = a_size[2] / 10.0;
} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = a_size[0] / 10.0;
- layoutSize = size;
} else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {
size = u_size;
- layoutSize = u_layout_size;
} else {
size = u_size;
- layoutSize = u_size;
}
+ vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ // See comments in symbol_sdf.vertex
+ highp float distance_ratio = u_pitch_with_map ?
+ camera_to_anchor_distance / u_camera_to_center_distance :
+ u_camera_to_center_distance / camera_to_anchor_distance;
+ highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;
+
+ size *= perspective_ratio;
+
float fontScale = u_is_text ? size / 24.0 : size;
- mediump float zoomAdjust = log2(size / layoutSize);
- mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0;
- // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise
- mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom));
+ highp float symbol_rotation = 0.0;
+ if (u_rotate_symbol) {
+ // See comments in symbol_sdf.vertex
+ vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1);
- vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0);
- if (u_rotate_with_map) {
- gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1);
- gl_Position.z += z * gl_Position.w;
- } else {
- gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
+ vec2 a = projectedPoint.xy / projectedPoint.w;
+ vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
+
+ symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x);
}
+ highp float angle_sin = sin(segment_angle + symbol_rotation);
+ highp float angle_cos = cos(segment_angle + symbol_rotation);
+ mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
+
+ vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
+ gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
+
v_tex = a_tex / u_texsize;
- v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0);
+ vec2 fade_opacity = unpack_opacity(a_fade_opacity);
+ float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;
+ v_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));
}
)MBGL_SHADER";
const char* symbol_icon::fragmentSource = R"MBGL_SHADER(
uniform sampler2D u_texture;
-uniform sampler2D u_fadetexture;
#ifndef HAS_UNIFORM_u_opacity
@@ -114,16 +121,18 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
varying vec2 v_tex;
-varying vec2 v_fade_tex;
+varying float v_fade_opacity;
void main() {
-
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
- lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity;
+
+ lowp float alpha = opacity * v_fade_opacity;
gl_FragColor = texture2D(u_texture, v_tex) * alpha;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp
index cce6b769a6..441eaf7aac 100644
--- a/src/mbgl/shaders/symbol_sdf.cpp
+++ b/src/mbgl/shaders/symbol_sdf.cpp
@@ -11,6 +11,8 @@ const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec4 a_data;
+attribute vec3 a_projected_pos;
+attribute float a_fade_opacity;
// contents of a_size vary based on the type of property value
// used for {text,icon}-size.
@@ -18,14 +20,11 @@ attribute vec4 a_data;
// For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature.
// For composite functions:
// [ text-size(lowerZoomStop, feature),
-// text-size(upperZoomStop, feature),
-// layoutSize == text-size(layoutZoomLevel, feature) ]
-attribute vec3 a_size;
+// text-size(upperZoomStop, feature) ]
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
-uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function
-uniform mediump float u_size; // used when size is both zoom and feature constant
-uniform mediump float u_layout_size; // used when size is feature constant
+uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function
+uniform highp float u_size; // used when size is both zoom and feature constant
#ifndef HAS_UNIFORM_u_fill_color
@@ -36,6 +35,7 @@ varying highp vec4 fill_color;
uniform highp vec4 u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
uniform lowp float a_halo_color_t;
attribute highp vec4 a_halo_color;
@@ -44,6 +44,7 @@ varying highp vec4 halo_color;
uniform highp vec4 u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -52,6 +53,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
uniform lowp float a_halo_width_t;
attribute lowp vec2 a_halo_width;
@@ -60,6 +62,7 @@ varying lowp float halo_width;
uniform lowp float u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
uniform lowp float a_halo_blur_t;
attribute lowp vec2 a_halo_blur;
@@ -68,150 +71,125 @@ varying lowp float halo_blur;
uniform lowp float u_halo_blur;
#endif
-// matrix is for the vertex position.
+
uniform mat4 u_matrix;
+uniform mat4 u_label_plane_matrix;
+uniform mat4 u_gl_coord_matrix;
uniform bool u_is_text;
-uniform mediump float u_zoom;
-uniform bool u_rotate_with_map;
uniform bool u_pitch_with_map;
-uniform mediump float u_pitch;
-uniform mediump float u_bearing;
-uniform mediump float u_aspect_ratio;
-uniform vec2 u_extrude_scale;
+uniform highp float u_pitch;
+uniform bool u_rotate_symbol;
+uniform highp float u_aspect_ratio;
+uniform highp float u_camera_to_center_distance;
+uniform float u_fade_change;
uniform vec2 u_texsize;
-varying vec4 v_data0;
-varying vec2 v_data1;
+varying vec2 v_data0;
+varying vec3 v_data1;
void main() {
-
+
#ifndef HAS_UNIFORM_u_fill_color
fill_color = unpack_mix_vec4(a_fill_color, a_fill_color_t);
#else
highp vec4 fill_color = u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
halo_color = unpack_mix_vec4(a_halo_color, a_halo_color_t);
#else
highp vec4 halo_color = u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
halo_width = unpack_mix_vec2(a_halo_width, a_halo_width_t);
#else
lowp float halo_width = u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
halo_blur = unpack_mix_vec2(a_halo_blur, a_halo_blur_t);
#else
lowp float halo_blur = u_halo_blur;
#endif
+
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
vec2 a_tex = a_data.xy;
+ vec2 a_size = a_data.zw;
- mediump vec2 label_data = unpack_float(a_data[2]);
- mediump float a_labelminzoom = label_data[0];
- mediump float a_labelangle = label_data[1];
-
- mediump vec2 a_zoom = unpack_float(a_data[3]);
- mediump float a_minzoom = a_zoom[0];
- mediump float a_maxzoom = a_zoom[1];
+ highp float segment_angle = -a_projected_pos[2];
float size;
- // In order to accommodate placing labels around corners in
- // symbol-placement: line, each glyph in a label could have multiple
- // "quad"s only one of which should be shown at a given zoom level.
- // The min/max zoom assigned to each quad is based on the font size at
- // the vector tile's zoom level, which might be different than at the
- // currently rendered zoom level if text-size is zoom-dependent.
- // Thus, we compensate for this difference by calculating an adjustment
- // based on the scale of rendered text size relative to layout text size.
- mediump float layoutSize;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = mix(a_size[0], a_size[1], u_size_t) / 10.0;
- layoutSize = a_size[2] / 10.0;
} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = a_size[0] / 10.0;
- layoutSize = size;
} else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {
size = u_size;
- layoutSize = u_layout_size;
} else {
size = u_size;
- layoutSize = u_size;
}
+ vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ // If the label is pitched with the map, layout is done in pitched space,
+ // which makes labels in the distance smaller relative to viewport space.
+ // We counteract part of that effect by multiplying by the perspective ratio.
+ // If the label isn't pitched with the map, we do layout in viewport space,
+ // which makes labels in the distance larger relative to the features around
+ // them. We counteract part of that effect by dividing by the perspective ratio.
+ highp float distance_ratio = u_pitch_with_map ?
+ camera_to_anchor_distance / u_camera_to_center_distance :
+ u_camera_to_center_distance / camera_to_anchor_distance;
+ highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;
+
+ size *= perspective_ratio;
+
float fontScale = u_is_text ? size / 24.0 : size;
- mediump float zoomAdjust = log2(size / layoutSize);
- mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0;
- // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise
- // Used below to move the vertex out of the clip space for when the current
- // zoom is out of the glyph's zoom range.
- mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom));
-
- // pitch-alignment: map
- // rotation-alignment: map | viewport
- if (u_pitch_with_map) {
- lowp float angle = u_rotate_with_map ? (a_labelangle / 256.0 * 2.0 * PI) : u_bearing;
- lowp float asin = sin(angle);
- lowp float acos = cos(angle);
- mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos);
- vec2 offset = RotationMatrix * a_offset;
- vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0);
- gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1);
- gl_Position.z += z * gl_Position.w;
- // pitch-alignment: viewport
- // rotation-alignment: map
- } else if (u_rotate_with_map) {
- // foreshortening factor to apply on pitched maps
- // as a label goes from horizontal <=> vertical in angle
- // it goes from 0% foreshortening to up to around 70% foreshortening
- lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75));
-
- lowp float lineangle = a_labelangle / 256.0 * 2.0 * PI;
-
- // use the lineangle to position points a,b along the line
- // project the points and calculate the label angle in projected space
- // this calculation allows labels to be rendered unskewed on pitched maps
- vec4 a = u_matrix * vec4(a_pos, 0, 1);
- vec4 b = u_matrix * vec4(a_pos + vec2(cos(lineangle),sin(lineangle)), 0, 1);
- lowp float angle = atan((b[1]/b[3] - a[1]/a[3])/u_aspect_ratio, b[0]/b[3] - a[0]/a[3]);
- lowp float asin = sin(angle);
- lowp float acos = cos(angle);
- mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos);
-
- vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset);
- vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0);
- gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
- gl_Position.z += z * gl_Position.w;
- // pitch-alignment: viewport
- // rotation-alignment: viewport
- } else {
- vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0);
- gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
+ highp float symbol_rotation = 0.0;
+ if (u_rotate_symbol) {
+ // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units
+ // To figure out that angle in projected space, we draw a short horizontal line in tile
+ // space, project it, and measure its angle in projected space.
+ vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1);
+
+ vec2 a = projectedPoint.xy / projectedPoint.w;
+ vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
+
+ symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x);
}
+ highp float angle_sin = sin(segment_angle + symbol_rotation);
+ highp float angle_cos = cos(segment_angle + symbol_rotation);
+ mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
+
+ vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
+ gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
float gamma_scale = gl_Position.w;
vec2 tex = a_tex / u_texsize;
- vec2 fade_tex = vec2(a_labelminzoom / 255.0, 0.0);
+ vec2 fade_opacity = unpack_opacity(a_fade_opacity);
+ float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;
+ float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));
- v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y);
- v_data1 = vec2(gamma_scale, size);
+ v_data0 = vec2(tex.x, tex.y);
+ v_data1 = vec3(gamma_scale, size, interpolated_fade_opacity);
}
)MBGL_SHADER";
@@ -227,64 +205,73 @@ varying highp vec4 fill_color;
uniform highp vec4 u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
varying highp vec4 halo_color;
#else
uniform highp vec4 u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
varying lowp float halo_width;
#else
uniform lowp float u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
varying lowp float halo_blur;
#else
uniform lowp float u_halo_blur;
#endif
+
uniform sampler2D u_texture;
-uniform sampler2D u_fadetexture;
uniform highp float u_gamma_scale;
uniform bool u_is_text;
-varying vec4 v_data0;
-varying vec2 v_data1;
+varying vec2 v_data0;
+varying vec3 v_data1;
void main() {
-
+
#ifdef HAS_UNIFORM_u_fill_color
highp vec4 fill_color = u_fill_color;
#endif
+
#ifdef HAS_UNIFORM_u_halo_color
highp vec4 halo_color = u_halo_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_halo_width
lowp float halo_width = u_halo_width;
#endif
+
#ifdef HAS_UNIFORM_u_halo_blur
lowp float halo_blur = u_halo_blur;
#endif
+
vec2 tex = v_data0.xy;
- vec2 fade_tex = v_data0.zw;
float gamma_scale = v_data1.x;
float size = v_data1.y;
+ float fade_opacity = v_data1[2];
float fontScale = u_is_text ? size / 24.0 : size;
@@ -298,11 +285,10 @@ void main() {
}
lowp float dist = texture2D(u_texture, tex).a;
- lowp float fade_alpha = texture2D(u_fadetexture, fade_tex).a;
highp float gamma_scaled = gamma * gamma_scale;
- highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha;
+ highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist);
- gl_FragColor = color * (alpha * opacity);
+ gl_FragColor = color * (alpha * opacity * fade_opacity);
#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(1.0);
diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp
index 60ece5ed73..93d6dfd9ae 100644
--- a/src/mbgl/sprite/sprite_loader.cpp
+++ b/src/mbgl/sprite/sprite_loader.cpp
@@ -10,8 +10,8 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <cassert>
@@ -21,7 +21,7 @@ static SpriteLoaderObserver nullObserver;
struct SpriteLoader::Loader {
Loader(Scheduler& scheduler, SpriteLoader& imageManager)
- : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ : mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) {
}
diff --git a/src/mbgl/storage/file_source_request.cpp b/src/mbgl/storage/file_source_request.cpp
deleted file mode 100644
index 8a6fb21181..0000000000
--- a/src/mbgl/storage/file_source_request.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <mbgl/storage/file_source_request.hpp>
-
-#include <mbgl/actor/mailbox.hpp>
-#include <mbgl/util/run_loop.hpp>
-
-namespace mbgl {
-
-FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback)
- : responseCallback(callback)
- , mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())) {
-}
-
-FileSourceRequest::~FileSourceRequest() {
- if (cancelCallback) {
- cancelCallback();
- }
-
- mailbox->close();
-}
-
-void FileSourceRequest::onCancel(std::function<void()>&& callback) {
- cancelCallback = std::move(callback);
-}
-
-void FileSourceRequest::setResponse(const Response& response) {
- // Copy, because calling the callback will sometimes self
- // destroy this object. We cannot move because this method
- // can be called more than one.
- auto callback = responseCallback;
- callback(response);
-}
-
-ActorRef<FileSourceRequest> FileSourceRequest::actor() {
- return ActorRef<FileSourceRequest>(*this, mailbox);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/storage/file_source_request.hpp b/src/mbgl/storage/file_source_request.hpp
deleted file mode 100644
index 6bd0d44df6..0000000000
--- a/src/mbgl/storage/file_source_request.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <mbgl/actor/actor_ref.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/async_request.hpp>
-
-#include <memory>
-#include <functional>
-
-namespace mbgl {
-
-class Mailbox;
-
-class FileSourceRequest : public AsyncRequest {
-public:
- FileSourceRequest(FileSource::Callback&& callback);
- ~FileSourceRequest() final;
-
- void onCancel(std::function<void()>&& callback);
- void setResponse(const Response& res);
-
- ActorRef<FileSourceRequest> actor();
-
-private:
- FileSource::Callback responseCallback = nullptr;
- std::function<void()> cancelCallback = nullptr;
-
- std::shared_ptr<Mailbox> mailbox;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp
index 94bba7f8bf..207dd2ee69 100644
--- a/src/mbgl/storage/resource.cpp
+++ b/src/mbgl/storage/resource.cpp
@@ -61,17 +61,19 @@ Resource Resource::image(const std::string& url) {
}
Resource Resource::spriteImage(const std::string& base, float pixelRatio) {
- return Resource {
- Resource::Kind::SpriteImage,
- base + (pixelRatio > 1 ? "@2x" : "") + ".png"
- };
+ util::URL url(base);
+ return Resource{ Resource::Kind::SpriteImage,
+ base.substr(0, url.path.first + url.path.second) +
+ (pixelRatio > 1 ? "@2x" : "") + ".png" +
+ base.substr(url.query.first, url.query.second) };
}
Resource Resource::spriteJSON(const std::string& base, float pixelRatio) {
- return Resource {
- Resource::Kind::SpriteJSON,
- base + (pixelRatio > 1 ? "@2x" : "") + ".json"
- };
+ util::URL url(base);
+ return Resource{ Resource::Kind::SpriteJSON,
+ base.substr(0, url.path.first + url.path.second) +
+ (pixelRatio > 1 ? "@2x" : "") + ".json" +
+ base.substr(url.query.first, url.query.second) };
}
Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange) {
@@ -95,7 +97,7 @@ Resource Resource::tile(const std::string& urlTemplate,
int32_t y,
int8_t z,
Tileset::Scheme scheme,
- Necessity necessity) {
+ LoadingMethod loadingMethod) {
bool supportsRatio = urlTemplate.find("{ratio}") != std::string::npos;
if (scheme == Tileset::Scheme::TMS) {
y = (1 << z) - y - 1;
@@ -131,7 +133,7 @@ Resource Resource::tile(const std::string& urlTemplate,
y,
z
},
- necessity
+ loadingMethod
};
}
diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp
index a174339074..222f55db84 100644
--- a/src/mbgl/storage/response.cpp
+++ b/src/mbgl/storage/response.cpp
@@ -14,6 +14,7 @@ Response& Response::operator=(const Response& res) {
error = res.error ? std::make_unique<Error>(*res.error) : nullptr;
noContent = res.noContent;
notModified = res.notModified;
+ mustRevalidate = res.mustRevalidate;
data = res.data;
modified = res.modified;
expires = res.expires;
diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp
new file mode 100644
index 0000000000..e837c4e70b
--- /dev/null
+++ b/src/mbgl/style/conversion/constant.cpp
@@ -0,0 +1,94 @@
+#include <mbgl/style/conversion/constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<bool> Converter<bool>::operator()(const Convertible& value, Error& error) const {
+ optional<bool> converted = toBool(value);
+ if (!converted) {
+ error = { "value must be a boolean" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<float> Converter<float>::operator()(const Convertible& value, Error& error) const {
+ optional<float> converted = toNumber(value);
+ if (!converted) {
+ error = { "value must be a number" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<std::string> Converter<std::string>::operator()(const Convertible& value, Error& error) const {
+ optional<std::string> converted = toString(value);
+ if (!converted) {
+ error = { "value must be a string" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<Color> Converter<Color>::operator()(const Convertible& value, Error& error) const {
+ optional<std::string> string = toString(value);
+ if (!string) {
+ error = { "value must be a string" };
+ return {};
+ }
+
+ optional<Color> color = Color::parse(*string);
+ if (!color) {
+ error = { "value must be a valid color" };
+ return {};
+ }
+
+ return *color;
+}
+
+optional<std::vector<float>> Converter<std::vector<float>>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "value must be an array" };
+ return {};
+ }
+
+ std::vector<float> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<float> number = toNumber(arrayMember(value, i));
+ if (!number) {
+ error = { "value must be an array of numbers" };
+ return {};
+ }
+ result.push_back(*number);
+ }
+
+ return result;
+}
+
+optional<std::vector<std::string>> Converter<std::vector<std::string>>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "value must be an array" };
+ return {};
+ }
+
+ std::vector<std::string> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<std::string> string = toString(arrayMember(value, i));
+ if (!string) {
+ error = { "value must be an array of strings" };
+ return {};
+ }
+ result.push_back(*string);
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/coordinate.cpp b/src/mbgl/style/conversion/coordinate.cpp
new file mode 100644
index 0000000000..9b2be3381e
--- /dev/null
+++ b/src/mbgl/style/conversion/coordinate.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/style/conversion/coordinate.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<LatLng> Converter<LatLng>::operator() (const Convertible& value, Error& error) const {
+ if (!isArray(value) || arrayLength(value) < 2 ) {
+ error = { "coordinate array must contain numeric longitude and latitude values" };
+ return {};
+ }
+ //Style spec uses GeoJSON convention for specifying coordinates
+ optional<double> latitude = toDouble(arrayMember(value, 1));
+ optional<double> longitude = toDouble(arrayMember(value, 0));
+
+ if (!latitude || !longitude) {
+ error = { "coordinate array must contain numeric longitude and latitude values" };
+ return {};
+ }
+ if (*latitude < -90 || *latitude > 90 ){
+ error = { "coordinate latitude must be between -90 and 90" };
+ return {};
+ }
+ return LatLng(*latitude, *longitude);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp
new file mode 100644
index 0000000000..bb7bb6ea98
--- /dev/null
+++ b/src/mbgl/style/conversion/filter.cpp
@@ -0,0 +1,248 @@
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+static optional<Value> normalizeValue(const optional<Value>& value, Error& error) {
+ if (!value) {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ } else {
+ return *value;
+ }
+}
+
+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 {};
+ }
+}
+
+static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
+ optional<Value> 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 {};
+ });
+ }
+}
+
+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 {} };
+ } else {
+ return { FilterType { *key } };
+ }
+}
+
+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 {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key 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 {
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { FilterType { *key, *filterValue } };
+ }
+}
+
+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<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ 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) } };
+
+ } else {
+ std::vector<Value> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { FilterType { *key, std::move(values) } };
+ }
+}
+
+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);
+ }
+
+ return { FilterType { std::move(filters) } };
+}
+
+optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
+ 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(value, 0));
+ if (!op) {
+ error = { "filter operator must be a string" };
+ return {};
+ }
+
+ 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
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/geojson.cpp b/src/mbgl/style/conversion/geojson.cpp
index 8103e9014a..e39a1a80eb 100644
--- a/src/mbgl/style/conversion/geojson.cpp
+++ b/src/mbgl/style/conversion/geojson.cpp
@@ -1,26 +1,16 @@
#include <mbgl/style/conversion/geojson.hpp>
#include <mbgl/style/conversion/json.hpp>
-#include <mbgl/util/rapidjson.hpp>
-
-#include <mapbox/geojson.hpp>
-#include <mapbox/geojson/rapidjson.hpp>
namespace mbgl {
namespace style {
namespace conversion {
-optional<GeoJSON> Converter<GeoJSON>::operator()(const std::string& value, Error& error) const {
- return convertJSON<GeoJSON>(value, error);
+optional<GeoJSON> Converter<GeoJSON>::operator()(const Convertible& value, Error& error) const {
+ return toGeoJSON(value, error);
}
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const JSValue& value, Error& error) const {
- try {
- return mapbox::geojson::convert(value);
- } catch (const std::exception& ex) {
- error = { ex.what() };
- return {};
- }
+optional<GeoJSON> parseGeoJSON(const std::string& value, Error& error) {
+ return convertJSON<GeoJSON>(value, error);
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp
new file mode 100644
index 0000000000..a2c5ed8816
--- /dev/null
+++ b/src/mbgl/style/conversion/geojson_options.cpp
@@ -0,0 +1,85 @@
+#include <mbgl/style/conversion/geojson_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value, Error& error) const {
+ GeoJSONOptions options;
+
+ const auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ if (toNumber(*minzoomValue)) {
+ options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
+ } else {
+ error = { "GeoJSON source minzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ if (toNumber(*maxzoomValue)) {
+ options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
+ } else {
+ error = { "GeoJSON source maxzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto bufferValue = objectMember(value, "buffer");
+ if (bufferValue) {
+ if (toNumber(*bufferValue)) {
+ options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
+ } else {
+ error = { "GeoJSON source buffer value must be a number" };
+ return {};
+ }
+ }
+
+ const auto toleranceValue = objectMember(value, "tolerance");
+ if (toleranceValue) {
+ if (toNumber(*toleranceValue)) {
+ options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
+ } else {
+ error = { "GeoJSON source tolerance value must be a number" };
+ return {};
+ }
+ }
+
+ const auto clusterValue = objectMember(value, "cluster");
+ if (clusterValue) {
+ if (toBool(*clusterValue)) {
+ options.cluster = *toBool(*clusterValue);
+ } else {
+ error = { "GeoJSON source cluster value must be a boolean" };
+ return {};
+ }
+ }
+
+ const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
+ if (clusterMaxZoomValue) {
+ if (toNumber(*clusterMaxZoomValue)) {
+ options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
+ } else {
+ error = { "GeoJSON source clusterMaxZoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto clusterRadiusValue = objectMember(value, "clusterRadius");
+ if (clusterRadiusValue) {
+ if (toNumber(*clusterRadiusValue)) {
+ options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
+ } else {
+ error = { "GeoJSON source clusterRadius value must be a number" };
+ return {};
+ }
+ }
+
+ return { options };
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/get_json_type.cpp b/src/mbgl/style/conversion/get_json_type.cpp
new file mode 100644
index 0000000000..cd3b4608b1
--- /dev/null
+++ b/src/mbgl/style/conversion/get_json_type.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/style/conversion/get_json_type.hpp>
+#include <mbgl/util/feature.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+std::string getJSONType(const Convertible& value) {
+ if (isUndefined(value)) {
+ return "null";
+ }
+ if (isArray(value)) {
+ return "array";
+ }
+ if (isObject(value)) {
+ return "object";
+ }
+ optional<mbgl::Value> v = toValue(value);
+
+ // Since we've already checked the non-atomic types above, value must then
+ // be a string, number, or boolean -- thus, assume that the toValue()
+ // conversion succeeds.
+ assert(v);
+
+ return v->match(
+ [&] (const std::string&) { return "string"; },
+ [&] (bool) { return "boolean"; },
+ [&] (auto) { return "number"; }
+ );
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/json.hpp b/src/mbgl/style/conversion/json.hpp
index 0817ac09df..7dd2378f6b 100644
--- a/src/mbgl/style/conversion/json.hpp
+++ b/src/mbgl/style/conversion/json.hpp
@@ -20,7 +20,7 @@ optional<T> convertJSON(const std::string& json, Error& error, Args&&...args) {
return {};
}
- return convert<T, JSValue>(document, error, std::forward<Args>(args)...);
+ return convert<T>(document, error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/layer.cpp b/src/mbgl/style/conversion/layer.cpp
new file mode 100644
index 0000000000..0ca582f8dc
--- /dev/null
+++ b/src/mbgl/style/conversion/layer.cpp
@@ -0,0 +1,206 @@
+#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/conversion/make_property_setters.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value) {
+ static const auto setters = makeLayoutPropertySetters();
+ auto it = setters.find(name);
+ if (it == setters.end()) {
+ return Error { "property not found" };
+ }
+ return it->second(layer, value);
+}
+
+optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value) {
+ static const auto setters = makePaintPropertySetters();
+ auto it = setters.find(name);
+ if (it == setters.end()) {
+ return Error { "property not found" };
+ }
+ return it->second(layer, value);
+}
+
+optional<Error> setPaintProperties(Layer& layer, const Convertible& value) {
+ auto paintValue = objectMember(value, "paint");
+ if (!paintValue) {
+ return {};
+ }
+ return eachMember(*paintValue, [&] (const std::string& k, const Convertible& v) {
+ return setPaintProperty(layer, k, v);
+ });
+}
+
+template <class LayerType>
+optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const Convertible& value, Error& error) {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ error = { "layer must have a source" };
+ return {};
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ error = { "layer source must be a string" };
+ return {};
+ }
+
+ std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
+
+ auto sourceLayerValue = objectMember(value, "source-layer");
+ if (sourceLayerValue) {
+ optional<std::string> sourceLayer = toString(*sourceLayerValue);
+ if (!sourceLayer) {
+ error = { "layer source-layer must be a string" };
+ return {};
+ }
+ layer->setSourceLayer(*sourceLayer);
+ }
+
+ auto filterValue = objectMember(value, "filter");
+ if (filterValue) {
+ optional<Filter> filter = convert<Filter>(*filterValue, error);
+ if (!filter) {
+ return {};
+ }
+ layer->setFilter(*filter);
+ }
+
+ return { std::move(layer) };
+}
+
+static optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const Convertible& value, Error& error) {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ error = { "layer must have a source" };
+ return {};
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ error = { "layer source must be a string" };
+ return {};
+ }
+
+ return { std::make_unique<RasterLayer>(id, *source) };
+}
+
+static optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const Convertible&, Error&) {
+ return { std::make_unique<BackgroundLayer>(id) };
+}
+
+optional<std::unique_ptr<Layer>> Converter<std::unique_ptr<Layer>>::operator()(const Convertible& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "layer must be an object" };
+ return {};
+ }
+
+ auto idValue = objectMember(value, "id");
+ if (!idValue) {
+ error = { "layer must have an id" };
+ return {};
+ }
+
+ optional<std::string> id = toString(*idValue);
+ if (!id) {
+ error = { "layer id must be a string" };
+ return {};
+ }
+
+ auto typeValue = objectMember(value, "type");
+ if (!typeValue) {
+ error = { "layer must have a type" };
+ return {};
+ }
+
+ optional<std::string> type = toString(*typeValue);
+ if (!type) {
+ error = { "layer type must be a string" };
+ return {};
+ }
+
+ optional<std::unique_ptr<Layer>> converted;
+
+ if (*type == "fill") {
+ converted = convertVectorLayer<FillLayer>(*id, value, error);
+ } else if (*type == "fill-extrusion") {
+ converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
+ } else if (*type == "line") {
+ converted = convertVectorLayer<LineLayer>(*id, value, error);
+ } else if (*type == "circle") {
+ converted = convertVectorLayer<CircleLayer>(*id, value, error);
+ } else if (*type == "symbol") {
+ converted = convertVectorLayer<SymbolLayer>(*id, value, error);
+ } else if (*type == "raster") {
+ converted = convertRasterLayer(*id, value, error);
+ } else if (*type == "background") {
+ converted = convertBackgroundLayer(*id, value, error);
+ } else {
+ error = { "invalid layer type" };
+ return {};
+ }
+
+ if (!converted) {
+ return converted;
+ }
+
+ std::unique_ptr<Layer> layer = std::move(*converted);
+
+ auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ optional<float> minzoom = toNumber(*minzoomValue);
+ if (!minzoom) {
+ error = { "minzoom must be numeric" };
+ return {};
+ }
+ layer->setMinZoom(*minzoom);
+ }
+
+ auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ optional<float> maxzoom = toNumber(*maxzoomValue);
+ if (!maxzoom) {
+ error = { "maxzoom must be numeric" };
+ return {};
+ }
+ layer->setMaxZoom(*maxzoom);
+ }
+
+ auto layoutValue = objectMember(value, "layout");
+ if (layoutValue) {
+ if (!isObject(*layoutValue)) {
+ error = { "layout must be an object" };
+ return {};
+ }
+ optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const Convertible& v) {
+ return setLayoutProperty(*layer, k, v);
+ });
+ if (error_) {
+ error = *error_;
+ return {};
+ }
+ }
+
+ optional<Error> error_ = setPaintProperties(*layer, value);
+ if (error_) {
+ error = *error_;
+ return {};
+ }
+
+ return std::move(layer);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/light.cpp b/src/mbgl/style/conversion/light.cpp
new file mode 100644
index 0000000000..f521f74386
--- /dev/null
+++ b/src/mbgl/style/conversion/light.cpp
@@ -0,0 +1,115 @@
+#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/conversion/position.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Light> Converter<Light>::operator()(const Convertible& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "light must be an object" };
+ return {};
+ }
+
+ Light light;
+
+ const auto anchor = objectMember(value, "anchor");
+ if (anchor) {
+ optional<PropertyValue<LightAnchorType>> convertedAnchor =
+ convert<PropertyValue<LightAnchorType>>(*anchor, error);
+
+ if (convertedAnchor) {
+ light.setAnchor(*convertedAnchor);
+ } else {
+ return {};
+ }
+ }
+
+ const auto anchorTransition = objectMember(value, "anchor-transition");
+ if (anchorTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*anchorTransition, error);
+ if (transition) {
+ light.setAnchorTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto color = objectMember(value, "color");
+ if (color) {
+ optional<PropertyValue<Color>> convertedColor =
+ convert<PropertyValue<Color>>(*color, error);
+
+ if (convertedColor) {
+ light.setColor(*convertedColor);
+ } else {
+ return {};
+ }
+ }
+
+ const auto colorTransition = objectMember(value, "color-transition");
+ if (colorTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*colorTransition, error);
+ if (transition) {
+ light.setColorTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto position = objectMember(value, "position");
+ if (position) {
+ optional<PropertyValue<Position>> convertedPosition =
+ convert<PropertyValue<Position>>(*position, error);
+
+ if (convertedPosition) {
+ light.setPosition(*convertedPosition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto positionTransition = objectMember(value, "position-transition");
+ if (positionTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*positionTransition, error);
+ if (transition) {
+ light.setPositionTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ const auto intensity = objectMember(value, "intensity");
+ if (intensity) {
+ optional<PropertyValue<float>> convertedIntensity =
+ convert<PropertyValue<float>>(*intensity, error);
+
+ if (convertedIntensity) {
+ light.setIntensity(*convertedIntensity);
+ } else {
+ return {};
+ }
+ }
+
+ const auto intensityTransition = objectMember(value, "intensity-transition");
+ if (intensityTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*intensityTransition, error);
+ if (transition) {
+ light.setIntensityTransition(*transition);
+ } else {
+ return {};
+ }
+ }
+
+ return { std::move(light) };
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp
new file mode 100644
index 0000000000..074d7eb730
--- /dev/null
+++ b/src/mbgl/style/conversion/make_property_setters.hpp
@@ -0,0 +1,209 @@
+#pragma once
+
+// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
+
+#include <mbgl/style/conversion/property_setter.hpp>
+
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline auto makeLayoutPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+ result["visibility"] = &setVisibility;
+
+
+ result["line-cap"] = &setProperty<LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
+ result["line-join"] = &setProperty<LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
+ result["line-miter-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
+ result["line-round-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
+
+ result["symbol-placement"] = &setProperty<SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
+ result["symbol-spacing"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
+ result["symbol-avoid-edges"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
+ result["icon-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
+ result["icon-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
+ result["icon-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
+ result["icon-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
+ result["icon-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
+ result["icon-text-fit"] = &setProperty<SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
+ result["icon-text-fit-padding"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
+ result["icon-image"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
+ result["icon-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
+ result["icon-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
+ result["icon-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
+ result["icon-offset"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
+ result["icon-anchor"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
+ result["icon-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
+ result["text-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
+ result["text-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
+ result["text-field"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
+ result["text-font"] = &setProperty<SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
+ result["text-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
+ result["text-max-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
+ result["text-line-height"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
+ result["text-letter-spacing"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
+ result["text-justify"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
+ result["text-anchor"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
+ result["text-max-angle"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
+ result["text-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
+ result["text-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
+ result["text-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
+ result["text-transform"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
+ result["text-offset"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
+ result["text-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
+ result["text-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
+ result["text-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
+
+
+
+
+
+ return result;
+}
+
+inline auto makePaintPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+ result["fill-antialias"] = &setProperty<FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
+ result["fill-antialias-transition"] = &setTransition<FillLayer, &FillLayer::setFillAntialiasTransition>;
+ result["fill-opacity"] = &setProperty<FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
+ result["fill-opacity-transition"] = &setTransition<FillLayer, &FillLayer::setFillOpacityTransition>;
+ result["fill-color"] = &setProperty<FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
+ result["fill-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillColorTransition>;
+ result["fill-outline-color"] = &setProperty<FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
+ result["fill-outline-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillOutlineColorTransition>;
+ result["fill-translate"] = &setProperty<FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
+ result["fill-translate-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateTransition>;
+ result["fill-translate-anchor"] = &setProperty<FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
+ result["fill-translate-anchor-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
+ result["fill-pattern"] = &setProperty<FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
+ result["fill-pattern-transition"] = &setTransition<FillLayer, &FillLayer::setFillPatternTransition>;
+
+ result["line-opacity"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
+ result["line-opacity-transition"] = &setTransition<LineLayer, &LineLayer::setLineOpacityTransition>;
+ result["line-color"] = &setProperty<LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
+ result["line-color-transition"] = &setTransition<LineLayer, &LineLayer::setLineColorTransition>;
+ result["line-translate"] = &setProperty<LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
+ result["line-translate-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateTransition>;
+ result["line-translate-anchor"] = &setProperty<LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
+ result["line-translate-anchor-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
+ result["line-width"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
+ result["line-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineWidthTransition>;
+ result["line-gap-width"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
+ result["line-gap-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineGapWidthTransition>;
+ result["line-offset"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
+ result["line-offset-transition"] = &setTransition<LineLayer, &LineLayer::setLineOffsetTransition>;
+ result["line-blur"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
+ result["line-blur-transition"] = &setTransition<LineLayer, &LineLayer::setLineBlurTransition>;
+ result["line-dasharray"] = &setProperty<LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
+ result["line-dasharray-transition"] = &setTransition<LineLayer, &LineLayer::setLineDasharrayTransition>;
+ result["line-pattern"] = &setProperty<LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
+ result["line-pattern-transition"] = &setTransition<LineLayer, &LineLayer::setLinePatternTransition>;
+
+ result["icon-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
+ result["icon-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
+ result["icon-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
+ result["icon-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconColorTransition>;
+ result["icon-halo-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
+ result["icon-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloColorTransition>;
+ result["icon-halo-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
+ result["icon-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>;
+ result["icon-halo-blur"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
+ result["icon-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
+ result["icon-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
+ result["icon-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateTransition>;
+ result["icon-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
+ result["icon-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
+ result["text-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
+ result["text-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
+ result["text-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
+ result["text-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextColorTransition>;
+ result["text-halo-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
+ result["text-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloColorTransition>;
+ result["text-halo-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
+ result["text-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>;
+ result["text-halo-blur"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
+ result["text-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
+ result["text-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
+ result["text-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateTransition>;
+ result["text-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
+ result["text-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
+
+ result["circle-radius"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
+ result["circle-radius-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleRadiusTransition>;
+ result["circle-color"] = &setProperty<CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
+ result["circle-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleColorTransition>;
+ result["circle-blur"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
+ result["circle-blur-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleBlurTransition>;
+ result["circle-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
+ result["circle-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleOpacityTransition>;
+ result["circle-translate"] = &setProperty<CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
+ result["circle-translate-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateTransition>;
+ result["circle-translate-anchor"] = &setProperty<CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
+ result["circle-translate-anchor-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>;
+ result["circle-pitch-scale"] = &setProperty<CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
+ result["circle-pitch-scale-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchScaleTransition>;
+ result["circle-pitch-alignment"] = &setProperty<CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
+ result["circle-pitch-alignment-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
+ result["circle-stroke-width"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
+ result["circle-stroke-width-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
+ result["circle-stroke-color"] = &setProperty<CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
+ result["circle-stroke-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
+ result["circle-stroke-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
+ result["circle-stroke-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
+
+ result["fill-extrusion-opacity"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
+ result["fill-extrusion-opacity-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
+ result["fill-extrusion-color"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
+ result["fill-extrusion-color-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
+ result["fill-extrusion-translate"] = &setProperty<FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
+ result["fill-extrusion-translate-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
+ result["fill-extrusion-translate-anchor"] = &setProperty<FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
+ result["fill-extrusion-translate-anchor-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
+ result["fill-extrusion-pattern"] = &setProperty<FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
+ result["fill-extrusion-pattern-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
+ result["fill-extrusion-height"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
+ result["fill-extrusion-height-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
+ result["fill-extrusion-base"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
+ result["fill-extrusion-base-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
+
+ result["raster-opacity"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
+ result["raster-opacity-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterOpacityTransition>;
+ result["raster-hue-rotate"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
+ result["raster-hue-rotate-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
+ result["raster-brightness-min"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
+ result["raster-brightness-min-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
+ result["raster-brightness-max"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
+ result["raster-brightness-max-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
+ result["raster-saturation"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
+ result["raster-saturation-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterSaturationTransition>;
+ result["raster-contrast"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
+ result["raster-contrast-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterContrastTransition>;
+ result["raster-fade-duration"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
+ result["raster-fade-duration-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
+
+ result["background-color"] = &setProperty<BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
+ result["background-color-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
+ result["background-pattern"] = &setProperty<BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
+ result["background-pattern-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
+ result["background-opacity"] = &setProperty<BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
+ result["background-opacity-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/make_property_setters.hpp.ejs b/src/mbgl/style/conversion/make_property_setters.hpp.ejs
new file mode 100644
index 0000000000..2975cb19f2
--- /dev/null
+++ b/src/mbgl/style/conversion/make_property_setters.hpp.ejs
@@ -0,0 +1,46 @@
+#pragma once
+
+// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
+
+#include <mbgl/style/conversion/property_setter.hpp>
+
+<% for (const layer of locals.layers) { -%>
+#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
+<% } -%>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline auto makeLayoutPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+ result["visibility"] = &setVisibility;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.layoutProperties) { -%>
+ result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+inline auto makePaintPropertySetters() {
+ std::unordered_map<std::string, PropertySetter> result;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.paintProperties) { -%>
+ result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
+ result["<%- property.name %>-transition"] = &setTransition<<%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/position.cpp b/src/mbgl/style/conversion/position.cpp
new file mode 100644
index 0000000000..702d250dbf
--- /dev/null
+++ b/src/mbgl/style/conversion/position.cpp
@@ -0,0 +1,22 @@
+#include <mbgl/style/conversion/position.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+
+#include <array>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Position> Converter<Position>::operator()(const Convertible& value, Error& error) const {
+ optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
+
+ if (!spherical) {
+ return {};
+ }
+
+ return Position(*spherical);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/property_setter.hpp b/src/mbgl/style/conversion/property_setter.hpp
new file mode 100644
index 0000000000..9e382b9c38
--- /dev/null
+++ b/src/mbgl/style/conversion/property_setter.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+#include <mbgl/style/conversion/data_driven_property_value.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+using PropertySetter = optional<Error> (*) (Layer&, const Convertible&);
+
+template <class L, class PropertyValue, void (L::*setter)(PropertyValue)>
+optional<Error> setProperty(Layer& layer, const Convertible& value) {
+ auto* typedLayer = layer.as<L>();
+ if (!typedLayer) {
+ return Error { "layer doesn't support this property" };
+ }
+
+ Error error;
+ optional<PropertyValue> typedValue = convert<PropertyValue>(value, error);
+ if (!typedValue) {
+ return error;
+ }
+
+ (typedLayer->*setter)(*typedValue);
+ return {};
+}
+
+template <class L, void (L::*setter)(const TransitionOptions&)>
+optional<Error> setTransition(Layer& layer, const Convertible& value) {
+ auto* typedLayer = layer.as<L>();
+ if (!typedLayer) {
+ return Error { "layer doesn't support this property" };
+ }
+
+ Error error;
+ optional<TransitionOptions> transition = convert<TransitionOptions>(value, error);
+ if (!transition) {
+ return error;
+ }
+
+ (typedLayer->*setter)(*transition);
+ return {};
+}
+
+inline optional<Error> setVisibility(Layer& layer, const Convertible& value) {
+ if (isUndefined(value)) {
+ layer.setVisibility(VisibilityType::Visible);
+ return {};
+ }
+
+ Error error;
+ optional<VisibilityType> visibility = convert<VisibilityType>(value, error);
+ if (!visibility) {
+ return error;
+ }
+
+ layer.setVisibility(*visibility);
+ return {};
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/source.cpp b/src/mbgl/style/conversion/source.cpp
new file mode 100644
index 0000000000..c10d0babcf
--- /dev/null
+++ b/src/mbgl/style/conversion/source.cpp
@@ -0,0 +1,175 @@
+#include <mbgl/style/conversion/source.hpp>
+#include <mbgl/style/conversion/coordinate.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/style/conversion/geojson_options.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/util/geo.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+// A tile source can either specify a URL to TileJSON, or inline TileJSON.
+static optional<variant<std::string, Tileset>> convertURLOrTileset(const Convertible& value, Error& error) {
+ auto urlVal = objectMember(value, "url");
+ if (!urlVal) {
+ optional<Tileset> tileset = convert<Tileset>(value, error);
+ if (!tileset) {
+ return {};
+ }
+ return { *tileset };
+ }
+
+ optional<std::string> url = toString(*urlVal);
+ if (!url) {
+ error = { "source url must be a string" };
+ return {};
+ }
+
+ return { *url };
+}
+
+static optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
+ if (!urlOrTileset) {
+ return {};
+ }
+
+ uint16_t tileSize = util::tileSize;
+ auto tileSizeValue = objectMember(value, "tileSize");
+ if (tileSizeValue) {
+ optional<float> size = toNumber(*tileSizeValue);
+ if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
+ error = { "invalid tileSize" };
+ return {};
+ }
+ tileSize = *size;
+ }
+
+ return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
+}
+
+static optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
+ if (!urlOrTileset) {
+ return {};
+ }
+
+ return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
+}
+
+static optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ auto dataValue = objectMember(value, "data");
+ if (!dataValue) {
+ error = { "GeoJSON source must have a data value" };
+ return {};
+ }
+
+ optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
+ if (!options) {
+ return {};
+ }
+
+ auto result = std::make_unique<GeoJSONSource>(id, *options);
+
+ if (isObject(*dataValue)) {
+ optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
+ if (!geoJSON) {
+ return {};
+ }
+ result->setGeoJSON(std::move(*geoJSON));
+ } else if (toString(*dataValue)) {
+ result->setURL(*toString(*dataValue));
+ } else {
+ error = { "GeoJSON data must be a URL or an object" };
+ return {};
+ }
+
+ return { std::move(result) };
+}
+
+static optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
+ const Convertible& value,
+ Error& error) {
+ auto urlValue = objectMember(value, "url");
+ if (!urlValue) {
+ error = { "Image source must have a url value" };
+ return {};
+ }
+
+ auto urlString = toString(*urlValue);
+ if (!urlString) {
+ error = { "Image url must be a URL string" };
+ return {};
+ }
+
+ auto coordinatesValue = objectMember(value, "coordinates");
+ if (!coordinatesValue) {
+ error = { "Image source must have a coordinates values" };
+ return {};
+ }
+
+ if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
+ error = { "Image coordinates must be an array of four longitude latitude pairs" };
+ return {};
+ }
+
+ std::array<LatLng, 4> coordinates;
+ for (std::size_t i=0; i < 4; i++) {
+ auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
+ if (!latLng) {
+ return {};
+ }
+ coordinates[i] = *latLng;
+ }
+ auto result = std::make_unique<ImageSource>(id, coordinates);
+ result->setURL(*urlString);
+
+ return { std::move(result) };
+}
+
+optional<std::unique_ptr<Source>> Converter<std::unique_ptr<Source>>::operator()(const Convertible& value, Error& error, const std::string& id) const {
+ if (!isObject(value)) {
+ error = { "source must be an object" };
+ return {};
+ }
+
+ auto typeValue = objectMember(value, "type");
+ if (!typeValue) {
+ error = { "source must have a type" };
+ return {};
+ }
+
+ optional<std::string> type = toString(*typeValue);
+ if (!type) {
+ error = { "source type must be a string" };
+ return {};
+ }
+
+ if (*type == "raster") {
+ return convertRasterSource(id, value, error);
+ } else if (*type == "vector") {
+ return convertVectorSource(id, value, error);
+ } else if (*type == "geojson") {
+ return convertGeoJSONSource(id, value, error);
+ } else if (*type == "image") {
+ return convertImageSource(id, value, error);
+ } else {
+ error = { "invalid source type" };
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp
new file mode 100644
index 0000000000..b9383c41b8
--- /dev/null
+++ b/src/mbgl/style/conversion/tileset.cpp
@@ -0,0 +1,73 @@
+#include <mbgl/style/conversion/tileset.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const {
+ Tileset result;
+
+ auto tiles = objectMember(value, "tiles");
+ if (!tiles) {
+ error = { "source must have tiles" };
+ return {};
+ }
+
+ if (!isArray(*tiles)) {
+ error = { "source tiles must be an array" };
+ return {};
+ }
+
+ for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
+ optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
+ if (!urlTemplate) {
+ error = { "source tiles member must be a string" };
+ return {};
+ }
+ result.tiles.push_back(std::move(*urlTemplate));
+ }
+
+ auto schemeValue = objectMember(value, "scheme");
+ if (schemeValue) {
+ optional<std::string> scheme = toString(*schemeValue);
+ if (scheme && *scheme == "tms") {
+ result.scheme = Tileset::Scheme::TMS;
+ }
+ }
+
+ auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ optional<float> minzoom = toNumber(*minzoomValue);
+ if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
+ error = { "invalid minzoom" };
+ return {};
+ }
+ result.zoomRange.min = *minzoom;
+ }
+
+ auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ optional<float> maxzoom = toNumber(*maxzoomValue);
+ if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
+ error = { "invalid maxzoom" };
+ return {};
+ }
+ result.zoomRange.max = *maxzoom;
+ }
+
+ auto attributionValue = objectMember(value, "attribution");
+ if (attributionValue) {
+ optional<std::string> attribution = toString(*attributionValue);
+ if (!attribution) {
+ error = { "source attribution must be a string" };
+ return {};
+ }
+ result.attribution = std::move(*attribution);
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/transition_options.cpp b/src/mbgl/style/conversion/transition_options.cpp
new file mode 100644
index 0000000000..8a60c5bfd8
--- /dev/null
+++ b/src/mbgl/style/conversion/transition_options.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/conversion/transition_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<TransitionOptions> Converter<TransitionOptions>::operator()(const Convertible& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "transition must be an object" };
+ return {};
+ }
+
+ TransitionOptions result;
+
+ auto duration = objectMember(value, "duration");
+ if (duration) {
+ auto number = toNumber(*duration);
+ if (!number) {
+ error = { "duration must be a number" };
+ return {};
+ }
+ result.duration = { std::chrono::milliseconds(int64_t(*number)) };
+ }
+
+ auto delay = objectMember(value, "delay");
+ if (delay) {
+ auto number = toNumber(*delay);
+ if (!number) {
+ error = { "delay must be a number" };
+ return {};
+ }
+ result.delay = { std::chrono::milliseconds(int64_t(*number)) };
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/custom_tile_loader.cpp b/src/mbgl/style/custom_tile_loader.cpp
new file mode 100644
index 0000000000..76248b84bd
--- /dev/null
+++ b/src/mbgl/style/custom_tile_loader.cpp
@@ -0,0 +1,109 @@
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/tile/custom_geometry_tile.hpp>
+
+namespace mbgl {
+namespace style {
+
+CustomTileLoader::CustomTileLoader(const TileFunction& fetchTileFn, const TileFunction& cancelTileFn) {
+ fetchTileFunction = fetchTileFn;
+ cancelTileFunction = cancelTileFn;
+}
+
+void CustomTileLoader::fetchTile(const OverscaledTileID& tileID, ActorRef<CustomGeometryTile> tileRef) {
+ auto cachedTileData = dataCache.find(tileID.canonical);
+ if (cachedTileData != dataCache.end()) {
+ tileRef.invoke(&CustomGeometryTile::setTileData, *(cachedTileData->second));
+ }
+ auto tileCallbacks = tileCallbackMap.find(tileID.canonical);
+ if (tileCallbacks == tileCallbackMap.end()) {
+ auto tuple = std::make_tuple(tileID.overscaledZ, tileID.wrap, tileRef);
+ tileCallbackMap.insert({ tileID.canonical, std::vector<OverscaledIDFunctionTuple>(1, tuple) });
+ } else {
+ for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
+ if (std::get<0>(*iter) == tileID.overscaledZ && std::get<1>(*iter) == tileID.wrap ) {
+ std::get<2>(*iter) = tileRef;
+ return;
+ }
+ }
+ tileCallbacks->second.emplace_back(std::make_tuple(tileID.overscaledZ, tileID.wrap, tileRef));
+ }
+ if (cachedTileData == dataCache.end()) {
+ invokeTileFetch(tileID.canonical);
+ }
+}
+
+void CustomTileLoader::cancelTile(const OverscaledTileID& tileID) {
+ if (tileCallbackMap.find(tileID.canonical) != tileCallbackMap.end()) {
+ invokeTileCancel(tileID.canonical);
+ }
+}
+
+void CustomTileLoader::removeTile(const OverscaledTileID& tileID) {
+ auto tileCallbacks = tileCallbackMap.find(tileID.canonical);
+ if (tileCallbacks == tileCallbackMap.end()) return;
+ for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
+ if (std::get<0>(*iter) == tileID.overscaledZ && std::get<1>(*iter) == tileID.wrap ) {
+ tileCallbacks->second.erase(iter);
+ invokeTileCancel(tileID.canonical);
+ break;
+ }
+ }
+ if (tileCallbacks->second.size() == 0) {
+ tileCallbackMap.erase(tileCallbacks);
+ dataCache.erase(tileID.canonical);
+ }
+}
+
+void CustomTileLoader::setTileData(const CanonicalTileID& tileID, const GeoJSON& data) {
+
+ auto iter = tileCallbackMap.find(tileID);
+ if (iter == tileCallbackMap.end()) return;
+ auto dataPtr = std::make_unique<mapbox::geojson::geojson>(std::move(data));
+ for (auto tuple : iter->second) {
+ auto actor = std::get<2>(tuple);
+ actor.invoke(&CustomGeometryTile::setTileData, *dataPtr);
+ }
+ dataCache[tileID] = std::move(dataPtr);
+}
+
+void CustomTileLoader::invalidateTile(const CanonicalTileID& tileID) {
+ auto tileCallbacks = tileCallbackMap.find(tileID);
+ if (tileCallbacks == tileCallbackMap.end()) { return; }
+ for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
+ auto actor = std::get<2>(*iter);
+ actor.invoke(&CustomGeometryTile::invalidateTileData);
+ invokeTileCancel(tileID);
+ }
+ tileCallbackMap.erase(tileCallbacks);
+ dataCache.erase(tileID);
+}
+
+void CustomTileLoader::invalidateRegion(const LatLngBounds& bounds, Range<uint8_t> ) {
+ for (auto idtuple= tileCallbackMap.begin(); idtuple != tileCallbackMap.end(); idtuple++) {
+ const LatLngBounds tileBounds(idtuple->first);
+ if (tileBounds.intersects(bounds, LatLng::Wrapped) || bounds.contains(tileBounds, LatLng::Wrapped) || tileBounds.contains(bounds, LatLng::Wrapped)) {
+ for (auto iter = idtuple->second.begin(); iter != idtuple->second.end(); iter++) {
+ auto actor = std::get<2>(*iter);
+ actor.invoke(&CustomGeometryTile::invalidateTileData);
+ invokeTileCancel(idtuple->first);
+ dataCache.erase(idtuple->first);
+ }
+ idtuple->second.clear();
+ }
+ }
+}
+
+void CustomTileLoader::invokeTileFetch(const CanonicalTileID& tileID) {
+ if (fetchTileFunction != nullptr) {
+ fetchTileFunction(tileID);
+ }
+}
+
+void CustomTileLoader::invokeTileCancel(const CanonicalTileID& tileID) {
+ if (cancelTileFunction != nullptr) {
+ cancelTileFunction(tileID);
+ }
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/custom_tile_loader.hpp b/src/mbgl/style/custom_tile_loader.hpp
new file mode 100644
index 0000000000..335d8c6143
--- /dev/null
+++ b/src/mbgl/style/custom_tile_loader.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/actor/actor_ref.hpp>
+
+#include <map>
+
+namespace mbgl {
+
+class CustomGeometryTile;
+
+namespace style {
+
+class CustomTileLoader : private util::noncopyable {
+public:
+
+ using OverscaledIDFunctionTuple = std::tuple<uint8_t, int16_t, ActorRef<CustomGeometryTile>>;
+
+ CustomTileLoader(const TileFunction& fetchTileFn, const TileFunction& cancelTileFn);
+
+ void fetchTile(const OverscaledTileID& tileID, ActorRef<CustomGeometryTile> tileRef);
+ void cancelTile(const OverscaledTileID& tileID);
+
+ void removeTile(const OverscaledTileID& tileID);
+ void setTileData(const CanonicalTileID& tileID, const GeoJSON& data);
+
+ void invalidateTile(const CanonicalTileID&);
+ void invalidateRegion(const LatLngBounds&, Range<uint8_t>);
+
+private:
+ void invokeTileFetch(const CanonicalTileID& tileID);
+ void invokeTileCancel(const CanonicalTileID& tileID);
+
+ TileFunction fetchTileFunction;
+ TileFunction cancelTileFunction;
+ std::unordered_map<CanonicalTileID, std::vector<OverscaledIDFunctionTuple>> tileCallbackMap;
+ // Keep around a cache of tile data to serve back for wrapped and over-zooomed tiles
+ std::map<CanonicalTileID, std::unique_ptr<GeoJSON>> dataCache;
+
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/array_assertion.cpp b/src/mbgl/style/expression/array_assertion.cpp
new file mode 100644
index 0000000000..29f6a47b10
--- /dev/null
+++ b/src/mbgl/style/expression/array_assertion.cpp
@@ -0,0 +1,86 @@
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult ArrayAssertion::evaluate(const EvaluationContext& params) const {
+ auto result = input->evaluate(params);
+ if (!result) {
+ return result.error();
+ }
+ type::Type expected = getType();
+ type::Type actual = typeOf(*result);
+ if (checkSubtype(expected, actual)) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(expected) +
+ ", but found " + toString(actual) + " instead."
+ };
+ }
+ return *result;
+}
+
+void ArrayAssertion::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+}
+
+using namespace mbgl::style::conversion;
+ParseResult ArrayAssertion::parse(const Convertible& value, ParsingContext& ctx) {
+
+ static std::unordered_map<std::string, type::Type> itemTypes {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean}
+ };
+
+ auto length = arrayLength(value);
+ if (length < 2 || length > 4) {
+ ctx.error("Expected 1, 2, or 3 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ optional<type::Type> itemType;
+ optional<std::size_t> N;
+ if (length > 2) {
+ optional<std::string> itemTypeName = toString(arrayMember(value, 1));
+ auto it = itemTypeName ? itemTypes.find(*itemTypeName) : itemTypes.end();
+ if (it == itemTypes.end()) {
+ ctx.error(
+ R"(The item type argument of "array" must be one of string, number, boolean)",
+ 1
+ );
+ return ParseResult();
+ }
+ itemType = it->second;
+ } else {
+ itemType = {type::Value};
+ }
+
+ if (length > 3) {
+ auto n = toNumber(arrayMember(value, 2));
+ if (!n || *n != std::floor(*n)) {
+ ctx.error(
+ R"(The length argument to "array" must be a positive integer literal.)",
+ 2
+ );
+ return ParseResult();
+ }
+ N = optional<std::size_t>(*n);
+ }
+
+ auto input = ctx.parse(arrayMember(value, length - 1), length - 1, {type::Value});
+ if (!input) {
+ return input;
+ }
+
+ return ParseResult(std::make_unique<ArrayAssertion>(
+ type::Array(*itemType, N),
+ std::move(*input)
+ ));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp
new file mode 100644
index 0000000000..a17c53cf54
--- /dev/null
+++ b/src/mbgl/style/expression/assertion.cpp
@@ -0,0 +1,73 @@
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using namespace mbgl::style::conversion;
+ParseResult Assertion::parse(const Convertible& value, ParsingContext& ctx) {
+ static std::unordered_map<std::string, type::Type> types {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean},
+ {"object", type::Object}
+ };
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ auto it = types.find(*toString(arrayMember(value, 0)));
+ assert(it != types.end());
+
+ std::vector<std::unique_ptr<Expression>> parsed;
+ parsed.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ ParseResult input = ctx.parse(arrayMember(value, i), i, {type::Value});
+ if (!input) return ParseResult();
+ parsed.push_back(std::move(*input));
+ }
+
+ return ParseResult(std::make_unique<Assertion>(it->second, std::move(parsed)));
+}
+
+EvaluationResult Assertion::evaluate(const EvaluationContext& params) const {
+ for (std::size_t i = 0; i < inputs.size(); i++) {
+ EvaluationResult value = inputs[i]->evaluate(params);
+ if (!value) return value;
+ if (!type::checkSubtype(getType(), typeOf(*value))) {
+ return value;
+ } else if (i == inputs.size() - 1) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(getType()) +
+ ", but found " + toString(typeOf(*value)) + " instead."
+ };
+ }
+ }
+
+ assert(false);
+ return EvaluationError { "Unreachable" };
+};
+
+void Assertion::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for(const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+};
+
+bool Assertion::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Assertion*>(&e)) {
+ return getType() == rhs->getType() && Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
diff --git a/src/mbgl/style/expression/at.cpp b/src/mbgl/style/expression/at.cpp
new file mode 100644
index 0000000000..e447d33bc7
--- /dev/null
+++ b/src/mbgl/style/expression/at.cpp
@@ -0,0 +1,64 @@
+#include <mbgl/style/expression/at.hpp>
+#include <mbgl/util/string.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult At::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult evaluatedIndex = index->evaluate(params);
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedIndex) {
+ return evaluatedIndex.error();
+ }
+ if (!evaluatedInput) {
+ return evaluatedInput.error();
+ }
+
+ const auto i = evaluatedIndex->get<double>();
+ const auto inputArray = evaluatedInput->get<std::vector<Value>>();
+
+ if (i < 0 || i >= inputArray.size()) {
+ return EvaluationError {
+ "Array index out of bounds: " + stringify(i) +
+ " > " + util::toString(inputArray.size()) + "."
+ };
+ }
+ if (i != std::floor(i)) {
+ return EvaluationError {
+ "Array index must be an integer, but found " + stringify(i) + " instead."
+ };
+ }
+ return inputArray[static_cast<std::size_t>(i)];
+}
+
+void At::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*index);
+ visit(*input);
+}
+
+using namespace mbgl::style::conversion;
+ParseResult At::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ std::size_t length = arrayLength(value);
+ if (length != 3) {
+ ctx.error("Expected 2 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ ParseResult index = ctx.parse(arrayMember(value, 1), 1, {type::Number});
+
+ type::Type inputType = type::Array(ctx.getExpected() ? *ctx.getExpected() : type::Value);
+ ParseResult input = ctx.parse(arrayMember(value, 2), 2, {inputType});
+
+ if (!index || !input) return ParseResult();
+
+ return ParseResult(std::make_unique<At>(std::move(*index), std::move(*input)));
+
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/boolean_operator.cpp b/src/mbgl/style/expression/boolean_operator.cpp
new file mode 100644
index 0000000000..88797f965a
--- /dev/null
+++ b/src/mbgl/style/expression/boolean_operator.cpp
@@ -0,0 +1,87 @@
+#include <mbgl/style/expression/boolean_operator.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Any::evaluate(const EvaluationContext& params) const {
+ for (auto it = inputs.begin(); it != inputs.end(); it++) {
+ const EvaluationResult result = (*it)->evaluate(params);
+ if (!result) return result;
+ if (result->get<bool>()) return EvaluationResult(true);
+ }
+ return EvaluationResult(false);
+}
+
+void Any::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+}
+
+bool Any::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Any*>(&e)) {
+ return Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+
+EvaluationResult All::evaluate(const EvaluationContext& params) const {
+ for (auto it = inputs.begin(); it != inputs.end(); it++) {
+ const EvaluationResult result = (*it)->evaluate(params);
+ if (!result) return result;
+ if (!result->get<bool>()) return EvaluationResult(false);
+ }
+ return EvaluationResult(true);
+}
+
+void All::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+}
+
+bool All::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const All*>(&e)) {
+ return Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+using namespace mbgl::style::conversion;
+
+template <class T>
+ParseResult parseBooleanOp(const Convertible& value, ParsingContext& ctx) {
+
+ assert(isArray(value));
+ auto length = arrayLength(value);
+
+ std::vector<std::unique_ptr<Expression>> parsedInputs;
+
+ parsedInputs.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = ctx.parse(arrayMember(value, i), i, {type::Boolean});
+ if (!parsed) {
+ return parsed;
+ }
+
+ parsedInputs.push_back(std::move(*parsed));
+ }
+
+ return ParseResult(std::make_unique<T>(std::move(parsedInputs)));
+}
+
+ParseResult Any::parse(const Convertible& value, ParsingContext& ctx) {
+ return parseBooleanOp<Any>(value, ctx);
+}
+
+ParseResult All::parse(const Convertible& value, ParsingContext& ctx) {
+ return parseBooleanOp<All>(value, ctx);
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/case.cpp b/src/mbgl/style/expression/case.cpp
new file mode 100644
index 0000000000..049f258606
--- /dev/null
+++ b/src/mbgl/style/expression/case.cpp
@@ -0,0 +1,91 @@
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Case::evaluate(const EvaluationContext& params) const {
+ for (const auto& branch : branches) {
+ const EvaluationResult evaluatedTest = branch.first->evaluate(params);
+ if (!evaluatedTest) {
+ return evaluatedTest.error();
+ }
+ if (evaluatedTest->get<bool>()) {
+ return branch.second->evaluate(params);
+ }
+ }
+
+ return otherwise->evaluate(params);
+}
+
+void Case::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const Branch& branch : branches) {
+ visit(*branch.first);
+ visit(*branch.second);
+ }
+ visit(*otherwise);
+}
+
+bool Case::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Case*>(&e)) {
+ return *otherwise == *(rhs->otherwise) && Expression::childrenEqual(branches, rhs->branches);
+ }
+ return false;
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Case::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 4) {
+ ctx.error("Expected at least 3 arguments, but found only " + util::toString(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // Expect even-length array: ["case", 2 * (n pairs)..., otherwise]
+ if (length % 2 != 0) {
+ ctx.error("Expected an odd number of arguments");
+ return ParseResult();
+ }
+
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ std::vector<Case::Branch> branches;
+ branches.reserve((length - 2) / 2);
+ for (size_t i = 1; i + 1 < length; i += 2) {
+ auto test = ctx.parse(arrayMember(value, i), i, {type::Boolean});
+ if (!test) {
+ return test;
+ }
+
+ auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return output;
+ }
+
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ branches.push_back(std::make_pair(std::move(*test), std::move(*output)));
+ }
+
+ assert(outputType);
+
+ auto otherwise = ctx.parse(arrayMember(value, length - 1), length - 1, outputType);
+ if (!otherwise) {
+ return otherwise;
+ }
+
+ return ParseResult(std::make_unique<Case>(*outputType,
+ std::move(branches),
+ std::move(*otherwise)));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/check_subtype.cpp b/src/mbgl/style/expression/check_subtype.cpp
new file mode 100644
index 0000000000..04a1643f0c
--- /dev/null
+++ b/src/mbgl/style/expression/check_subtype.cpp
@@ -0,0 +1,60 @@
+#include <string>
+#include <mbgl/style/expression/check_subtype.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+namespace type {
+
+std::string errorMessage(const Type& expected, const Type& t) {
+ return {"Expected " + toString(expected) + " but found " + toString(t) + " instead."};
+}
+
+optional<std::string> checkSubtype(const Type& expected, const Type& t) {
+ if (t.is<ErrorType>()) return {};
+
+ optional<std::string> result = expected.match(
+ [&] (const Array& expectedArray) -> optional<std::string> {
+ if (!t.is<Array>()) { return {errorMessage(expected, t)}; }
+ const auto& actualArray = t.get<Array>();
+ const auto err = checkSubtype(expectedArray.itemType, actualArray.itemType);
+ if (err) return { errorMessage(expected, t) };
+ if (expectedArray.N && expectedArray.N != actualArray.N) return { errorMessage(expected, t) };
+ return {};
+ },
+ [&] (const ValueType&) -> optional<std::string> {
+ if (t.is<ValueType>()) return {};
+
+ const Type members[] = {
+ Null,
+ Boolean,
+ Number,
+ String,
+ Object,
+ Color,
+ Array(Value)
+ };
+
+ for (const auto& member : members) {
+ const auto err = checkSubtype(member, t);
+ if (!err) {
+ return {};
+ }
+ }
+ return { errorMessage(expected, t) };
+ },
+ [&] (const auto&) -> optional<std::string> {
+ if (expected != t) {
+ return { errorMessage(expected, t) };
+ }
+ return {};
+ }
+ );
+
+ return result;
+}
+
+} // namespace type
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/coalesce.cpp b/src/mbgl/style/expression/coalesce.cpp
new file mode 100644
index 0000000000..bfde3c7581
--- /dev/null
+++ b/src/mbgl/style/expression/coalesce.cpp
@@ -0,0 +1,62 @@
+#include <mbgl/style/expression/coalesce.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Coalesce::evaluate(const EvaluationContext& params) const {
+ EvaluationResult result = Null;
+ for (const auto& arg : args) {
+ result = arg->evaluate(params);
+ if (!result || *result != Null) break;
+ }
+ return result;
+}
+
+void Coalesce::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (const std::unique_ptr<Expression>& arg : args) {
+ visit(*arg);
+ }
+}
+
+bool Coalesce::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Coalesce*>(&e)) {
+ return Expression::childrenEqual(args, rhs->args);
+ }
+ return false;
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ Coalesce::Args args;
+ args.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = ctx.parse(arrayMember(value, i), i, outputType);
+ if (!parsed) {
+ return parsed;
+ }
+ if (!outputType) {
+ outputType = (*parsed)->getType();
+ }
+ args.push_back(std::move(*parsed));
+ }
+
+ assert(outputType);
+ return ParseResult(std::make_unique<Coalesce>(*outputType, std::move(args)));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp
new file mode 100644
index 0000000000..8ed8e160dd
--- /dev/null
+++ b/src/mbgl/style/expression/coercion.cpp
@@ -0,0 +1,144 @@
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult toNumber(const Value& v) {
+ optional<double> result = v.match(
+ [](const double f) -> optional<double> { return f; },
+ [](const std::string& s) -> optional<double> {
+ try {
+ return util::stof(s);
+ } catch(std::exception) {
+ return optional<double>();
+ }
+ },
+ [](const auto&) { return optional<double>(); }
+ );
+ if (!result) {
+ return EvaluationError {
+ "Could not convert " + stringify(v) + " to number."
+ };
+ }
+ return *result;
+}
+
+EvaluationResult toColor(const Value& colorValue) {
+ return colorValue.match(
+ [&](const std::string& colorString) -> EvaluationResult {
+ const optional<Color> result = Color::parse(colorString);
+ if (result) {
+ return *result;
+ } else {
+ return EvaluationError{
+ "Could not parse color from value '" + colorString + "'"
+ };
+ }
+ },
+ [&](const std::vector<Value>& components) -> EvaluationResult {
+ std::size_t len = components.size();
+ bool isNumeric = std::all_of(components.begin(), components.end(), [](const Value& item) {
+ return item.template is<double>();
+ });
+ if ((len == 3 || len == 4) && isNumeric) {
+ Result<Color> c = {rgba(
+ components[0].template get<double>(),
+ components[1].template get<double>(),
+ components[2].template get<double>(),
+ len == 4 ? components[3].template get<double>() : 1.0
+ )};
+ if (!c) return c.error();
+ return *c;
+ } else {
+ return EvaluationError{
+ "Invalid rbga value " + stringify(colorValue) + ": expected an array containing either three or four numeric values."
+ };
+ }
+ },
+ [&](const auto&) -> EvaluationResult {
+ return EvaluationError{
+ "Could not parse color from value '" + stringify(colorValue) + "'"
+ };
+ }
+ );
+}
+
+Coercion::Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(std::move(type_)),
+ inputs(std::move(inputs_))
+{
+ type::Type t = getType();
+ if (t.is<type::NumberType>()) {
+ coerceSingleValue = toNumber;
+ } else if (t.is<type::ColorType>()) {
+ coerceSingleValue = toColor;
+ } else {
+ assert(false);
+ }
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Coercion::parse(const Convertible& value, ParsingContext& ctx) {
+ static std::unordered_map<std::string, type::Type> types {
+ {"to-number", type::Number},
+ {"to-color", type::Color}
+ };
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ auto it = types.find(*toString(arrayMember(value, 0)));
+ assert(it != types.end());
+
+ std::vector<std::unique_ptr<Expression>> parsed;
+ parsed.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ ParseResult input = ctx.parse(arrayMember(value, i), i, {type::Value});
+ if (!input) return ParseResult();
+ parsed.push_back(std::move(*input));
+ }
+
+ return ParseResult(std::make_unique<Coercion>(it->second, std::move(parsed)));
+}
+
+EvaluationResult Coercion::evaluate(const EvaluationContext& params) const {
+ for (std::size_t i = 0; i < inputs.size(); i++) {
+ EvaluationResult value = inputs[i]->evaluate(params);
+ if (!value) return value;
+ EvaluationResult coerced = coerceSingleValue(*value);
+ if (coerced || i == inputs.size() - 1) {
+ return coerced;
+ }
+ }
+
+ assert(false);
+ return EvaluationError { "Unreachable" };
+};
+
+void Coercion::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for(const std::unique_ptr<Expression>& input : inputs) {
+ visit(*input);
+ }
+};
+
+bool Coercion::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Coercion*>(&e)) {
+ return getType() == rhs->getType() && Expression::childrenEqual(inputs, rhs->inputs);
+ }
+ return false;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
+
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
new file mode 100644
index 0000000000..70bc6dfd95
--- /dev/null
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -0,0 +1,568 @@
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/math/log2.hpp>
+#include <mbgl/util/ignore.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/platform.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+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>
+
+ Signature<R (const Varargs<T>&)>:
+ Wraps an evaluate function that takes an arbitrary number of arguments (via
+ a Varargs<T>, which is just an alias for std::vector).
+
+ 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
+ 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.
+*/
+template <class, class Enable = void>
+struct Signature;
+
+// Simple evaluate function (const T0&, const T1&, ...) -> Result<U>
+template <class R, class... Params>
+struct Signature<R (Params...)> : SignatureBase {
+ using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
+
+ Signature(R (*evaluate_)(Params...)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...}
+ ),
+ evaluate(evaluate_)
+ {}
+
+ EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
+ return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
+ }
+
+ std::unique_ptr<Expression> makeExpression(const std::string& name,
+ 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));
+ }
+
+ R (*evaluate)(Params...);
+private:
+ template <std::size_t ...I>
+ EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
+ const std::array<EvaluationResult, sizeof...(I)> evaluated = {{std::get<I>(args)->evaluate(evaluationParameters)...}};
+ for (const auto& arg : evaluated) {
+ if(!arg) return arg.error();
+ }
+ const R value = evaluate(*fromExpressionValue<std::decay_t<Params>>(*(evaluated[I]))...);
+ if (!value) return value.error();
+ return *value;
+ }
+};
+
+// Varargs evaluate function (const Varargs<T>&) -> Result<U>
+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>&)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ VarargsType { valueTypeToExpressionType<T>() }
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(const std::string& name,
+ 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(evaluated);
+ if (!value) return value.error();
+ return *value;
+ }
+
+ R (*evaluate)(const Varargs<T>&);
+};
+
+// Evaluate function needing parameter access,
+// (const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>
+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...)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...}
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(const std::string& name,
+ 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 {
+ const std::array<EvaluationResult, sizeof...(I)> evaluated = {{std::get<I>(args)->evaluate(evaluationParameters)...}};
+ for (const auto& arg : evaluated) {
+ if(!arg) return arg.error();
+ }
+ // TODO: assert correct runtime type of each arg value
+ const R value = evaluate(evaluationParameters, *fromExpressionValue<std::decay_t<Params>>(*(evaluated[I]))...);
+ if (!value) return value.error();
+ return *value;
+ }
+
+ R (*evaluate)(const EvaluationContext&, Params...);
+};
+
+// Machinery to pull out function types from class methods, lambdas, etc.
+template <class R, class... Params>
+struct Signature<R (*)(Params...)>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class T, class R, class... Params>
+struct Signature<R (T::*)(Params...) const>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class T, class R, class... Params>
+struct Signature<R (T::*)(Params...)>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class Lambda>
+struct Signature<Lambda, std::enable_if_t<std::is_class<Lambda>::value>>
+ : Signature<decltype(&Lambda::operator())>
+{ using Signature<decltype(&Lambda::operator())>::Signature; };
+
+} // namespace detail
+
+using Definition = CompoundExpressionRegistry::Definition;
+
+template <typename T>
+Result<bool> equal(const T& lhs, const T& rhs) { return lhs == rhs; }
+
+template <typename T>
+Result<bool> notEqual(const T& lhs, const T& rhs) { return lhs != rhs; }
+
+template <typename Fn>
+static std::unique_ptr<detail::SignatureBase> makeSignature(Fn evaluateFunction) {
+ return std::make_unique<detail::Signature<Fn>>(evaluateFunction);
+}
+
+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));
+ };
+
+ 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 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; },
+ [&] (const std::string& s) { return s.length() > 0; },
+ [&] (bool b) { return b; },
+ [&] (const NullValue&) { return false; },
+ [&] (const auto&) { return true; }
+ );
+ });
+ define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> {
+ return std::array<double, 4> {{ color.r, color.g, color.b, color.a }};
+ });
+
+ 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 {
+ "The 'zoom' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.zoom);
+ });
+
+ define("heatmap-density", [](const EvaluationContext& params) -> Result<double> {
+ if (!params.heatmapDensity) {
+ return EvaluationError {
+ "The 'heatmap-density' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.heatmapDensity);
+ });
+
+ define("has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
+ if (!params.feature) {
+ return EvaluationError {
+ "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> {
+ return object.find(key) != object.end();
+ });
+
+ define("get", [](const EvaluationContext& params, const std::string& key) -> Result<Value> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto propertyValue = params.feature->getValue(key);
+ if (!propertyValue) {
+ return Null;
+ }
+ return Value(toExpressionValue(*propertyValue));
+ });
+ define("get", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<Value> {
+ if (object.find(key) == object.end()) {
+ return Null;
+ }
+ return object.at(key);
+ });
+
+ define("length", [](const std::vector<Value>& arr) -> Result<double> {
+ return arr.size();
+ });
+ define("length", [] (const std::string s) -> Result<double> {
+ return s.size();
+ });
+
+ define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+ std::unordered_map<std::string, Value> result;
+ const PropertyMap properties = params.feature->getProperties();
+ for (const auto& entry : properties) {
+ result[entry.first] = toExpressionValue(entry.second);
+ }
+ 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";
+ } else if (type == FeatureType::LineString) {
+ return "LineString";
+ } else if (type == FeatureType::Polygon) {
+ return "Polygon";
+ } else {
+ 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;
+ }
+ return id->match(
+ [](const auto& idValue) {
+ return toExpressionValue(mbgl::Value(idValue));
+ }
+ );
+ });
+
+ define("+", [](const Varargs<double>& args) -> Result<double> {
+ double sum = 0.0f;
+ for (auto arg : args) {
+ sum += arg;
+ }
+ return sum;
+ });
+ define("-", [](double a, double b) -> Result<double> { return a - b; });
+ define("-", [](double a) -> Result<double> { return -a; });
+ define("*", [](const Varargs<double>& args) -> Result<double> {
+ double prod = 1.0f;
+ for (auto arg : args) {
+ prod *= arg;
+ }
+ return prod;
+ });
+ define("/", [](double a, double b) -> Result<double> { return a / b; });
+ define("%", [](double a, double b) -> Result<double> { return fmod(a, b); });
+ define("^", [](double a, double b) -> Result<double> { return pow(a, b); });
+ define("sqrt", [](double x) -> Result<double> { return sqrt(x); });
+ define("log10", [](double x) -> Result<double> { return log10(x); });
+ define("ln", [](double x) -> Result<double> { return log(x); });
+ define("log2", [](double x) -> Result<double> { return util::log2(x); });
+ define("sin", [](double x) -> Result<double> { return sin(x); });
+ define("cos", [](double x) -> Result<double> { return cos(x); });
+ define("tan", [](double x) -> Result<double> { return tan(x); });
+ 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) {
+ result = fmin(arg, result);
+ }
+ return result;
+ });
+ define("max", [](const Varargs<double>& args) -> Result<double> {
+ double result = -std::numeric_limits<double>::infinity();
+ for (double arg : args) {
+ result = fmax(arg, result);
+ }
+ return result;
+ });
+
+ define("==", equal<double>);
+ define("==", equal<const std::string&>);
+ define("==", equal<bool>);
+ define("==", equal<NullValue>);
+
+ define("!=", notEqual<double>);
+ define("!=", notEqual<const std::string&>);
+ define("!=", notEqual<bool>);
+ define("!=", notEqual<NullValue>);
+
+ define(">", [](double lhs, double rhs) -> Result<bool> { return lhs > rhs; });
+ define(">", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs > rhs; });
+ define(">=", [](double lhs, double rhs) -> Result<bool> { return lhs >= rhs; });
+ 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("<=", [](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("upcase", [](const std::string& input) -> Result<std::string> {
+ return platform::uppercase(input);
+ });
+ define("downcase", [](const std::string& input) -> Result<std::string> {
+ return platform::lowercase(input);
+ });
+ define("concat", [](const Varargs<std::string>& args) -> Result<std::string> {
+ std::string s;
+ for (const std::string& arg : args) {
+ s += arg;
+ }
+ return s;
+ });
+ define("error", [](const std::string& input) -> Result<type::ErrorType> {
+ return EvaluationError { input };
+ });
+
+ return definitions;
+}
+
+std::unordered_map<std::string, Definition> CompoundExpressionRegistry::definitions = initializeDefinitions();
+
+using namespace mbgl::style::conversion;
+ParseResult parseCompoundExpression(const std::string name, const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value) && arrayLength(value) > 0);
+
+ auto it = CompoundExpressionRegistry::definitions.find(name);
+ if (it == CompoundExpressionRegistry::definitions.end()) {
+ ctx.error(
+ R"(Unknown expression ")" + name + R"(". If you wanted a literal array, use ["literal", [...]].)",
+ 0
+ );
+ return ParseResult();
+ }
+ const CompoundExpressionRegistry::Definition& definition = it->second;
+
+ auto length = arrayLength(value);
+
+ // Check if we have a single signature with the correct number of
+ // parameters. If so, then use that signature's parameter types for parsing
+ // (and inferring the types of) the arguments.
+ optional<std::size_t> singleMatchingSignature;
+ for (std::size_t j = 0; j < definition.size(); j++) {
+ const std::unique_ptr<detail::SignatureBase>& signature = definition[j];
+ if (
+ signature->params.is<VarargsType>() ||
+ signature->params.get<std::vector<type::Type>>().size() == length - 1
+ ) {
+ if (singleMatchingSignature) {
+ singleMatchingSignature = {};
+ } else {
+ singleMatchingSignature = j;
+ }
+ }
+ }
+
+ // parse subexpressions first
+ std::vector<std::unique_ptr<Expression>> args;
+ 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;
+ }
+ args.push_back(std::move(*parsed));
+ }
+ return createCompoundExpression(name, definition, std::move(args), ctx);
+}
+
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx)
+{
+ return createCompoundExpression(name, CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx);
+}
+
+
+ParseResult createCompoundExpression(const std::string& name,
+ const Definition& definition,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx)
+{
+ ParsingContext signatureContext(ctx.getKey());
+
+ 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()) {
+ signatureContext.error(
+ "Expected " + util::toString(params.size()) +
+ " arguments, but found " + util::toString(args.size()) + " instead."
+ );
+ continue;
+ }
+
+ for (std::size_t i = 0; i < args.size(); i++) {
+ const std::unique_ptr<Expression>& arg = args[i];
+ optional<std::string> err = type::checkSubtype(params.at(i), arg->getType());
+ if (err) {
+ signatureContext.error(*err, i + 1);
+ }
+ }
+ } else if (signature->params.is<VarargsType>()) {
+ const type::Type& paramType = signature->params.get<VarargsType>().type;
+ for (std::size_t i = 0; i < args.size(); i++) {
+ const std::unique_ptr<Expression>& arg = args[i];
+ optional<std::string> err = type::checkSubtype(paramType, arg->getType());
+ if (err) {
+ signatureContext.error(*err, i + 1);
+ }
+ }
+ }
+
+ if (signatureContext.getErrors().size() == 0) {
+ return ParseResult(signature->makeExpression(name, std::move(args)));
+ }
+ }
+
+ if (definition.size() == 1) {
+ ctx.appendErrors(std::move(signatureContext));
+ } else {
+ std::string signatures;
+ for (const auto& signature : definition) {
+ signatures += (signatures.size() > 0 ? " | " : "");
+ signature->params.match(
+ [&](const VarargsType& varargs) {
+ signatures += "(" + toString(varargs.type) + ")";
+ },
+ [&](const std::vector<type::Type>& params) {
+ signatures += "(";
+ bool first = true;
+ for (const type::Type& param : params) {
+ if (!first) signatures += ", ";
+ signatures += toString(param);
+ first = false;
+ }
+ signatures += ")";
+ }
+ );
+
+ }
+ std::string actualTypes;
+ for (const auto& arg : args) {
+ if (actualTypes.size() > 0) {
+ actualTypes += ", ";
+ }
+ actualTypes += toString(arg->getType());
+ }
+ ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead.");
+ }
+
+ return ParseResult();
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/find_zoom_curve.cpp b/src/mbgl/style/expression/find_zoom_curve.cpp
new file mode 100644
index 0000000000..5d39e0791e
--- /dev/null
+++ b/src/mbgl/style/expression/find_zoom_curve.cpp
@@ -0,0 +1,76 @@
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+optional<variant<const InterpolateBase*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e) {
+ optional<variant<const InterpolateBase*, const Step*, ParsingError>> result;
+
+ if (auto let = dynamic_cast<const Let*>(e)) {
+ result = findZoomCurve(let->getResult());
+ } else if (auto coalesce = dynamic_cast<const Coalesce*>(e)) {
+ std::size_t length = coalesce->getLength();
+ for (std::size_t i = 0; i < length; i++) {
+ result = findZoomCurve(coalesce->getChild(i));
+ if (result) {
+ break;
+ }
+ }
+ } else if (auto curve = dynamic_cast<const InterpolateBase*>(e)) {
+ auto z = dynamic_cast<CompoundExpressionBase*>(curve->getInput().get());
+ if (z && z->getName() == "zoom") {
+ result = {curve};
+ }
+ } else if (auto step = dynamic_cast<const Step*>(e)) {
+ auto z = dynamic_cast<CompoundExpressionBase*>(step->getInput().get());
+ if (z && z->getName() == "zoom") {
+ result = {step};
+ }
+ }
+
+ if (result && result->is<ParsingError>()) {
+ return result;
+ }
+
+ e->eachChild([&](const Expression& child) {
+ optional<variant<const InterpolateBase*, const Step*, ParsingError>> childResult(findZoomCurve(&child));
+ if (childResult) {
+ if (childResult->is<ParsingError>()) {
+ result = childResult;
+ } else if (!result && childResult) {
+ result = {ParsingError {
+ R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)", ""
+ }};
+ } else if (result && childResult && result != childResult) {
+ result = {ParsingError {
+ R"(Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.)", ""
+ }};
+ }
+ }
+ });
+
+ return result;
+}
+
+variant<const InterpolateBase*, const Step*> findZoomCurveChecked(const expression::Expression* e) {
+ return findZoomCurve(e)->match(
+ [](const ParsingError&) -> variant<const InterpolateBase*, const Step*> {
+ assert(false);
+ return {};
+ },
+ [](auto zoomCurve) -> variant<const InterpolateBase*, const Step*> {
+ return {std::move(zoomCurve)};
+ }
+ );
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/get_covering_stops.cpp b/src/mbgl/style/expression/get_covering_stops.cpp
new file mode 100644
index 0000000000..c9f87d93ac
--- /dev/null
+++ b/src/mbgl/style/expression/get_covering_stops.cpp
@@ -0,0 +1,26 @@
+#include <mbgl/style/expression/get_covering_stops.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Range<float> getCoveringStops(const std::map<double, std::unique_ptr<Expression>>& stops,
+ const double lower, const double upper) {
+ assert(!stops.empty());
+ auto minIt = stops.lower_bound(lower);
+ auto maxIt = stops.lower_bound(upper);
+
+ // lower_bound yields first element >= lowerZoom, but we want the *last*
+ // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one.
+ if (minIt != stops.begin() && minIt != stops.end() && minIt->first > lower) {
+ minIt--;
+ }
+ return Range<float> {
+ static_cast<float>(minIt == stops.end() ? stops.rbegin()->first : minIt->first),
+ static_cast<float>(maxIt == stops.end() ? stops.rbegin()->first : maxIt->first)
+ };
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp
new file mode 100644
index 0000000000..5ddfca8e9f
--- /dev/null
+++ b/src/mbgl/style/expression/interpolate.cpp
@@ -0,0 +1,211 @@
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using Interpolator = variant<ExponentialInterpolator,
+ CubicBezierInterpolator>;
+
+using namespace mbgl::style::conversion;
+
+ParseResult parseInterpolate(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ auto length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected an interpolation type expression.");
+ return ParseResult();
+ }
+
+ const Convertible& interp = arrayMember(value, 1);
+ if (!isArray(interp) || arrayLength(interp) == 0) {
+ ctx.error("Expected an interpolation type expression.");
+ return ParseResult();
+ }
+
+ optional<Interpolator> interpolator;
+
+ const optional<std::string> interpName = toString(arrayMember(interp, 0));
+ if (interpName && *interpName == "linear") {
+ interpolator = {ExponentialInterpolator(1.0)};
+ } else if (interpName && *interpName == "exponential") {
+ optional<double> base;
+ if (arrayLength(interp) == 2) {
+ base = toDouble(arrayMember(interp, 1));
+ }
+ if (!base) {
+ ctx.error("Exponential interpolation requires a numeric base.", 1, 1);
+ return ParseResult();
+ }
+ interpolator = {ExponentialInterpolator(*base)};
+ } else if (interpName && *interpName == "cubic-bezier") {
+ optional<double> x1;
+ optional<double> y1;
+ optional<double> x2;
+ optional<double> y2;
+ if (arrayLength(interp) == 5) {
+ x1 = toDouble(arrayMember(interp, 1));
+ y1 = toDouble(arrayMember(interp, 2));
+ x2 = toDouble(arrayMember(interp, 3));
+ y2 = toDouble(arrayMember(interp, 4));
+ }
+ if (
+ !x1 || !y1 || !x2 || !y2 ||
+ *x1 < 0 || *x1 > 1 ||
+ *y1 < 0 || *y1 > 1 ||
+ *x2 < 0 || *x2 > 1 ||
+ *y2 < 0 || *y2 > 1
+ ) {
+ ctx.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
+ return ParseResult();
+
+ }
+ interpolator = {CubicBezierInterpolator(*x1, *y1, *x2, *y2)};
+ }
+
+ if (!interpolator) {
+ ctx.error("Unknown interpolation type " + (interpName ? *interpName : ""), 1, 0);
+ return ParseResult();
+ }
+
+ std::size_t minArgs = 4;
+ if (length - 1 < minArgs) {
+ ctx.error("Expected at least 4 arguments, but found only " + util::toString(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // [interpolation, interp_type, input, 2 * (n pairs)...]
+ if ((length - 1) % 2 != 0) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ ParseResult input = ctx.parse(arrayMember(value, 2), 2, {type::Number});
+ if (!input) {
+ return input;
+ }
+
+ std::map<double, std::unique_ptr<Expression>> stops;
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ double previous = - std::numeric_limits<double>::infinity();
+
+ for (std::size_t i = 3; i + 1 < length; i += 2) {
+ const optional<mbgl::Value> labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ optional<std::string> labelError;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
+ if (!label) {
+ ctx.error(labelError ? *labelError :
+ R"(Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
+ i);
+ return ParseResult();
+ }
+
+ if (*label <= previous) {
+ ctx.error(
+ R"(Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.)",
+ i
+ );
+ return ParseResult();
+ }
+ previous = *label;
+
+ auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ stops.emplace(*label, std::move(*output));
+ }
+
+ assert(outputType);
+
+ if (
+ *outputType != type::Number &&
+ *outputType != type::Color &&
+ !(
+ outputType->is<type::Array>() &&
+ outputType->get<type::Array>().itemType == type::Number &&
+ outputType->get<type::Array>().N
+ )
+ )
+ {
+ ctx.error("Type " + toString(*outputType) + " is not interpolatable.");
+ return ParseResult();
+ }
+
+ return outputType->match(
+ [&](const type::NumberType&) -> ParseResult {
+ return interpolator->match([&](const auto& interpolator_) {
+ return ParseResult(std::make_unique<Interpolate<double>>(
+ *outputType, interpolator_, std::move(*input), std::move(stops)
+ ));
+ });
+ },
+ [&](const type::ColorType&) -> ParseResult {
+ return interpolator->match([&](const auto& interpolator_) {
+ return ParseResult(std::make_unique<Interpolate<Color>>(
+ *outputType, interpolator_, std::move(*input), std::move(stops)
+ ));
+ });
+ },
+ [&](const type::Array& arrayType) -> ParseResult {
+ return interpolator->match(
+ [&](const auto& continuousInterpolator) {
+ if (arrayType.itemType != type::Number || !arrayType.N) {
+ assert(false); // interpolability already checked above.
+ return ParseResult();
+ }
+ return ParseResult(std::make_unique<Interpolate<std::vector<Value>>>(
+ *outputType, continuousInterpolator, std::move(*input), std::move(stops)
+ ));
+ }
+ );
+ },
+ [&](const auto&) {
+ // unreachable: Null, Boolean, String, Object, Value output types
+ // are not interpolatable, and interpolability was already checked above
+ assert(false);
+ return ParseResult();
+ }
+ );
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_constant.cpp b/src/mbgl/style/expression/is_constant.cpp
new file mode 100644
index 0000000000..0ebb37faa9
--- /dev/null
+++ b/src/mbgl/style/expression/is_constant.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/expression/is_constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isFeatureConstant(const Expression& expression) {
+ if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ const std::string name = e->getName();
+ optional<std::size_t> parameterCount = e->getParameterCount();
+ if (name == "get" && parameterCount && *parameterCount == 1) {
+ return false;
+ } else if (name == "has" && parameterCount && *parameterCount == 1) {
+ return false;
+ } else if (
+ name == "properties" ||
+ name == "geometry-type" ||
+ name == "id"
+ ) {
+ return false;
+ }
+ }
+
+ bool featureConstant = true;
+ expression.eachChild([&](const Expression& e) {
+ if (featureConstant && !isFeatureConstant(e)) {
+ featureConstant = false;
+ }
+ });
+ return featureConstant;
+}
+
+bool isZoomConstant(const Expression& e) {
+ return isGlobalPropertyConstant(e, std::array<std::string, 1>{{"zoom"}});
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_expression.cpp b/src/mbgl/style/expression/is_expression.cpp
new file mode 100644
index 0000000000..77212f6a1e
--- /dev/null
+++ b/src/mbgl/style/expression/is_expression.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <mbgl/style/conversion.hpp>
+
+#include <unordered_set>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using namespace mbgl::style::conversion;
+
+bool isExpression(const Convertible& value) {
+ const ExpressionRegistry& registry = getExpressionRegistry();
+
+ if (!isArray(value) || arrayLength(value) == 0) return false;
+ optional<std::string> name = toString(arrayMember(value, 0));
+ if (!name) return false;
+
+ return (registry.find(*name) != registry.end()) ||
+ (CompoundExpressionRegistry::definitions.find(*name) != CompoundExpressionRegistry::definitions.end());
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp
new file mode 100644
index 0000000000..561e8515d4
--- /dev/null
+++ b/src/mbgl/style/expression/let.cpp
@@ -0,0 +1,92 @@
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/conversion/get_json_type.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Let::evaluate(const EvaluationContext& params) const {
+ return result->evaluate(params);
+}
+
+void Let::eachChild(const std::function<void(const Expression&)>& visit) const {
+ for (auto it = bindings.begin(); it != bindings.end(); it++) {
+ visit(*it->second);
+ }
+ visit(*result);
+}
+
+using namespace mbgl::style::conversion;
+
+ParseResult Let::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 4) {
+ ctx.error("Expected at least 3 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ std::map<std::string, std::shared_ptr<Expression>> bindings_;
+ for(std::size_t i = 1; i < length - 1; i += 2) {
+ optional<std::string> name = toString(arrayMember(value, i));
+ if (!name) {
+ ctx.error("Expected string, but found " + getJSONType(arrayMember(value, i)) + " instead.", i);
+ return ParseResult();
+ }
+
+ bool isValidName = std::all_of(name->begin(), name->end(), [](unsigned char c) {
+ return std::isalnum(c) || c == '_';
+ });
+ if (!isValidName) {
+ ctx.error("Variable names must contain only alphanumeric characters or '_'.", 1);
+ return ParseResult();
+ }
+
+ ParseResult bindingValue = ctx.parse(arrayMember(value, i + 1), i + 1);
+ if (!bindingValue) {
+ return ParseResult();
+ }
+
+ bindings_.emplace(*name, std::move(*bindingValue));
+ }
+
+ ParseResult result_ = ctx.parse(arrayMember(value, length - 1), length - 1, ctx.getExpected(), bindings_);
+ if (!result_) {
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Let>(std::move(bindings_), std::move(*result_)));
+}
+
+EvaluationResult Var::evaluate(const EvaluationContext& params) const {
+ return value->evaluate(params);
+}
+
+void Var::eachChild(const std::function<void(const Expression&)>&) const {}
+
+ParseResult Var::parse(const Convertible& value_, ParsingContext& ctx) {
+ assert(isArray(value_));
+
+ if (arrayLength(value_) != 2 || !toString(arrayMember(value_, 1))) {
+ ctx.error("'var' expression requires exactly one string literal argument.");
+ return ParseResult();
+ }
+
+ std::string name_ = *toString(arrayMember(value_, 1));
+
+ optional<std::shared_ptr<Expression>> bindingValue = ctx.getBinding(name_);
+ if (!bindingValue) {
+ ctx.error(R"(Unknown variable ")" + name_ + R"(". Make sure ")" +
+ name_ + R"(" has been bound in an enclosing "let" expression before using it.)", 1);
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Var>(name_, std::move(*bindingValue)));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp
new file mode 100644
index 0000000000..7e79fcbfe6
--- /dev/null
+++ b/src/mbgl/style/expression/literal.cpp
@@ -0,0 +1,108 @@
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+optional<Value> checkNumber(T n) {
+ if (n > std::numeric_limits<double>::max()) {
+ return {std::numeric_limits<double>::infinity()};
+ } else {
+ return {static_cast<double>(n)};
+ }
+}
+
+using namespace mbgl::style::conversion;
+optional<Value> parseValue(const Convertible& value, ParsingContext& ctx) {
+ if (isUndefined(value)) return {Null};
+ if (isObject(value)) {
+ std::unordered_map<std::string, Value> result;
+ bool error = false;
+ eachMember(value, [&] (const std::string& k, const mbgl::style::conversion::Convertible& v) -> optional<conversion::Error> {
+ if (!error) {
+ optional<Value> memberValue = parseValue(v, ctx);
+ if (memberValue) {
+ result.emplace(k, *memberValue);
+ } else {
+ error = true;
+ }
+ }
+ return {};
+ });
+ return error ? optional<Value>() : optional<Value>(result);
+ }
+
+ if (isArray(value)) {
+ std::vector<Value> result;
+ const auto length = arrayLength(value);
+ for(std::size_t i = 0; i < length; i++) {
+ optional<Value> item = parseValue(arrayMember(value, i), ctx);
+ if (item) {
+ result.emplace_back(*item);
+ } else {
+ return optional<Value>();
+ }
+ }
+ return optional<Value>(result);
+ }
+
+ optional<mbgl::Value> v = toValue(value);
+ // since value represents a JSON value, if it's not undefined, object, or
+ // array, it must be convertible to mbgl::Value
+ assert(v);
+
+ return v->match(
+ [&](uint64_t n) { return checkNumber(n); },
+ [&](int64_t n) { return checkNumber(n); },
+ [&](double n) { return checkNumber(n); },
+ [&](const auto&) {
+ return optional<Value>(toExpressionValue(*v));
+ }
+ );
+}
+
+ParseResult Literal::parse(const Convertible& value, ParsingContext& ctx) {
+ if (isObject(value)) {
+ ctx.error(R"(Bare objects invalid. Use ["literal", {...}] instead.)");
+ return ParseResult();
+ } else if (isArray(value)) {
+ // object or array value, quoted with ["literal", value]
+ if (arrayLength(value) != 2) {
+ ctx.error("'literal' expression requires exactly one argument, but found " + util::toString(arrayLength(value) - 1) + " instead.");
+ return ParseResult();
+ }
+ const optional<Value> parsedValue = parseValue(arrayMember(value, 1), ctx);
+ if (!parsedValue) {
+ return ParseResult();
+ }
+
+ // special case: infer the item type if possible for zero-length arrays
+ if (
+ ctx.getExpected() &&
+ ctx.getExpected()->template is<type::Array>() &&
+ parsedValue->template is<std::vector<Value>>()
+ ) {
+ auto type = typeOf(*parsedValue).template get<type::Array>();
+ auto expected = ctx.getExpected()->template get<type::Array>();
+ if (
+ type.N && (*type.N == 0) &&
+ (!expected.N || (*expected.N == 0))
+ ) {
+ return ParseResult(std::make_unique<Literal>(expected, parsedValue->template get<std::vector<Value>>()));
+ }
+ }
+
+ return ParseResult(std::make_unique<Literal>(*parsedValue));
+ } else {
+ // bare primitive value (string, number, boolean, null)
+ const optional<Value> parsedValue = parseValue(value, ctx);
+ return ParseResult(std::make_unique<Literal>(*parsedValue));
+ }
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp
new file mode 100644
index 0000000000..35356747c9
--- /dev/null
+++ b/src/mbgl/style/expression/match.cpp
@@ -0,0 +1,263 @@
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+void Match<T>::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+ for (const std::pair<T, std::shared_ptr<Expression>>& branch : branches) {
+ visit(*branch.second);
+ }
+ visit(*otherwise);
+}
+
+template <typename T>
+bool Match<T>::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Match*>(&e)) {
+ return (*input == *(rhs->input) &&
+ *otherwise == *(rhs->otherwise) &&
+ Expression::childrenEqual(branches, rhs->branches));
+ }
+ return false;
+}
+
+
+template<> EvaluationResult Match<std::string>::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult inputValue = input->evaluate(params);
+ if (!inputValue) {
+ return inputValue.error();
+ }
+
+ auto it = branches.find(inputValue->get<std::string>());
+ if (it != branches.end()) {
+ return (*it).second->evaluate(params);
+ }
+
+ return otherwise->evaluate(params);
+}
+
+template<> EvaluationResult Match<int64_t>::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult inputValue = input->evaluate(params);
+ if (!inputValue) {
+ return inputValue.error();
+ }
+
+ const auto numeric = inputValue->get<double>();
+ int64_t rounded = std::floor(numeric);
+ if (numeric == rounded) {
+ auto it = branches.find(rounded);
+ if (it != branches.end()) {
+ return (*it).second->evaluate(params);
+ }
+ }
+
+ return otherwise->evaluate(params);
+}
+
+template class Match<int64_t>;
+template class Match<std::string>;
+
+using InputType = variant<int64_t, std::string>;
+
+using namespace mbgl::style::conversion;
+optional<InputType> parseInputValue(const Convertible& input, ParsingContext& parentContext, std::size_t index, optional<type::Type>& inputType) {
+ using namespace mbgl::style::conversion;
+ optional<InputType> result;
+ optional<type::Type> type;
+
+ auto value = toValue(input);
+
+ if (value) {
+ value->match(
+ [&] (uint64_t n) {
+ if (!Value::isSafeInteger(n)) {
+ parentContext.error("Branch labels must be integers no larger than " + util::toString(Value::maxSafeInteger()) + ".", index);
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (int64_t n) {
+ if (!Value::isSafeInteger(n)) {
+ parentContext.error("Branch labels must be integers no larger than " + util::toString(Value::maxSafeInteger()) + ".", index);
+ } else {
+ type = {type::Number};
+ result = {n};
+ }
+ },
+ [&] (double n) {
+ if (!Value::isSafeInteger(n)) {
+ parentContext.error("Branch labels must be integers no larger than " + util::toString(Value::maxSafeInteger()) + ".", index);
+ } else if (n != std::floor(n)) {
+ parentContext.error("Numeric branch labels must be integer values.", index);
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (const std::string& s) {
+ type = {type::String};
+ result = {s};
+ },
+ [&] (const auto&) {
+ parentContext.error("Branch labels must be numbers or strings.", index);
+ }
+ );
+ } else {
+ parentContext.error("Branch labels must be numbers or strings.", index);
+ }
+
+ if (!type) {
+ return result;
+ }
+
+ if (!inputType) {
+ inputType = type;
+ } else {
+ optional<std::string> err = type::checkSubtype(*inputType, *type);
+ if (err) {
+ parentContext.error(*err, index);
+ return optional<InputType>();
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+static ParseResult create(type::Type outputType,
+ std::unique_ptr<Expression>input,
+ std::vector<std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>> branches,
+ std::unique_ptr<Expression> otherwise,
+ ParsingContext& ctx) {
+ typename Match<T>::Branches typedBranches;
+
+ std::size_t index = 2;
+
+ typedBranches.reserve(branches.size());
+ for (std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>& pair : branches) {
+ std::shared_ptr<Expression> result = std::move(pair.second);
+ for (const InputType& label : pair.first) {
+ const auto& typedLabel = label.template get<T>();
+ if (typedBranches.find(typedLabel) != typedBranches.end()) {
+ ctx.error("Branch labels must be unique.", index);
+ return ParseResult();
+ }
+ typedBranches.emplace(typedLabel, result);
+ }
+
+ index += 2;
+ }
+ return ParseResult(std::make_unique<Match<T>>(
+ outputType,
+ std::move(input),
+ std::move(typedBranches),
+ std::move(otherwise)
+ ));
+}
+
+ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 5) {
+ ctx.error(
+ "Expected at least 4 arguments, but found only " + util::toString(length - 1) + "."
+ );
+ return ParseResult();
+ }
+
+ // Expect odd-length array: ["match", input, 2 * (n pairs)..., otherwise]
+ if (length % 2 != 1) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ optional<type::Type> inputType;
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ std::vector<std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>> branches;
+
+ branches.reserve((length - 3) / 2);
+ for (size_t i = 2; i + 1 < length; i += 2) {
+ const auto& label = arrayMember(value, i);
+
+ std::vector<InputType> labels;
+ // Match pair inputs are provided as either a literal value or a
+ // raw JSON array of string / number / boolean values.
+ if (isArray(label)) {
+ auto groupLength = arrayLength(label);
+ if (groupLength == 0) {
+ ctx.error("Expected at least one branch label.", i);
+ return ParseResult();
+ }
+
+ labels.reserve(groupLength);
+ for (size_t j = 0; j < groupLength; j++) {
+ const optional<InputType> inputValue = parseInputValue(arrayMember(label, j), ctx, i, inputType);
+ if (!inputValue) {
+ return ParseResult();
+ }
+ labels.push_back(*inputValue);
+ }
+ } else {
+ const optional<InputType> inputValue = parseInputValue(label, ctx, i, inputType);
+ if (!inputValue) {
+ return ParseResult();
+ }
+ labels.push_back(*inputValue);
+ }
+
+ ParseResult output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return ParseResult();
+ }
+
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ branches.push_back(std::make_pair(std::move(labels), std::move(*output)));
+ }
+
+ auto input = ctx.parse(arrayMember(value, 1), 1, inputType);
+ if (!input) {
+ return ParseResult();
+ }
+
+ auto otherwise = ctx.parse(arrayMember(value, length - 1), length - 1, outputType);
+ if (!otherwise) {
+ return ParseResult();
+ }
+
+ assert(inputType && outputType);
+
+ return inputType->match(
+ [&](const type::NumberType&) {
+ return create<int64_t>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx);
+ },
+ [&](const type::StringType&) {
+ return create<std::string>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx);
+ },
+ [&](const auto&) {
+ // unreachable: inputType is set by parseInputValue(), which only
+ // accepts string and (integer) numeric values.
+ assert(false);
+ return ParseResult();
+ }
+ );
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp
new file mode 100644
index 0000000000..501ba2749f
--- /dev/null
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -0,0 +1,208 @@
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/type.hpp>
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/at.hpp>
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/boolean_operator.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+#include <mbgl/style/conversion/get_json_type.hpp>
+
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isConstant(const Expression& expression) {
+ if (dynamic_cast<const Var*>(&expression)) {
+ return false;
+ }
+
+ if (auto compound = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ if (compound->getName() == "error") {
+ return false;
+ }
+ }
+
+ bool literalArgs = true;
+ expression.eachChild([&](const Expression& child) {
+ if (!dynamic_cast<const Literal*>(&child)) {
+ literalArgs = false;
+ }
+ });
+ if (!literalArgs) {
+ return false;
+ }
+
+ return isFeatureConstant(expression) &&
+ isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}});
+}
+
+using namespace mbgl::style::conversion;
+
+ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_) {
+ ParsingContext child(key + "[" + util::toString(index_) + "]",
+ errors,
+ std::move(expected_),
+ scope);
+ return child.parse(value);
+}
+
+ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_,
+ const std::map<std::string, std::shared_ptr<Expression>>& bindings) {
+ ParsingContext child(key + "[" + util::toString(index_) + "]",
+ errors,
+ std::move(expected_),
+ std::make_shared<detail::Scope>(bindings, scope));
+ return child.parse(value);
+}
+
+const ExpressionRegistry& getExpressionRegistry() {
+ static ExpressionRegistry registry {{
+ {"all", All::parse},
+ {"any", Any::parse},
+ {"array", ArrayAssertion::parse},
+ {"at", At::parse},
+ {"boolean", Assertion::parse},
+ {"case", Case::parse},
+ {"coalesce", Coalesce::parse},
+ {"interpolate", parseInterpolate},
+ {"let", Let::parse},
+ {"literal", Literal::parse},
+ {"match", parseMatch},
+ {"number", Assertion::parse},
+ {"object", Assertion::parse},
+ {"step", Step::parse},
+ {"string", Assertion::parse},
+ {"to-color", Coercion::parse},
+ {"to-number", Coercion::parse},
+ {"var", Var::parse}
+ }};
+ return registry;
+}
+
+ParseResult ParsingContext::parse(const Convertible& value)
+{
+ ParseResult parsed;
+
+ if (isArray(value)) {
+ const std::size_t length = arrayLength(value);
+ if (length == 0) {
+ error(R"(Expected an array with at least one element. If you wanted a literal array, use ["literal", []].)");
+ return ParseResult();
+ }
+
+ const optional<std::string> op = toString(arrayMember(value, 0));
+ if (!op) {
+ error(
+ "Expression name must be a string, but found " + getJSONType(arrayMember(value, 0)) +
+ R"( instead. If you wanted a literal array, use ["literal", [...]].)",
+ 0
+ );
+ return ParseResult();
+ }
+
+ const ExpressionRegistry& registry = getExpressionRegistry();
+ auto parseFunction = registry.find(*op);
+ if (parseFunction != registry.end()) {
+ parsed = parseFunction->second(value, *this);
+ } else {
+ parsed = parseCompoundExpression(*op, value, *this);
+ }
+ } else {
+ parsed = Literal::parse(value, *this);
+ }
+
+ if (!parsed) {
+ assert(errors->size() > 0);
+ } else if (expected) {
+ auto wrapForType = [&](const type::Type& target, std::unique_ptr<Expression> expression) -> std::unique_ptr<Expression> {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(expression));
+ if (target == type::Color) {
+ return std::make_unique<Coercion>(target, std::move(args));
+ } else {
+ return std::make_unique<Assertion>(target, std::move(args));
+ }
+ };
+
+ const type::Type actual = (*parsed)->getType();
+ if (*expected == type::Color && (actual == type::String || actual == type::Value)) {
+ parsed = wrapForType(type::Color, std::move(*parsed));
+ } else if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) {
+ parsed = wrapForType(*expected, std::move(*parsed));
+ }
+
+ checkType((*parsed)->getType());
+ if (errors->size() > 0) {
+ return ParseResult();
+ }
+ }
+
+ // If an expression's arguments are all literals, we can evaluate
+ // it immediately and replace it with a literal value in the
+ // parsed result.
+ if (parsed && !dynamic_cast<Literal *>(parsed->get()) && isConstant(**parsed)) {
+ EvaluationContext params(nullptr);
+ EvaluationResult evaluated((*parsed)->evaluate(params));
+ if (!evaluated) {
+ error(evaluated.error().message);
+ return ParseResult();
+ }
+
+ const type::Type type = (*parsed)->getType();
+ if (type.is<type::Array>()) {
+ // keep the original expression's array type, even if the evaluated
+ // type is more specific.
+ return ParseResult(std::make_unique<Literal>(
+ type.get<type::Array>(),
+ evaluated->get<std::vector<Value>>())
+ );
+ } else {
+ return ParseResult(std::make_unique<Literal>(*evaluated));
+ }
+ }
+
+ // if this is the root expression, enforce constraints on the use ["zoom"].
+ if (key.size() == 0 && parsed && !isZoomConstant(**parsed)) {
+ optional<variant<const InterpolateBase*, const Step*, ParsingError>> zoomCurve = findZoomCurve(parsed->get());
+ if (!zoomCurve) {
+ error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)");
+ return ParseResult();
+ } else if (zoomCurve->is<ParsingError>()) {
+ error(zoomCurve->get<ParsingError>().message);
+ return ParseResult();
+ }
+ }
+
+ return parsed;
+}
+
+optional<std::string> ParsingContext::checkType(const type::Type& t) {
+ assert(expected);
+ optional<std::string> err = type::checkSubtype(*expected, t);
+ if (err) {
+ error(*err);
+ }
+ return err;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp
new file mode 100644
index 0000000000..bfcb6fd270
--- /dev/null
+++ b/src/mbgl/style/expression/step.cpp
@@ -0,0 +1,152 @@
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/get_covering_stops.hpp>
+#include <mbgl/util/string.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult Step::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedInput) { return evaluatedInput.error(); }
+ float x = *fromExpressionValue<float>(*evaluatedInput);
+
+ if (stops.empty()) {
+ return EvaluationError { "No stops in step curve." };
+ }
+
+ auto it = stops.upper_bound(x);
+ if (it == stops.end()) {
+ return stops.rbegin()->second->evaluate(params);
+ } else if (it == stops.begin()) {
+ return stops.begin()->second->evaluate(params);
+ } else {
+ return std::prev(it)->second->evaluate(params);
+ }
+}
+
+void Step::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+ for (auto it = stops.begin(); it != stops.end(); it++) {
+ visit(*it->second);
+ }
+}
+
+bool Step::operator==(const Expression& e) const {
+ if (auto rhs = dynamic_cast<const Step*>(&e)) {
+ return *input == *(rhs->input) && Expression::childrenEqual(stops, rhs->stops);
+ }
+ return false;
+}
+
+Range<float> Step::getCoveringStops(const double lower, const double upper) const {
+ return ::mbgl::style::expression::getCoveringStops(stops, lower, upper);
+}
+
+
+ParseResult Step::parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ auto length = arrayLength(value);
+
+ if (length - 1 < 4) {
+ ctx.error("Expected at least 4 arguments, but found only " + util::toString(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // [step, input, firstOutput_value, 2 * (n pairs)...]
+ if ((length - 1) % 2 != 0) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ ParseResult input = ctx.parse(arrayMember(value, 1), 1, {type::Number});
+ if (!input) {
+ return input;
+ }
+
+ std::map<double, std::unique_ptr<Expression>> stops;
+ optional<type::Type> outputType;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ double previous = - std::numeric_limits<double>::infinity();
+
+ // consume the first output value, which doesn't have a corresponding input value,
+ // before proceeding into the "stops" loop below.
+ auto firstOutput = ctx.parse(arrayMember(value, 2), 2, outputType);
+ if (!firstOutput) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*firstOutput)->getType();
+ }
+ stops.emplace(-std::numeric_limits<double>::infinity(), std::move(*firstOutput));
+
+
+ for (std::size_t i = 3; i + 1 < length; i += 2) {
+ const optional<mbgl::Value> labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (n > std::numeric_limits<double>::max()) {
+ label = {std::numeric_limits<double>::infinity()};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
+ if (!label) {
+ ctx.error(R"(Input/output pairs for "step" expressions must be defined using literal numeric values (not computed expressions) for the input values.)", i);
+ return ParseResult();
+ }
+
+ if (*label <= previous) {
+ ctx.error(
+ R"(Input/output pairs for "step" expressions must be arranged with input values in strictly ascending order.)",
+ i
+ );
+ return ParseResult();
+ }
+ previous = *label;
+
+ auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
+ if (!output) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ stops.emplace(*label, std::move(*output));
+ }
+
+ assert(outputType);
+
+ return ParseResult(std::make_unique<Step>(*outputType, std::move(*input), std::move(stops)));
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/util.cpp b/src/mbgl/style/expression/util.cpp
new file mode 100644
index 0000000000..f198fb3e1b
--- /dev/null
+++ b/src/mbgl/style/expression/util.cpp
@@ -0,0 +1,39 @@
+
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/style/expression/value.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+std::string stringifyColor(double r, double g, double b, double a) {
+ return stringify(r) + ", " +
+ stringify(g) + ", " +
+ stringify(b) + ", " +
+ stringify(a);
+}
+
+Result<Color> rgba(double r, double g, double b, double a) {
+ if (
+ r < 0 || r > 255 ||
+ g < 0 || g > 255 ||
+ b < 0 || b > 255
+ ) {
+ return EvaluationError {
+ "Invalid rgba value [" + stringifyColor(r, g, b, a) +
+ "]: 'r', 'g', and 'b' must be between 0 and 255."
+ };
+ }
+ if (a < 0 || a > 1) {
+ return EvaluationError {
+ "Invalid rgba value [" + stringifyColor(r, g, b, a) +
+ "]: 'a' must be between 0 and 1."
+ };
+ }
+ return Color(r / 255, g / 255, b / 255, a);
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/util.hpp b/src/mbgl/style/expression/util.hpp
new file mode 100644
index 0000000000..b6fc408ed9
--- /dev/null
+++ b/src/mbgl/style/expression/util.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/color.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Result<Color> rgba(double r, double g, double b, double a);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp
new file mode 100644
index 0000000000..b75f471ce3
--- /dev/null
+++ b/src/mbgl/style/expression/value.cpp
@@ -0,0 +1,322 @@
+#include <rapidjson/writer.h>
+#include <rapidjson/stringbuffer.h>
+#include <mbgl/style/expression/value.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+type::Type typeOf(const Value& value) {
+ return value.match(
+ [&](bool) -> type::Type { return type::Boolean; },
+ [&](double) -> type::Type { return type::Number; },
+ [&](const std::string&) -> type::Type { return type::String; },
+ [&](const Color&) -> type::Type { return type::Color; },
+ [&](const NullValue&) -> type::Type { return type::Null; },
+ [&](const std::unordered_map<std::string, Value>&) -> type::Type { return type::Object; },
+ [&](const std::vector<Value>& arr) -> type::Type {
+ optional<type::Type> itemType;
+ for (const auto& item : arr) {
+ const type::Type t = typeOf(item);
+ if (!itemType) {
+ itemType = {t};
+ } else if (*itemType == t) {
+ continue;
+ } else {
+ itemType = {type::Value};
+ break;
+ }
+ }
+
+ return type::Array(itemType.value_or(type::Value), arr.size());
+ }
+ );
+}
+
+void writeJSON(rapidjson::Writer<rapidjson::StringBuffer>& writer, const Value& value) {
+ value.match(
+ [&] (const NullValue&) { writer.Null(); },
+ [&] (bool b) { writer.Bool(b); },
+ [&] (double f) {
+ // make sure integer values are stringified without trailing ".0".
+ f == std::floor(f) ? writer.Int(f) : writer.Double(f);
+ },
+ [&] (const std::string& s) { writer.String(s); },
+ [&] (const Color& c) { writer.String(c.stringify()); },
+ [&] (const std::vector<Value>& arr) {
+ writer.StartArray();
+ for(const auto& item : arr) {
+ writeJSON(writer, item);
+ }
+ writer.EndArray();
+ },
+ [&] (const std::unordered_map<std::string, Value>& obj) {
+ writer.StartObject();
+ for(const auto& entry : obj) {
+ writer.Key(entry.first.c_str());
+ writeJSON(writer, entry.second);
+ }
+ writer.EndObject();
+ }
+ );
+}
+
+std::string stringify(const Value& value) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ writeJSON(writer, value);
+ return buffer.GetString();
+}
+
+struct FromMBGLValue {
+ Value operator()(const std::vector<mbgl::Value>& v) {
+ std::vector<Value> result;
+ result.reserve(v.size());
+ for(const auto& item : v) {
+ result.emplace_back(toExpressionValue(item));
+ }
+ return result;
+ }
+
+ Value operator()(const std::unordered_map<std::string, mbgl::Value>& v) {
+ std::unordered_map<std::string, Value> result;
+ result.reserve(v.size());
+ for(const auto& entry : v) {
+ result.emplace(entry.first, toExpressionValue(entry.second));
+ }
+ return result;
+ }
+
+ Value operator()(const std::string& s) { return s; }
+ Value operator()(const bool b) { return b; }
+ Value operator()(const NullValue) { return Null; }
+ Value operator()(const double v) { return v; }
+ Value operator()(const uint64_t& v) {
+ return static_cast<double>(v);
+ }
+ Value operator()(const int64_t& v) {
+ return static_cast<double>(v);
+ }
+};
+
+Value ValueConverter<mbgl::Value>::toExpressionValue(const mbgl::Value& value) {
+ return mbgl::Value::visit(value, FromMBGLValue());
+}
+
+Value ValueConverter<float>::toExpressionValue(const float value) {
+ return static_cast<double>(value);
+}
+
+optional<float> ValueConverter<float>::fromExpressionValue(const Value& value) {
+ if (value.template is<double>()) {
+ double v = value.template get<double>();
+ if (v <= std::numeric_limits<float>::max()) {
+ return static_cast<float>(v);
+ }
+ }
+ return optional<float>();
+}
+
+
+template <typename T, typename Container>
+std::vector<Value> toArrayValue(const Container& value) {
+ std::vector<Value> result;
+ result.reserve(value.size());
+ for (const T& item : value) {
+ result.push_back(ValueConverter<T>::toExpressionValue(item));
+ }
+ return result;
+}
+
+template <typename T, std::size_t N>
+Value ValueConverter<std::array<T, N>>::toExpressionValue(const std::array<T, N>& value) {
+ return toArrayValue<T>(value);
+}
+
+template <typename T, std::size_t N>
+optional<std::array<T, N>> ValueConverter<std::array<T, N>>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&] (const std::vector<Value>& v) -> optional<std::array<T, N>> {
+ if (v.size() != N) return optional<std::array<T, N>>();
+ std::array<T, N> result;
+ auto it = result.begin();
+ for(const Value& item : v) {
+ optional<T> convertedItem = ValueConverter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<std::array<T, N>>();
+ }
+ *it = *convertedItem;
+ it = std::next(it);
+ }
+ return result;
+ },
+ [&] (const auto&) { return optional<std::array<T, N>>(); }
+ );
+}
+
+
+template <typename T>
+Value ValueConverter<std::vector<T>>::toExpressionValue(const std::vector<T>& value) {
+ return toArrayValue<T>(value);
+}
+
+template <typename T>
+optional<std::vector<T>> ValueConverter<std::vector<T>>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&] (const std::vector<Value>& v) -> optional<std::vector<T>> {
+ std::vector<T> result;
+ result.reserve(v.size());
+ for(const Value& item : v) {
+ optional<T> convertedItem = ValueConverter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<std::vector<T>>();
+ }
+ result.push_back(*convertedItem);
+ }
+ return result;
+ },
+ [&] (const auto&) { return optional<std::vector<T>>(); }
+ );
+}
+
+Value ValueConverter<Position>::toExpressionValue(const mbgl::style::Position& value) {
+ return ValueConverter<std::array<float, 3>>::toExpressionValue(value.getSpherical());
+}
+
+optional<Position> ValueConverter<Position>::fromExpressionValue(const Value& v) {
+ auto pos = ValueConverter<std::array<float, 3>>::fromExpressionValue(v);
+ return pos ? optional<Position>(Position(*pos)) : optional<Position>();
+}
+
+template <typename T>
+Value ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >>::toExpressionValue(const T& value) {
+ return std::string(Enum<T>::toString(value));
+}
+
+template <typename T>
+optional<T> ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >>::fromExpressionValue(const Value& value) {
+ return value.match(
+ [&] (const std::string& v) { return Enum<T>::toEnum(v); },
+ [&] (const auto&) { return optional<T>(); }
+ );
+}
+
+
+Value toExpressionValue(const Value& v) {
+ return v;
+}
+
+template <typename T, typename Enable>
+Value toExpressionValue(const T& value) {
+ return ValueConverter<T>::toExpressionValue(value);
+}
+
+optional<Value> fromExpressionValue(const Value& v) {
+ return optional<Value>(v);
+}
+
+template <typename T>
+std::enable_if_t< !std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return ValueConverter<T>::fromExpressionValue(v);
+}
+
+template <typename T>
+type::Type valueTypeToExpressionType() {
+ return ValueConverter<T>::expressionType();
+}
+
+template <> type::Type valueTypeToExpressionType<Value>() { return type::Value; }
+template <> type::Type valueTypeToExpressionType<NullValue>() { return type::Null; }
+template <> type::Type valueTypeToExpressionType<bool>() { return type::Boolean; }
+template <> type::Type valueTypeToExpressionType<double>() { return type::Number; }
+template <> type::Type valueTypeToExpressionType<std::string>() { return type::String; }
+template <> type::Type valueTypeToExpressionType<Color>() { return type::Color; }
+template <> type::Type valueTypeToExpressionType<std::unordered_map<std::string, Value>>() { return type::Object; }
+template <> type::Type valueTypeToExpressionType<std::vector<Value>>() { return type::Array(type::Value); }
+
+// used only for the special (and private) "error" expression
+template <> type::Type valueTypeToExpressionType<type::ErrorType>() { return type::Error; }
+
+
+template Value toExpressionValue(const mbgl::Value&);
+
+
+// for to_rgba expression
+template type::Type valueTypeToExpressionType<std::array<double, 4>>();
+template optional<std::array<double, 4>> fromExpressionValue<std::array<double, 4>>(const Value&);
+template Value toExpressionValue(const std::array<double, 4>&);
+
+// layout/paint property types
+template type::Type valueTypeToExpressionType<float>();
+template optional<float> fromExpressionValue<float>(const Value&);
+template Value toExpressionValue(const float&);
+
+template type::Type valueTypeToExpressionType<std::array<float, 2>>();
+template optional<std::array<float, 2>> fromExpressionValue<std::array<float, 2>>(const Value&);
+template Value toExpressionValue(const std::array<float, 2>&);
+
+template type::Type valueTypeToExpressionType<std::array<float, 4>>();
+template optional<std::array<float, 4>> fromExpressionValue<std::array<float, 4>>(const Value&);
+template Value toExpressionValue(const std::array<float, 4>&);
+
+template type::Type valueTypeToExpressionType<std::vector<float>>();
+template optional<std::vector<float>> fromExpressionValue<std::vector<float>>(const Value&);
+template Value toExpressionValue(const std::vector<float>&);
+
+template type::Type valueTypeToExpressionType<std::vector<std::string>>();
+template optional<std::vector<std::string>> fromExpressionValue<std::vector<std::string>>(const Value&);
+template Value toExpressionValue(const std::vector<std::string>&);
+
+template type::Type valueTypeToExpressionType<AlignmentType>();
+template optional<AlignmentType> fromExpressionValue<AlignmentType>(const Value&);
+template Value toExpressionValue(const AlignmentType&);
+
+template type::Type valueTypeToExpressionType<CirclePitchScaleType>();
+template optional<CirclePitchScaleType> fromExpressionValue<CirclePitchScaleType>(const Value&);
+template Value toExpressionValue(const CirclePitchScaleType&);
+
+template type::Type valueTypeToExpressionType<IconTextFitType>();
+template optional<IconTextFitType> fromExpressionValue<IconTextFitType>(const Value&);
+template Value toExpressionValue(const IconTextFitType&);
+
+template type::Type valueTypeToExpressionType<LineCapType>();
+template optional<LineCapType> fromExpressionValue<LineCapType>(const Value&);
+template Value toExpressionValue(const LineCapType&);
+
+template type::Type valueTypeToExpressionType<LineJoinType>();
+template optional<LineJoinType> fromExpressionValue<LineJoinType>(const Value&);
+template Value toExpressionValue(const LineJoinType&);
+
+template type::Type valueTypeToExpressionType<SymbolPlacementType>();
+template optional<SymbolPlacementType> fromExpressionValue<SymbolPlacementType>(const Value&);
+template Value toExpressionValue(const SymbolPlacementType&);
+
+template type::Type valueTypeToExpressionType<SymbolAnchorType>();
+template optional<SymbolAnchorType> fromExpressionValue<SymbolAnchorType>(const Value&);
+template Value toExpressionValue(const SymbolAnchorType&);
+
+template type::Type valueTypeToExpressionType<TextJustifyType>();
+template optional<TextJustifyType> fromExpressionValue<TextJustifyType>(const Value&);
+template Value toExpressionValue(const TextJustifyType&);
+
+template type::Type valueTypeToExpressionType<TextTransformType>();
+template optional<TextTransformType> fromExpressionValue<TextTransformType>(const Value&);
+template Value toExpressionValue(const TextTransformType&);
+
+template type::Type valueTypeToExpressionType<TranslateAnchorType>();
+template optional<TranslateAnchorType> fromExpressionValue<TranslateAnchorType>(const Value&);
+template Value toExpressionValue(const TranslateAnchorType&);
+
+template type::Type valueTypeToExpressionType<LightAnchorType>();
+template optional<LightAnchorType> fromExpressionValue<LightAnchorType>(const Value&);
+template Value toExpressionValue(const LightAnchorType&);
+
+template type::Type valueTypeToExpressionType<Position>();
+template optional<Position> fromExpressionValue<Position>(const Value&);
+template Value toExpressionValue(const Position&);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp
index 2984c3832f..dd179f5376 100644
--- a/src/mbgl/style/function/categorical_stops.cpp
+++ b/src/mbgl/style/function/categorical_stops.cpp
@@ -33,6 +33,9 @@ template class CategoricalStops<Color>;
template class CategoricalStops<std::array<float, 2>>;
template class CategoricalStops<std::string>;
template class CategoricalStops<TextTransformType>;
+template class CategoricalStops<TextJustifyType>;
+template class CategoricalStops<SymbolAnchorType>;
+template class CategoricalStops<LineJoinType>;
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/function/expression.cpp b/src/mbgl/style/function/expression.cpp
new file mode 100644
index 0000000000..d9dbbfa1d3
--- /dev/null
+++ b/src/mbgl/style/function/expression.cpp
@@ -0,0 +1,38 @@
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class GeoJSONFeature : public GeometryTileFeature {
+public:
+ const Feature& feature;
+
+ GeoJSONFeature(const Feature& feature_) : feature(feature_) {}
+
+ FeatureType getType() const override {
+ return apply_visitor(ToFeatureType(), feature.geometry);
+ }
+ PropertyMap getProperties() const override { return feature.properties; }
+ optional<FeatureIdentifier> getID() const override { return feature.id; }
+ GeometryCollection getGeometries() const override { return {}; }
+ optional<mbgl::Value> getValue(const std::string& key) const override {
+ auto it = feature.properties.find(key);
+ if (it != feature.properties.end()) {
+ return optional<mbgl::Value>(it->second);
+ }
+ return optional<mbgl::Value>();
+ }
+};
+
+
+EvaluationResult Expression::evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const {
+ GeoJSONFeature f(feature);
+ return this->evaluate(EvaluationContext(zoom, &f, heatmapDensity));
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp
index 0c6891eac5..0ac6fda846 100644
--- a/src/mbgl/style/function/identity_stops.cpp
+++ b/src/mbgl/style/function/identity_stops.cpp
@@ -36,11 +36,38 @@ optional<TextTransformType> IdentityStops<TextTransformType>::evaluate(const Val
if (!value.is<std::string>()) {
return {};
}
-
+
return Enum<TextTransformType>::toEnum(value.get<std::string>());
}
template <>
+optional<TextJustifyType> IdentityStops<TextJustifyType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<TextJustifyType>::toEnum(value.get<std::string>());
+}
+
+template <>
+optional<SymbolAnchorType> IdentityStops<SymbolAnchorType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<SymbolAnchorType>::toEnum(value.get<std::string>());
+}
+
+template <>
+optional<LineJoinType> IdentityStops<LineJoinType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<LineJoinType>::toEnum(value.get<std::string>());
+}
+
+template <>
optional<std::array<float, 2>> IdentityStops<std::array<float, 2>>::evaluate(const Value& value) const {
if (!value.is<std::vector<Value>>()) {
return {};
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
index 75dc83206c..e439e42695 100644
--- a/src/mbgl/style/image_impl.hpp
+++ b/src/mbgl/style/image_impl.hpp
@@ -28,5 +28,6 @@ public:
using ImageMap = std::unordered_map<std::string, Immutable<style::Image::Impl>>;
using ImageDependencies = std::set<std::string>;
+using ImageRequestPair = std::pair<ImageDependencies, uint64_t>;
} // namespace mbgl
diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp
index 3bba135c84..9854932699 100644
--- a/src/mbgl/style/layers/circle_layer.cpp
+++ b/src/mbgl/style/layers/circle_layer.cpp
@@ -283,6 +283,33 @@ TransitionOptions CircleLayer::getCirclePitchScaleTransition() const {
return impl().paint.template get<CirclePitchScale>().options;
}
+PropertyValue<AlignmentType> CircleLayer::getDefaultCirclePitchAlignment() {
+ return { AlignmentType::Viewport };
+}
+
+PropertyValue<AlignmentType> CircleLayer::getCirclePitchAlignment() const {
+ return impl().paint.template get<CirclePitchAlignment>().value;
+}
+
+void CircleLayer::setCirclePitchAlignment(PropertyValue<AlignmentType> value) {
+ if (value == getCirclePitchAlignment())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchAlignment>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void CircleLayer::setCirclePitchAlignmentTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchAlignment>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions CircleLayer::getCirclePitchAlignmentTransition() const {
+ return impl().paint.template get<CirclePitchAlignment>().options;
+}
+
DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeWidth() {
return { 0 };
}
diff --git a/src/mbgl/style/layers/circle_layer_properties.hpp b/src/mbgl/style/layers/circle_layer_properties.hpp
index 73b7028465..bc0c961e75 100644
--- a/src/mbgl/style/layers/circle_layer_properties.hpp
+++ b/src/mbgl/style/layers/circle_layer_properties.hpp
@@ -40,6 +40,10 @@ struct CirclePitchScale : PaintProperty<CirclePitchScaleType> {
static CirclePitchScaleType defaultValue() { return CirclePitchScaleType::Map; }
};
+struct CirclePitchAlignment : PaintProperty<AlignmentType> {
+ static AlignmentType defaultValue() { return AlignmentType::Viewport; }
+};
+
struct CircleStrokeWidth : DataDrivenPaintProperty<float, attributes::a_stroke_width, uniforms::u_stroke_width> {
static float defaultValue() { return 0; }
};
@@ -60,6 +64,7 @@ class CirclePaintProperties : public Properties<
CircleTranslate,
CircleTranslateAnchor,
CirclePitchScale,
+ CirclePitchAlignment,
CircleStrokeWidth,
CircleStrokeColor,
CircleStrokeOpacity
diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp
index e37382d5ef..854c771847 100644
--- a/src/mbgl/style/layers/custom_layer.cpp
+++ b/src/mbgl/style/layers/custom_layer.cpp
@@ -8,9 +8,18 @@ namespace style {
CustomLayer::CustomLayer(const std::string& layerID,
CustomLayerInitializeFunction init,
CustomLayerRenderFunction render,
+ CustomLayerContextLostFunction contextLost,
CustomLayerDeinitializeFunction deinit,
void* context)
- : Layer(makeMutable<Impl>(layerID, init, render, deinit, context)) {
+ : Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) {
+}
+
+CustomLayer::CustomLayer(const std::string& layerID,
+ CustomLayerInitializeFunction init,
+ CustomLayerRenderFunction render,
+ CustomLayerDeinitializeFunction deinit,
+ void* context)
+ : Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) {
}
CustomLayer::~CustomLayer() = default;
diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp
index 42e60c582c..1de268d2e2 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -6,12 +6,14 @@ namespace style {
CustomLayer::Impl::Impl(const std::string& id_,
CustomLayerInitializeFunction initializeFn_,
CustomLayerRenderFunction renderFn_,
+ CustomLayerContextLostFunction contextLostFn_,
CustomLayerDeinitializeFunction deinitializeFn_,
void* context_)
: Layer::Impl(LayerType::Custom, id_, std::string()) {
initializeFn = initializeFn_;
renderFn = renderFn_;
deinitializeFn = deinitializeFn_;
+ contextLostFn = contextLostFn_;
context = context_;
}
diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp
index defbbe6894..62efbbe15b 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -14,6 +14,7 @@ public:
Impl(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
@@ -22,6 +23,7 @@ public:
CustomLayerInitializeFunction initializeFn = nullptr;
CustomLayerRenderFunction renderFn = nullptr;
+ CustomLayerContextLostFunction contextLostFn = nullptr;
CustomLayerDeinitializeFunction deinitializeFn = nullptr;
void* context = nullptr;
};
diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp
index 6fbdf19568..1c7f0d28ee 100644
--- a/src/mbgl/style/layers/line_layer.cpp
+++ b/src/mbgl/style/layers/line_layer.cpp
@@ -108,15 +108,15 @@ void LineLayer::setLineCap(PropertyValue<LineCapType> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() {
+DataDrivenPropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() {
return LineJoin::defaultValue();
}
-PropertyValue<LineJoinType> LineLayer::getLineJoin() const {
+DataDrivenPropertyValue<LineJoinType> LineLayer::getLineJoin() const {
return impl().layout.get<LineJoin>();
}
-void LineLayer::setLineJoin(PropertyValue<LineJoinType> value) {
+void LineLayer::setLineJoin(DataDrivenPropertyValue<LineJoinType> value) {
if (value == getLineJoin())
return;
auto impl_ = mutableImpl();
diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp
index b2c7f3199c..aeaf51698a 100644
--- a/src/mbgl/style/layers/line_layer_properties.hpp
+++ b/src/mbgl/style/layers/line_layer_properties.hpp
@@ -17,7 +17,7 @@ struct LineCap : LayoutProperty<LineCapType> {
static LineCapType defaultValue() { return LineCapType::Butt; }
};
-struct LineJoin : LayoutProperty<LineJoinType> {
+struct LineJoin : DataDrivenLayoutProperty<LineJoinType> {
static constexpr const char * key = "line-join";
static LineJoinType defaultValue() { return LineJoinType::Miter; }
};
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 182b4b0a48..9a944657ca 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -332,6 +332,38 @@ void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> va
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultIconAnchor() {
+ return IconAnchor::defaultValue();
+}
+
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getIconAnchor() const {
+ return impl().layout.get<IconAnchor>();
+}
+
+void SymbolLayer::setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) {
+ if (value == getIconAnchor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconAnchor>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+PropertyValue<AlignmentType> SymbolLayer::getDefaultIconPitchAlignment() {
+ return IconPitchAlignment::defaultValue();
+}
+
+PropertyValue<AlignmentType> SymbolLayer::getIconPitchAlignment() const {
+ return impl().layout.get<IconPitchAlignment>();
+}
+
+void SymbolLayer::setIconPitchAlignment(PropertyValue<AlignmentType> value) {
+ if (value == getIconPitchAlignment())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconPitchAlignment>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
PropertyValue<AlignmentType> SymbolLayer::getDefaultTextPitchAlignment() {
return TextPitchAlignment::defaultValue();
}
@@ -412,15 +444,15 @@ void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
return TextMaxWidth::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextMaxWidth() const {
+DataDrivenPropertyValue<float> SymbolLayer::getTextMaxWidth() const {
return impl().layout.get<TextMaxWidth>();
}
-void SymbolLayer::setTextMaxWidth(PropertyValue<float> value) {
+void SymbolLayer::setTextMaxWidth(DataDrivenPropertyValue<float> value) {
if (value == getTextMaxWidth())
return;
auto impl_ = mutableImpl();
@@ -444,15 +476,15 @@ void SymbolLayer::setTextLineHeight(PropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
return TextLetterSpacing::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
+DataDrivenPropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
return impl().layout.get<TextLetterSpacing>();
}
-void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) {
+void SymbolLayer::setTextLetterSpacing(DataDrivenPropertyValue<float> value) {
if (value == getTextLetterSpacing())
return;
auto impl_ = mutableImpl();
@@ -460,15 +492,15 @@ void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() {
+DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() {
return TextJustify::defaultValue();
}
-PropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const {
+DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const {
return impl().layout.get<TextJustify>();
}
-void SymbolLayer::setTextJustify(PropertyValue<TextJustifyType> value) {
+void SymbolLayer::setTextJustify(DataDrivenPropertyValue<TextJustifyType> value) {
if (value == getTextJustify())
return;
auto impl_ = mutableImpl();
@@ -476,15 +508,15 @@ void SymbolLayer::setTextJustify(PropertyValue<TextJustifyType> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<TextAnchorType> SymbolLayer::getDefaultTextAnchor() {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultTextAnchor() {
return TextAnchor::defaultValue();
}
-PropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getTextAnchor() const {
return impl().layout.get<TextAnchor>();
}
-void SymbolLayer::setTextAnchor(PropertyValue<TextAnchorType> value) {
+void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) {
if (value == getTextAnchor())
return;
auto impl_ = mutableImpl();
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index f7ceaecdaa..436b5cbd4f 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -87,6 +87,16 @@ struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> {
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
+struct IconAnchor : DataDrivenLayoutProperty<SymbolAnchorType> {
+ static constexpr const char * key = "icon-anchor";
+ static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; }
+};
+
+struct IconPitchAlignment : LayoutProperty<AlignmentType> {
+ static constexpr const char * key = "icon-pitch-alignment";
+ static AlignmentType defaultValue() { return AlignmentType::Auto; }
+};
+
struct TextPitchAlignment : LayoutProperty<AlignmentType> {
static constexpr const char * key = "text-pitch-alignment";
static AlignmentType defaultValue() { return AlignmentType::Auto; }
@@ -112,7 +122,7 @@ struct TextSize : DataDrivenLayoutProperty<float> {
static float defaultValue() { return 16; }
};
-struct TextMaxWidth : LayoutProperty<float> {
+struct TextMaxWidth : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-max-width";
static float defaultValue() { return 10; }
};
@@ -122,19 +132,19 @@ struct TextLineHeight : LayoutProperty<float> {
static float defaultValue() { return 1.2; }
};
-struct TextLetterSpacing : LayoutProperty<float> {
+struct TextLetterSpacing : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-letter-spacing";
static float defaultValue() { return 0; }
};
-struct TextJustify : LayoutProperty<TextJustifyType> {
+struct TextJustify : DataDrivenLayoutProperty<TextJustifyType> {
static constexpr const char * key = "text-justify";
static TextJustifyType defaultValue() { return TextJustifyType::Center; }
};
-struct TextAnchor : LayoutProperty<TextAnchorType> {
+struct TextAnchor : DataDrivenLayoutProperty<SymbolAnchorType> {
static constexpr const char * key = "text-anchor";
- static TextAnchorType defaultValue() { return TextAnchorType::Center; }
+ static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; }
};
struct TextMaxAngle : LayoutProperty<float> {
@@ -254,6 +264,8 @@ class SymbolLayoutProperties : public Properties<
IconPadding,
IconKeepUpright,
IconOffset,
+ IconAnchor,
+ IconPitchAlignment,
TextPitchAlignment,
TextRotationAlignment,
TextField,
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index ea19c599e9..cc6378b366 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,7 +1,6 @@
#pragma once
#include <mbgl/style/source_observer.hpp>
-#include <mbgl/map/update.hpp>
#include <exception>
@@ -12,7 +11,7 @@ class Observer : public SourceObserver {
public:
virtual void onStyleLoading() {}
virtual void onStyleLoaded() {}
- virtual void onUpdate(Update) {}
+ virtual void onUpdate() {}
virtual void onStyleError(std::exception_ptr) {}
virtual void onResourceError(std::exception_ptr) {}
};
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index 467b44632c..10fce33986 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -1,13 +1,16 @@
#include <mbgl/style/parser.hpp>
#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/coordinate.hpp>
#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/util/logging.hpp>
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
@@ -148,7 +151,7 @@ void Parser::parseSources(const JSValue& value) {
}
for (const auto& property : value.GetObject()) {
- std::string id = *conversion::toString(property.name);
+ std::string id { property.name.GetString(), property.name.GetStringLength() };
conversion::Error error;
optional<std::unique_ptr<Source>> source =
@@ -255,7 +258,7 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
}
layer = reference->cloneRef(id);
- conversion::setPaintProperties(*layer, value);
+ conversion::setPaintProperties(*layer, conversion::Convertible(&value));
} else {
conversion::Error error;
optional<std::unique_ptr<Layer>> converted = conversion::convert<std::unique_ptr<Layer>>(value, error);
diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp
index 48a764ccb4..79bd9c928b 100644
--- a/src/mbgl/style/rapidjson_conversion.hpp
+++ b/src/mbgl/style/rapidjson_conversion.hpp
@@ -1,103 +1,125 @@
#pragma once
#include <mbgl/util/rapidjson.hpp>
-#include <mbgl/util/feature.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mapbox/geojson.hpp>
+#include <mapbox/geojson/rapidjson.hpp>
+
namespace mbgl {
namespace style {
namespace conversion {
-inline bool isUndefined(const JSValue& value) {
- return value.IsNull();
-}
-
-inline bool isArray(const JSValue& value) {
- return value.IsArray();
-}
+template <>
+class ConversionTraits<const JSValue*> {
+public:
+ static bool isUndefined(const JSValue* value) {
+ return value->IsNull();
+ }
-inline std::size_t arrayLength(const JSValue& value) {
- return value.Size();
-}
+ static bool isArray(const JSValue* value) {
+ return value->IsArray();
+ }
-inline const JSValue& arrayMember(const JSValue& value, std::size_t i) {
- return value[rapidjson::SizeType(i)];
-}
+ static std::size_t arrayLength(const JSValue* value) {
+ return value->Size();
+ }
-inline bool isObject(const JSValue& value) {
- return value.IsObject();
-}
+ static const JSValue* arrayMember(const JSValue* value, std::size_t i) {
+ return &(*value)[rapidjson::SizeType(i)];
+ }
-inline const JSValue* objectMember(const JSValue& value, const char * name) {
- if (!value.HasMember(name)) {
- return nullptr;
+ static bool isObject(const JSValue* value) {
+ return value->IsObject();
}
- return &value[name];
-}
-template <class Fn>
-optional<Error> eachMember(const JSValue& value, Fn&& fn) {
- assert(value.IsObject());
- for (const auto& property : value.GetObject()) {
- optional<Error> result =
- fn({ property.name.GetString(), property.name.GetStringLength() }, property.value);
- if (result) {
- return result;
+ static optional<const JSValue*> objectMember(const JSValue* value, const char * name) {
+ if (!value->HasMember(name)) {
+ return optional<const JSValue*>();
}
+ const JSValue* const& member = &(*value)[name];
+ return {member};
}
- return {};
-}
-inline optional<bool> toBool(const JSValue& value) {
- if (!value.IsBool()) {
+ template <class Fn>
+ static optional<Error> eachMember(const JSValue* value, Fn&& fn) {
+ assert(value->IsObject());
+ for (const auto& property : value->GetObject()) {
+ optional<Error> result =
+ fn({ property.name.GetString(), property.name.GetStringLength() }, &property.value);
+ if (result) {
+ return result;
+ }
+ }
return {};
}
- return value.GetBool();
-}
-inline optional<float> toNumber(const JSValue& value) {
- if (!value.IsNumber()) {
- return {};
+ static optional<bool> toBool(const JSValue* value) {
+ if (!value->IsBool()) {
+ return {};
+ }
+ return value->GetBool();
}
- return value.GetDouble();
-}
-inline optional<double> toDouble(const JSValue& value) {
- if (!value.IsNumber()) {
- return {};
+ static optional<float> toNumber(const JSValue* value) {
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->GetDouble();
}
- return value.GetDouble();
-}
-inline optional<std::string> toString(const JSValue& value) {
- if (!value.IsString()) {
- return {};
+ static optional<double> toDouble(const JSValue* value) {
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->GetDouble();
+ }
+
+ static optional<std::string> toString(const JSValue* value) {
+ if (!value->IsString()) {
+ return {};
+ }
+ return {{ value->GetString(), value->GetStringLength() }};
}
- return {{ value.GetString(), value.GetStringLength() }};
-}
-inline optional<Value> toValue(const JSValue& value) {
- switch (value.GetType()) {
- case rapidjson::kNullType:
- case rapidjson::kFalseType:
- return { false };
+ static optional<Value> toValue(const JSValue* value) {
+ switch (value->GetType()) {
+ case rapidjson::kNullType:
+ case rapidjson::kFalseType:
+ return { false };
- case rapidjson::kTrueType:
- return { true };
+ case rapidjson::kTrueType:
+ return { true };
- case rapidjson::kStringType:
- return { std::string { value.GetString(), value.GetStringLength() } };
+ case rapidjson::kStringType:
+ return { std::string { value->GetString(), value->GetStringLength() } };
- case rapidjson::kNumberType:
- if (value.IsUint64()) return { value.GetUint64() };
- if (value.IsInt64()) return { value.GetInt64() };
- return { value.GetDouble() };
+ case rapidjson::kNumberType:
+ if (value->IsUint64()) return { value->GetUint64() };
+ if (value->IsInt64()) return { value->GetInt64() };
+ return { value->GetDouble() };
- default:
+ default:
+ return {};
+ }
+ }
+
+ static optional<GeoJSON> toGeoJSON(const JSValue* value, Error& error) {
+ try {
+ return mapbox::geojson::convert(*value);
+ } catch (const std::exception& ex) {
+ error = { ex.what() };
return {};
+ }
}
+};
+
+template <class T, class...Args>
+optional<T> convert(const JSValue& value, Error& error, Args&&...args) {
+ return convert<T>(Convertible(&value), error, std::forward<Args>(args)...);
}
} // namespace conversion
} // namespace style
} // namespace mbgl
+
diff --git a/src/mbgl/style/sources/custom_geometry_source.cpp b/src/mbgl/style/sources/custom_geometry_source.cpp
new file mode 100644
index 0000000000..b37490a5ce
--- /dev/null
+++ b/src/mbgl/style/sources/custom_geometry_source.cpp
@@ -0,0 +1,45 @@
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <tuple>
+#include <map>
+
+namespace mbgl {
+namespace style {
+
+CustomGeometrySource::CustomGeometrySource(std::string id,
+ const CustomGeometrySource::Options options)
+ : Source(makeMutable<CustomGeometrySource::Impl>(std::move(id), options)),
+ loader(std::make_unique<Actor<CustomTileLoader>>(*sharedThreadPool(), options.fetchTileFunction, options.cancelTileFunction)) {
+}
+
+CustomGeometrySource::~CustomGeometrySource() = default;
+
+const CustomGeometrySource::Impl& CustomGeometrySource::impl() const {
+ return static_cast<const CustomGeometrySource::Impl&>(*baseImpl);
+}
+
+void CustomGeometrySource::loadDescription(FileSource&) {
+ baseImpl = makeMutable<CustomGeometrySource::Impl>(impl(), loader->self());
+ loaded = true;
+}
+
+void CustomGeometrySource::setTileData(const CanonicalTileID& tileID,
+ const GeoJSON& data) {
+ loader->invoke(&CustomTileLoader::setTileData, tileID, data);
+}
+
+void CustomGeometrySource::invalidateTile(const CanonicalTileID& tileID) {
+ loader->invoke(&CustomTileLoader::invalidateTile, tileID);
+}
+
+void CustomGeometrySource::invalidateRegion(const LatLngBounds& bounds) {
+ loader->invoke(&CustomTileLoader::invalidateRegion, bounds, impl().getZoomRange());
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/custom_geometry_source_impl.cpp b/src/mbgl/style/sources/custom_geometry_source_impl.cpp
new file mode 100644
index 0000000000..67d52bdc24
--- /dev/null
+++ b/src/mbgl/style/sources/custom_geometry_source_impl.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+
+namespace mbgl {
+namespace style {
+
+CustomGeometrySource::Impl::Impl(std::string id_,
+ const CustomGeometrySource::Options options)
+ : Source::Impl(SourceType::CustomVector, std::move(id_)),
+ tileOptions(options.tileOptions),
+ zoomRange(options.zoomRange),
+ loaderRef({}) {
+}
+
+CustomGeometrySource::Impl::Impl(const Impl& impl, ActorRef<CustomTileLoader> loaderRef_)
+ : Source::Impl(impl),
+ tileOptions(impl.tileOptions),
+ zoomRange(impl.zoomRange),
+ loaderRef(loaderRef_){
+
+}
+
+optional<std::string> CustomGeometrySource::Impl::getAttribution() const {
+ return {};
+}
+
+CustomGeometrySource::TileOptions CustomGeometrySource::Impl::getTileOptions() const {
+ return tileOptions;
+}
+
+Range<uint8_t> CustomGeometrySource::Impl::getZoomRange() const {
+ return zoomRange;
+}
+
+optional<ActorRef<CustomTileLoader>> CustomGeometrySource::Impl::getTileLoader() const {
+ return loaderRef;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/custom_geometry_source_impl.hpp b/src/mbgl/style/sources/custom_geometry_source_impl.hpp
new file mode 100644
index 0000000000..ce7187202d
--- /dev/null
+++ b/src/mbgl/style/sources/custom_geometry_source_impl.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/actor/actor_ref.hpp>
+
+namespace mbgl {
+namespace style {
+
+class CustomGeometrySource::Impl : public Source::Impl {
+public:
+ Impl(std::string id, CustomGeometrySource::Options options);
+ Impl(const Impl&, ActorRef<CustomTileLoader>);
+
+ optional<std::string> getAttribution() const final;
+
+ CustomGeometrySource::TileOptions getTileOptions() const;
+ Range<uint8_t> getZoomRange() const;
+ optional<ActorRef<CustomTileLoader>> getTileLoader() const;
+
+private:
+ CustomGeometrySource::TileOptions tileOptions;
+ Range<uint8_t> zoomRange;
+ optional<ActorRef<CustomTileLoader>> loaderRef;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
index be347af2ab..fd6d7d3013 100644
--- a/src/mbgl/style/sources/geojson_source_impl.cpp
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -1,10 +1,13 @@
#include <mbgl/style/sources/geojson_source_impl.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <supercluster.hpp>
+#include <cmath>
+
namespace mbgl {
namespace style {
@@ -52,14 +55,14 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
mapbox::supercluster::Options clusterOptions;
clusterOptions.maxZoom = options.clusterMaxZoom;
clusterOptions.extent = util::EXTENT;
- clusterOptions.radius = std::round(scale * options.clusterRadius);
+ clusterOptions.radius = ::round(scale * options.clusterRadius);
data = std::make_unique<SuperclusterData>(
geoJSON.get<mapbox::geometry::feature_collection<double>>(), clusterOptions);
} else {
mapbox::geojsonvt::Options vtOptions;
vtOptions.maxZoom = options.maxzoom;
vtOptions.extent = util::EXTENT;
- vtOptions.buffer = std::round(scale * options.buffer);
+ vtOptions.buffer = ::round(scale * options.buffer);
vtOptions.tolerance = scale * options.tolerance;
data = std::make_unique<GeoJSONVTData>(geoJSON, vtOptions);
}
@@ -68,7 +71,7 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
GeoJSONSource::Impl::~Impl() = default;
Range<uint8_t> GeoJSONSource::Impl::getZoomRange() const {
- return { 0, options.maxzoom };
+ return { options.minzoom, options.maxzoom };
}
GeoJSONData* GeoJSONSource::Impl::getData() const {
diff --git a/src/mbgl/style/sources/image_source.cpp b/src/mbgl/style/sources/image_source.cpp
index 9313d8da4a..fa268da0ef 100644
--- a/src/mbgl/style/sources/image_source.cpp
+++ b/src/mbgl/style/sources/image_source.cpp
@@ -37,7 +37,7 @@ void ImageSource::setURL(const std::string& url_) {
}
}
-void ImageSource::setImage(UnassociatedImage&& image_) {
+void ImageSource::setImage(PremultipliedImage&& image_) {
url = {};
if (req) {
req.reset();
@@ -59,7 +59,7 @@ void ImageSource::loadDescription(FileSource& fileSource) {
if (req || loaded) {
return;
}
- const Resource imageResource { Resource::Image, *url, {}, Resource::Necessity::Required };
+ const Resource imageResource { Resource::Image, *url, {} };
req = fileSource.request(imageResource, [this](Response res) {
if (res.error) {
@@ -70,8 +70,7 @@ void ImageSource::loadDescription(FileSource& fileSource) {
observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty image url")));
} else {
try {
- UnassociatedImage image = util::unpremultiply(decodeImage(*res.data));
- baseImpl = makeMutable<Impl>(impl(), std::move(image));
+ baseImpl = makeMutable<Impl>(impl(), decodeImage(*res.data));
} catch (...) {
observer->onSourceError(*this, std::current_exception());
}
diff --git a/src/mbgl/style/sources/image_source_impl.cpp b/src/mbgl/style/sources/image_source_impl.cpp
index 98f3cc9db9..c1f31dbdc6 100644
--- a/src/mbgl/style/sources/image_source_impl.cpp
+++ b/src/mbgl/style/sources/image_source_impl.cpp
@@ -12,17 +12,17 @@ ImageSource::Impl::Impl(std::string id_, std::array<LatLng, 4> coords_)
ImageSource::Impl::Impl(const Impl& other, std::array<LatLng, 4> coords_)
: Source::Impl(other),
coords(std::move(coords_)),
- image(other.image.clone()) {
+ image(other.image) {
}
-ImageSource::Impl::Impl(const Impl& rhs, UnassociatedImage image_)
+ImageSource::Impl::Impl(const Impl& rhs, PremultipliedImage&& image_)
: Source::Impl(rhs),
coords(rhs.coords),
- image(std::move(image_)) {
+ image(std::make_shared<PremultipliedImage>(std::move(image_))) {
}
ImageSource::Impl::~Impl() = default;
-const UnassociatedImage& ImageSource::Impl::getImage() const {
+std::shared_ptr<PremultipliedImage> ImageSource::Impl::getImage() const {
return image;
}
diff --git a/src/mbgl/style/sources/image_source_impl.hpp b/src/mbgl/style/sources/image_source_impl.hpp
index 5fd41ac6e6..1e1b005a32 100644
--- a/src/mbgl/style/sources/image_source_impl.hpp
+++ b/src/mbgl/style/sources/image_source_impl.hpp
@@ -13,17 +13,17 @@ class ImageSource::Impl : public Source::Impl {
public:
Impl(std::string id, std::array<LatLng, 4> coords);
Impl(const Impl& rhs, std::array<LatLng, 4> coords);
- Impl(const Impl& rhs, UnassociatedImage image);
+ Impl(const Impl& rhs, PremultipliedImage&& image);
~Impl() final;
- const UnassociatedImage& getImage() const;
+ std::shared_ptr<PremultipliedImage> getImage() const;
std::array<LatLng, 4> getCoordinates() const;
optional<std::string> getAttribution() const final;
private:
std::array<LatLng, 4> coords;
- UnassociatedImage image;
+ std::shared_ptr<PremultipliedImage> image;
};
} // namespace style
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 5fe1ab4a06..bd8631fc52 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -34,20 +34,8 @@ std::string Style::getName() const {
return impl->getName();
}
-LatLng Style::getDefaultLatLng() const {
- return impl->getDefaultLatLng();
-}
-
-double Style::getDefaultZoom() const {
- return impl->getDefaultZoom();
-}
-
-double Style::getDefaultBearing() const {
- return impl->getDefaultBearing();
-}
-
-double Style::getDefaultPitch() const {
- return impl->getDefaultPitch();
+CameraOptions Style::getDefaultCamera() const {
+ return impl->getDefaultCamera();
}
TransitionOptions Style::getTransitionOptions() const {
diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp
index 2d42afd086..3214c6316e 100644
--- a/src/mbgl/style/style_impl.cpp
+++ b/src/mbgl/style/style_impl.cpp
@@ -38,6 +38,7 @@ Style::Impl::Impl(Scheduler& scheduler_, FileSource& fileSource_, float pixelRat
Style::Impl::~Impl() = default;
void Style::Impl::loadJSON(const std::string& json_) {
+ lastError = nullptr;
observer->onStyleLoading();
url.clear();
@@ -45,6 +46,7 @@ void Style::Impl::loadJSON(const std::string& json_) {
}
void Style::Impl::loadURL(const std::string& url_) {
+ lastError = nullptr;
observer->onStyleLoading();
loaded = false;
@@ -86,7 +88,7 @@ void Style::Impl::parse(const std::string& json_) {
}
mutated = false;
- loaded = true;
+ loaded = false;
json = json_;
sources.clear();
@@ -105,15 +107,18 @@ void Style::Impl::parse(const std::string& json_) {
}
name = parser.name;
- defaultLatLng = parser.latLng;
- defaultZoom = parser.zoom;
- defaultBearing = parser.bearing;
- defaultPitch = parser.pitch;
+ defaultCamera.center = parser.latLng;
+ defaultCamera.zoom = parser.zoom;
+ defaultCamera.angle = parser.bearing;
+ defaultCamera.pitch = parser.pitch;
+
setLight(std::make_unique<Light>(parser.light));
+ spriteLoaded = false;
spriteLoader->load(parser.spriteURL, scheduler, fileSource);
glyphURL = parser.glyphURL;
+ loaded = true;
observer->onStyleLoaded();
}
@@ -199,9 +204,10 @@ Layer* Style::Impl::addLayer(std::unique_ptr<Layer> layer, optional<std::string>
}
layer->setObserver(this);
- observer->onUpdate(Update::Repaint);
+ Layer* result = layers.add(std::move(layer), before);
+ observer->onUpdate();
- return layers.add(std::move(layer), before);
+ return result;
}
std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) {
@@ -209,7 +215,7 @@ std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) {
if (layer) {
layer->setObserver(nullptr);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
return layer;
@@ -229,20 +235,8 @@ std::string Style::Impl::getName() const {
return name;
}
-LatLng Style::Impl::getDefaultLatLng() const {
- return defaultLatLng;
-}
-
-double Style::Impl::getDefaultZoom() const {
- return defaultZoom;
-}
-
-double Style::Impl::getDefaultBearing() const {
- return defaultBearing;
-}
-
-double Style::Impl::getDefaultPitch() const {
- return defaultPitch;
+CameraOptions Style::Impl::getDefaultCamera() const {
+ return defaultCamera;
}
std::vector<Source*> Style::Impl::getSources() {
@@ -296,13 +290,13 @@ void Style::Impl::setObserver(style::Observer* observer_) {
void Style::Impl::onSourceLoaded(Source& source) {
sources.update(source);
observer->onSourceLoaded(source);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onSourceChanged(Source& source) {
sources.update(source);
observer->onSourceChanged(source);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onSourceError(Source& source, std::exception_ptr error) {
@@ -326,7 +320,7 @@ void Style::Impl::onSpriteLoaded(std::vector<std::unique_ptr<Image>>&& images_)
addImage(std::move(image));
}
spriteLoaded = true;
- observer->onUpdate(Update::Repaint); // For *-pattern properties.
+ observer->onUpdate(); // For *-pattern properties.
}
void Style::Impl::onSpriteError(std::exception_ptr error) {
@@ -337,11 +331,11 @@ void Style::Impl::onSpriteError(std::exception_ptr error) {
void Style::Impl::onLayerChanged(Layer& layer) {
layers.update(layer);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onLightChanged(const Light&) {
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::dumpDebugLogs() const {
diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp
index 76f244d5a4..3dc222bfad 100644
--- a/src/mbgl/style/style_impl.hpp
+++ b/src/mbgl/style/style_impl.hpp
@@ -12,6 +12,8 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/collection.hpp>
+#include <mbgl/map/camera.hpp>
+
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/geo.hpp>
@@ -69,10 +71,7 @@ public:
std::unique_ptr<Layer> removeLayer(const std::string& layerID);
std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
+ CameraOptions getDefaultCamera() const;
TransitionOptions getTransitionOptions() const;
void setTransitionOptions(const TransitionOptions&);
@@ -117,10 +116,7 @@ private:
// Defaults
std::string name;
- LatLng defaultLatLng;
- double defaultZoom = 0;
- double defaultBearing = 0;
- double defaultPitch = 0;
+ CameraOptions defaultCamera;
// SpriteLoaderObserver implementation.
void onSpriteLoaded(std::vector<std::unique_ptr<Image>>&&) override;
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index 4fbf767e11..cd5e597fc0 100644
--- a/src/mbgl/style/types.cpp
+++ b/src/mbgl/style/types.cpp
@@ -12,6 +12,7 @@ MBGL_DEFINE_ENUM(SourceType, {
{ SourceType::Video, "video" },
{ SourceType::Annotations, "annotations" },
{ SourceType::Image, "image" },
+ { SourceType::CustomVector, "customvector" }
});
MBGL_DEFINE_ENUM(VisibilityType, {
@@ -53,16 +54,16 @@ MBGL_DEFINE_ENUM(SymbolPlacementType, {
{ SymbolPlacementType::Line, "line" },
});
-MBGL_DEFINE_ENUM(TextAnchorType, {
- { TextAnchorType::Center, "center" },
- { TextAnchorType::Left, "left" },
- { TextAnchorType::Right, "right" },
- { TextAnchorType::Top, "top" },
- { TextAnchorType::Bottom, "bottom" },
- { TextAnchorType::TopLeft, "top-left" },
- { TextAnchorType::TopRight, "top-right" },
- { TextAnchorType::BottomLeft, "bottom-left" },
- { TextAnchorType::BottomRight, "bottom-right" }
+MBGL_DEFINE_ENUM(SymbolAnchorType, {
+ { SymbolAnchorType::Center, "center" },
+ { SymbolAnchorType::Left, "left" },
+ { SymbolAnchorType::Right, "right" },
+ { SymbolAnchorType::Top, "top" },
+ { SymbolAnchorType::Bottom, "bottom" },
+ { SymbolAnchorType::TopLeft, "top-left" },
+ { SymbolAnchorType::TopRight, "top-right" },
+ { SymbolAnchorType::BottomLeft, "bottom-left" },
+ { SymbolAnchorType::BottomRight, "bottom-right" }
});
MBGL_DEFINE_ENUM(TextJustifyType, {
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 71d7cc74e0..6d6f2aabc7 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -13,8 +13,9 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const float padding,
const style::SymbolPlacementType placement,
IndexedSubfeature indexedFeature_,
- const AlignmentType alignment)
- : indexedFeature(std::move(indexedFeature_)) {
+ const float overscaling)
+ : indexedFeature(std::move(indexedFeature_))
+ , alongLine(placement == style::SymbolPlacementType::Line) {
if (top == 0 && bottom == 0 && left == 0 && right == 0) return;
const float y1 = top * boxScale - padding;
@@ -22,7 +23,7 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const float x1 = left * boxScale - padding;
const float x2 = right * boxScale + padding;
- if (placement == style::SymbolPlacementType::Line) {
+ if (alongLine) {
float height = y2 - y1;
const double length = x2 - x1;
@@ -31,25 +32,26 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
height = std::max(10.0f * boxScale, height);
GeometryCoordinate anchorPoint = convertPoint<int16_t>(anchor.point);
-
- if (alignment == AlignmentType::Straight) {
- // used for icon labels that are aligned with the line, but don't curve along it
- const GeometryCoordinate vector = convertPoint<int16_t>(util::unit(convertPoint<double>(line[anchor.segment + 1] - line[anchor.segment])) * length);
- const GeometryCoordinates newLine({ anchorPoint - vector, anchorPoint + vector });
- bboxifyLabel(newLine, anchorPoint, 0, length, height);
- } else {
- // used for text labels that curve along a line
- bboxifyLabel(line, anchorPoint, anchor.segment, length, height);
- }
+ bboxifyLabel(line, anchorPoint, anchor.segment, length, height, overscaling);
} else {
- boxes.emplace_back(anchor.point, x1, y1, x2, y2, std::numeric_limits<float>::infinity());
+ boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2);
}
}
void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
- const int segment, const float labelLength, const float boxSize) {
+ const int segment, const float labelLength, const float boxSize, const float overscaling) {
const float step = boxSize / 2;
- const unsigned int nBoxes = std::floor(labelLength / step);
+ const int nBoxes = std::floor(labelLength / step);
+ // We calculate line collision circles out to 300% of what would normally be our
+ // max size, to allow collision detection to work on labels that expand as
+ // they move into the distance
+ // Vertically oriented labels in the distant field can extend past this padding
+ // This is a noticeable problem in overscaled tiles where the pitch 0-based
+ // symbol spacing will put labels very close together in a pitched map.
+ // To reduce the cost of adding extra collision circles, we slowly increase
+ // them for overscaled tiles.
+ const float overscalingPaddingFactor = 1 + .4 * std::log(overscaling) / std::log(2);
+ const int nPitchPaddingBoxes = std::floor(nBoxes * overscalingPaddingFactor / 2);
// offset the center of the first box by half a box so that the edge of the
// box is at the edge of the label.
@@ -58,24 +60,46 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
GeometryCoordinate &p = anchorPoint;
int index = segment + 1;
float anchorDistance = firstBoxOffset;
+ const float labelStartDistance = -labelLength / 2;
+ const float paddingStartDistance = labelStartDistance - labelLength / 8;
// move backwards along the line to the first segment the label appears on
do {
index--;
- // there isn't enough room for the label after the beginning of the line
- // checkMaxAngle should have already caught this
- if (index < 0) return;
+ if (index < 0) {
+ if (anchorDistance > labelStartDistance) {
+ // there isn't enough room for the label after the beginning of the line
+ // checkMaxAngle should have already caught this
+ return;
+ } else {
+ // The line doesn't extend far enough back for all of our padding,
+ // but we got far enough to show the label under most conditions.
+ index = 0;
+ break;
+ }
+ }
anchorDistance -= util::dist<float>(line[index], p);
p = line[index];
- } while (anchorDistance > -labelLength / 2);
+ } while (anchorDistance > paddingStartDistance);
auto segmentLength = util::dist<float>(line[index], line[index + 1]);
- for (unsigned int i = 0; i < nBoxes; i++) {
+ for (int i = -nPitchPaddingBoxes; i < nBoxes + nPitchPaddingBoxes; i++) {
// the distance the box will be from the anchor
- const float boxDistanceToAnchor = -labelLength / 2 + i * step;
+ const float boxOffset = i * step;
+ float boxDistanceToAnchor = labelStartDistance + boxOffset;
+
+ // make the distance between pitch padding boxes bigger
+ if (boxOffset < 0) boxDistanceToAnchor += boxOffset;
+ if (boxOffset > labelLength) boxDistanceToAnchor += boxOffset - labelLength;
+
+ if (boxDistanceToAnchor < anchorDistance) {
+ // The line doesn't extend far enough back for this box, skip it
+ // (This could allow for line collisions on distant tiles)
+ continue;
+ }
// the box is not on the current segment. Move to the next segment.
while (anchorDistance + segmentLength < boxDistanceToAnchor) {
@@ -98,12 +122,18 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
p0.x + segmentBoxDistance / segmentLength * (p1.x - p0.x),
p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y)
};
-
- const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f);
- const float maxScale = labelLength / 2 / distanceToInnerEdge;
-
- boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale);
+
+ // If the box is within boxSize of the anchor, force the box to be used
+ // (so even 0-width labels use at least one box)
+ // Otherwise, the .8 multiplication gives us a little bit of conservative
+ // padding in choosing which boxes to use (see CollisionIndex#placedCollisionCircles)
+ const float paddedAnchorDistance = std::abs(boxDistanceToAnchor - firstBoxOffset) < step ?
+ 0 :
+ (boxDistanceToAnchor - firstBoxOffset) * 0.8;
+
+ boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance, boxSize / 2);
}
}
+
} // namespace mbgl
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index c94ec23513..df1b12819c 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -11,11 +11,14 @@ namespace mbgl {
class CollisionBox {
public:
- CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _maxScale) :
- anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
+ CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0, float _radius = 0) :
+ anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), used(true), signedDistanceFromAnchor(_signedDistanceFromAnchor), radius(_radius) {}
// the box is centered around the anchor point
Point<float> anchor;
+
+ // the offset of the box from the label's anchor point
+ Point<float> offset;
// distances to the edges from the anchor
float x1;
@@ -23,20 +26,23 @@ public:
float x2;
float y2;
- // the box is only valid for scales < maxScale.
- // The box does not block other boxes at scales >= maxScale;
- float maxScale;
+ // Projected box geometry: generated/updated at placement time
+ float px1;
+ float py1;
+ float px2;
+ float py2;
+
+ // Projected circle geometry: generated/updated at placement time
+ float px;
+ float py;
+ bool used;
- // the scale at which the label can first be shown
- float placementScale = 0.0f;
+ float signedDistanceFromAnchor;
+ float radius;
};
class CollisionFeature {
public:
- enum class AlignmentType : bool {
- Straight = false,
- Curved
- };
// for text
CollisionFeature(const GeometryCoordinates& line,
@@ -45,23 +51,31 @@ public:
const float boxScale,
const float padding,
const style::SymbolPlacementType placement,
- const IndexedSubfeature& indexedFeature_)
- : CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, AlignmentType::Curved) {}
+ const IndexedSubfeature& indexedFeature_,
+ const float overscaling)
+ : CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, overscaling) {}
// for icons
+ // Icons collision features are always SymbolPlacementType::Point, which means the collision feature
+ // will be viewport-rotation-aligned even if the icon is map-rotation-aligned (e.g. `icon-rotation-alignment: map`
+ // _or_ `symbol-placement: line`). We're relying on most icons being "close enough" to square that having
+ // incorrect rotation alignment doesn't throw off collision detection too much.
+ // See: https://github.com/mapbox/mapbox-gl-js/issues/4861
CollisionFeature(const GeometryCoordinates& line,
const Anchor& anchor,
optional<PositionedIcon> shapedIcon,
const float boxScale,
const float padding,
- const style::SymbolPlacementType placement,
const IndexedSubfeature& indexedFeature_)
: CollisionFeature(line, anchor,
(shapedIcon ? shapedIcon->top() : 0),
(shapedIcon ? shapedIcon->bottom() : 0),
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
- boxScale, padding, placement, indexedFeature_, AlignmentType::Straight) {}
+ boxScale,
+ padding,
+ style::SymbolPlacementType::Point,
+ indexedFeature_, 1) {}
CollisionFeature(const GeometryCoordinates& line,
const Anchor&,
@@ -73,14 +87,15 @@ public:
const float padding,
const style::SymbolPlacementType,
IndexedSubfeature,
- const AlignmentType);
+ const float overscaling);
std::vector<CollisionBox> boxes;
IndexedSubfeature indexedFeature;
+ bool alongLine;
private:
void bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
- const int segment, const float length, const float height);
+ const int segment, const float length, const float height, const float overscaling);
};
} // namespace mbgl
diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp
new file mode 100644
index 0000000000..8014efe6c7
--- /dev/null
+++ b/src/mbgl/text/collision_index.cpp
@@ -0,0 +1,359 @@
+#include <mbgl/text/collision_index.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/math/log2.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/math/minmax.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+
+#include <mapbox/geometry/envelope.hpp>
+
+#include <mbgl/renderer/buckets/symbol_bucket.hpp> // For PlacedSymbol: pull out to another location
+
+#include <cmath>
+
+namespace mbgl {
+
+// When a symbol crosses the edge that causes it to be included in
+// collision detection, it will cause changes in the symbols around
+// it. This constant specifies how many pixels to pad the edge of
+// the viewport for collision detection so that the bulk of the changes
+// occur offscreen. Making this constant greater increases label
+// stability, but it's expensive.
+static const float viewportPadding = 100;
+
+CollisionIndex::CollisionIndex(const TransformState& transformState_)
+ : transformState(transformState_)
+ , collisionGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25)
+ , ignoredGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25)
+ , screenRightBoundary(transformState.getSize().width + viewportPadding)
+ , screenBottomBoundary(transformState.getSize().height + viewportPadding)
+ , gridRightBoundary(transformState.getSize().width + 2 * viewportPadding)
+ , gridBottomBoundary(transformState.getSize().height + 2 * viewportPadding)
+ , pitchFactor(std::cos(transformState.getPitch()) * transformState.getCameraToCenterDistance())
+{}
+
+float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap) {
+ // This is a quick and dirty solution for chosing which collision circles to use (since collision circles are
+ // laid out in tile units). Ideally, I think we should generate collision circles on the fly in viewport coordinates
+ // at the time we do collision detection.
+
+ // incidenceStretch is the ratio of how much y space a label takes up on a tile while drawn perpendicular to the viewport vs
+ // how much space it would take up if it were drawn flat on the tile
+ // Using law of sines, camera_to_anchor/sin(ground_angle) = camera_to_center/sin(incidence_angle)
+ // Incidence angle 90 -> head on, sin(incidence_angle) = 1, no stretch
+ // Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of stretch
+ // ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch)
+ // incidenceStretch = 1 / sin(incidenceAngle)
+
+ const float incidenceStretch = pitchWithMap ? 1 : cameraToAnchorDistance / pitchFactor;
+ const float lastSegmentTile = tileDistance.lastSegmentViewportDistance * pixelsToTileUnits;
+ return tileDistance.prevTileDistance +
+ lastSegmentTile +
+ (incidenceStretch - 1) * lastSegmentTile * std::abs(std::sin(lastSegmentAngle));
+}
+
+bool CollisionIndex::isOffscreen(const CollisionBox& box) const {
+ return box.px2 < viewportPadding || box.px1 >= screenRightBoundary || box.py2 < viewportPadding || box.py1 >= screenBottomBoundary;
+}
+
+bool CollisionIndex::isInsideGrid(const CollisionBox& box) const {
+ return box.px2 >= 0 && box.px1 < gridRightBoundary && box.py2 >= 0 && box.py1 < gridBottomBoundary;
+}
+
+
+std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug) {
+ if (!feature.alongLine) {
+ CollisionBox& box = feature.boxes.front();
+ const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
+ const float tileToViewport = textPixelRatio * projectedPoint.second;
+ box.px1 = box.x1 / tileToViewport + projectedPoint.first.x;
+ box.py1 = box.y1 / tileToViewport + projectedPoint.first.y;
+ box.px2 = box.x2 / tileToViewport + projectedPoint.first.x;
+ box.py2 = box.y2 / tileToViewport + projectedPoint.first.y;
+
+ if (!isInsideGrid(box) ||
+ (!allowOverlap && collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }}))) {
+ return { false, false };
+ }
+
+ return {true, isOffscreen(box)};
+ } else {
+ return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug);
+ }
+}
+
+std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug) {
+
+ const auto tileUnitAnchorPoint = symbol.anchorPoint;
+ const auto projectedAnchor = projectAnchor(posMatrix, tileUnitAnchorPoint);
+
+ const float fontScale = fontSize / 24;
+ const float lineOffsetX = symbol.lineOffset[0] * fontSize;
+ const float lineOffsetY = symbol.lineOffset[1] * fontSize;
+
+ const auto labelPlaneAnchorPoint = project(tileUnitAnchorPoint, labelPlaneMatrix).first;
+
+ const auto firstAndLastGlyph = placeFirstAndLastGlyph(
+ fontScale,
+ lineOffsetX,
+ lineOffsetY,
+ /*flip*/ false,
+ labelPlaneAnchorPoint,
+ tileUnitAnchorPoint,
+ symbol,
+ labelPlaneMatrix,
+ /*return tile distance*/ true);
+
+ bool collisionDetected = false;
+ bool inGrid = false;
+ bool entirelyOffscreen = true;
+
+ const auto tileToViewport = projectedAnchor.first * textPixelRatio;
+ // equivalent to pixel_to_tile_units
+ const auto pixelsToTileUnits = tileToViewport / scale;
+
+ float firstTileDistance = 0, lastTileDistance = 0;
+ if (firstAndLastGlyph) {
+ firstTileDistance = approximateTileDistance(*(firstAndLastGlyph->first.tileDistance), firstAndLastGlyph->first.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
+ lastTileDistance = approximateTileDistance(*(firstAndLastGlyph->second.tileDistance), firstAndLastGlyph->second.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
+ }
+
+ bool atLeastOneCirclePlaced = false;
+ for (size_t i = 0; i < feature.boxes.size(); i++) {
+ CollisionBox& circle = feature.boxes[i];
+ const float boxSignedDistanceFromAnchor = circle.signedDistanceFromAnchor;
+ if (!firstAndLastGlyph ||
+ (boxSignedDistanceFromAnchor < -firstTileDistance) ||
+ (boxSignedDistanceFromAnchor > lastTileDistance)) {
+ // The label either doesn't fit on its line or we
+ // don't need to use this circle because the label
+ // doesn't extend this far. Either way, mark the circle unused.
+ circle.used = false;
+ continue;
+ }
+
+ const auto projectedPoint = projectPoint(posMatrix, circle.anchor);
+ const float tileUnitRadius = (circle.x2 - circle.x1) / 2;
+ const float radius = tileUnitRadius / tileToViewport;
+
+ if (atLeastOneCirclePlaced) {
+ const CollisionBox& previousCircle = feature.boxes[i - 1];
+ const float dx = projectedPoint.x - previousCircle.px;
+ const float dy = projectedPoint.y - previousCircle.py;
+ // The circle edges touch when the distance between their centers is 2x the radius
+ // When the distance is 1x the radius, they're doubled up, and we could remove
+ // every other circle while keeping them all in touch.
+ // We actually start removing circles when the distance is √2x the radius:
+ // thinning the number of circles as much as possible is a major performance win,
+ // and the small gaps introduced don't make a very noticeable difference.
+ const bool placedTooDensely = radius * radius * 2 > dx * dx + dy * dy;
+ if (placedTooDensely) {
+ const bool atLeastOneMoreCircle = (i + 1) < feature.boxes.size();
+ if (atLeastOneMoreCircle) {
+ const CollisionBox& nextCircle = feature.boxes[i + 1];
+ const float nextBoxDistanceFromAnchor = nextCircle.signedDistanceFromAnchor;
+ if ((nextBoxDistanceFromAnchor > -firstTileDistance) &&
+ (nextBoxDistanceFromAnchor < lastTileDistance)) {
+ // Hide significantly overlapping circles, unless this is the last one we can
+ // use, in which case we want to keep it in place even if it's tightly packed
+ // with the one before it.
+ circle.used = false;
+ continue;
+ }
+ }
+ }
+ }
+
+ atLeastOneCirclePlaced = true;
+ circle.px1 = projectedPoint.x - radius;
+ circle.px2 = projectedPoint.x + radius;
+ circle.py1 = projectedPoint.y - radius;
+ circle.py2 = projectedPoint.y + radius;
+
+ circle.used = true;
+
+ circle.px = projectedPoint.x;
+ circle.py = projectedPoint.y;
+ circle.radius = radius;
+
+ entirelyOffscreen &= isOffscreen(circle);
+ inGrid |= isInsideGrid(circle);
+
+ if (!allowOverlap) {
+ if (collisionGrid.hitTest({{circle.px, circle.py}, circle.radius})) {
+ if (!collisionDebug) {
+ return {false, false};
+ } else {
+ // Don't early exit if we're showing the debug circles because we still want to calculate
+ // which circles are in use
+ collisionDetected = true;
+ }
+ }
+ }
+ }
+
+ return {!collisionDetected && firstAndLastGlyph && inGrid, entirelyOffscreen};
+}
+
+
+void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) {
+ if (feature.alongLine) {
+ for (auto& circle : feature.boxes) {
+ if (!circle.used) {
+ continue;
+ }
+
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ 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 }});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ }
+ }
+}
+
+bool polygonIntersectsBox(const LineString<float>& polygon, const GridIndex<IndexedSubfeature>::BBox& bbox) {
+ // This is just a wrapper that allows us to use the integer-based util::polygonIntersectsPolygon
+ // Conversion limits our query accuracy to single-pixel resolution
+ GeometryCoordinates integerPolygon;
+ for (const auto& point : polygon) {
+ integerPolygon.push_back(convertPoint<int16_t>(point));
+ }
+ int16_t minX1 = bbox.min.x;
+ int16_t maxY1 = bbox.max.y;
+ int16_t minY1 = bbox.min.y;
+ int16_t maxX1 = bbox.max.x;
+
+ auto bboxPoints = GeometryCoordinates {
+ { minX1, minY1 }, { maxX1, minY1 }, { maxX1, maxY1 }, { minX1, maxY1 }
+ };
+
+ 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;
+ 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;
+ for (const auto& point : queryGeometry) {
+ auto projected = projectPoint(posMatrix, convertPoint<float>(point));
+ projectedQuery.push_back(projected);
+ }
+
+ auto envelope = mapbox::geometry::envelope(projectedQuery);
+
+ 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);
+ }
+ }
+
+ std::unordered_map<std::string, std::unordered_map<std::string, std::unordered_set<std::size_t>>> sourceLayerFeatures;
+ for (auto& queryResult : thisTileFeatures) {
+ auto& feature = queryResult.first;
+ auto& bbox = queryResult.second;
+
+ // Skip already seen features.
+ auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName];
+ if (seenFeatures.find(feature.index) != seenFeatures.end())
+ continue;
+
+ seenFeatures.insert(feature.index);
+
+ if (!polygonIntersectsBox(projectedQuery, bbox)) {
+ continue;
+ }
+
+ result.push_back(feature);
+ }
+
+ return result;
+
+}
+
+std::pair<float,float> CollisionIndex::projectAnchor(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return std::make_pair(
+ 0.5 + 0.5 * (p[3] / transformState.getCameraToCenterDistance()),
+ p[3]
+ );
+}
+
+std::pair<Point<float>,float> CollisionIndex::projectAndGetPerspectiveRatio(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return std::make_pair(
+ Point<float>(
+ (((p[0] / p[3] + 1) / 2) * transformState.getSize().width) + viewportPadding,
+ (((-p[1] / p[3] + 1) / 2) * transformState.getSize().height) + viewportPadding
+ ),
+ 0.5 + 0.5 * (p[3] / transformState.getCameraToCenterDistance())
+ );
+}
+
+Point<float> CollisionIndex::projectPoint(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return Point<float>(
+ (((p[0] / p[3] + 1) / 2) * transformState.getSize().width) + viewportPadding,
+ (((-p[1] / p[3] + 1) / 2) * transformState.getSize().height) + viewportPadding
+ );
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp
new file mode 100644
index 0000000000..8653c1d76c
--- /dev/null
+++ b/src/mbgl/text/collision_index.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/text/collision_feature.hpp>
+#include <mbgl/util/grid_index.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+class PlacedSymbol;
+
+struct TileDistance;
+
+class CollisionIndex {
+public:
+ using CollisionGrid = GridIndex<IndexedSubfeature>;
+
+ explicit CollisionIndex(const TransformState&);
+
+ std::pair<bool,bool> placeFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug);
+
+ void insertFeature(CollisionFeature& feature, bool ignorePlacement);
+
+ std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const;
+
+
+private:
+ bool isOffscreen(const CollisionBox&) const;
+ bool isInsideGrid(const CollisionBox&) const;
+
+ std::pair<bool,bool> placeLineFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug);
+
+ float approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap);
+
+ std::pair<float,float> projectAnchor(const mat4& posMatrix, const Point<float>& point) const;
+ std::pair<Point<float>,float> projectAndGetPerspectiveRatio(const mat4& posMatrix, const Point<float>& point) const;
+ Point<float> projectPoint(const mat4& posMatrix, const Point<float>& point) const;
+
+ const TransformState transformState;
+
+ CollisionGrid collisionGrid;
+ CollisionGrid ignoredGrid;
+
+ const float screenRightBoundary;
+ const float screenBottomBoundary;
+ const float gridRightBoundary;
+ const float gridBottomBoundary;
+
+ const float pitchFactor;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp
deleted file mode 100644
index 368750c89f..0000000000
--- a/src/mbgl/text/collision_tile.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-#include <mbgl/text/collision_tile.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/math/log2.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/math/minmax.hpp>
-#include <mbgl/util/intersection_tests.hpp>
-
-#include <mapbox/geometry/envelope.hpp>
-#include <mapbox/geometry/multi_point.hpp>
-
-#include <cmath>
-
-namespace mbgl {
-
-CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)) {
- // Compute the transformation matrix.
- const float angle_sin = std::sin(config.angle);
- const float angle_cos = std::cos(config.angle);
- rotationMatrix = { { angle_cos, -angle_sin, angle_sin, angle_cos } };
- reverseRotationMatrix = { { angle_cos, angle_sin, -angle_sin, angle_cos } };
-
- // Stretch boxes in y direction to account for the map tilt.
- const float _yStretch = 1.0f / std::cos(config.pitch);
-
- // The amount the map is squished depends on the y position.
- // Sort of account for this by making all boxes a bit bigger.
- yStretch = std::pow(_yStretch, 1.3f);
-}
-
-
-float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) {
- float minPlacementScale = minScale;
-
- // Find the lowest scale at which the two boxes can fit side by side without overlapping.
- // Original algorithm:
- float s1 = (blocking.x1 - box.x2) / (anchor.x - blockingAnchor.x); // scale at which new box is to the left of old box
- float s2 = (blocking.x2 - box.x1) / (anchor.x - blockingAnchor.x); // scale at which new box is to the right of old box
- float s3 = (blocking.y1 - box.y2) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the top of old box
- float s4 = (blocking.y2 - box.y1) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the bottom of old box
-
- if (std::isnan(s1) || std::isnan(s2)) s1 = s2 = 1;
- if (std::isnan(s3) || std::isnan(s4)) s3 = s4 = 1;
-
- float collisionFreeScale = util::min(util::max(s1, s2), util::max(s3, s4));
-
- if (collisionFreeScale > blocking.maxScale) {
- // After a box's maxScale the label has shrunk enough that the box is no longer needed to cover it,
- // so unblock the new box at the scale that the old box disappears.
- collisionFreeScale = blocking.maxScale;
- }
-
- if (collisionFreeScale > box.maxScale) {
- // If the box can only be shown after it is visible, then the box can never be shown.
- // But the label can be shown after this box is not visible.
- collisionFreeScale = box.maxScale;
- }
-
- if (collisionFreeScale > minPlacementScale &&
- collisionFreeScale >= blocking.placementScale) {
- // If this collision occurs at a lower scale than previously found collisions
- // and the collision occurs while the other label is visible
-
- // this this is the lowest scale at which the label won't collide with anything
- minPlacementScale = collisionFreeScale;
- }
-
- return minPlacementScale;
-}
-
-float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOverlap, bool avoidEdges) {
- static const float infinity = std::numeric_limits<float>::infinity();
- static const std::array<CollisionBox, 4> edges {{
- // left
- CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity),
- // right
- CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity),
- // top
- CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity),
- // bottom
- CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity)
- }};
-
- float minPlacementScale = minScale;
-
- for (auto& box : feature.boxes) {
- const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor);
-
- if (!allowOverlap) {
- for (auto it = tree.qbegin(bgi::intersects(getTreeBox(anchor, box))); it != tree.qend(); ++it) {
- const CollisionBox& blocking = std::get<1>(*it);
- Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor);
-
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, blockingAnchor, blocking));
- if (minPlacementScale >= maxScale) return minPlacementScale;
- }
- }
-
- if (avoidEdges) {
- const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y1 });
- const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y1 });
- const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 });
- const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 });
- CollisionBox rotatedBox(box.anchor,
- util::min(rtl.x, rtr.x, rbl.x, rbr.x),
- util::min(rtl.y, rtr.y, rbl.y, rbr.y),
- util::max(rtl.x, rtr.x, rbl.x, rbr.x),
- util::max(rtl.y, rtr.y, rbl.y, rbr.y),
- box.maxScale);
-
- for (auto& blocking : edges) {
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, blocking.anchor, blocking));
- if (minPlacementScale >= maxScale) return minPlacementScale;
- }
- }
- }
-
- return minPlacementScale;
-}
-
-void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementScale, bool ignorePlacement) {
- for (auto& box : feature.boxes) {
- box.placementScale = minPlacementScale;
- }
-
- if (minPlacementScale < maxScale) {
- std::vector<CollisionTreeBox> treeBoxes;
- for (auto& box : feature.boxes) {
- treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), box, feature.indexedFeature);
- }
- if (ignorePlacement) {
- ignoredTree.insert(treeBoxes.begin(), treeBoxes.end());
- } else {
- tree.insert(treeBoxes.begin(), treeBoxes.end());
- }
- }
-
-}
-
-// +---------------------------+ As you zoom, the size of the symbol changes
-// |(x1,y1) | | relative to the tile e.g. when zooming in,
-// | | | the symbol gets smaller relative to the tile.
-// | (x1',y1') v |
-// | +-------+-------+ | The boxes inserted into the tree represents
-// | | | | | the bounds at the integer zoom level (where
-// | | | | | the symbol is biggest relative to the tile).
-// | | | | |
-// |---->+-------+-------+<----| This happens because placement is updated
-// | | |(xa,ya)| | once every new integer zoom level e.g.
-// | | | | | std::floor(oldZoom) != std::floor(newZoom).
-// | | | | |
-// | +-------+-------+ | Thus, they don't represent the exact bounds
-// | ^ (x2',y2') | of the symbol at the current zoom level. For
-// | | | calculating the bounds at current zoom level
-// | | (x2,y2)| we must unscale the box using its center as
-// +---------------------------+ transform origin.
-Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale) {
- assert(box.x1 <= box.x2 && box.y1 <= box.y2);
- return Box{
- CollisionPoint{
- anchor.x + box.x1 / scale,
- anchor.y + box.y1 / scale * yStretch
- },
- CollisionPoint{
- anchor.x + box.x2 / scale,
- anchor.y + box.y2 / scale * yStretch
- }
- };
-}
-
-std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, float scale) const {
- std::vector<IndexedSubfeature> result;
- if (queryGeometry.empty() || (tree.empty() && ignoredTree.empty())) {
- return result;
- }
-
- // Generate a rotated geometry out of the original query geometry.
- // Scale has already been handled by the prior conversions.
- GeometryCoordinates polygon;
- for (const auto& point : queryGeometry) {
- auto rotated = util::matrixMultiply(rotationMatrix, convertPoint<float>(point));
- polygon.push_back(convertPoint<int16_t>(rotated));
- }
-
- // Predicate for ruling out already seen features.
- std::unordered_map<std::string, std::unordered_set<std::size_t>> sourceLayerFeatures;
- auto seenFeature = [&] (const CollisionTreeBox& treeBox) -> bool {
- const IndexedSubfeature& feature = std::get<2>(treeBox);
- const auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName];
- return seenFeatures.find(feature.index) == seenFeatures.end();
- };
-
- // Account for the rounding done when updating symbol shader variables.
- const float roundedScale = std::pow(2.0f, std::ceil(util::log2(scale) * 10.0f) / 10.0f);
-
- // Check if feature is rendered (collision free) at current scale.
- auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
- const CollisionBox& box = std::get<1>(treeBox);
- return roundedScale >= box.placementScale && roundedScale <= box.maxScale;
- };
-
- // Check if query polygon intersects with the feature box at current scale.
- auto intersectsAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
- const CollisionBox& collisionBox = std::get<1>(treeBox);
- const auto anchor = util::matrixMultiply(rotationMatrix, collisionBox.anchor);
- const int16_t x1 = anchor.x + collisionBox.x1 / scale;
- const int16_t y1 = anchor.y + collisionBox.y1 / scale * yStretch;
- const int16_t x2 = anchor.x + collisionBox.x2 / scale;
- const int16_t y2 = anchor.y + collisionBox.y2 / scale * yStretch;
- auto bbox = GeometryCoordinates {
- { x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 }
- };
- return util::polygonIntersectsPolygon(polygon, bbox);
- };
-
- auto predicates = bgi::satisfies(seenFeature)
- && bgi::satisfies(visibleAtScale)
- && bgi::satisfies(intersectsAtScale);
-
- auto queryTree = [&](const auto& tree_) {
- for (auto it = tree_.qbegin(predicates); it != tree_.qend(); ++it) {
- const IndexedSubfeature& feature = std::get<2>(*it);
- auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName];
- seenFeatures.insert(feature.index);
- result.push_back(feature);
- }
- };
-
- queryTree(tree);
- queryTree(ignoredTree);
-
- return result;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp
deleted file mode 100644
index bdacbb7437..0000000000
--- a/src/mbgl/text/collision_tile.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-
-#include <mbgl/text/collision_feature.hpp>
-#include <mbgl/text/placement_config.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wshadow"
-#ifdef __clang__
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#endif
-#pragma GCC diagnostic ignored "-Wpragmas"
-#pragma GCC diagnostic ignored "-Wdeprecated-register"
-#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#pragma GCC diagnostic ignored "-Wmisleading-indentation"
-#include <boost/geometry.hpp>
-#include <boost/geometry/geometries/point.hpp>
-#include <boost/geometry/geometries/box.hpp>
-#include <boost/geometry/index/rtree.hpp>
-#pragma GCC diagnostic pop
-
-namespace mbgl {
-
-namespace bg = boost::geometry;
-namespace bgm = bg::model;
-namespace bgi = bg::index;
-using CollisionPoint = bgm::point<float, 2, bg::cs::cartesian>;
-using Box = bgm::box<CollisionPoint>;
-using CollisionTreeBox = std::tuple<Box, CollisionBox, IndexedSubfeature>;
-using Tree = bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>>;
-
-class IndexedSubfeature;
-
-class CollisionTile {
-public:
- explicit CollisionTile(PlacementConfig);
-
- float placeFeature(const CollisionFeature&, bool allowOverlap, bool avoidEdges);
- void insertFeature(CollisionFeature&, float minPlacementScale, bool ignorePlacement);
-
- std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, float scale) const;
-
- const PlacementConfig config;
-
- const float minScale = 0.5f;
- const float maxScale = 2.0f;
- float yStretch;
-
- std::array<float, 4> rotationMatrix;
- std::array<float, 4> reverseRotationMatrix;
-
-private:
- float findPlacementScale(
- const Point<float>& anchor, const CollisionBox& box,
- const Point<float>& blockingAnchor, const CollisionBox& blocking);
- Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0);
-
- Tree tree;
- Tree ignoredTree;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp
new file mode 100644
index 0000000000..177615857f
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -0,0 +1,165 @@
+#include <mbgl/text/cross_tile_symbol_index.hpp>
+#include <mbgl/layout/symbol_instance.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/tile/tile.hpp>
+
+namespace mbgl {
+
+
+TileLayerIndex::TileLayerIndex(OverscaledTileID coord_, std::vector<SymbolInstance>& symbolInstances, uint32_t bucketInstanceId_)
+ : coord(coord_), bucketInstanceId(bucketInstanceId_) {
+ for (SymbolInstance& symbolInstance : symbolInstances) {
+ indexedSymbolInstances[symbolInstance.key].emplace_back(symbolInstance.crossTileID, getScaledCoordinates(symbolInstance, coord));
+ }
+ }
+
+Point<int64_t> TileLayerIndex::getScaledCoordinates(SymbolInstance& symbolInstance, const OverscaledTileID& childTileCoord) {
+ // Round anchor positions to roughly 4 pixel grid
+ const double roundingFactor = 512.0 / util::EXTENT / 2.0;
+ const double scale = roundingFactor / std::pow(2, childTileCoord.canonical.z - coord.canonical.z);
+ return {
+ static_cast<int64_t>(std::floor((childTileCoord.canonical.x * util::EXTENT + symbolInstance.anchor.point.x) * scale)),
+ static_cast<int64_t>(std::floor((childTileCoord.canonical.y * util::EXTENT + symbolInstance.anchor.point.y) * scale))
+ };
+}
+
+void TileLayerIndex::findMatches(std::vector<SymbolInstance>& symbolInstances, const OverscaledTileID& newCoord) {
+ float tolerance = coord.canonical.z < newCoord.canonical.z ? 1 : std::pow(2, coord.canonical.z - newCoord.canonical.z);
+
+ for (auto& symbolInstance : symbolInstances) {
+ if (symbolInstance.crossTileID) {
+ // already has a match, skip
+ continue;
+ }
+
+ auto it = indexedSymbolInstances.find(symbolInstance.key);
+ if (it == indexedSymbolInstances.end()) {
+ // No symbol with this key in this bucket
+ continue;
+ }
+
+ auto scaledSymbolCoord = getScaledCoordinates(symbolInstance, newCoord);
+
+ for (IndexedSymbolInstance& thisTileSymbol: it->second) {
+ // Return any symbol with the same keys whose coordinates are within 1
+ // grid unit. (with a 4px grid, this covers a 12px by 12px area)
+ if (std::abs(thisTileSymbol.coord.x - scaledSymbolCoord.x) <= tolerance &&
+ std::abs(thisTileSymbol.coord.y - scaledSymbolCoord.y) <= tolerance) {
+
+ symbolInstance.crossTileID = thisTileSymbol.crossTileID;
+ break;
+ }
+ }
+ }
+}
+
+CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() {
+}
+
+void CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& coord, SymbolBucket& bucket, uint32_t& maxCrossTileID) {
+ if (bucket.bucketInstanceId) return;
+ bucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ uint8_t minZoom = 25;
+ uint8_t maxZoom = 0;
+ for (auto& it : indexes) {
+ auto z = it.first;
+ minZoom = std::min(minZoom, z);
+ maxZoom = std::max(maxZoom, z);
+ }
+
+
+ // make all higher-res child tiles block duplicate labels in this tile
+ for (auto z = maxZoom; z > coord.overscaledZ; z--) {
+ auto zoomIndexes = indexes.find(z);
+ if (zoomIndexes != indexes.end()) {
+ for (auto& childIndex : zoomIndexes->second) {
+ if (!childIndex.second.coord.isChildOf(coord)) {
+ continue;
+ }
+ childIndex.second.findMatches(bucket.symbolInstances, coord);
+ }
+ }
+ if (z == 0) {
+ break;
+ }
+ }
+
+ // make this tile block duplicate labels in lower-res parent tiles
+ for (auto z = coord.overscaledZ; z >= minZoom; z--) {
+ auto parentCoord = coord.scaledTo(z);
+ auto zoomIndexes = indexes.find(z);
+ if (zoomIndexes != indexes.end()) {
+ auto parentIndex = zoomIndexes->second.find(parentCoord);
+ if (parentIndex != zoomIndexes->second.end()) {
+ parentIndex->second.findMatches(bucket.symbolInstances, coord);
+ }
+ }
+ if (z == 0) {
+ break;
+ }
+ }
+
+ for (auto& symbolInstance : bucket.symbolInstances) {
+ if (!symbolInstance.crossTileID) {
+ // symbol did not match any known symbol, assign a new id
+ symbolInstance.crossTileID = ++maxCrossTileID;
+ }
+ }
+
+ indexes[coord.overscaledZ].emplace(coord, TileLayerIndex(coord, bucket.symbolInstances, bucket.bucketInstanceId));
+}
+
+bool CrossTileSymbolLayerIndex::removeStaleBuckets(const std::unordered_set<uint32_t>& currentIDs) {
+ bool tilesChanged = false;
+ for (auto& zoomIndexes : indexes) {
+ for (auto it = zoomIndexes.second.begin(); it != zoomIndexes.second.end();) {
+ if (!currentIDs.count(it->second.bucketInstanceId)) {
+ it = zoomIndexes.second.erase(it);
+ tilesChanged = true;
+ } else {
+ ++it;
+ }
+ }
+ }
+ return tilesChanged;
+}
+
+CrossTileSymbolIndex::CrossTileSymbolIndex() {}
+
+bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) {
+
+ auto& layerIndex = layerIndexes[symbolLayer.getID()];
+
+ bool symbolBucketsChanged = false;
+ std::unordered_set<uint32_t> currentBucketIDs;
+
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ if (!symbolBucket.bucketInstanceId) {
+ symbolBucketsChanged = true;
+ }
+ layerIndex.addBucket(renderTile.tile.id, symbolBucket, maxCrossTileID);
+ currentBucketIDs.insert(symbolBucket.bucketInstanceId);
+ }
+
+ if (layerIndex.removeStaleBuckets(currentBucketIDs)) {
+ symbolBucketsChanged = true;
+ }
+ return symbolBucketsChanged;
+}
+
+void CrossTileSymbolIndex::reset() {
+ layerIndexes.clear();
+}
+
+} // namespace mbgl
+
diff --git a/src/mbgl/text/cross_tile_symbol_index.hpp b/src/mbgl/text/cross_tile_symbol_index.hpp
new file mode 100644
index 0000000000..c67cd37e00
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <map>
+#include <vector>
+#include <string>
+#include <memory>
+#include <unordered_set>
+
+namespace mbgl {
+
+class SymbolInstance;
+class RenderSymbolLayer;
+class SymbolBucket;
+
+class IndexedSymbolInstance {
+public:
+ IndexedSymbolInstance(uint32_t crossTileID_, Point<int64_t> coord_)
+ : crossTileID(crossTileID_), coord(coord_)
+ {}
+
+ uint32_t crossTileID;
+ Point<int64_t> coord;
+};
+
+class TileLayerIndex {
+public:
+ TileLayerIndex(OverscaledTileID coord, std::vector<SymbolInstance>&, uint32_t bucketInstanceId);
+
+ Point<int64_t> getScaledCoordinates(SymbolInstance&, const OverscaledTileID&);
+ void findMatches(std::vector<SymbolInstance>&, const OverscaledTileID&);
+
+ OverscaledTileID coord;
+ uint32_t bucketInstanceId;
+ std::map<std::u16string,std::vector<IndexedSymbolInstance>> indexedSymbolInstances;
+};
+
+class CrossTileSymbolLayerIndex {
+public:
+ CrossTileSymbolLayerIndex();
+ void addBucket(const OverscaledTileID&, SymbolBucket&, uint32_t& maxCrossTileID);
+ bool removeStaleBuckets(const std::unordered_set<uint32_t>& currentIDs);
+private:
+ std::map<uint8_t,std::map<OverscaledTileID,TileLayerIndex>> indexes;
+ uint32_t maxBucketInstanceId = 0;
+};
+
+class CrossTileSymbolIndex {
+public:
+ CrossTileSymbolIndex();
+
+ bool addLayer(RenderSymbolLayer&);
+
+ void reset();
+private:
+ std::map<std::string, CrossTileSymbolLayerIndex> layerIndexes;
+ uint32_t maxCrossTileID = 0;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index b9eaedd302..08ff82a20a 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -7,6 +7,7 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/util/image.hpp>
+#include <mbgl/util/util.hpp>
#include <cstdint>
#include <vector>
@@ -57,13 +58,13 @@ using GlyphMap = std::map<FontStack, Glyphs>;
class PositionedGlyph {
public:
- explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, float angle_)
- : glyph(glyph_), x(x_), y(y_), angle(angle_) {}
+ explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_)
+ : glyph(glyph_), x(x_), y(y_), vertical(vertical_) {}
GlyphID glyph = 0;
float x = 0;
float y = 0;
- float angle = 0;
+ bool vertical = false;
};
enum class WritingModeType : uint8_t;
@@ -74,10 +75,10 @@ class Shaping {
explicit Shaping(float x, float y, WritingModeType writingMode_)
: top(y), bottom(y), left(x), right(x), writingMode(writingMode_) {}
std::vector<PositionedGlyph> positionedGlyphs;
- int32_t top = 0;
- int32_t bottom = 0;
- int32_t left = 0;
- int32_t right = 0;
+ float top = 0;
+ float bottom = 0;
+ float left = 0;
+ float right = 0;
WritingModeType writingMode;
explicit operator bool() const { return !positionedGlyphs.empty(); }
@@ -89,23 +90,23 @@ enum class WritingModeType : uint8_t {
Vertical = 1 << 1,
};
-constexpr WritingModeType operator|(WritingModeType a, WritingModeType b) {
+MBGL_CONSTEXPR WritingModeType operator|(WritingModeType a, WritingModeType b) {
return WritingModeType(mbgl::underlying_type(a) | mbgl::underlying_type(b));
}
-constexpr WritingModeType& operator|=(WritingModeType& a, WritingModeType b) {
+MBGL_CONSTEXPR WritingModeType& operator|=(WritingModeType& a, WritingModeType b) {
return (a = a | b);
}
-constexpr bool operator&(WritingModeType lhs, WritingModeType rhs) {
+MBGL_CONSTEXPR bool operator&(WritingModeType lhs, WritingModeType rhs) {
return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
-constexpr WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) {
+MBGL_CONSTEXPR WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) {
return (lhs = WritingModeType(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs)));
}
-constexpr WritingModeType operator~(WritingModeType value) {
+MBGL_CONSTEXPR WritingModeType operator~(WritingModeType value) {
return WritingModeType(~mbgl::underlying_type(value));
}
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
index 916d39ae62..3130418908 100644
--- a/src/mbgl/text/glyph_manager.cpp
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -4,14 +4,16 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
+#include <mbgl/util/tiny_sdf.hpp>
namespace mbgl {
static GlyphManagerObserver nullObserver;
-GlyphManager::GlyphManager(FileSource& fileSource_)
+GlyphManager::GlyphManager(FileSource& fileSource_, std::unique_ptr<LocalGlyphRasterizer> localGlyphRasterizer_)
: fileSource(fileSource_),
- observer(&nullObserver) {
+ observer(&nullObserver),
+ localGlyphRasterizer(std::move(localGlyphRasterizer_)) {
}
GlyphManager::~GlyphManager() = default;
@@ -30,14 +32,21 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
const GlyphIDs& glyphIDs = dependency.second;
GlyphRangeSet ranges;
for (const auto& glyphID : glyphIDs) {
- ranges.insert(getGlyphRange(glyphID));
+ if (localGlyphRasterizer->canRasterizeGlyph(fontStack, glyphID)) {
+ if (entry.glyphs.find(glyphID) == entry.glyphs.end()) {
+ entry.glyphs.emplace(glyphID, makeMutable<Glyph>(generateLocalSDF(fontStack, glyphID)));
+ }
+ } else {
+ ranges.insert(getGlyphRange(glyphID));
+ }
}
for (const auto& range : ranges) {
auto it = entry.ranges.find(range);
if (it == entry.ranges.end() || !it->second.parsed) {
- GlyphRequest& request = requestRange(entry, fontStack, range);
+ GlyphRequest& request = entry.ranges[range];
request.requestors[&requestor] = dependencies;
+ requestRange(request, fontStack, range);
}
}
}
@@ -49,18 +58,20 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
}
}
-GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
- GlyphRequest& request = entry.ranges[range];
+Glyph GlyphManager::generateLocalSDF(const FontStack& fontStack, GlyphID glyphID) {
+ Glyph local = localGlyphRasterizer->rasterizeGlyph(fontStack, glyphID);
+ local.bitmap = util::transformRasterToSDF(local.bitmap, 8, .25);
+ return local;
+}
+void GlyphManager::requestRange(GlyphRequest& request, const FontStack& fontStack, const GlyphRange& range) {
if (request.req) {
- return request;
+ return;
}
request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
processResponse(res, fontStack, range);
});
-
- return request;
}
void GlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp
index 00df079462..84db2c4be5 100644
--- a/src/mbgl/text/glyph_manager.hpp
+++ b/src/mbgl/text/glyph_manager.hpp
@@ -3,6 +3,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/text/glyph_manager_observer.hpp>
#include <mbgl/text/glyph_range.hpp>
+#include <mbgl/text/local_glyph_rasterizer.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/immutable.hpp>
@@ -24,7 +25,7 @@ public:
class GlyphManager : public util::noncopyable {
public:
- GlyphManager(FileSource&);
+ GlyphManager(FileSource&, std::unique_ptr<LocalGlyphRasterizer> = std::make_unique<LocalGlyphRasterizer>(optional<std::string>()));
~GlyphManager();
// Workers send a `getGlyphs` message to the main thread once they have determined
@@ -42,6 +43,8 @@ public:
void setObserver(GlyphManagerObserver*);
private:
+ Glyph generateLocalSDF(const FontStack& fontStack, GlyphID glyphID);
+
FileSource& fileSource;
std::string glyphURL;
@@ -58,11 +61,13 @@ private:
std::unordered_map<FontStack, Entry, FontStackHash> entries;
- GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
+ void requestRange(GlyphRequest&, const FontStack&, const GlyphRange&);
void processResponse(const Response&, const FontStack&, const GlyphRange&);
void notify(GlyphRequestor&, const GlyphDependencies&);
-
+
GlyphManagerObserver* observer = nullptr;
+
+ std::unique_ptr<LocalGlyphRasterizer> localGlyphRasterizer;
};
} // namespace mbgl
diff --git a/src/mbgl/text/local_glyph_rasterizer.hpp b/src/mbgl/text/local_glyph_rasterizer.hpp
new file mode 100644
index 0000000000..82b16b534d
--- /dev/null
+++ b/src/mbgl/text/local_glyph_rasterizer.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/text/glyph.hpp>
+
+namespace mbgl {
+
+/*
+ Given a font stack and a glyph ID, platform-specific implementations of
+ LocalGlyphRasterizer will decide which, if any, local fonts to use and
+ then generate a matching glyph object with a greyscale rasterization of
+ the glyph and appropriate metrics. GlyphManager will then use TinySDF to
+ transform the rasterized bitmap into an SDF.
+
+ The JS equivalent of this functionality will only generate glyphs in the
+ 'CJK Unified Ideographs' and 'Hangul Syllables' ranges, for which it can
+ get away with rendering a fixed 30px square image and GlyphMetrics of:
+
+ width: 24,
+ height: 24,
+ left: 0,
+ top: -8,
+ advance: 24
+
+ The JS equivalent also uses heuristic evaluation of the font stack name
+ to control the font-weight it uses during rasterization.
+
+ It is left to platform-specific implementation to decide how best to
+ map a FontStack to a particular rasterization.
+
+ The default implementation simply refuses to rasterize any glyphs.
+*/
+
+class LocalGlyphRasterizer {
+public:
+ virtual ~LocalGlyphRasterizer();
+ LocalGlyphRasterizer(const optional<std::string> fontFamily = optional<std::string>());
+
+ // virtual so that test harness can override platform-specific behavior
+ virtual bool canRasterizeGlyph(const FontStack&, GlyphID);
+ virtual Glyph rasterizeGlyph(const FontStack&, GlyphID);
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
new file mode 100644
index 0000000000..334748dda6
--- /dev/null
+++ b/src/mbgl/text/placement.cpp
@@ -0,0 +1,332 @@
+#include <mbgl/text/placement.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/bucket.hpp>
+
+namespace mbgl {
+
+OpacityState::OpacityState(bool placed_, bool offscreen)
+ : opacity((offscreen && placed_) ? 1 : 0)
+ , placed(placed_)
+{
+}
+
+OpacityState::OpacityState(const OpacityState& prevState, float increment, bool placed_) :
+ opacity(::fmax(0, ::fmin(1, prevState.opacity + (prevState.placed ? increment : -increment)))),
+ placed(placed_) {}
+
+bool OpacityState::isHidden() const {
+ return opacity == 0 && !placed;
+}
+
+JointOpacityState::JointOpacityState(bool placedIcon, bool placedText, bool offscreen) :
+ icon(OpacityState(placedIcon, offscreen)),
+ text(OpacityState(placedText, offscreen)) {}
+
+JointOpacityState::JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedIcon, bool placedText) :
+ icon(OpacityState(prevOpacityState.icon, increment, placedIcon)),
+ text(OpacityState(prevOpacityState.text, increment, placedText)) {}
+
+bool JointOpacityState::isHidden() const {
+ return icon.isHidden() && text.isHidden();
+}
+
+Placement::Placement(const TransformState& state_, MapMode mapMode_)
+ : collisionIndex(state_)
+ , state(state_)
+ , mapMode(mapMode_)
+ , recentUntil(TimePoint::min())
+{}
+
+void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatrix, bool showCollisionBoxes) {
+
+ std::unordered_set<uint32_t> seenCrossTileIDs;
+
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ 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::EXTENT / (util::tileSize * renderTile.tile.id.overscaleFactor());
+
+ mat4 posMatrix;
+ state.matrixFor(posMatrix, renderTile.id);
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
+ layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
+ layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::IconRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade());
+ }
+}
+
+void Placement::placeLayerBucket(
+ SymbolBucket& bucket,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const float textPixelRatio,
+ const bool showCollisionBoxes,
+ std::unordered_set<uint32_t>& seenCrossTileIDs,
+ const bool holdingForFade) {
+
+ auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
+ auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom());
+
+ const bool iconWithoutText = !bucket.hasTextData() || bucket.layout.get<style::TextOptional>();
+ const bool textWithoutIcon = !bucket.hasIconData() || bucket.layout.get<style::IconOptional>();
+
+ for (auto& symbolInstance : bucket.symbolInstances) {
+
+ if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
+ if (holdingForFade) {
+ // Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
+ // know yet if we have a duplicate in a parent tile that _should_ be placed.
+ placements.emplace(symbolInstance.crossTileID, JointPlacement(false, false, false));
+ continue;
+ }
+
+ bool placeText = false;
+ bool placeIcon = false;
+ bool offscreen = true;
+
+ if (symbolInstance.placedTextIndex) {
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedTextIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
+ posMatrix, textLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::TextAllowOverlap>(),
+ bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ placeText = placed.first;
+ offscreen &= placed.second;
+ }
+
+ if (symbolInstance.placedIconIndex) {
+ PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
+ posMatrix, iconLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::IconAllowOverlap>(),
+ bucket.layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ placeIcon = placed.first;
+ offscreen &= placed.second;
+ }
+
+ // combine placements for icon and text
+ if (!iconWithoutText && !textWithoutIcon) {
+ placeText = placeIcon = placeText && placeIcon;
+ } else if (!textWithoutIcon) {
+ placeText = placeText && placeIcon;
+ } else if (!iconWithoutText) {
+ placeIcon = placeText && placeIcon;
+ }
+
+ if (placeText) {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>());
+ }
+
+ if (placeIcon) {
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>());
+ }
+
+ assert(symbolInstance.crossTileID != 0);
+
+ if (placements.find(symbolInstance.crossTileID) != placements.end()) {
+ // If there's a previous placement with this ID, it comes from a tile that's fading out
+ // Erase it so that the placement result from the non-fading tile supersedes it
+ placements.erase(symbolInstance.crossTileID);
+ }
+
+ placements.emplace(symbolInstance.crossTileID, JointPlacement(placeText, placeIcon, offscreen));
+ seenCrossTileIDs.insert(symbolInstance.crossTileID);
+ }
+ }
+}
+
+bool Placement::commit(const Placement& prevPlacement, TimePoint now) {
+ commitTime = now;
+
+ bool placementChanged = false;
+
+ float increment = mapMode == MapMode::Continuous ?
+ std::chrono::duration<float>(commitTime - prevPlacement.commitTime) / Duration(std::chrono::milliseconds(300)) :
+ 1.0;
+
+ // add the opacities from the current placement, and copy their current values from the previous placement
+ for (auto& jointPlacement : placements) {
+ auto prevOpacity = prevPlacement.opacities.find(jointPlacement.first);
+ if (prevOpacity != prevPlacement.opacities.end()) {
+ opacities.emplace(jointPlacement.first, JointOpacityState(prevOpacity->second, increment, jointPlacement.second.icon, jointPlacement.second.text));
+ placementChanged = placementChanged ||
+ jointPlacement.second.icon != prevOpacity->second.icon.placed ||
+ jointPlacement.second.text != prevOpacity->second.text.placed;
+ } else {
+ opacities.emplace(jointPlacement.first, JointOpacityState(jointPlacement.second.icon, jointPlacement.second.text, jointPlacement.second.offscreen));
+ placementChanged = placementChanged || jointPlacement.second.icon || jointPlacement.second.text;
+ }
+ }
+
+ // copy and update values from the previous placement that aren't in the current placement but haven't finished fading
+ for (auto& prevOpacity : prevPlacement.opacities) {
+ if (opacities.find(prevOpacity.first) == opacities.end()) {
+ JointOpacityState jointOpacity(prevOpacity.second, increment, false, false);
+ if (!jointOpacity.isHidden()) {
+ opacities.emplace(prevOpacity.first, jointOpacity);
+ placementChanged = placementChanged || prevOpacity.second.icon.placed || prevOpacity.second.text.placed;
+ }
+ }
+ }
+
+ return placementChanged;
+}
+
+void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) {
+ std::set<uint32_t> seenCrossTileIDs;
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+ updateBucketOpacities(symbolBucket, seenCrossTileIDs);
+ }
+}
+
+void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>& seenCrossTileIDs) {
+ if (bucket.hasTextData()) bucket.text.opacityVertices.clear();
+ if (bucket.hasIconData()) bucket.icon.opacityVertices.clear();
+ if (bucket.hasCollisionBoxData()) bucket.collisionBox.dynamicVertices.clear();
+ if (bucket.hasCollisionCircleData()) bucket.collisionCircle.dynamicVertices.clear();
+
+ for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
+ auto opacityState = seenCrossTileIDs.count(symbolInstance.crossTileID) == 0 ?
+ getOpacity(symbolInstance.crossTileID) :
+ JointOpacityState(false, false, false);
+
+ seenCrossTileIDs.insert(symbolInstance.crossTileID);
+
+ if (symbolInstance.hasText) {
+ auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.text.placed, opacityState.text.opacity);
+ for (size_t i = 0; i < symbolInstance.horizontalGlyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ if (symbolInstance.placedTextIndex) {
+ bucket.text.placedSymbols[*symbolInstance.placedTextIndex].hidden = opacityState.isHidden();
+ }
+ if (symbolInstance.placedVerticalTextIndex) {
+ bucket.text.placedSymbols[*symbolInstance.placedVerticalTextIndex].hidden = opacityState.isHidden();
+ }
+ }
+ if (symbolInstance.hasIcon) {
+ auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.icon.placed, opacityState.icon.opacity);
+ if (symbolInstance.iconQuad) {
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ }
+ if (symbolInstance.placedIconIndex) {
+ bucket.icon.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden();
+ }
+ }
+
+ auto updateCollisionBox = [&](const auto& feature, const bool placed) {
+ for (const CollisionBox& box : feature.boxes) {
+ if (feature.alongLine) {
+ auto dynamicVertex = CollisionBoxDynamicAttributes::vertex(placed, !box.used);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
+ } else {
+ auto dynamicVertex = CollisionBoxDynamicAttributes::vertex(placed, false);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
+ }
+ }
+ };
+ updateCollisionBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
+ updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
+ }
+
+ bucket.updateOpacity();
+ bucket.sortFeatures(state.getAngle());
+}
+
+JointOpacityState Placement::getOpacity(uint32_t crossTileSymbolID) const {
+ auto it = opacities.find(crossTileSymbolID);
+ if (it != opacities.end()) {
+ return it->second;
+ } else {
+ return JointOpacityState(false, false, false);
+ }
+
+}
+
+float Placement::symbolFadeChange(TimePoint now) const {
+ if (mapMode == MapMode::Continuous) {
+ return std::chrono::duration<float>(now - commitTime) / Duration(std::chrono::milliseconds(300));
+ } else {
+ return 1.0;
+ }
+}
+
+bool Placement::hasTransitions(TimePoint now) const {
+ return symbolFadeChange(now) < 1.0 || stale;
+}
+
+bool Placement::stillRecent(TimePoint now) const {
+ return mapMode == MapMode::Continuous && recentUntil > now;
+}
+void Placement::setRecent(TimePoint now) {
+ stale = false;
+ if (mapMode == MapMode::Continuous) {
+ // Only set in continuous mode because "now" isn't defined in still mode
+ recentUntil = now + Duration(std::chrono::milliseconds(300));
+ }
+}
+
+void Placement::setStale() {
+ stale = true;
+}
+
+const CollisionIndex& Placement::getCollisionIndex() const {
+ return collisionIndex;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
new file mode 100644
index 0000000000..bcc20f15a4
--- /dev/null
+++ b/src/mbgl/text/placement.hpp
@@ -0,0 +1,91 @@
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/text/collision_index.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <unordered_set>
+
+namespace mbgl {
+
+class RenderSymbolLayer;
+class SymbolBucket;
+
+class OpacityState {
+public:
+ OpacityState(bool placed, bool offscreen);
+ OpacityState(const OpacityState& prevOpacityState, float increment, bool placed);
+ bool isHidden() const;
+ float opacity;
+ bool placed;
+};
+
+class JointOpacityState {
+public:
+ JointOpacityState(bool placedIcon, bool placedText, bool offscreen);
+ JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedIcon, bool placedText);
+ bool isHidden() const;
+ OpacityState icon;
+ OpacityState text;
+};
+
+class JointPlacement {
+public:
+ JointPlacement(bool text_, bool icon_, bool offscreen_)
+ : text(text_), icon(icon_), offscreen(offscreen_)
+ {}
+
+ const bool text;
+ const bool icon;
+ // offscreen = outside viewport, but within CollisionIndex::viewportPadding px of the edge
+ // Because these symbols aren't onscreen yet, we can skip the "fade in" animation,
+ // and if a subsequent viewport change brings them into view, they'll be fully
+ // visible right away.
+ const bool offscreen;
+};
+
+class Placement {
+public:
+ Placement(const TransformState&, MapMode mapMode);
+ void placeLayer(RenderSymbolLayer&, const mat4&, bool showCollisionBoxes);
+ bool commit(const Placement& prevPlacement, TimePoint);
+ void updateLayerOpacities(RenderSymbolLayer&);
+ JointOpacityState getOpacity(uint32_t crossTileSymbolID) const;
+ float symbolFadeChange(TimePoint now) const;
+ bool hasTransitions(TimePoint now) const;
+
+ const CollisionIndex& getCollisionIndex() const;
+
+ bool stillRecent(TimePoint now) const;
+ void setRecent(TimePoint now);
+ void setStale();
+private:
+
+ void placeLayerBucket(
+ SymbolBucket&,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const float pixelRatio,
+ const bool showCollisionBoxes,
+ std::unordered_set<uint32_t>& seenCrossTileIDs,
+ const bool holdingForFade);
+
+ void updateBucketOpacities(SymbolBucket&, std::set<uint32_t>&);
+
+ CollisionIndex collisionIndex;
+
+ TransformState state;
+ MapMode mapMode;
+ TimePoint commitTime;
+
+ std::unordered_map<uint32_t, JointPlacement> placements;
+ std::unordered_map<uint32_t, JointOpacityState> opacities;
+
+ TimePoint recentUntil;
+ bool stale = false;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp
deleted file mode 100644
index 7e61cabc24..0000000000
--- a/src/mbgl/text/placement_config.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-namespace mbgl {
-
-class PlacementConfig {
-public:
- PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false)
- : angle(angle_), pitch(pitch_), debug(debug_) {
- }
-
- bool operator==(const PlacementConfig& rhs) const {
- return angle == rhs.angle && pitch == rhs.pitch && debug == rhs.debug;
- }
-
- bool operator!=(const PlacementConfig& rhs) const {
- return !operator==(rhs);
- }
-
-public:
- float angle;
- float pitch;
- bool debug;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index ab10c5a6b7..0014ae8d01 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -13,14 +13,9 @@ namespace mbgl {
using namespace style;
-const float globalMinScale = 0.5f; // underscale by 1 zoom level
-
-SymbolQuad getIconQuad(const Anchor& anchor,
- const PositionedIcon& shapedIcon,
- const GeometryCoordinates& line,
+SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
- const style::SymbolPlacementType placement,
const Shaping& shapedText) {
const ImagePosition& image = shapedIcon.image();
@@ -71,18 +66,7 @@ SymbolQuad getIconQuad(const Anchor& anchor,
bl = {left, bottom};
}
- float angle = shapedIcon.angle();
- if (placement == style::SymbolPlacementType::Line) {
- assert(static_cast<unsigned int>(anchor.segment) < line.size());
- const GeometryCoordinate &prev= line[anchor.segment];
- if (anchor.point.y == prev.y && anchor.point.x == prev.x &&
- static_cast<unsigned int>(anchor.segment + 1) < line.size()) {
- const GeometryCoordinate &next= line[anchor.segment + 1];
- angle += std::atan2(anchor.point.y - next.y, anchor.point.x - next.x) + M_PI;
- } else {
- angle += std::atan2(anchor.point.y - prev.y, anchor.point.x - prev.x);
- }
- }
+ const float angle = shapedIcon.angle();
if (angle) {
// Compute the transformation matrix.
@@ -104,212 +88,19 @@ SymbolQuad getIconQuad(const Anchor& anchor,
static_cast<uint16_t>(image.textureRect.h + border * 2)
};
- return SymbolQuad { tl, tr, bl, br, textureRect, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity(), shapedText.writingMode };
-}
-
-struct GlyphInstance {
- explicit GlyphInstance(Point<float> anchorPoint_) : anchorPoint(std::move(anchorPoint_)) {}
- explicit GlyphInstance(Point<float> anchorPoint_, bool upsideDown_, float minScale_, float maxScale_,
- float angle_)
- : anchorPoint(std::move(anchorPoint_)), upsideDown(upsideDown_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
-
- const Point<float> anchorPoint;
- const bool upsideDown = false;
- const float minScale = globalMinScale;
- const float maxScale = std::numeric_limits<float>::infinity();
- const float angle = 0.0f;
-};
-
-using GlyphInstances = std::vector<GlyphInstance>;
-
-struct VirtualSegment {
- Point<float> anchor;
- Point<float> end;
- size_t index;
- float minScale;
- float maxScale;
-};
-
-inline void insertSegmentGlyph(std::back_insert_iterator<GlyphInstances> glyphs,
- const VirtualSegment& virtualSegment,
- const bool glyphIsLogicallyForward,
- const bool upsideDown) {
- float segmentAngle = std::atan2(virtualSegment.end.y - virtualSegment.anchor.y, virtualSegment.end.x - virtualSegment.anchor.x);
- // If !glyphIsLogicallyForward, we're iterating through the segments in reverse logical order as well, so we need to flip the segment angle
- float glyphAngle = glyphIsLogicallyForward ? segmentAngle : segmentAngle + M_PI;
-
- // Insert a glyph rotated at this angle for display in the range from [scale, previous(larger) scale].
- glyphs = GlyphInstance{
- /* anchor */ virtualSegment.anchor,
- /* upsideDown */ upsideDown,
- /* minScale */ virtualSegment.minScale,
- /* maxScale */ virtualSegment.maxScale,
- /* angle */ static_cast<float>(std::fmod((glyphAngle + 2.0 * M_PI), (2.0 * M_PI)))};
-}
-
-/**
- Given the distance along the line from the label anchor to the beginning of the current segment,
- project a "virtual anchor" point at the same distance along the line extending out from this segment.
-
- B <-- beginning of current segment
-* . . . . . . . *--------* E <-- end of current segment
-VA |
- / VA = "virtual segment anchor"
- /
- ---*-----`
- A = label anchor
-
- Distance _along line_ from A to B == straight-line distance from VA to B.
- */
-inline Point<float> getVirtualSegmentAnchor(const Point<float>& segmentBegin, const Point<float>& segmentEnd, float distanceFromAnchorToSegmentBegin) {
- Point<float> segmentDirectionUnitVector = util::normal<float>(segmentBegin, segmentEnd);
- return segmentBegin - (segmentDirectionUnitVector * distanceFromAnchorToSegmentBegin);
-}
-
-/*
- Given the segment joining `segmentAnchor` and `segmentEnd` and a desired offset
- `glyphDistanceFromAnchor` at which a glyph is to be placed, calculate the minimum
- "scale" at which the glyph will fall on the segment (i.e., not past the end)
-
- "Scale" here refers to the ratio between the *rendered* zoom level and the text-layout
- zoom level, which is 1 + (source tile's zoom level). `glyphDistanceFromAnchor`, although
- passed in units consistent with the text-layout zoom level, is based on text size. So
- when the tile is being rendered at z < text-layout zoom, the glyph's actual distance from
- the anchor is larger relative to the segment's length than at layout time:
-
-
- GLYPH
- z == layout-zoom, scale == 1: segmentAnchor *--------------^-------------* segmentEnd
- z == layout-zoom - 1, scale == 0.5: segmentAnchor *--------------^* segmentEnd
-
- <-------------->
- Anchor-to-glyph distance stays visually fixed,
- so it changes relative to the segment.
-*/
-inline float getMinScaleForSegment(const float glyphDistanceFromAnchor,
- const Point<float>& segmentAnchor,
- const Point<float>& segmentEnd) {
- const auto distanceFromAnchorToEnd = util::dist<float>(segmentAnchor, segmentEnd);
- return glyphDistanceFromAnchor / distanceFromAnchorToEnd;
-}
-
-inline Point<float> getSegmentEnd(const bool glyphIsLogicallyForward,
- const GeometryCoordinates& line,
- const size_t segmentIndex) {
- return convertPoint<float>(glyphIsLogicallyForward ? line[segmentIndex+1] : line[segmentIndex]);
-}
-
-optional<VirtualSegment> getNextVirtualSegment(const VirtualSegment& previousVirtualSegment,
- const GeometryCoordinates& line,
- const float glyphDistanceFromAnchor,
- const bool glyphIsLogicallyForward) {
- auto nextSegmentBegin = previousVirtualSegment.end;
-
- auto end = nextSegmentBegin;
- size_t index = previousVirtualSegment.index;
-
- // skip duplicate nodes
- while (end == nextSegmentBegin) {
- // look ahead by 2 points in the line because the segment index refers to the beginning
- // of the segment, and we need an endpoint too
- if (glyphIsLogicallyForward && (index + 2 < line.size())) {
- index += 1;
- } else if (!glyphIsLogicallyForward && index != 0) {
- index -= 1;
- } else {
- return {};
- }
-
- end = getSegmentEnd(glyphIsLogicallyForward, line, index);
- }
-
- const auto anchor = getVirtualSegmentAnchor(nextSegmentBegin, end,
- util::dist<float>(previousVirtualSegment.anchor,
- previousVirtualSegment.end));
- return VirtualSegment {
- anchor,
- end,
- index,
- getMinScaleForSegment(glyphDistanceFromAnchor, anchor, end),
- previousVirtualSegment.minScale
- };
-}
-
-/*
- Given (1) a glyph positioned relative to an anchor point and (2) a line to follow,
- calculates which segment of the line the glyph will fall on for each possible
- scale range, and for each range produces a "virtual" anchor point and an angle that will
- place the glyph on the right segment and rotated to the correct angle.
-
- Because one glyph quad is made ahead of time for each possible orientation, the
- symbol_sdf shader can quickly handle changing layout as we zoom in and out
-
- If the "keepUpright" property is set, we call getLineGlyphs twice (once upright and
- once "upside down"). This will generate two sets of glyphs following the line in opposite
- directions. Later, SymbolLayout::place will look at the glyphs and based on the placement
- angle determine if their original anchor was "upright" or not -- based on that, it throws
- away one set of glyphs or the other (this work has to be done in the CPU, but it's just a
- filter so it's fast)
- */
-void getLineGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
- Anchor& anchor,
- float glyphHorizontalOffsetFromAnchor,
- const GeometryCoordinates& line,
- size_t anchorSegment,
- bool upsideDown) {
- assert(line.size() > anchorSegment+1);
-
- // This is true if the glyph is "logically forward" of the anchor point, based on the ordering of line segments
- // The actual angle of the line is irrelevant
- // If "upsideDown" is set, everything is flipped
- const bool glyphIsLogicallyForward = (glyphHorizontalOffsetFromAnchor >= 0) ^ upsideDown;
- const float glyphDistanceFromAnchor = std::fabs(glyphHorizontalOffsetFromAnchor);
-
- const auto initialSegmentEnd = getSegmentEnd(glyphIsLogicallyForward, line, anchorSegment);
- VirtualSegment virtualSegment = {
- anchor.point,
- initialSegmentEnd,
- anchorSegment,
- getMinScaleForSegment(glyphDistanceFromAnchor, anchor.point, initialSegmentEnd),
- std::numeric_limits<float>::infinity()
- };
-
- while (true) {
- insertSegmentGlyph(glyphs,
- virtualSegment,
- glyphIsLogicallyForward,
- upsideDown);
-
- if (virtualSegment.minScale <= anchor.scale) {
- // No need to calculate below the scale where the label starts showing
- return;
- }
-
- optional<VirtualSegment> nextVirtualSegment = getNextVirtualSegment(virtualSegment,
- line,
- glyphDistanceFromAnchor,
- glyphIsLogicallyForward);
- if (!nextVirtualSegment) {
- // There are no more segments, so we can't fit this glyph on the line at a lower scale
- // This implies we can't show the label at all at lower scale, so we update the anchor's min scale
- anchor.scale = virtualSegment.minScale;
- return;
- } else {
- virtualSegment = *nextVirtualSegment;
- }
- }
-
+ return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } };
}
-SymbolQuads getGlyphQuads(Anchor& anchor,
- const Shaping& shapedText,
- const float boxScale,
- const GeometryCoordinates& line,
+SymbolQuads getGlyphQuads(const Shaping& shapedText,
const SymbolLayoutProperties::Evaluated& layout,
const style::SymbolPlacementType placement,
const GlyphPositionMap& positions) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
- const bool keepUpright = layout.get<TextKeepUpright>();
+
+ const float oneEm = 24.0;
+ std::array<float, 2> textOffset = layout.get<TextOffset>();
+ textOffset[0] *= oneEm;
+ textOffset[1] *= oneEm;
SymbolQuads quads;
@@ -320,67 +111,64 @@ SymbolQuads getGlyphQuads(Anchor& anchor,
const GlyphPosition& glyph = positionsIt->second;
const Rect<uint16_t>& rect = glyph.rect;
- const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale;
-
- GlyphInstances glyphInstances;
- if (placement == style::SymbolPlacementType::Line) {
- getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false);
- if (keepUpright)
- getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true);
- } else {
- glyphInstances.emplace_back(GlyphInstance{anchor.point});
- }
// The rects have an addditional buffer that is not included in their size;
const float glyphPadding = 1.0f;
const float rectBuffer = 3.0f + glyphPadding;
- const float x1 = positionedGlyph.x + glyph.metrics.left - rectBuffer;
- const float y1 = positionedGlyph.y - glyph.metrics.top - rectBuffer;
- const float x2 = x1 + rect.w;
- const float y2 = y1 + rect.h;
+ const float halfAdvance = glyph.metrics.advance / 2.0;
+ const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement == SymbolPlacementType::Line;
- const Point<float> center{positionedGlyph.x, static_cast<float>(static_cast<float>(glyph.metrics.advance) / 2.0)};
+ const Point<float> glyphOffset = alongLine ?
+ Point<float>{ positionedGlyph.x + halfAdvance, positionedGlyph.y } :
+ Point<float>{ 0.0f, 0.0f };
- Point<float> otl{x1, y1};
- Point<float> otr{x2, y1};
- Point<float> obl{x1, y2};
- Point<float> obr{x2, y2};
-
- if (positionedGlyph.angle != 0) {
- otl = util::rotate(otl - center, positionedGlyph.angle) + center;
- otr = util::rotate(otr - center, positionedGlyph.angle) + center;
- obl = util::rotate(obl - center, positionedGlyph.angle) + center;
- obr = util::rotate(obr - center, positionedGlyph.angle) + center;
- }
+ const Point<float> builtInOffset = alongLine ?
+ Point<float>{ 0.0f, 0.0f } :
+ Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] };
- for (const GlyphInstance &instance : glyphInstances) {
- Point<float> tl = otl;
- Point<float> tr = otr;
- Point<float> bl = obl;
- Point<float> br = obr;
- if (textRotate) {
- // Compute the transformation matrix.
- float angle_sin = std::sin(textRotate);
- float angle_cos = std::cos(textRotate);
- std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
+ const float x1 = glyph.metrics.left - rectBuffer - halfAdvance + builtInOffset.x;
+ const float y1 = -glyph.metrics.top - rectBuffer + builtInOffset.y;
+ const float x2 = x1 + rect.w;
+ const float y2 = y1 + rect.h;
- tl = util::matrixMultiply(matrix, tl);
- tr = util::matrixMultiply(matrix, tr);
- bl = util::matrixMultiply(matrix, bl);
- br = util::matrixMultiply(matrix, br);
- }
+ Point<float> tl{x1, y1};
+ Point<float> tr{x2, y1};
+ Point<float> bl{x1, y2};
+ Point<float> br{x2, y2};
+
+ if (alongLine && positionedGlyph.vertical) {
+ // Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em)
+ // In horizontal orientation, the y values for glyphs are below the midline
+ // and we use a "yOffset" of -17 to pull them up to the middle.
+ // By rotating counter-clockwise around the point at the center of the left
+ // edge of a 24x24 layout box centered below the midline, we align the center
+ // of the glyphs with the horizontal midline, so the yOffset is no longer
+ // necessary, but we also pull the glyph to the left along the x axis
+ const Point<float> center{-halfAdvance, halfAdvance};
+ const float verticalRotation = -M_PI_2;
+ const Point<float> xOffsetCorrection{5, 0};
+
+ tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection;
+ tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection;
+ bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection;
+ br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection;
+ }
- // Prevent label from extending past the end of the line
- const float glyphMinScale = std::max(instance.minScale, anchor.scale);
+ if (textRotate) {
+ // Compute the transformation matrix.
+ float angle_sin = std::sin(textRotate);
+ float angle_cos = std::cos(textRotate);
+ std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
- // All the glyphs for a label are tagged with either the "right side up" or "upside down" anchor angle,
- // which is used at placement time to determine which set to show
- const float anchorAngle = std::fmod((anchor.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI));
- const float glyphAngle = std::fmod((instance.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI));
- quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale, shapedText.writingMode);
+ tl = util::matrixMultiply(matrix, tl);
+ tr = util::matrixMultiply(matrix, tr);
+ bl = util::matrixMultiply(matrix, bl);
+ br = util::matrixMultiply(matrix, br);
}
+
+ quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset);
}
return quads;
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index b29f6b0ad3..33d003c935 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -19,50 +19,33 @@ public:
Point<float> bl_,
Point<float> br_,
Rect<uint16_t> tex_,
- float anchorAngle_,
- float glyphAngle_,
- Point<float> anchorPoint_,
- float minScale_,
- float maxScale_,
- WritingModeType writingMode_)
+ WritingModeType writingMode_,
+ Point<float> glyphOffset_)
: tl(std::move(tl_)),
tr(std::move(tr_)),
bl(std::move(bl_)),
br(std::move(br_)),
tex(std::move(tex_)),
- anchorAngle(anchorAngle_),
- glyphAngle(glyphAngle_),
- anchorPoint(std::move(anchorPoint_)),
- minScale(minScale_),
- maxScale(maxScale_),
- writingMode(writingMode_) {}
+ writingMode(writingMode_),
+ glyphOffset(glyphOffset_) {}
Point<float> tl;
Point<float> tr;
Point<float> bl;
Point<float> br;
Rect<uint16_t> tex;
- float anchorAngle, glyphAngle;
- Point<float> anchorPoint;
- float minScale;
- float maxScale;
WritingModeType writingMode;
+ Point<float> glyphOffset;
};
using SymbolQuads = std::vector<SymbolQuad>;
-SymbolQuad getIconQuad(const Anchor& anchor,
- const PositionedIcon& shapedIcon,
- const GeometryCoordinates& line,
+SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
- style::SymbolPlacementType placement,
const Shaping& shapedText);
-SymbolQuads getGlyphQuads(Anchor& anchor,
- const Shaping& shapedText,
- const float boxScale,
- const GeometryCoordinates& line,
+SymbolQuads getGlyphQuads(const Shaping& shapedText,
const style::SymbolLayoutProperties::Evaluated&,
style::SymbolPlacementType placement,
const GlyphPositionMap& positions);
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 338abe2e43..a8232836b6 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -7,15 +7,67 @@
#include <boost/algorithm/string.hpp>
#include <algorithm>
+#include <cmath>
namespace mbgl {
-PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
+struct AnchorAlignment {
+ AnchorAlignment(float horizontal_, float vertical_)
+ : horizontalAlign(horizontal_), verticalAlign(vertical_) {
+ }
+
+ float horizontalAlign;
+ float verticalAlign;
+};
+
+AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) {
+ float horizontalAlign = 0.5;
+ float verticalAlign = 0.5;
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::TopRight:
+ case style::SymbolAnchorType::BottomRight:
+ horizontalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::BottomLeft:
+ horizontalAlign = 0;
+ break;
+ }
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::BottomLeft:
+ case style::SymbolAnchorType::BottomRight:
+ verticalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::TopRight:
+ verticalAlign = 0;
+ break;
+ }
+
+ return AnchorAlignment(horizontalAlign, verticalAlign);
+}
+
+PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
+ AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
- float x1 = dx - image.displaySize()[0] / 2.0f;
+ float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
float x2 = x1 + image.displaySize()[0];
- float y1 = dy - image.displaySize()[1] / 2.0f;
+ float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
float y2 = y1 + image.displaySize()[1];
return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
@@ -27,12 +79,9 @@ void align(Shaping& shaping,
const float verticalAlign,
const float maxLineLength,
const float lineHeight,
- const std::size_t lineCount,
- const Point<float>& translate) {
- const float shiftX =
- (justify - horizontalAlign) * maxLineLength + ::round(translate.x);
- const float shiftY =
- (-verticalAlign * lineCount + 0.5) * lineHeight + ::round(translate.y);
+ const std::size_t lineCount) {
+ const float shiftX = (justify - horizontalAlign) * maxLineLength;
+ const float shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight;
for (auto& glyph : shaping.positionedGlyphs) {
glyph.x += shiftX;
@@ -75,7 +124,7 @@ float determineAverageLineWidth(const std::u16string& logicalInput,
}
}
- int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth));
+ int32_t targetLineCount = ::fmax(1, std::ceil(totalWidth / maxWidth));
return totalWidth / targetLineCount;
}
@@ -202,10 +251,8 @@ void shapeLines(Shaping& shaping,
const std::vector<std::u16string>& lines,
const float spacing,
const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
- const Point<float>& translate,
+ const style::SymbolAnchorType textAnchor,
+ const style::TextJustifyType textJustify,
const float verticalHeight,
const WritingModeType writingMode,
const Glyphs& glyphs) {
@@ -217,6 +264,10 @@ void shapeLines(Shaping& shaping,
float y = yOffset;
float maxLineLength = 0;
+
+ const float justify = textJustify == style::TextJustifyType::Right ? 1 :
+ textJustify == style::TextJustifyType::Left ? 0 :
+ 0.5;
for (std::u16string line : lines) {
// Collapse whitespace so it doesn't throw off justification
@@ -237,10 +288,10 @@ void shapeLines(Shaping& shaping,
const Glyph& glyph = **it->second;
if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) {
- shaping.positionedGlyphs.emplace_back(chr, x, y, 0);
+ shaping.positionedGlyphs.emplace_back(chr, x, y, false);
x += glyph.metrics.advance + spacing;
} else {
- shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2);
+ shaping.positionedGlyphs.emplace_back(chr, x, 0, true);
x += verticalHeight + spacing;
}
}
@@ -257,24 +308,25 @@ void shapeLines(Shaping& shaping,
x = 0;
y += lineHeight;
}
-
- align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight,
- lines.size(), translate);
- const uint32_t height = lines.size() * lineHeight;
-
+
+ auto anchorAlign = getAnchorAlignment(textAnchor);
+
+ align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
+ lineHeight, lines.size());
+ const float height = lines.size() * lineHeight;
+
// Calculate the bounding box
- shaping.top += -verticalAlign * height;
+ shaping.top += -anchorAlign.verticalAlign * height;
shaping.bottom = shaping.top + height;
- shaping.left += -horizontalAlign * maxLineLength;
+ shaping.left += -anchorAlign.horizontalAlign * maxLineLength;
shaping.right = shaping.left + maxLineLength;
}
const Shaping getShaping(const std::u16string& logicalInput,
const float maxWidth,
const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
+ const style::SymbolAnchorType textAnchor,
+ const style::TextJustifyType textJustify,
const float spacing,
const Point<float>& translate,
const float verticalHeight,
@@ -287,8 +339,8 @@ const Shaping getShaping(const std::u16string& logicalInput,
bidi.processText(logicalInput,
determineLineBreaks(logicalInput, spacing, maxWidth, writingMode, glyphs));
- shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign,
- justify, translate, verticalHeight, writingMode, glyphs);
+ shapeLines(shaping, reorderedLines, spacing, lineHeight, textAnchor,
+ textJustify, verticalHeight, writingMode, glyphs);
return shaping;
}
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index ca475e2a6c..0a961849e5 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -2,6 +2,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/renderer/image_atlas.hpp>
+#include <mbgl/style/types.hpp>
namespace mbgl {
@@ -31,7 +32,10 @@ private:
float _angle;
public:
- static PositionedIcon shapeIcon(const ImagePosition&, const std::array<float, 2>& iconOffset, const float iconRotation);
+ static PositionedIcon shapeIcon(const ImagePosition&,
+ const std::array<float, 2>& iconOffset,
+ style::SymbolAnchorType iconAnchor,
+ const float iconRotation);
const ImagePosition& image() const { return _image; }
float top() const { return _top; }
@@ -44,9 +48,8 @@ public:
const Shaping getShaping(const std::u16string& string,
float maxWidth,
float lineHeight,
- float horizontalAlign,
- float verticalAlign,
- float justify,
+ style::SymbolAnchorType textAnchor,
+ style::TextJustifyType textJustify,
float spacing,
const Point<float>& translate,
float verticalHeight,
diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp
new file mode 100644
index 0000000000..7c8a85c4a4
--- /dev/null
+++ b/src/mbgl/tile/custom_geometry_tile.cpp
@@ -0,0 +1,91 @@
+#include <mbgl/tile/custom_geometry_tile.hpp>
+#include <mbgl/tile/geojson_tile_data.hpp>
+#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>
+
+#include <mapbox/geojsonvt.hpp>
+
+namespace mbgl {
+
+CustomGeometryTile::CustomGeometryTile(const OverscaledTileID& overscaledTileID,
+ std::string sourceID_,
+ const TileParameters& parameters,
+ const style::CustomGeometrySource::TileOptions options_,
+ ActorRef<style::CustomTileLoader> loader_)
+ : GeometryTile(overscaledTileID, sourceID_, parameters),
+ necessity(TileNecessity::Optional),
+ options(options_),
+ loader(loader_),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
+ actorRef(*this, mailbox) {
+}
+
+CustomGeometryTile::~CustomGeometryTile() {
+ loader.invoke(&style::CustomTileLoader::removeTile, id);
+}
+
+void CustomGeometryTile::setTileData(const GeoJSON& geoJSON) {
+
+ auto featureData = mapbox::geometry::feature_collection<int16_t>();
+ if (geoJSON.is<FeatureCollection>() && !geoJSON.get<FeatureCollection>().empty()) {
+ const double scale = util::EXTENT / options.tileSize;
+
+ mapbox::geojsonvt::TileOptions vtOptions;
+ vtOptions.extent = util::EXTENT;
+ vtOptions.buffer = ::round(scale * options.buffer);
+ vtOptions.tolerance = scale * options.tolerance;
+ featureData = mapbox::geojsonvt::geoJSONToTile(geoJSON, id.canonical.z, id.canonical.x, id.canonical.y, vtOptions).features;
+ } else {
+ setNecessity(TileNecessity::Optional);
+ }
+ setData(std::make_unique<GeoJSONTileData>(std::move(featureData)));
+}
+
+void CustomGeometryTile::invalidateTileData() {
+ stale = true;
+ observer->onTileChanged(*this);
+}
+
+//Fetching tile data for custom sources is assumed to be an expensive operation.
+// Only required tiles make fetchTile requests. Attempt to cancel a tile
+// that is no longer required.
+void CustomGeometryTile::setNecessity(TileNecessity newNecessity) {
+ if (newNecessity != necessity || stale ) {
+ necessity = newNecessity;
+ if (necessity == TileNecessity::Required) {
+ loader.invoke(&style::CustomTileLoader::fetchTile, id, actorRef);
+ stale = false;
+ } else if (!isRenderable()) {
+ loader.invoke(&style::CustomTileLoader::cancelTile, id);
+ }
+ }
+}
+
+void CustomGeometryTile::querySourceFeatures(
+ std::vector<Feature>& result,
+ const SourceQueryOptions& queryOptions) {
+
+ // Ignore the sourceLayer, there is only one
+ auto layer = getData()->getLayer({});
+
+ if (layer) {
+ auto featureCount = layer->featureCount();
+ for (std::size_t i = 0; i < featureCount; i++) {
+ auto feature = layer->getFeature(i);
+
+ // Apply filter, if any
+ if (queryOptions.filter && !(*queryOptions.filter)(*feature)) {
+ continue;
+ }
+
+ result.push_back(convertFeature(*feature, id.canonical));
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/custom_geometry_tile.hpp b/src/mbgl/tile/custom_geometry_tile.hpp
new file mode 100644
index 0000000000..1df44e6b2a
--- /dev/null
+++ b/src/mbgl/tile/custom_geometry_tile.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/actor/mailbox.hpp>
+
+namespace mbgl {
+
+class TileParameters;
+
+namespace style {
+class CustomTileLoader;
+} // namespace style
+
+class CustomGeometryTile: public GeometryTile {
+public:
+ CustomGeometryTile(const OverscaledTileID&,
+ std::string sourceID,
+ const TileParameters&,
+ const style::CustomGeometrySource::TileOptions,
+ ActorRef<style::CustomTileLoader> loader);
+ ~CustomGeometryTile() override;
+
+ void setTileData(const GeoJSON& data);
+ void invalidateTileData();
+
+ void setNecessity(TileNecessity) final;
+
+ void querySourceFeatures(
+ std::vector<Feature>& result,
+ const SourceQueryOptions&) override;
+
+private:
+ bool stale = true;
+ TileNecessity necessity;
+ const style::CustomGeometrySource::TileOptions options;
+ ActorRef<style::CustomTileLoader> loader;
+ std::shared_ptr<Mailbox> mailbox;
+ ActorRef<CustomGeometryTile> actorRef;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index e9865e8272..bbec899950 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -1,102 +1,11 @@
#include <mbgl/tile/geojson_tile.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/tile/geojson_tile_data.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-
-#include <mapbox/geojsonvt.hpp>
-#include <supercluster.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
namespace mbgl {
-// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have
-// one layer, and it is always returned regardless of which layer is requested.
-
-class GeoJSONTileFeature : public GeometryTileFeature {
-public:
- const mapbox::geometry::feature<int16_t>& feature;
-
- GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& feature_)
- : feature(feature_) {
- }
-
- FeatureType getType() const override {
- return apply_visitor(ToFeatureType(), feature.geometry);
- }
-
- PropertyMap getProperties() const override {
- return feature.properties;
- }
-
- optional<FeatureIdentifier> getID() const override {
- return feature.id;
- }
-
- GeometryCollection getGeometries() const override {
- GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry);
-
- // https://github.com/mapbox/geojson-vt-cpp/issues/44
- if (getType() == FeatureType::Polygon) {
- geometry = fixupPolygons(geometry);
- }
-
- return geometry;
- }
-
- optional<Value> getValue(const std::string& key) const override {
- auto it = feature.properties.find(key);
- if (it != feature.properties.end()) {
- return optional<Value>(it->second);
- }
- return optional<Value>();
- }
-};
-
-class GeoJSONTileLayer : public GeometryTileLayer {
-public:
- GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
- : features(std::move(features_)) {
- }
-
- std::size_t featureCount() const override {
- return features->size();
- }
-
- std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
- return std::make_unique<GeoJSONTileFeature>((*features)[i]);
- }
-
- std::string getName() const override {
- return "";
- }
-
-private:
- std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
-};
-
-class GeoJSONTileData : public GeometryTileData {
-public:
- GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
- : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>(
- std::move(features_))) {
- }
-
- GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
- : features(std::move(features_)) {
- }
-
- std::unique_ptr<GeometryTileData> clone() const override {
- return std::make_unique<GeoJSONTileData>(features);
- }
-
- std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override {
- return std::make_unique<GeoJSONTileLayer>(features);
- }
-
-
-private:
- std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
-};
-
GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
std::string sourceID_,
const TileParameters& parameters,
@@ -108,27 +17,25 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
void GeoJSONTile::updateData(mapbox::geometry::feature_collection<int16_t> features) {
setData(std::make_unique<GeoJSONTileData>(std::move(features)));
}
-
-void GeoJSONTile::setNecessity(Necessity) {}
void GeoJSONTile::querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions& options) {
// Ignore the sourceLayer, there is only one
- auto layer = getData()->getLayer({});
-
- if (layer) {
- auto featureCount = layer->featureCount();
- for (std::size_t i = 0; i < featureCount; i++) {
- auto feature = layer->getFeature(i);
-
- // Apply filter, if any
- if (options.filter && !(*options.filter)(*feature)) {
- continue;
+ if (auto tileData = getData()) {
+ if (auto layer = tileData->getLayer({})) {
+ auto featureCount = layer->featureCount();
+ for (std::size_t i = 0; i < featureCount; i++) {
+ auto feature = layer->getFeature(i);
+
+ // Apply filter, if any
+ if (options.filter && !(*options.filter)(*feature)) {
+ continue;
+ }
+
+ result.push_back(convertFeature(*feature, id.canonical));
}
-
- result.push_back(convertFeature(*feature, id.canonical));
}
}
}
diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp
index d8a0a379d7..270406267c 100644
--- a/src/mbgl/tile/geojson_tile.hpp
+++ b/src/mbgl/tile/geojson_tile.hpp
@@ -15,8 +15,6 @@ public:
mapbox::geometry::feature_collection<int16_t>);
void updateData(mapbox::geometry::feature_collection<int16_t>);
-
- void setNecessity(Necessity) final;
void querySourceFeatures(
std::vector<Feature>& result,
diff --git a/src/mbgl/tile/geojson_tile_data.hpp b/src/mbgl/tile/geojson_tile_data.hpp
new file mode 100644
index 0000000000..3402c2a009
--- /dev/null
+++ b/src/mbgl/tile/geojson_tile_data.hpp
@@ -0,0 +1,94 @@
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+
+// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have
+// one layer, and it is always returned regardless of which layer is requested.
+
+class GeoJSONTileFeature : public GeometryTileFeature {
+public:
+ const mapbox::geometry::feature<int16_t>& feature;
+
+ GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& feature_)
+ : feature(feature_) {
+ }
+
+ FeatureType getType() const override {
+ return apply_visitor(ToFeatureType(), feature.geometry);
+ }
+
+ PropertyMap getProperties() const override {
+ return feature.properties;
+ }
+
+ optional<FeatureIdentifier> getID() const override {
+ return feature.id;
+ }
+
+ GeometryCollection getGeometries() const override {
+ GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry);
+
+ // https://github.com/mapbox/geojson-vt-cpp/issues/44
+ if (getType() == FeatureType::Polygon) {
+ geometry = fixupPolygons(geometry);
+ }
+
+ return geometry;
+ }
+
+ optional<Value> getValue(const std::string& key) const override {
+ auto it = feature.properties.find(key);
+ if (it != feature.properties.end()) {
+ return optional<Value>(it->second);
+ }
+ return optional<Value>();
+ }
+};
+
+class GeoJSONTileLayer : public GeometryTileLayer {
+public:
+ GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
+ : features(std::move(features_)) {
+ }
+
+ std::size_t featureCount() const override {
+ return features->size();
+ }
+
+ std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
+ return std::make_unique<GeoJSONTileFeature>((*features)[i]);
+ }
+
+ std::string getName() const override {
+ return "";
+ }
+
+private:
+ std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
+};
+
+class GeoJSONTileData : public GeometryTileData {
+public:
+ GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
+ : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>(
+ std::move(features_))) {
+ }
+
+ GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
+ : features(std::move(features_)) {
+ }
+
+ std::unique_ptr<GeometryTileData> clone() const override {
+ return std::make_unique<GeoJSONTileData>(features);
+ }
+
+ std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override {
+ return std::make_unique<GeoJSONTileLayer>(features);
+ }
+
+
+private:
+ std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 4ab11d79fe..701e7cf2d6 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -10,17 +10,15 @@
#include <mbgl/renderer/layers/render_custom_layer.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/map/query.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/util/logging.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <iostream>
@@ -28,21 +26,40 @@ namespace mbgl {
using namespace style;
+/*
+ Correlation between GeometryTile and GeometryTileWorker is safeguarded by two
+ correlation schemes:
+
+ GeometryTile's 'correlationID' is used for ensuring the tile will be flagged
+ as non-pending only when the placement coming from the last operation (as in
+ 'setData', 'setLayers', 'setShowCollisionBoxes') occurs. This is important for
+ still mode rendering as we want to render only when all layout and placement
+ operations are completed.
+
+ GeometryTileWorker's 'imageCorrelationID' is used for checking whether an
+ image request reply coming from `GeometryTile` is valid. Previous image
+ request replies are ignored as they result in incomplete placement attempts
+ that could flag the tile as non-pending too early.
+ */
+
GeometryTile::GeometryTile(const OverscaledTileID& id_,
std::string sourceID_,
const TileParameters& parameters)
: Tile(id_),
sourceID(std::move(sourceID_)),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(parameters.workerScheduler,
ActorRef<GeometryTile>(*this, mailbox),
id_,
+ sourceID,
obsolete,
parameters.mode,
- parameters.pixelRatio),
+ parameters.pixelRatio,
+ parameters.debugOptions & MapDebugOptions::Collision),
glyphManager(parameters.glyphManager),
imageManager(parameters.imageManager),
- placementThrottler(Milliseconds(300), [this] { invokePlacement(); }) {
+ mode(parameters.mode),
+ showCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision) {
}
GeometryTile::~GeometryTile() {
@@ -61,7 +78,6 @@ void GeometryTile::markObsolete() {
void GeometryTile::setError(std::exception_ptr err) {
loaded = true;
- renderable = false;
observer->onTileError(*this, err);
}
@@ -74,25 +90,6 @@ void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
}
-void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
- if (requestedConfig == desiredConfig) {
- return;
- }
-
- // Mark the tile as pending again if it was complete before to prevent signaling a complete
- // state despite pending parse operations.
- pending = true;
-
- ++correlationID;
- requestedConfig = desiredConfig;
- placementThrottler.invoke();
-}
-
-void GeometryTile::invokePlacement() {
- if (requestedConfig) {
- worker.invoke(&GeometryTileWorker::setPlacementConfig, *requestedConfig, correlationID);
- }
-}
void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) {
// Mark the tile as pending again if it was complete before to prevent signaling a complete
@@ -119,37 +116,47 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers)
worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
}
-void GeometryTile::onLayout(LayoutResult result) {
- loaded = true;
- renderable = true;
+void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) {
+ if (showCollisionBoxes != showCollisionBoxes_) {
+ showCollisionBoxes = showCollisionBoxes_;
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setShowCollisionBoxes, showCollisionBoxes, correlationID);
+ }
+}
+
+void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
+ // Don't mark ourselves loaded or renderable until the first successful placement
+ // TODO: Ideally we'd render this tile without symbols as long as this tile wasn't
+ // replacing a tile at a different zoom that _did_ have symbols.
+ (void)resultCorrelationID;
nonSymbolBuckets = std::move(result.nonSymbolBuckets);
featureIndex = std::move(result.featureIndex);
data = std::move(result.tileData);
- collisionTile.reset();
observer->onTileChanged(*this);
}
-void GeometryTile::onPlacement(PlacementResult result) {
+void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) {
loaded = true;
renderable = true;
- if (result.correlationID == correlationID) {
+ if (resultCorrelationID == correlationID) {
pending = false;
}
symbolBuckets = std::move(result.symbolBuckets);
- collisionTile = std::move(result.collisionTile);
if (result.glyphAtlasImage) {
glyphAtlasImage = std::move(*result.glyphAtlasImage);
}
if (result.iconAtlasImage) {
iconAtlasImage = std::move(*result.iconAtlasImage);
}
+
observer->onTileChanged(*this);
}
-void GeometryTile::onError(std::exception_ptr err) {
+void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
loaded = true;
- pending = false;
- renderable = false;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
observer->onTileError(*this, err);
}
@@ -161,27 +168,27 @@ void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) {
glyphManager.getGlyphs(*this, std::move(glyphDependencies));
}
-void GeometryTile::onImagesAvailable(ImageMap images) {
- worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images));
+void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) {
+ worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID);
}
-void GeometryTile::getImages(ImageDependencies imageDependencies) {
- imageManager.getImages(*this, std::move(imageDependencies));
+void GeometryTile::getImages(ImageRequestPair pair) {
+ imageManager.getImages(*this, std::move(pair));
}
void GeometryTile::upload(gl::Context& context) {
- auto upload = [&] (Bucket& bucket) {
+ auto uploadFn = [&] (Bucket& bucket) {
if (bucket.needsUpload()) {
bucket.upload(context);
}
};
for (auto& entry : nonSymbolBuckets) {
- upload(*entry.second);
+ uploadFn(*entry.second);
}
for (auto& entry : symbolBuckets) {
- upload(*entry.second);
+ uploadFn(*entry.second);
}
if (glyphAtlasImage) {
@@ -210,11 +217,21 @@ void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) {
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) {
if (!featureIndex || !data) return;
+ // Determine the additional radius needed factoring in property functions
+ float additionalRadius = 0;
+ for (const RenderLayer* layer : layers) {
+ auto bucket = getBucket(*layer->baseImpl);
+ if (bucket) {
+ additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer));
+ }
+ }
+
featureIndex->query(result,
queryGeometry,
transformState.getAngle(),
@@ -222,10 +239,11 @@ void GeometryTile::queryRenderedFeatures(
std::pow(2, transformState.getZoom() - id.overscaledZ),
options,
*data,
- id.canonical,
- style,
- collisionTile.get(),
- *this);
+ id.toUnwrapped(),
+ sourceID,
+ layers,
+ collisionIndex,
+ additionalRadius);
}
void GeometryTile::querySourceFeatures(
@@ -264,4 +282,37 @@ void GeometryTile::querySourceFeatures(
}
}
+void GeometryTile::resetCrossTileIDs() {
+ for (auto& bucket : symbolBuckets) {
+ auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.second.get());
+ if (symbolBucket && symbolBucket->bucketInstanceId) {
+ symbolBucket->bucketInstanceId = 0;
+ for (auto& symbolInstance : symbolBucket->symbolInstances) {
+ symbolInstance.crossTileID = 0;
+ }
+ }
+ }
+}
+
+bool GeometryTile::holdForFade() const {
+ return mode == MapMode::Continuous &&
+ (fadeState == FadeState::NeedsFirstPlacement || fadeState == FadeState::NeedsSecondPlacement);
+}
+
+void GeometryTile::markRenderedIdeal() {
+ fadeState = FadeState::Loaded;
+}
+void GeometryTile::markRenderedPreviously() {
+ if (fadeState == FadeState::Loaded) {
+ fadeState = FadeState::NeedsFirstPlacement;
+ }
+}
+void GeometryTile::performedFadePlacement() {
+ if (fadeState == FadeState::NeedsFirstPlacement) {
+ fadeState = FadeState::NeedsSecondPlacement;
+ } else if (fadeState == FadeState::NeedsSecondPlacement) {
+ fadeState = FadeState::CanRemove;
+ }
+}
+
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index 77202d20b6..6f871819a4 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -4,10 +4,10 @@
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/throttler.hpp>
#include <mbgl/actor/actor.hpp>
+#include <mbgl/geometry/feature_index.hpp>
#include <atomic>
#include <memory>
@@ -17,9 +17,6 @@
namespace mbgl {
class GeometryTileData;
-class FeatureIndex;
-class CollisionTile;
-class RenderStyle;
class RenderLayer;
class SourceQueryOptions;
class TileParameters;
@@ -37,14 +34,14 @@ public:
void setError(std::exception_ptr);
void setData(std::unique_ptr<const GeometryTileData>);
- void setPlacementConfig(const PlacementConfig&) override;
void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override;
-
+ void setShowCollisionBoxes(const bool showCollisionBoxes) override;
+
void onGlyphsAvailable(GlyphMap) override;
- void onImagesAvailable(ImageMap) override;
+ void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override;
void getGlyphs(GlyphDependencies);
- void getImages(ImageDependencies);
+ void getImages(ImageRequestPair);
void upload(gl::Context&) override;
Bucket* getBucket(const style::Layer::Impl&) const override;
@@ -56,8 +53,9 @@ public:
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions& options) override;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) override;
void querySourceFeatures(
std::vector<Feature>& result,
@@ -70,21 +68,39 @@ public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<GeometryTileData> tileData;
- uint64_t correlationID;
+
+ LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_,
+ std::unique_ptr<FeatureIndex> featureIndex_,
+ std::unique_ptr<GeometryTileData> tileData_)
+ : nonSymbolBuckets(std::move(nonSymbolBuckets_)),
+ featureIndex(std::move(featureIndex_)),
+ tileData(std::move(tileData_)) {}
};
- void onLayout(LayoutResult);
+ void onLayout(LayoutResult, uint64_t correlationID);
class PlacementResult {
public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
- std::unique_ptr<CollisionTile> collisionTile;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
- uint64_t correlationID;
+
+ PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
+ optional<AlphaImage> glyphAtlasImage_,
+ optional<PremultipliedImage> iconAtlasImage_)
+ : symbolBuckets(std::move(symbolBuckets_)),
+ glyphAtlasImage(std::move(glyphAtlasImage_)),
+ iconAtlasImage(std::move(iconAtlasImage_)) {}
};
- void onPlacement(PlacementResult);
+ void onPlacement(PlacementResult, uint64_t correlationID);
- void onError(std::exception_ptr);
+ void onError(std::exception_ptr, uint64_t correlationID);
+
+ void resetCrossTileIDs() override;
+
+ bool holdForFade() const override;
+ void markRenderedIdeal() override;
+ void markRenderedPreviously() override;
+ void performedFadePlacement() override;
protected:
const GeometryTileData* getData() {
@@ -93,7 +109,6 @@ protected:
private:
void markObsolete();
- void invokePlacement();
const std::string sourceID;
@@ -107,7 +122,6 @@ private:
ImageManager& imageManager;
uint64_t correlationID = 0;
- optional<PlacementConfig> requestedConfig;
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
std::unique_ptr<FeatureIndex> featureIndex;
@@ -117,10 +131,19 @@ private:
optional<PremultipliedImage> iconAtlasImage;
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
- std::unique_ptr<CollisionTile> collisionTile;
+
+ const MapMode mode;
- util::Throttler placementThrottler;
+ bool showCollisionBoxes;
+
+ enum class FadeState {
+ Loaded,
+ NeedsFirstPlacement,
+ NeedsSecondPlacement,
+ CanRemove
+ };
+ FadeState fadeState = FadeState::Loaded;
public:
optional<gl::Texture> glyphAtlasTexture;
optional<gl::Texture> iconAtlasTexture;
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index 12bb84d7e3..cf74bf3647 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -1,7 +1,6 @@
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/layout/symbol_layout.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/group_by_layout.hpp>
@@ -24,20 +23,36 @@ using namespace style;
GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
ActorRef<GeometryTile> parent_,
OverscaledTileID id_,
+ const std::string& sourceID_,
const std::atomic<bool>& obsolete_,
const MapMode mode_,
- const float pixelRatio_)
+ const float pixelRatio_,
+ const bool showCollisionBoxes_)
: self(std::move(self_)),
parent(std::move(parent_)),
id(std::move(id_)),
+ sourceID(sourceID_),
obsolete(obsolete_),
mode(mode_),
- pixelRatio(pixelRatio_) {
+ pixelRatio(pixelRatio_),
+ showCollisionBoxes(showCollisionBoxes_) {
}
GeometryTileWorker::~GeometryTileWorker() = default;
/*
+ NOTE: The comments below are technically correct, but currently
+ conceptually misleading. The change to foreground label placement
+ means that:
+ (1) "placement" here is a misnomer: the remaining role of
+ "attemptPlacement" is symbol buffer generation
+ (2) Once a tile has completed layout, we will only run
+ "attemptPlacement" once
+ (3) Tiles won't be rendered until "attemptPlacement" has run once
+
+ TODO: Simplify GeometryTileWorker to fit its new role
+ https://github.com/mapbox/mapbox-gl-native/issues/10457
+
GeometryTileWorker is a state machine. This is its transition diagram.
States are indicated by [state], lines are transitions triggered by
messages, (parentheses) are actions taken on transition.
@@ -88,7 +103,7 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -112,13 +127,13 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_,
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
-void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, uint64_t correlationID_) {
+void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_) {
try {
- placementConfig = std::move(placementConfig_);
+ showCollisionBoxes = showCollisionBoxes_;
correlationID = correlationID_;
switch (state) {
@@ -136,7 +151,7 @@ void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, ui
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -144,14 +159,14 @@ void GeometryTileWorker::symbolDependenciesChanged() {
try {
switch (state) {
case Idle:
- if (hasPendingSymbolLayouts()) {
+ if (symbolLayoutsNeedPreparation) {
attemptPlacement();
coalesce();
}
break;
case Coalescing:
- if (hasPendingSymbolLayouts()) {
+ if (symbolLayoutsNeedPreparation) {
state = NeedPlacement;
}
break;
@@ -161,7 +176,7 @@ void GeometryTileWorker::symbolDependenciesChanged() {
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -187,7 +202,7 @@ void GeometryTileWorker::coalesced() {
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -216,7 +231,10 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) {
symbolDependenciesChanged();
}
-void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap) {
+void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) {
+ if (imageCorrelationID != imageCorrelationID_) {
+ return; // Ignore outdated image request replies.
+ }
imageMap = std::move(newImageMap);
pendingImageDependencies.clear();
symbolDependenciesChanged();
@@ -239,7 +257,7 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen
void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) {
pendingImageDependencies = imageDependencies;
if (!pendingImageDependencies.empty()) {
- parent.invoke(&GeometryTile::getImages, pendingImageDependencies);
+ parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID));
}
}
@@ -312,6 +330,7 @@ void GeometryTileWorker::redoLayout() {
auto layout = leader.as<RenderSymbolLayer>()->createLayout(
parameters, group, std::move(geometryLayer), glyphDependencies, imageDependencies);
symbolLayoutMap.emplace(leader.getID(), std::move(layout));
+ symbolLayoutsNeedPreparation = true;
} else {
const Filter& filter = leader.baseImpl->filter;
const std::string& sourceLayerID = leader.baseImpl->sourceLayer;
@@ -353,22 +372,11 @@ void GeometryTileWorker::redoLayout() {
std::move(buckets),
std::move(featureIndex),
*data ? (*data)->clone() : nullptr,
- correlationID
- });
+ }, correlationID);
attemptPlacement();
}
-bool GeometryTileWorker::hasPendingSymbolLayouts() const {
- for (const auto& symbolLayout : symbolLayouts) {
- if (symbolLayout->state == SymbolLayout::Pending) {
- return true;
- }
- }
-
- return false;
-}
-
bool GeometryTileWorker::hasPendingSymbolDependencies() const {
for (auto& glyphDependency : pendingGlyphDependencies) {
if (!glyphDependency.second.empty()) {
@@ -378,40 +386,46 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const {
return !pendingImageDependencies.empty();
}
-
void GeometryTileWorker::attemptPlacement() {
- if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
+ if (!data || !layers || hasPendingSymbolDependencies()) {
return;
}
- auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
- std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
-
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
- for (auto& symbolLayout : symbolLayouts) {
- if (obsolete) {
- return;
- }
+ if (symbolLayoutsNeedPreparation) {
+ GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap);
+ ImageAtlas imageAtlas = makeImageAtlas(imageMap);
+
+ glyphAtlasImage = std::move(glyphAtlas.image);
+ iconAtlasImage = std::move(imageAtlas.image);
- if (symbolLayout->state == SymbolLayout::Pending) {
- GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap);
- ImageAtlas imageAtlas = makeImageAtlas(imageMap);
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
+ }
symbolLayout->prepare(glyphMap, glyphAtlas.positions,
- imageMap, imageAtlas.positions);
- symbolLayout->state = SymbolLayout::Placed;
+ imageMap, imageAtlas.positions,
+ id, sourceID);
+ }
+
+ symbolLayoutsNeedPreparation = false;
+ }
+
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
- glyphAtlasImage = std::move(glyphAtlas.image);
- iconAtlasImage = std::move(imageAtlas.image);
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
}
if (!symbolLayout->hasSymbolInstances()) {
continue;
}
- std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile);
+ std::shared_ptr<Bucket> bucket = symbolLayout->place(showCollisionBoxes);
for (const auto& pair : symbolLayout->layerPaintProperties) {
buckets.emplace(pair.first, bucket);
}
@@ -419,11 +433,9 @@ void GeometryTileWorker::attemptPlacement() {
parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
std::move(buckets),
- std::move(collisionTile),
std::move(glyphAtlasImage),
std::move(iconAtlasImage),
- correlationID
- });
+ }, correlationID);
}
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 194477e7b8..cc86248cec 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -4,7 +4,6 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/image_impl.hpp>
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/immutable.hpp>
@@ -28,17 +27,19 @@ public:
GeometryTileWorker(ActorRef<GeometryTileWorker> self,
ActorRef<GeometryTile> parent,
OverscaledTileID,
+ const std::string&,
const std::atomic<bool>&,
const MapMode,
- const float pixelRatio);
+ const float pixelRatio,
+ const bool showCollisionBoxes_);
~GeometryTileWorker();
void setLayers(std::vector<Immutable<style::Layer::Impl>>, uint64_t correlationID);
void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID);
- void setPlacementConfig(PlacementConfig, uint64_t correlationID);
+ void setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_);
void onGlyphsAvailable(GlyphMap glyphs);
- void onImagesAvailable(ImageMap images);
+ void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID);
private:
void coalesced();
@@ -52,12 +53,12 @@ private:
void symbolDependenciesChanged();
bool hasPendingSymbolDependencies() const;
- bool hasPendingSymbolLayouts() const;
ActorRef<GeometryTileWorker> self;
ActorRef<GeometryTile> parent;
const OverscaledTileID id;
+ const std::string sourceID;
const std::atomic<bool>& obsolete;
const MapMode mode;
const float pixelRatio;
@@ -71,17 +72,20 @@ private:
State state = Idle;
uint64_t correlationID = 0;
+ uint64_t imageCorrelationID = 0;
// Outer optional indicates whether we've received it or not.
optional<std::vector<Immutable<style::Layer::Impl>>> layers;
optional<std::unique_ptr<const GeometryTileData>> data;
- optional<PlacementConfig> placementConfig;
+ bool symbolLayoutsNeedPreparation = false;
std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
GlyphDependencies pendingGlyphDependencies;
ImageDependencies pendingImageDependencies;
GlyphMap glyphMap;
ImageMap imageMap;
+
+ bool showCollisionBoxes;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
index 8a92c40e4a..85fcea77b7 100644
--- a/src/mbgl/tile/raster_tile.cpp
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -8,7 +8,7 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/scheduler.hpp>
namespace mbgl {
@@ -17,7 +17,7 @@ RasterTile::RasterTile(const OverscaledTileID& id_,
const Tileset& tileset)
: Tile(id_),
loader(*this, id_, parameters, tileset),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(parameters.workerScheduler,
ActorRef<RasterTile>(*this, mailbox)) {
}
@@ -29,29 +29,35 @@ void RasterTile::cancel() {
void RasterTile::setError(std::exception_ptr err) {
loaded = true;
- renderable = false;
observer->onTileError(*this, err);
}
-void RasterTile::setData(std::shared_ptr<const std::string> data,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_) {
+void RasterTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) {
modified = modified_;
expires = expires_;
- worker.invoke(&RasterTileWorker::parse, data);
}
-void RasterTile::onParsed(std::unique_ptr<Bucket> result) {
+void RasterTile::setData(std::shared_ptr<const std::string> data) {
+ pending = true;
+ ++correlationID;
+ worker.invoke(&RasterTileWorker::parse, data, correlationID);
+}
+
+void RasterTile::onParsed(std::unique_ptr<RasterBucket> result, const uint64_t resultCorrelationID) {
bucket = std::move(result);
loaded = true;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
renderable = bucket ? true : false;
observer->onTileChanged(*this);
}
-void RasterTile::onError(std::exception_ptr err) {
- bucket.reset();
+void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
loaded = true;
- renderable = false;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
observer->onTileError(*this, err);
}
@@ -65,7 +71,13 @@ Bucket* RasterTile::getBucket(const style::Layer::Impl&) const {
return bucket.get();
}
-void RasterTile::setNecessity(Necessity necessity) {
+void RasterTile::setMask(TileMask&& mask) {
+ if (bucket) {
+ bucket->setMask(std::move(mask));
+ }
+}
+
+void RasterTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
index 51075a2dbc..192769ed8f 100644
--- a/src/mbgl/tile/raster_tile.hpp
+++ b/src/mbgl/tile/raster_tile.hpp
@@ -9,6 +9,7 @@ namespace mbgl {
class Tileset;
class TileParameters;
+class RasterBucket;
namespace style {
class Layer;
@@ -21,20 +22,21 @@ public:
const Tileset&);
~RasterTile() final;
- void setNecessity(Necessity) final;
+ void setNecessity(TileNecessity) final;
void setError(std::exception_ptr);
- void setData(std::shared_ptr<const std::string> data,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_);
+ void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
+ void setData(std::shared_ptr<const std::string> data);
void cancel() override;
void upload(gl::Context&) override;
Bucket* getBucket(const style::Layer::Impl&) const override;
- void onParsed(std::unique_ptr<Bucket> result);
- void onError(std::exception_ptr);
+ void setMask(TileMask&&) override;
+
+ void onParsed(std::unique_ptr<RasterBucket> result, uint64_t correlationID);
+ void onError(std::exception_ptr, uint64_t correlationID);
private:
TileLoader<RasterTile> loader;
@@ -42,9 +44,11 @@ private:
std::shared_ptr<Mailbox> mailbox;
Actor<RasterTileWorker> worker;
+ uint64_t correlationID = 0;
+
// Contains the Bucket object for the tile. Buckets are render
// objects and they get added by tile parsing operations.
- std::unique_ptr<Bucket> bucket;
+ std::unique_ptr<RasterBucket> bucket;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp
index 86fb5f181d..4afa876429 100644
--- a/src/mbgl/tile/raster_tile_worker.cpp
+++ b/src/mbgl/tile/raster_tile_worker.cpp
@@ -10,17 +10,17 @@ RasterTileWorker::RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTi
: parent(std::move(parent_)) {
}
-void RasterTileWorker::parse(std::shared_ptr<const std::string> data) {
+void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID) {
if (!data) {
- parent.invoke(&RasterTile::onParsed, nullptr); // No data; empty tile.
+ parent.invoke(&RasterTile::onParsed, nullptr, correlationID); // No data; empty tile.
return;
}
try {
- auto bucket = std::make_unique<RasterBucket>(util::unpremultiply(decodeImage(*data)));
- parent.invoke(&RasterTile::onParsed, std::move(bucket));
+ auto bucket = std::make_unique<RasterBucket>(decodeImage(*data));
+ parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID);
} catch (...) {
- parent.invoke(&RasterTile::onError, std::current_exception());
+ parent.invoke(&RasterTile::onError, std::current_exception(), correlationID);
}
}
diff --git a/src/mbgl/tile/raster_tile_worker.hpp b/src/mbgl/tile/raster_tile_worker.hpp
index 44bc37ca5d..520973c3c3 100644
--- a/src/mbgl/tile/raster_tile_worker.hpp
+++ b/src/mbgl/tile/raster_tile_worker.hpp
@@ -13,7 +13,7 @@ class RasterTileWorker {
public:
RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile>);
- void parse(std::shared_ptr<const std::string> data);
+ void parse(std::shared_ptr<const std::string> data, uint64_t correlationID);
private:
ActorRef<RasterTile> parent;
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 5080d42933..85899a98cb 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -1,9 +1,9 @@
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/tile_observer.hpp>
#include <mbgl/renderer/buckets/debug_bucket.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/logging.hpp>
-#include <mbgl/map/query.hpp>
namespace mbgl {
@@ -18,7 +18,7 @@ void Tile::setObserver(TileObserver* observer_) {
observer = observer_;
}
-void Tile::setTriedOptional() {
+void Tile::setTriedCache() {
triedOptional = true;
observer->onTileChanged(*this);
}
@@ -33,8 +33,9 @@ void Tile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>&,
const GeometryCoordinates&,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions&) {}
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex&) {}
void Tile::querySourceFeatures(
std::vector<Feature>&,
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index a925d88af3..1186b74111 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -6,6 +6,8 @@
#include <mbgl/util/feature.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/tile/tile_necessity.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/storage/resource.hpp>
@@ -21,11 +23,12 @@ namespace mbgl {
class DebugBucket;
class TransformState;
class TileObserver;
-class PlacementConfig;
-class RenderStyle;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
+class CollisionIndex;
+
namespace gl {
class Context;
} // namespace gl
@@ -37,14 +40,7 @@ public:
void setObserver(TileObserver* observer);
- // Tiles can have two states: optional or required.
- // - optional means that only low-cost actions should be taken to obtain the data
- // (e.g. load from cache, but accept stale data)
- // - required means that every effort should be taken to obtain the data (e.g. load
- // from internet and keep the data fresh if it expires)
- using Necessity = Resource::Necessity;
-
- virtual void setNecessity(Necessity) = 0;
+ virtual void setNecessity(TileNecessity) {}
// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
@@ -52,25 +48,27 @@ public:
virtual void upload(gl::Context&) = 0;
virtual Bucket* getBucket(const style::Layer::Impl&) const = 0;
- virtual void setPlacementConfig(const PlacementConfig&) {}
+ virtual void setShowCollisionBoxes(const bool) {}
virtual void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) {}
+ virtual void setMask(TileMask&&) {}
virtual void queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions& options);
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions& options,
+ const CollisionIndex&);
virtual void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&);
- void setTriedOptional();
+ void setTriedCache();
// Returns true when the tile source has received a first response, regardless of whether a load
// error occurred or actual data was loaded.
- bool hasTriedOptional() const {
+ bool hasTriedCache() const {
return triedOptional;
}
@@ -96,6 +94,22 @@ public:
bool isComplete() const {
return loaded && !pending;
}
+
+ // "holdForFade" is used to keep tiles in the render tree after they're no longer
+ // ideal tiles in order to allow symbols to fade out
+ virtual bool holdForFade() const {
+ return false;
+ }
+ // Set whenever this tile is used as an ideal tile
+ virtual void markRenderedIdeal() {}
+ // Set when the tile is removed from the ideal render set but may still be held for fading
+ virtual void markRenderedPreviously() {}
+ // Placement operation performed while this tile is fading
+ // We hold onto a tile for two placements: fading starts with the first placement
+ // and will have time to finish by the second placement.
+ virtual void performedFadePlacement() {}
+
+ virtual void resetCrossTileIDs() {};
void dumpDebugLogs() const;
diff --git a/src/mbgl/tile/tile_id.hpp b/src/mbgl/tile/tile_id.hpp
deleted file mode 100644
index 1ce3eea98e..0000000000
--- a/src/mbgl/tile/tile_id.hpp
+++ /dev/null
@@ -1,275 +0,0 @@
-#pragma once
-
-#include <mbgl/util/constants.hpp>
-
-#include <cstdint>
-#include <array>
-#include <forward_list>
-#include <algorithm>
-#include <iosfwd>
-#include <cassert>
-#include <boost/functional/hash.hpp>
-
-namespace mbgl {
-
-class OverscaledTileID;
-class CanonicalTileID;
-class UnwrappedTileID;
-
-// Has integer z/x/y coordinates
-// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid)
-// Used for requesting data; represents data tiles that exist out there.
-// z is never larger than the source's maxzoom
-class CanonicalTileID {
-public:
- CanonicalTileID(uint8_t z, uint32_t x, uint32_t y);
- bool operator==(const CanonicalTileID&) const;
- bool operator!=(const CanonicalTileID&) const;
- bool operator<(const CanonicalTileID&) const;
- bool isChildOf(const CanonicalTileID&) const;
- CanonicalTileID scaledTo(uint8_t z) const;
- std::array<CanonicalTileID, 4> children() const;
-
- const uint8_t z;
- const uint32_t x;
- const uint32_t y;
-};
-
-::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs);
-namespace util {
-std::string toString(const CanonicalTileID&);
-} // namespace util
-
-// Has integer z/x/y coordinates
-// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data
-// z is never larger than the source's maxzoom
-// z/x/y describe the
-class OverscaledTileID {
-public:
- OverscaledTileID(uint8_t overscaledZ, CanonicalTileID);
- OverscaledTileID(uint8_t overscaledZ, uint8_t z, uint32_t x, uint32_t y);
- OverscaledTileID(uint8_t z, uint32_t x, uint32_t y);
- explicit OverscaledTileID(const CanonicalTileID&);
- explicit OverscaledTileID(CanonicalTileID&&);
- bool operator==(const OverscaledTileID&) const;
- bool operator!=(const OverscaledTileID&) const;
- bool operator<(const OverscaledTileID&) const;
- bool isChildOf(const OverscaledTileID&) const;
- uint32_t overscaleFactor() const;
- OverscaledTileID scaledTo(uint8_t z) const;
- UnwrappedTileID unwrapTo(int16_t wrap) const;
-
- const uint8_t overscaledZ;
- const CanonicalTileID canonical;
-};
-
-::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs);
-namespace util {
-std::string toString(const OverscaledTileID&);
-} // namespace util
-
-// Has integer z/x/y coordinates
-// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world
-// Used for describing what position tiles are getting rendered at (= calc the matrix)
-// z is never larger than the source's maxzoom
-class UnwrappedTileID {
-public:
- UnwrappedTileID(uint8_t z, int64_t x, int64_t y);
- UnwrappedTileID(int16_t wrap, CanonicalTileID);
- bool operator==(const UnwrappedTileID&) const;
- bool operator!=(const UnwrappedTileID&) const;
- bool operator<(const UnwrappedTileID&) const;
- bool isChildOf(const UnwrappedTileID&) const;
- std::array<UnwrappedTileID, 4> children() const;
- OverscaledTileID overscaleTo(uint8_t z) const;
- float pixelsToTileUnits(float pixelValue, float zoom) const;
-
- const int16_t wrap;
- const CanonicalTileID canonical;
-};
-
-::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs);
-namespace util {
-std::string toString(const UnwrappedTileID&);
-} // namespace util
-
-inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) {
- assert(z <= 32);
- assert(x < (1ull << z));
- assert(y < (1ull << z));
-}
-
-inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const {
- return z == rhs.z && x == rhs.x && y == rhs.y;
-}
-
-inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const {
- return z != rhs.z || x != rhs.x || y != rhs.y;
-}
-
-inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const {
- return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y);
-}
-
-inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const {
- // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined.
- return parent.z == 0 ||
- (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z)));
-}
-
-inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const {
- if (targetZ <= z) {
- return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same
- } else {
- return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child
- }
-}
-
-inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const {
- const uint8_t childZ = z + 1;
- const uint32_t childX = x * 2;
- const uint32_t childY = y * 2;
- return { {
- { childZ, childX, childY },
- { childZ, childX, childY + 1 },
- { childZ, childX + 1, childY },
- { childZ, childX + 1, childY + 1 },
- } };
-}
-
-inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, CanonicalTileID canonical_)
- : overscaledZ(overscaledZ_), canonical(std::move(canonical_)) {
- assert(overscaledZ >= canonical.z);
-}
-
-inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, uint8_t z, uint32_t x, uint32_t y)
- : overscaledZ(overscaledZ_), canonical(z, x, y) {
- assert(overscaledZ >= canonical.z);
-}
-
-inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y)
- : overscaledZ(z), canonical(z, x, y) {
-}
-
-inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_)
- : overscaledZ(canonical_.z), canonical(canonical_) {
- assert(overscaledZ >= canonical.z);
-}
-
-inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_)
- : overscaledZ(canonical_.z), canonical(std::forward<CanonicalTileID>(canonical_)) {
- assert(overscaledZ >= canonical.z);
-}
-
-inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const {
- return overscaledZ == rhs.overscaledZ && canonical == rhs.canonical;
-}
-
-inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const {
- return overscaledZ != rhs.overscaledZ || canonical != rhs.canonical;
-}
-
-inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const {
- return std::tie(overscaledZ, canonical) < std::tie(rhs.overscaledZ, rhs.canonical);
-}
-
-inline uint32_t OverscaledTileID::overscaleFactor() const {
- return 1u << (overscaledZ - canonical.z);
-}
-
-inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const {
- return overscaledZ > rhs.overscaledZ &&
- (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical));
-}
-
-inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const {
- return { z, z >= canonical.z ? canonical : canonical.scaledTo(z) };
-}
-
-inline UnwrappedTileID OverscaledTileID::unwrapTo(int16_t wrap) const {
- return { wrap, canonical };
-}
-
-inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_)
- : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)),
- canonical(
- z_,
- static_cast<uint32_t>(x_ - wrap * (1ll << z_)),
- y_ < 0 ? 0 : std::min(static_cast<uint32_t>(y_), static_cast<uint32_t>(1ull << z_) - 1)) {
-}
-
-inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_)
- : wrap(wrap_), canonical(std::move(canonical_)) {
-}
-
-inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const {
- return wrap == rhs.wrap && canonical == rhs.canonical;
-}
-
-inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const {
- return wrap != rhs.wrap || canonical != rhs.canonical;
-}
-
-inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const {
- return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical);
-}
-
-inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const {
- return wrap == parent.wrap && canonical.isChildOf(parent.canonical);
-}
-
-inline std::array<UnwrappedTileID, 4> UnwrappedTileID::children() const {
- const uint8_t childZ = canonical.z + 1;
- const uint32_t childX = canonical.x * 2;
- const uint32_t childY = canonical.y * 2;
- return { {
- { wrap, { childZ, childX, childY } },
- { wrap, { childZ, childX, childY + 1 } },
- { wrap, { childZ, childX + 1, childY } },
- { wrap, { childZ, childX + 1, childY + 1 } },
- } };
-}
-
-inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const {
- assert(overscaledZ >= canonical.z);
- return { overscaledZ, canonical };
-}
-
-inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const {
- return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z)));
-}
-
-} // namespace mbgl
-
-namespace std {
-
-template <> struct hash<mbgl::CanonicalTileID> {
- size_t operator()(const mbgl::CanonicalTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, id.x);
- boost::hash_combine(seed, id.y);
- boost::hash_combine(seed, id.z);
- return seed;
- }
-};
-
-template <> struct hash<mbgl::UnwrappedTileID> {
- size_t operator()(const mbgl::UnwrappedTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
- boost::hash_combine(seed, id.wrap);
- return seed;
- }
-};
-
-template <> struct hash<mbgl::OverscaledTileID> {
- size_t operator()(const mbgl::OverscaledTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
- boost::hash_combine(seed, id.overscaledZ);
- return seed;
- }
-};
-
-} // namespace std
-
diff --git a/src/mbgl/tile/tile_id_hash.cpp b/src/mbgl/tile/tile_id_hash.cpp
new file mode 100644
index 0000000000..4a1f185817
--- /dev/null
+++ b/src/mbgl/tile/tile_id_hash.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/tile/tile_id.hpp>
+
+#include <boost/functional/hash.hpp>
+
+namespace std {
+
+size_t hash<mbgl::CanonicalTileID>::operator()(const mbgl::CanonicalTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, id.x);
+ boost::hash_combine(seed, id.y);
+ boost::hash_combine(seed, id.z);
+ return seed;
+}
+
+size_t hash<mbgl::UnwrappedTileID>::operator()(const mbgl::UnwrappedTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
+ boost::hash_combine(seed, id.wrap);
+ return seed;
+}
+
+size_t hash<mbgl::OverscaledTileID>::operator()(const mbgl::OverscaledTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
+ boost::hash_combine(seed, id.overscaledZ);
+ return seed;
+}
+
+} // namespace std
diff --git a/src/mbgl/tile/tile_id_io.cpp b/src/mbgl/tile/tile_id_io.cpp
index f6adbf183f..d8be6b93d6 100644
--- a/src/mbgl/tile/tile_id_io.cpp
+++ b/src/mbgl/tile/tile_id_io.cpp
@@ -6,6 +6,8 @@
namespace mbgl {
::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs) {
+ // Uncomment this to create code instead of shorthands.
+ // return os << "CanonicalTileID{ " << uint32_t(rhs.z) << ", " << rhs.x << ", " << rhs.y << " }";
return os << uint32_t(rhs.z) << "/" << rhs.x << "/" << rhs.y;
}
@@ -26,6 +28,9 @@ std::string toString(const OverscaledTileID& rhs) {
} // namespace util
::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs) {
+ // Uncomment this to create code instead of shorthands.
+ // return os << "UnwrappedTileID{ " << uint32_t(rhs.wrap) << ", { " << uint32_t(rhs.canonical.z)
+ // << ", " << rhs.canonical.x << ", " << rhs.canonical.y << " } }";
return os << rhs.canonical << (rhs.wrap >= 0 ? "+" : "") << rhs.wrap;
}
diff --git a/src/mbgl/tile/tile_loader.hpp b/src/mbgl/tile/tile_loader.hpp
index bc408ebaf6..92ca74330f 100644
--- a/src/mbgl/tile/tile_loader.hpp
+++ b/src/mbgl/tile/tile_loader.hpp
@@ -21,12 +21,10 @@ public:
const Tileset&);
~TileLoader();
- using Necessity = Resource::Necessity;
-
- void setNecessity(Necessity newNecessity) {
+ void setNecessity(TileNecessity newNecessity) {
if (newNecessity != necessity) {
necessity = newNecessity;
- if (necessity == Necessity::Required) {
+ if (necessity == TileNecessity::Required) {
makeRequired();
} else {
makeOptional();
@@ -45,12 +43,12 @@ private:
// an up-to-date version or load new data
void makeOptional();
- void loadOptional();
+ void loadFromCache();
void loadedData(const Response&);
- void loadRequired();
+ void loadFromNetwork();
T& tile;
- Necessity necessity;
+ TileNecessity necessity;
Resource resource;
FileSource& fileSource;
std::unique_ptr<AsyncRequest> request;
diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp
index 899cbaf9b0..1b29638269 100644
--- a/src/mbgl/tile/tile_loader_impl.hpp
+++ b/src/mbgl/tile/tile_loader_impl.hpp
@@ -15,32 +15,31 @@ TileLoader<T>::TileLoader(T& tile_,
const TileParameters& parameters,
const Tileset& tileset)
: tile(tile_),
- necessity(Necessity::Optional),
+ necessity(TileNecessity::Optional),
resource(Resource::tile(
tileset.tiles.at(0),
parameters.pixelRatio,
id.canonical.x,
id.canonical.y,
id.canonical.z,
- tileset.scheme)),
+ tileset.scheme,
+ Resource::LoadingMethod::CacheOnly)),
fileSource(parameters.fileSource) {
assert(!request);
- if (fileSource.supportsOptionalRequests()) {
+ if (fileSource.supportsCacheOnlyRequests()) {
// When supported, the first request is always optional, even if the TileLoader
// is marked as required. That way, we can let the first optional request continue
// to load when the TileLoader is later changed from required to optional. If we
// started out with a required request, we'd have to cancel everything, including the
// initial optional part of the request.
- loadOptional();
+ loadFromCache();
+ } else if (necessity == TileNecessity::Required) {
+ // When the file source doesn't support cache-only requests, and we definiitely need this
+ // data, we can start out with a network request immediately.
+ loadFromNetwork();
} else {
- // When the FileSource doesn't support optional requests, we do nothing until the
+ // When the FileSource doesn't support cache-only requests, we do nothing until the
// data is definitely required.
- if (necessity == Necessity::Required) {
- loadRequired();
- } else {
- // We're using this field to check whether the pending request is optional or required.
- resource.necessity = Resource::Optional;
- }
}
}
@@ -48,26 +47,31 @@ template <typename T>
TileLoader<T>::~TileLoader() = default;
template <typename T>
-void TileLoader<T>::loadOptional() {
+void TileLoader<T>::loadFromCache() {
assert(!request);
- resource.necessity = Resource::Optional;
+ resource.loadingMethod = Resource::LoadingMethod::CacheOnly;
request = fileSource.request(resource, [this](Response res) {
request.reset();
- tile.setTriedOptional();
+ tile.setTriedCache();
if (res.error && res.error->reason == Response::Error::Reason::NotFound) {
- // When the optional request could not be satisfied, don't treat it as an error.
- // Instead, we make sure that the next request knows that there has been an optional
- // request before by setting one of the prior* fields.
- resource.priorExpires = Timestamp{ Seconds::zero() };
+ // When the cache-only request could not be satisfied, don't treat it as an error.
+ // A cache lookup could still return data, _and_ an error, in particular when we were
+ // able to find the data, but it is expired and the Cache-Control headers indicated that
+ // we aren't allowed to use expired responses. In this case, we still get the data which
+ // we can use in our conditional network request.
+ resource.priorModified = res.modified;
+ resource.priorExpires = res.expires;
+ resource.priorEtag = res.etag;
+ resource.priorData = res.data;
} else {
loadedData(res);
}
- if (necessity == Necessity::Required) {
- loadRequired();
+ if (necessity == TileNecessity::Required) {
+ loadFromNetwork();
}
});
}
@@ -75,14 +79,15 @@ void TileLoader<T>::loadOptional() {
template <typename T>
void TileLoader<T>::makeRequired() {
if (!request) {
- loadRequired();
+ loadFromNetwork();
}
}
template <typename T>
void TileLoader<T>::makeOptional() {
- if (resource.necessity == Resource::Required && request) {
- // Abort a potential HTTP request.
+ if (resource.loadingMethod == Resource::LoadingMethod::NetworkOnly && request) {
+ // Abort the current request, but only when we know that we're specifically querying for a
+ // network resource only.
request.reset();
}
}
@@ -95,19 +100,23 @@ void TileLoader<T>::loadedData(const Response& res) {
resource.priorExpires = res.expires;
// Do not notify the tile; when we get this message, it already has the current
// version of the data.
+ tile.setMetadata(res.modified, res.expires);
} else {
resource.priorModified = res.modified;
resource.priorExpires = res.expires;
resource.priorEtag = res.etag;
- tile.setData(res.noContent ? nullptr : res.data, res.modified, res.expires);
+ tile.setMetadata(res.modified, res.expires);
+ tile.setData(res.noContent ? nullptr : res.data);
}
}
template <typename T>
-void TileLoader<T>::loadRequired() {
+void TileLoader<T>::loadFromNetwork() {
assert(!request);
- resource.necessity = Resource::Required;
+ // Instead of using Resource::LoadingMethod::All, we're first doing a CacheOnly, and then a
+ // NetworkOnly request.
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
request = fileSource.request(resource, [this](Response res) { loadedData(res); });
}
diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp
index e2e700b7b7..0756d3e526 100644
--- a/src/mbgl/tile/vector_tile.cpp
+++ b/src/mbgl/tile/vector_tile.cpp
@@ -12,16 +12,16 @@ VectorTile::VectorTile(const OverscaledTileID& id_,
: GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) {
}
-void VectorTile::setNecessity(Necessity necessity) {
+void VectorTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}
-void VectorTile::setData(std::shared_ptr<const std::string> data_,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_) {
+void VectorTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) {
modified = modified_;
expires = expires_;
+}
+void VectorTile::setData(std::shared_ptr<const std::string> data_) {
GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr);
}
diff --git a/src/mbgl/tile/vector_tile.hpp b/src/mbgl/tile/vector_tile.hpp
index 566cde4f37..7dae414fef 100644
--- a/src/mbgl/tile/vector_tile.hpp
+++ b/src/mbgl/tile/vector_tile.hpp
@@ -15,10 +15,9 @@ public:
const TileParameters&,
const Tileset&);
- void setNecessity(Necessity) final;
- void setData(std::shared_ptr<const std::string> data,
- optional<Timestamp> modified,
- optional<Timestamp> expires);
+ void setNecessity(TileNecessity) final;
+ void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
+ void setData(std::shared_ptr<const std::string> data);
private:
TileLoader<VectorTile> loader;
diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp
index 5c8fd3c0ff..a880093b74 100644
--- a/src/mbgl/util/chrono.cpp
+++ b/src/mbgl/util/chrono.cpp
@@ -3,6 +3,13 @@
#include <parsedate/parsedate.h>
#include <cstdio>
+#include <ctime>
+
+#if defined(_WINDOWS)
+#define _gmtime(t, i) gmtime_s(i, t)
+#else
+#define _gmtime(t, i) gmtime_r(t, i)
+#endif
namespace mbgl {
namespace util {
@@ -14,7 +21,7 @@ static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
std::string rfc1123(Timestamp timestamp) {
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
std::tm info;
- gmtime_r(&time, &info);
+ _gmtime(&time, &info);
char buffer[30];
snprintf(buffer, 30, "%s, %02d %s %4d %02d:%02d:%02d GMT", week[info.tm_wday], info.tm_mday,
months[info.tm_mon], 1900 + info.tm_year, info.tm_hour, info.tm_min, info.tm_sec);
@@ -24,7 +31,7 @@ std::string rfc1123(Timestamp timestamp) {
std::string iso8601(Timestamp timestamp) {
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
std::tm info;
- gmtime_r(&time, &info);
+ _gmtime(&time, &info);
char buffer[30];
std::strftime(buffer, sizeof(buffer), "%F %T", &info);
return buffer;
diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp
index 9faef140ef..56f78c9885 100644
--- a/src/mbgl/util/constants.cpp
+++ b/src/mbgl/util/constants.cpp
@@ -11,7 +11,6 @@ const bool tileParseWarnings = false;
const bool styleParseWarnings = false;
const bool spriteWarnings = false;
const bool renderWarnings = false;
-const bool renderTree = false;
const bool labelTextMissingWarning = true;
const bool missingFontStackWarning = true;
const bool missingFontFaceWarning = true;
@@ -22,7 +21,6 @@ const bool tileParseWarnings = false;
const bool styleParseWarnings = false;
const bool spriteWarnings = false;
const bool renderWarnings = false;
-const bool renderTree = false;
const bool labelTextMissingWarning = false;
const bool missingFontStackWarning = false;
const bool missingFontFaceWarning = false;
diff --git a/src/mbgl/util/dtoa.cpp b/src/mbgl/util/dtoa.cpp
index dd4fba0f89..0e3aef6117 100644
--- a/src/mbgl/util/dtoa.cpp
+++ b/src/mbgl/util/dtoa.cpp
@@ -1,10 +1,18 @@
#include "dtoa.hpp"
+// Clang/C2 on Windows 64-bits can't parse rapidjson's dtoa
+// and it was causing the compiler to crash.
+#if !defined(_WINDOWS)
#include <rapidjson/internal/dtoa.h>
+#endif
+
+#include <mbgl/util/string.hpp>
namespace mbgl {
namespace util {
+#if !defined(_WINDOWS)
+
namespace {
// From https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h
@@ -101,5 +109,13 @@ std::string dtoa(double value) {
return data;
}
+#else
+
+std::string dtoa(double value) {
+ return std::to_string(value);
+}
+
+#endif
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/dtoa.hpp b/src/mbgl/util/dtoa.hpp
index db7d309452..4cb81a94be 100644
--- a/src/mbgl/util/dtoa.hpp
+++ b/src/mbgl/util/dtoa.hpp
@@ -5,7 +5,6 @@
namespace mbgl {
namespace util {
-char* dtoa(double value, char* buffer);
std::string dtoa(double value);
} // end namespace util
diff --git a/src/mbgl/util/geojson.cpp b/src/mbgl/util/geojson_impl.cpp
index d1608e09fe..d1608e09fe 100644
--- a/src/mbgl/util/geojson.cpp
+++ b/src/mbgl/util/geojson_impl.cpp
diff --git a/src/mbgl/util/grid_index.cpp b/src/mbgl/util/grid_index.cpp
index b3afd3fdc8..afd469501d 100644
--- a/src/mbgl/util/grid_index.cpp
+++ b/src/mbgl/util/grid_index.cpp
@@ -3,83 +3,301 @@
#include <mbgl/math/minmax.hpp>
#include <unordered_set>
+#include <cmath>
namespace mbgl {
template <class T>
-GridIndex<T>::GridIndex(int32_t extent_, int32_t n_, int32_t padding_) :
- extent(extent_),
- n(n_),
- padding(padding_),
- d(n + 2 * padding),
- scale(double(n) / double(extent)),
- min(-double(padding) / n * extent),
- max(extent + double(padding) / n * extent)
+GridIndex<T>::GridIndex(const float width_, const float height_, const int16_t cellSize_) :
+ width(width_),
+ height(height_),
+ xCellCount(std::ceil(width_ / cellSize_)),
+ yCellCount(std::ceil(height_ / cellSize_)),
+ xScale(xCellCount / width_),
+ yScale(yCellCount / height_)
{
- cells.resize(d * d);
+ boxCells.resize(xCellCount * yCellCount);
+ circleCells.resize(xCellCount * yCellCount);
}
template <class T>
void GridIndex<T>::insert(T&& t, const BBox& bbox) {
- size_t uid = elements.size();
+ size_t uid = boxElements.size();
- auto cx1 = convertToCellCoord(bbox.min.x);
- auto cy1 = convertToCellCoord(bbox.min.y);
- auto cx2 = convertToCellCoord(bbox.max.x);
- auto cy2 = convertToCellCoord(bbox.max.y);
+ auto cx1 = convertToXCellCoord(bbox.min.x);
+ auto cy1 = convertToYCellCoord(bbox.min.y);
+ auto cx2 = convertToXCellCoord(bbox.max.x);
+ auto cy2 = convertToYCellCoord(bbox.max.y);
- int32_t x, y, cellIndex;
+ int16_t x, y, cellIndex;
for (x = cx1; x <= cx2; ++x) {
for (y = cy1; y <= cy2; ++y) {
- cellIndex = d * y + x;
- cells[cellIndex].push_back(uid);
+ cellIndex = xCellCount * y + x;
+ boxCells[cellIndex].push_back(uid);
}
}
- elements.emplace_back(t, bbox);
+ boxElements.emplace_back(t, bbox);
+}
+
+template <class T>
+void GridIndex<T>::insert(T&& t, const BCircle& bcircle) {
+ size_t uid = circleElements.size();
+
+ auto cx1 = convertToXCellCoord(bcircle.center.x - bcircle.radius);
+ auto cy1 = convertToYCellCoord(bcircle.center.y - bcircle.radius);
+ auto cx2 = convertToXCellCoord(bcircle.center.x + bcircle.radius);
+ auto cy2 = convertToYCellCoord(bcircle.center.y + bcircle.radius);
+
+ int16_t x, y, cellIndex;
+ for (x = cx1; x <= cx2; ++x) {
+ for (y = cy1; y <= cy2; ++y) {
+ cellIndex = xCellCount * y + x;
+ circleCells[cellIndex].push_back(uid);
+ }
+ }
+
+ circleElements.emplace_back(t, bcircle);
}
template <class T>
std::vector<T> GridIndex<T>::query(const BBox& queryBBox) const {
std::vector<T> result;
- std::unordered_set<size_t> seenUids;
+ query(queryBBox, [&](const T& t, const BBox&) -> bool {
+ result.push_back(t);
+ return false;
+ });
+ return result;
+}
+
+template <class T>
+std::vector<std::pair<T, typename GridIndex<T>::BBox>> GridIndex<T>::queryWithBoxes(const BBox& queryBBox) const {
+ std::vector<std::pair<T, BBox>> result;
+ query(queryBBox, [&](const T& t, const BBox& bbox) -> bool {
+ result.push_back(std::make_pair(t, bbox));
+ return false;
+ });
+ return result;
+}
+
+template <class T>
+bool GridIndex<T>::hitTest(const BBox& queryBBox) const {
+ bool hit = false;
+ query(queryBBox, [&](const T&, const BBox&) -> bool {
+ hit = true;
+ return true;
+ });
+ return hit;
+}
+
+template <class T>
+bool GridIndex<T>::hitTest(const BCircle& queryBCircle) const {
+ bool hit = false;
+ query(queryBCircle, [&](const T&, const BBox&) -> bool {
+ hit = true;
+ return true;
+ });
+ return hit;
+}
- auto cx1 = convertToCellCoord(queryBBox.min.x);
- auto cy1 = convertToCellCoord(queryBBox.min.y);
- auto cx2 = convertToCellCoord(queryBBox.max.x);
- auto cy2 = convertToCellCoord(queryBBox.max.y);
+template <class T>
+bool GridIndex<T>::noIntersection(const BBox& queryBBox) const {
+ return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height;
+}
+
+template <class T>
+bool GridIndex<T>::completeIntersection(const BBox& queryBBox) const {
+ return queryBBox.min.x <= 0 && queryBBox.min.y <= 0 && width <= queryBBox.max.x && height <= queryBBox.max.y;
+}
+
+template <class T>
+typename GridIndex<T>::BBox GridIndex<T>::convertToBox(const BCircle& circle) const {
+ return BBox{{circle.center.x - circle.radius, circle.center.y - circle.radius},
+ {circle.center.x + circle.radius, circle.center.y + circle.radius}};
+}
+
+template <class T>
+void GridIndex<T>::query(const BBox& queryBBox, std::function<bool (const T&, const BBox&)> resultFn) const {
+ std::unordered_set<size_t> seenBoxes;
+ std::unordered_set<size_t> seenCircles;
+
+ if (noIntersection(queryBBox)) {
+ return;
+ } else if (completeIntersection(queryBBox)) {
+ for (auto& element : boxElements) {
+ if (resultFn(element.first, element.second)) {
+ return;
+ }
+ }
+ for (auto& element : circleElements) {
+ if (resultFn(element.first, convertToBox(element.second))) {
+ return;
+ }
+ }
+ return;
+ }
+
+ auto cx1 = convertToXCellCoord(queryBBox.min.x);
+ auto cy1 = convertToYCellCoord(queryBBox.min.y);
+ auto cx2 = convertToXCellCoord(queryBBox.max.x);
+ auto cy2 = convertToYCellCoord(queryBBox.max.y);
- int32_t x, y, cellIndex;
+ int16_t x, y, cellIndex;
for (x = cx1; x <= cx2; ++x) {
for (y = cy1; y <= cy2; ++y) {
- cellIndex = d * y + x;
- for (auto uid : cells[cellIndex]) {
- if (seenUids.count(uid) == 0) {
- seenUids.insert(uid);
+ cellIndex = xCellCount * y + x;
+ // Look up other boxes
+ for (auto uid : boxCells[cellIndex]) {
+ if (seenBoxes.count(uid) == 0) {
+ seenBoxes.insert(uid);
- auto& pair = elements.at(uid);
+ auto& pair = boxElements.at(uid);
auto& bbox = pair.second;
- if (queryBBox.min.x <= bbox.max.x &&
- queryBBox.min.y <= bbox.max.y &&
- queryBBox.max.x >= bbox.min.x &&
- queryBBox.max.y >= bbox.min.y) {
+ if (boxesCollide(queryBBox, bbox)) {
+ if (resultFn(pair.first, bbox)) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Look up circles
+ for (auto uid : circleCells[cellIndex]) {
+ if (seenCircles.count(uid) == 0) {
+ seenCircles.insert(uid);
- result.push_back(pair.first);
+ auto& pair = circleElements.at(uid);
+ auto& bcircle = pair.second;
+ if (circleAndBoxCollide(bcircle, queryBBox)) {
+ if (resultFn(pair.first, convertToBox(bcircle))) {
+ return;
+ }
}
}
}
}
}
+}
- return result;
+template <class T>
+void GridIndex<T>::query(const BCircle& queryBCircle, std::function<bool (const T&, const BBox&)> resultFn) const {
+ std::unordered_set<size_t> seenBoxes;
+ std::unordered_set<size_t> seenCircles;
+
+ BBox queryBBox = convertToBox(queryBCircle);
+ if (noIntersection(queryBBox)) {
+ return;
+ } else if (completeIntersection(queryBBox)) {
+ for (auto& element : boxElements) {
+ if (resultFn(element.first, element.second)) {
+ return;
+ }
+ }
+ for (auto& element : circleElements) {
+ if (resultFn(element.first, convertToBox(element.second))) {
+ return;
+ }
+ }
+ }
+
+ auto cx1 = convertToXCellCoord(queryBCircle.center.x - queryBCircle.radius);
+ auto cy1 = convertToYCellCoord(queryBCircle.center.y - queryBCircle.radius);
+ auto cx2 = convertToXCellCoord(queryBCircle.center.x + queryBCircle.radius);
+ auto cy2 = convertToYCellCoord(queryBCircle.center.y + queryBCircle.radius);
+
+ int16_t x, y, cellIndex;
+ for (x = cx1; x <= cx2; ++x) {
+ for (y = cy1; y <= cy2; ++y) {
+ cellIndex = xCellCount * y + x;
+ // Look up boxes
+ for (auto uid : boxCells[cellIndex]) {
+ if (seenBoxes.count(uid) == 0) {
+ seenBoxes.insert(uid);
+
+ auto& pair = boxElements.at(uid);
+ auto& bbox = pair.second;
+ if (circleAndBoxCollide(queryBCircle, bbox)) {
+ if (resultFn(pair.first, bbox)) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Look up other circles
+ for (auto uid : circleCells[cellIndex]) {
+ if (seenCircles.count(uid) == 0) {
+ seenCircles.insert(uid);
+
+ auto& pair = circleElements.at(uid);
+ auto& bcircle = pair.second;
+ if (circlesCollide(queryBCircle, bcircle)) {
+ if (resultFn(pair.first, convertToBox(bcircle))) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
}
+template <class T>
+int16_t GridIndex<T>::convertToXCellCoord(const float x) const {
+ return util::max(0.0, util::min(xCellCount - 1.0, std::floor(x * xScale)));
+}
+
+template <class T>
+int16_t GridIndex<T>::convertToYCellCoord(const float y) const {
+ return util::max(0.0, util::min(yCellCount - 1.0, std::floor(y * yScale)));
+}
template <class T>
-int32_t GridIndex<T>::convertToCellCoord(int32_t x) const {
- return util::max(0.0, util::min(d - 1.0, std::floor(x * scale) + padding));
+bool GridIndex<T>::boxesCollide(const BBox& first, const BBox& second) const {
+ return first.min.x <= second.max.x &&
+ first.min.y <= second.max.y &&
+ first.max.x >= second.min.x &&
+ first.max.y >= second.min.y;
}
+template <class T>
+bool GridIndex<T>::circlesCollide(const BCircle& first, const BCircle& second) const {
+ auto dx = second.center.x - first.center.x;
+ auto dy = second.center.y - first.center.y;
+ auto bothRadii = first.radius + second.radius;
+ return (bothRadii * bothRadii) > (dx * dx + dy * dy);
+}
+
+template <class T>
+bool GridIndex<T>::circleAndBoxCollide(const BCircle& circle, const BBox& box) const {
+ auto halfRectWidth = (box.max.x - box.min.x) / 2;
+ auto distX = std::abs(circle.center.x - (box.min.x + halfRectWidth));
+ if (distX > (halfRectWidth + circle.radius)) {
+ return false;
+ }
+
+ auto halfRectHeight = (box.max.y - box.min.y) / 2;
+ auto distY = std::abs(circle.center.y - (box.min.y + halfRectHeight));
+ if (distY > (halfRectHeight + circle.radius)) {
+ return false;
+ }
+
+ if (distX <= halfRectWidth || distY <= halfRectHeight) {
+ return true;
+ }
+
+ auto dx = distX - halfRectWidth;
+ auto dy = distY - halfRectHeight;
+ return (dx * dx + dy * dy) <= (circle.radius * circle.radius);
+}
+
+template <class T>
+bool GridIndex<T>::empty() const {
+ return boxElements.empty() && circleElements.empty();
+}
+
+
template class GridIndex<IndexedSubfeature>;
+
} // namespace mbgl
diff --git a/src/mbgl/util/grid_index.hpp b/src/mbgl/util/grid_index.hpp
index 8ef8fb35b7..6ef2966bee 100644
--- a/src/mbgl/util/grid_index.hpp
+++ b/src/mbgl/util/grid_index.hpp
@@ -6,32 +6,100 @@
#include <cstdint>
#include <cstddef>
#include <vector>
+#include <functional>
namespace mbgl {
+namespace geometry {
+
+template <typename T>
+struct circle
+{
+ using point_type = mapbox::geometry::point<T>;
+
+ constexpr circle(point_type const& center_, T const& radius_)
+ : center(center_), radius(radius_)
+ {}
+
+ point_type center;
+ T radius;
+};
+
+template <typename T>
+constexpr bool operator==(circle<T> const& lhs, circle<T> const& rhs)
+{
+ return lhs.center == rhs.center && lhs.radius == rhs.radius;
+}
+
+template <typename T>
+constexpr bool operator!=(circle<T> const& lhs, circle<T> const& rhs)
+{
+ return lhs.center != rhs.center || lhs.radius != rhs.radius;
+}
+
+} // namespace geometry
+
+
+/*
+ GridIndex is a data structure for testing the intersection of
+ circles and rectangles in a 2d plane.
+ It is optimized for rapid insertion and querying.
+ GridIndex splits the plane into a set of "cells" and keeps track
+ of which geometries intersect with each cell. At query time,
+ full geometry comparisons are only done for items that share
+ at least one cell. As long as the geometries are relatively
+ uniformly distributed across the plane, this greatly reduces
+ the number of comparisons necessary.
+*/
+
template <class T>
class GridIndex {
public:
- GridIndex(int32_t extent_, int32_t n_, int32_t padding_);
- using BBox = mapbox::geometry::box<int16_t>;
+ GridIndex(const float width_, const float height_, const int16_t cellSize_);
+
+ using BBox = mapbox::geometry::box<float>;
+ using BCircle = geometry::circle<float>;
void insert(T&& t, const BBox&);
+ void insert(T&& t, const BCircle&);
+
std::vector<T> query(const BBox&) const;
+ std::vector<std::pair<T,BBox>> queryWithBoxes(const BBox&) const;
+
+ bool hitTest(const BBox&) const;
+ bool hitTest(const BCircle&) const;
+
+ bool empty() const;
private:
- int32_t convertToCellCoord(int32_t x) const;
-
- const int32_t extent;
- const int32_t n;
- const int32_t padding;
- const int32_t d;
- const double scale;
- const int32_t min;
- const int32_t max;
-
- std::vector<std::pair<T, BBox>> elements;
- std::vector<std::vector<size_t>> cells;
+ bool noIntersection(const BBox& queryBBox) const;
+ bool completeIntersection(const BBox& queryBBox) const;
+ BBox convertToBox(const BCircle& circle) const;
+
+ void query(const BBox&, std::function<bool (const T&, const BBox&)>) const;
+ void query(const BCircle&, std::function<bool (const T&, const BBox&)>) const;
+
+ int16_t convertToXCellCoord(const float x) const;
+ int16_t convertToYCellCoord(const float y) const;
+
+ bool boxesCollide(const BBox&, const BBox&) const;
+ bool circlesCollide(const BCircle&, const BCircle&) const;
+ bool circleAndBoxCollide(const BCircle&, const BBox&) const;
+
+ const float width;
+ const float height;
+
+ const int16_t xCellCount;
+ const int16_t yCellCount;
+ const double xScale;
+ const double yScale;
+
+ std::vector<std::pair<T, BBox>> boxElements;
+ std::vector<std::pair<T, BCircle>> circleElements;
+
+ std::vector<std::vector<size_t>> boxCells;
+ std::vector<std::vector<size_t>> circleCells;
};
diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp
index 40711232ff..ce31a06c5e 100644
--- a/src/mbgl/util/http_header.cpp
+++ b/src/mbgl/util/http_header.cpp
@@ -1,5 +1,7 @@
#include <mbgl/util/http_header.hpp>
+#include <mbgl/util/string.hpp>
+
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-parameter"
diff --git a/src/mbgl/util/http_timeout.cpp b/src/mbgl/util/http_timeout.cpp
index ca9a93498f..3456369250 100644
--- a/src/mbgl/util/http_timeout.cpp
+++ b/src/mbgl/util/http_timeout.cpp
@@ -1,6 +1,8 @@
#include <mbgl/util/http_timeout.hpp>
#include <mbgl/util/constants.hpp>
+#include <cassert>
+
namespace mbgl {
namespace http {
diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp
index ada6f6526c..1fc13bfb7d 100644
--- a/src/mbgl/util/i18n.cpp
+++ b/src/mbgl/util/i18n.cpp
@@ -15,7 +15,7 @@ namespace {
return codepoint >= first && codepoint <= last; \
}
-// The following table comes from <http://www.unicode.org/Public/9.0.0/ucd/Blocks.txt>.
+// The following table comes from <http://www.unicode.org/Public/10.0.0/ucd/Blocks.txt>.
// Keep it synchronized with <http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt>.
// DEFINE_IS_IN_UNICODE_BLOCK(BasicLatin, 0x0000, 0x007F)
@@ -37,6 +37,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(ArabicSupplement, 0x0750, 0x077F)
// DEFINE_IS_IN_UNICODE_BLOCK(NKo, 0x07C0, 0x07FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Samaritan, 0x0800, 0x083F)
// DEFINE_IS_IN_UNICODE_BLOCK(Mandaic, 0x0840, 0x085F)
+// DEFINE_IS_IN_UNICODE_BLOCK(Syriac Supplement, 0x0860, 0x086F)
DEFINE_IS_IN_UNICODE_BLOCK(ArabicExtendedA, 0x08A0, 0x08FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Devanagari, 0x0900, 0x097F)
// DEFINE_IS_IN_UNICODE_BLOCK(Bengali, 0x0980, 0x09FF)
@@ -239,9 +240,12 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(Takri, 0x11680, 0x116CF)
// DEFINE_IS_IN_UNICODE_BLOCK(Ahom, 0x11700, 0x1173F)
// DEFINE_IS_IN_UNICODE_BLOCK(WarangCiti, 0x118A0, 0x118FF)
+// DEFINE_IS_IN_UNICODE_BLOCK(ZanabazarSquare, 0x11A00, 0x11A4F)
+// DEFINE_IS_IN_UNICODE_BLOCK(Soyombo, 0x11A50, 0x11AAF)
// DEFINE_IS_IN_UNICODE_BLOCK(PauCinHau, 0x11AC0, 0x11AFF)
// DEFINE_IS_IN_UNICODE_BLOCK(Bhaiksuki, 0x11C00, 0x11C6F)
// DEFINE_IS_IN_UNICODE_BLOCK(Marchen, 0x11C70, 0x11CBF)
+// DEFINE_IS_IN_UNICODE_BLOCK(MasaramGondi, 0x11D00, 0x11D5F)
// DEFINE_IS_IN_UNICODE_BLOCK(Cuneiform, 0x12000, 0x123FF)
// DEFINE_IS_IN_UNICODE_BLOCK(CuneiformNumbersandPunctuation, 0x12400, 0x1247F)
// DEFINE_IS_IN_UNICODE_BLOCK(EarlyDynasticCuneiform, 0x12480, 0x1254F)
@@ -256,6 +260,8 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(Tangut, 0x17000, 0x187FF)
// DEFINE_IS_IN_UNICODE_BLOCK(TangutComponents, 0x18800, 0x18AFF)
// DEFINE_IS_IN_UNICODE_BLOCK(KanaSupplement, 0x1B000, 0x1B0FF)
+// DEFINE_IS_IN_UNICODE_BLOCK(KanaExtendedA, 0x1B100, 0x1B12F)
+// DEFINE_IS_IN_UNICODE_BLOCK(Nushu, 0x1B170, 0x1B2FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Duployan, 0x1BC00, 0x1BC9F)
// DEFINE_IS_IN_UNICODE_BLOCK(ShorthandFormatControls, 0x1BCA0, 0x1BCAF)
// DEFINE_IS_IN_UNICODE_BLOCK(ByzantineMusicalSymbols, 0x1D000, 0x1D0FF)
@@ -286,6 +292,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionC, 0x2A700, 0x2B73F)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionD, 0x2B740, 0x2B81F)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionE, 0x2B820, 0x2CEAF)
+// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionF, 0x2CEB0, 0x2EBEF)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityIdeographsSupplement, 0x2F800, 0x2FA1F)
// DEFINE_IS_IN_UNICODE_BLOCK(Tags, 0xE0000, 0xE007F)
// DEFINE_IS_IN_UNICODE_BLOCK(VariationSelectorsSupplement, 0xE0100, 0xE01EF)
@@ -375,14 +382,21 @@ bool allowsIdeographicBreaking(char16_t chr) {
// return (isInTangut(chr)
// || isInTangutComponents(chr)
// || isInIdeographicSymbolsandPunctuation(chr)
+ // || isInNushu(chr)
// || isInEnclosedIdeographicSupplement(chr)
// || isInCJKUnifiedIdeographsExtensionB(chr)
// || isInCJKUnifiedIdeographsExtensionC(chr)
// || isInCJKUnifiedIdeographsExtensionD(chr)
// || isInCJKUnifiedIdeographsExtensionE(chr)
+ // || isInCJKUnifiedIdeographsExtensionF(chr)
// || isInCJKCompatibilityIdeographsSupplement(chr));
}
+bool allowsFixedWidthGlyphGeneration(char16_t chr) {
+ // Mirrors conservative set of characters used in glyph_manager.js/_tinySDF
+ return isInCJKUnifiedIdeographs(chr) || isInHangulSyllables(chr);
+}
+
bool allowsVerticalWritingMode(const std::u16string& string) {
for (char32_t chr : string) {
if (hasUprightVerticalOrientation(chr)) {
@@ -393,7 +407,7 @@ bool allowsVerticalWritingMode(const std::u16string& string) {
}
// The following logic comes from
-// <http://www.unicode.org/Public/vertical/revision-16/VerticalOrientation-16.txt>.
+// <http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt>.
// The data file denotes with “U” or “Tu” any codepoint that may be drawn
// upright in vertical text but does not distinguish between upright and
// “neutral” characters.
@@ -457,6 +471,8 @@ bool hasUprightVerticalOrientation(char16_t chr) {
// if (isInTangut(chr)) return true;
// if (isInTangutComponents(chr)) return true;
// if (isInKanaSupplement(chr)) return true;
+ // if (isInKanaExtendedA(chr)) return true;
+ // if (isInNushu(chr)) return true;
// if (isInByzantineMusicalSymbols(chr)) return true;
// if (isInMusicalSymbols(chr)) return true;
// if (isInTaiXuanJingSymbols(chr)) return true;
@@ -478,6 +494,7 @@ bool hasUprightVerticalOrientation(char16_t chr) {
// if (isInCJKUnifiedIdeographsExtensionC(chr)) return true;
// if (isInCJKUnifiedIdeographsExtensionD(chr)) return true;
// if (isInCJKUnifiedIdeographsExtensionE(chr)) return true;
+ // if (isInCJKUnifiedIdeographsExtensionF(chr)) return true;
// if (isInCJKCompatibilityIdeographsSupplement(chr)) return true;
return false;
@@ -542,7 +559,7 @@ std::u16string verticalizePunctuation(const std::u16string& input) {
std::u16string output;
for (size_t i = 0; i < input.size(); i++) {
- char16_t nextCharCode = i < input.size() ? input[i + 1] : 0;
+ char16_t nextCharCode = i < input.size() - 1 ? input[i + 1] : 0;
char16_t prevCharCode = i ? input[i - 1] : 0;
bool canReplacePunctuation =
diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp
index 61c5a1ea96..b3960c743c 100644
--- a/src/mbgl/util/i18n.hpp
+++ b/src/mbgl/util/i18n.hpp
@@ -23,6 +23,10 @@ bool allowsIdeographicBreaking(const std::u16string& string);
by the given Unicode codepoint due to ideographic breaking. */
bool allowsIdeographicBreaking(char16_t chr);
+/** Conservative set of characters expected to have relatively fixed sizes and
+ advances */
+bool allowsFixedWidthGlyphGeneration(char16_t chr);
+
/** Returns whether any substring of the given string can be drawn as vertical
text with upright glyphs. */
bool allowsVerticalWritingMode(const std::u16string& string);
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
index 9adc3b8988..6a6ed7b250 100644
--- a/src/mbgl/util/io.cpp
+++ b/src/mbgl/util/io.cpp
@@ -6,8 +6,6 @@
#include <sstream>
#include <fstream>
-#include <unistd.h>
-
namespace mbgl {
namespace util {
@@ -43,8 +41,8 @@ optional<std::string> readFile(const std::string &filename) {
}
void deleteFile(const std::string& filename) {
- const int ret = unlink(filename.c_str());
- if (ret == -1) {
+ const int ret = std::remove(filename.c_str());
+ if (ret != 0) {
throw IOException(errno, "failed to unlink file");
}
}
diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp
index 8cbc85d492..802b527a26 100644
--- a/src/mbgl/util/mapbox.cpp
+++ b/src/mbgl/util/mapbox.cpp
@@ -114,7 +114,7 @@ std::string normalizeTileURL(const std::string& baseURL,
}
std::string
-canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_t tileSize) {
+canonicalizeTileURL(const std::string& str, const style::SourceType type, const uint16_t tileSize) {
const char* version = "/v4/";
const size_t versionLen = strlen(version);
@@ -133,7 +133,7 @@ canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_
std::string result = "mapbox://tiles/";
result.append(str, path.directory.first + versionLen, path.directory.second - versionLen);
result.append(str, path.filename.first, path.filename.second);
- if (type == SourceType::Raster) {
+ if (type == style::SourceType::Raster) {
result += tileSize == util::tileSize ? "@2x" : "{ratio}";
}
@@ -171,7 +171,7 @@ canonicalizeTileURL(const std::string& str, const SourceType type, const uint16_
return result;
}
-void canonicalizeTileset(Tileset& tileset, const std::string& sourceURL, SourceType type, uint16_t tileSize) {
+void canonicalizeTileset(Tileset& tileset, const std::string& sourceURL, style::SourceType type, uint16_t tileSize) {
// TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with.
if (isMapboxURL(sourceURL)) {
for (auto& url : tileset.tiles) {
diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp
index f3dfdd0b01..aa128f2667 100644
--- a/src/mbgl/util/mapbox.hpp
+++ b/src/mbgl/util/mapbox.hpp
@@ -19,10 +19,10 @@ std::string normalizeGlyphsURL(const std::string& baseURL, const std::string& ur
std::string normalizeTileURL(const std::string& baseURL, const std::string& url, const std::string& accessToken);
// Return a "mapbox://tiles/..." URL (suitable for normalizeTileURL) for the given Mapbox tile URL.
-std::string canonicalizeTileURL(const std::string& url, SourceType, uint16_t tileSize);
+std::string canonicalizeTileURL(const std::string& url, style::SourceType, uint16_t tileSize);
// Replace URL templates with "mapbox://tiles/..." URLs (suitable for normalizeTileURL).
-void canonicalizeTileset(Tileset&, const std::string& url, SourceType, uint16_t tileSize);
+void canonicalizeTileset(Tileset&, const std::string& url, style::SourceType, uint16_t tileSize);
extern const uint64_t DEFAULT_OFFLINE_TILE_COUNT_LIMIT;
diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp
index d3d3617b7b..0ad0d371e5 100644
--- a/src/mbgl/util/mat4.cpp
+++ b/src/mbgl/util/mat4.cpp
@@ -336,10 +336,11 @@ void multiply(mat4& out, const mat4& a, const mat4& b) {
}
void transformMat4(vec4& out, const vec4& a, const mat4& m) {
- out[0] = m[0] * a[0] + m[4] * a[1] + m[8] * a[2] + m[12] * a[3];
- out[1] = m[1] * a[0] + m[5] * a[1] + m[9] * a[2] + m[13] * a[3];
- out[2] = m[2] * a[0] + m[6] * a[1] + m[10] * a[2] + m[14] * a[3];
- out[3] = m[3] * a[0] + m[7] * a[1] + m[11] * a[2] + m[15] * a[3];
+ double x = a[0], y = a[1], z = a[2], w = a[3];
+ out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
+ out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
+ out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
+ out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
}
} // namespace matrix
diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp
index f969ecaedd..c18ce0c254 100644
--- a/src/mbgl/util/math.hpp
+++ b/src/mbgl/util/math.hpp
@@ -77,9 +77,9 @@ T mag(const S& a) {
return std::sqrt(a.x * a.x + a.y * a.y);
}
-template <typename S>
+template <typename T = double, typename S>
S unit(const S& a) {
- auto magnitude = mag(a);
+ auto magnitude = mag<T>(a);
if (magnitude == 0) {
return a;
}
@@ -106,5 +106,18 @@ T smoothstep(T edge0, T edge1, T x) {
return t * t * (T(3) - T(2) * t);
}
+template <typename T>
+inline T division(const T dividend, const T divisor, const T nan) {
+ if (divisor == 0) {
+ if (dividend == 0) {
+ return nan;
+ } else {
+ return ::copysign(std::numeric_limits<T>::infinity(), dividend);
+ }
+ } else {
+ return dividend / divisor;
+ }
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp
index 77a1416e48..339e74b250 100644
--- a/src/mbgl/util/offscreen_texture.cpp
+++ b/src/mbgl/util/offscreen_texture.cpp
@@ -11,20 +11,22 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default;
class OffscreenTexture::Impl {
public:
- Impl(gl::Context& context_, const Size size_, OffscreenTextureAttachment type_)
- : context(context_), size(std::move(size_)), type(type_) {
+ Impl(gl::Context& context_, const Size size_)
+ : context(context_), size(std::move(size_)) {
+ assert(!size.isEmpty());
+ }
+ Impl(gl::Context& context_,
+ const Size size_,
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_)
+ : context(context_), size(std::move(size_)), depth(&depth_) {
assert(!size.isEmpty());
}
void bind() {
if (!framebuffer) {
texture = context.createTexture(size, gl::TextureFormat::RGBA);
-
- if (type == OffscreenTextureAttachment::Depth) {
- gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depth =
- context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
- framebuffer = context.createFramebuffer(*texture, depth);
-
+ if (depth) {
+ framebuffer = context.createFramebuffer(*texture, *depth);
} else {
framebuffer = context.createFramebuffer(*texture);
}
@@ -32,7 +34,7 @@ public:
context.bindFramebuffer = framebuffer->framebuffer;
}
- context.activeTexture = 0;
+ context.activeTextureUnit = 0;
context.scissorTest = false;
context.viewport = { 0, 0, size };
}
@@ -53,15 +55,21 @@ public:
private:
gl::Context& context;
const Size size;
- OffscreenTextureAttachment type;
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr;
};
OffscreenTexture::OffscreenTexture(gl::Context& context,
+ const Size size)
+ : impl(std::make_unique<Impl>(context, std::move(size))) {
+ assert(!size.isEmpty());
+}
+
+OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size,
- OffscreenTextureAttachment type)
- : impl(std::make_unique<Impl>(context, std::move(size), type)) {
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer)
+ : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) {
assert(!size.isEmpty());
}
diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp
index c265700555..7f7e0f0338 100644
--- a/src/mbgl/util/offscreen_texture.hpp
+++ b/src/mbgl/util/offscreen_texture.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/map/view.hpp>
#include <mbgl/util/image.hpp>
namespace mbgl {
@@ -10,21 +9,18 @@ class Context;
class Texture;
} // namespace gl
-enum class OffscreenTextureAttachment {
- None,
- Depth,
-};
-
-class OffscreenTexture : public View {
+class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
- Size size = { 256, 256 },
- OffscreenTextureAttachment type = OffscreenTextureAttachment::None);
- ~OffscreenTexture() override;
+ Size size = { 256, 256 });
+ OffscreenTexture(gl::Context&,
+ Size size,
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&);
+ ~OffscreenTexture();
OffscreenTexture(OffscreenTexture&&);
OffscreenTexture& operator=(OffscreenTexture&&);
- void bind() override;
+ void bind();
PremultipliedImage readStillImage();
diff --git a/src/mbgl/util/std.hpp b/src/mbgl/util/std.hpp
index 974e21329c..1db20e09e5 100644
--- a/src/mbgl/util/std.hpp
+++ b/src/mbgl/util/std.hpp
@@ -8,10 +8,10 @@ namespace mbgl {
namespace util {
template <typename Container, typename ForwardIterator, typename Predicate>
-void erase_if(Container &container, ForwardIterator it, const ForwardIterator end, Predicate pred) {
- while (it != end) {
+void erase_if(Container &container, ForwardIterator it, Predicate pred) {
+ while (it != container.end()) {
if (pred(*it)) {
- container.erase(it++);
+ it = container.erase(it);
} else {
++it;
}
@@ -20,7 +20,7 @@ void erase_if(Container &container, ForwardIterator it, const ForwardIterator en
template <typename Container, typename Predicate>
void erase_if(Container &container, Predicate pred) {
- erase_if(container, container.begin(), container.end(), pred);
+ erase_if(container, container.begin(), pred);
}
} // namespace util
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
deleted file mode 100644
index 572f46080e..0000000000
--- a/src/mbgl/util/thread.hpp
+++ /dev/null
@@ -1,163 +0,0 @@
-#pragma once
-
-#include <mbgl/actor/actor.hpp>
-#include <mbgl/actor/mailbox.hpp>
-#include <mbgl/actor/scheduler.hpp>
-#include <mbgl/util/platform.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/util.hpp>
-
-#include <cassert>
-#include <future>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <string>
-#include <thread>
-#include <utility>
-
-namespace mbgl {
-namespace util {
-
-// Manages a thread with `Object`.
-
-// Upon creation of this object, it launches a thread and creates an object of type `Object`
-// in that thread. When the `Thread<>` object is destructed, the destructor waits
-// for thread termination. The `Thread<>` constructor blocks until the thread and
-// the `Object` are fully created, so after the object creation, it's safe to obtain the
-// `Object` stored in this thread. The thread created will always have low priority on
-// the platforms that support setting thread priority.
-//
-// The following properties make this class different from `ThreadPool`:
-//
-// - Only one thread is created.
-// - `Object` will live in a single thread, providing thread affinity.
-// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>`
-// - A `RunLoop` is created for the `Object` thread.
-// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events.
-//
-template<class Object>
-class Thread : public Scheduler {
-public:
- template <class... Args>
- Thread(const std::string& name, Args&&... args) {
- std::promise<void> running;
-
- thread = std::thread([&] {
- platform::setCurrentThreadName(name);
- platform::makeThreadLowPriority();
-
- util::RunLoop loop_(util::RunLoop::Type::New);
- loop = &loop_;
-
- object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...);
- running.set_value();
-
- loop->run();
- loop = nullptr;
- });
-
- running.get_future().get();
- }
-
- ~Thread() override {
- MBGL_VERIFY_THREAD(tid);
-
- if (paused) {
- resume();
- }
-
- std::promise<void> joinable;
-
- // Kill the actor, so we don't get more
- // messages posted on this scheduler after
- // we delete the RunLoop.
- loop->invoke([&] {
- object.reset();
- joinable.set_value();
- });
-
- joinable.get_future().get();
-
- loop->stop();
- thread.join();
- }
-
- // Returns a non-owning reference to `Object` that
- // can be used to send messages to `Object`. It is safe
- // to the non-owning reference to outlive this object
- // and be used after the `Thread<>` gets destroyed.
- ActorRef<std::decay_t<Object>> actor() const {
- return object->self();
- }
-
- // Pauses the `Object` thread. It will prevent the object to wake
- // up from events such as timers and file descriptor I/O. Messages
- // sent to a paused `Object` will be queued and only processed after
- // `resume()` is called.
- void pause() {
- MBGL_VERIFY_THREAD(tid);
-
- assert(!paused);
-
- paused = std::make_unique<std::promise<void>>();
- resumed = std::make_unique<std::promise<void>>();
-
- auto pausing = paused->get_future();
-
- loop->invoke([this] {
- auto resuming = resumed->get_future();
- paused->set_value();
- resuming.get();
- });
-
- pausing.get();
- }
-
- // Resumes the `Object` thread previously paused by `pause()`.
- void resume() {
- MBGL_VERIFY_THREAD(tid);
-
- assert(paused);
-
- resumed->set_value();
-
- resumed.reset();
- paused.reset();
- }
-
-private:
- MBGL_STORE_THREAD(tid);
-
- void schedule(std::weak_ptr<Mailbox> mailbox) override {
- {
- std::lock_guard<std::mutex> lock(mutex);
- queue.push(mailbox);
- }
-
- loop->invoke([this] { receive(); });
- }
-
- void receive() {
- std::unique_lock<std::mutex> lock(mutex);
-
- auto mailbox = queue.front();
- queue.pop();
- lock.unlock();
-
- Mailbox::maybeReceive(mailbox);
- }
-
- std::mutex mutex;
- std::queue<std::weak_ptr<Mailbox>> queue;
- std::thread thread;
- std::unique_ptr<Actor<Object>> object;
-
- std::unique_ptr<std::promise<void>> paused;
- std::unique_ptr<std::promise<void>> resumed;
-
- util::RunLoop* loop = nullptr;
-};
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/thread_local.hpp b/src/mbgl/util/thread_local.hpp
index 15d4d56524..b0e26356b4 100644
--- a/src/mbgl/util/thread_local.hpp
+++ b/src/mbgl/util/thread_local.hpp
@@ -1,12 +1,8 @@
#pragma once
-#include <mbgl/util/logging.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <cassert>
-#include <stdexcept>
-
-#include <pthread.h>
+#include <memory>
namespace mbgl {
namespace util {
@@ -14,40 +10,20 @@ namespace util {
template <class T>
class ThreadLocal : public noncopyable {
public:
- ThreadLocal() {
- int ret = pthread_key_create(&key, [](void *ptr) {
- delete reinterpret_cast<T *>(ptr);
- });
-
- if (ret) {
- throw std::runtime_error("Failed to init local storage key.");
- }
- }
-
- ~ThreadLocal() {
- if (pthread_key_delete(key)) {
- Log::Error(Event::General, "Failed to delete local storage key.");
- assert(false);
- }
+ ThreadLocal(T* val) {
+ ThreadLocal();
+ set(val);
}
- T* get() {
- auto* ret = reinterpret_cast<T*>(pthread_getspecific(key));
- if (!ret) {
- return nullptr;
- }
+ ThreadLocal();
+ ~ThreadLocal();
- return ret;
- }
-
- void set(T* ptr) {
- if (pthread_setspecific(key, ptr)) {
- throw std::runtime_error("Failed to set local storage.");
- }
- }
+ T* get();
+ void set(T* ptr);
private:
- pthread_key_t key;
+ class Impl;
+ std::unique_ptr<Impl> impl;
};
} // namespace util
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index b53e91162c..39b562d811 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -126,9 +126,9 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl,
} // namespace
-int32_t coveringZoomLevel(double zoom, SourceType type, uint16_t size) {
+int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) {
zoom += std::log(util::tileSize / size) / std::log(2);
- if (type == SourceType::Raster || type == SourceType::Video) {
+ if (type == style::SourceType::Raster || type == style::SourceType::Video) {
return ::round(zoom);
} else {
return std::floor(zoom);
@@ -169,5 +169,26 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
+// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs
+// Computes the projected tiles for the lower left and upper right points of the bounds
+// and uses that to compute the tile cover count
+uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){
+
+ auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_);
+ auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_);
+
+ auto x1 = floor(sw.x/ tileSize_);
+ auto x2 = floor((ne.x - 1) / tileSize_);
+ auto y1 = floor(sw.y/ tileSize_);
+ auto y2 = floor((ne.y - 1) / tileSize_);
+
+ auto minX = ::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);
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index 2d32d8bf41..b2098b59b8 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -13,10 +13,13 @@ class LatLngBounds;
namespace util {
-int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize);
+int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize);
std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
+// Compute only the count of tiles needed for tileCover
+uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize);
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp
new file mode 100644
index 0000000000..60839357d5
--- /dev/null
+++ b/src/mbgl/util/tiny_sdf.cpp
@@ -0,0 +1,105 @@
+#include <mbgl/util/tiny_sdf.hpp>
+
+#include <mbgl/util/math.hpp>
+
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+namespace tinysdf {
+
+static const double INF = 1e20;
+
+// 1D squared distance transform
+void edt1d(std::vector<double>& f,
+ std::vector<double>& d,
+ std::vector<int16_t>& v,
+ std::vector<double>& z,
+ uint32_t n) {
+ v[0] = 0;
+ z[0] = -INF;
+ z[1] = +INF;
+
+ for (uint32_t q = 1, k = 0; q < n; q++) {
+ double s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);
+ while (s <= z[k]) {
+ k--;
+ s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);
+ }
+ k++;
+ v[k] = q;
+ z[k] = s;
+ z[k + 1] = +INF;
+ }
+
+ for (uint32_t q = 0, k = 0; q < n; q++) {
+ while (z[k + 1] < q) k++;
+ d[q] = (q - v[k]) * (q - v[k]) + f[v[k]];
+ }
+}
+
+
+// 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/dt/
+void edt(std::vector<double>& data,
+ uint32_t width,
+ uint32_t height,
+ std::vector<double>& f,
+ std::vector<double>& d,
+ std::vector<int16_t>& v,
+ std::vector<double>& z) {
+ for (uint32_t x = 0; x < width; x++) {
+ for (uint32_t y = 0; y < height; y++) {
+ f[y] = data[y * width + x];
+ }
+ edt1d(f, d, v, z, height);
+ for (uint32_t y = 0; y < height; y++) {
+ data[y * width + x] = d[y];
+ }
+ }
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ f[x] = data[y * width + x];
+ }
+ edt1d(f, d, v, z, width);
+ for (uint32_t x = 0; x < width; x++) {
+ data[y * width + x] = std::sqrt(d[x]);
+ }
+ }
+}
+
+} // namespace tinysdf
+
+AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff) {
+ uint32_t size = rasterInput.size.width * rasterInput.size.height;
+ uint32_t maxDimension = std::max(rasterInput.size.width, rasterInput.size.height);
+
+ AlphaImage sdf(rasterInput.size);
+
+ // temporary arrays for the distance transform
+ std::vector<double> gridOuter(size);
+ std::vector<double> gridInner(size);
+ std::vector<double> f(maxDimension);
+ std::vector<double> d(maxDimension);
+ std::vector<double> z(maxDimension + 1);
+ std::vector<int16_t> v(maxDimension);
+
+ for (uint32_t i = 0; i < size; i++) {
+ double a = double(rasterInput.data[i]) / 255; // alpha value
+ gridOuter[i] = a == 1.0 ? 0.0 : a == 0.0 ? tinysdf::INF : std::pow(std::max(0.0, 0.5 - a), 2.0);
+ gridInner[i] = a == 1.0 ? tinysdf::INF : a == 0.0 ? 0.0 : std::pow(std::max(0.0, a - 0.5), 2.0);
+ }
+
+ tinysdf::edt(gridOuter, rasterInput.size.width, rasterInput.size.height, f, d, v, z);
+ tinysdf::edt(gridInner, rasterInput.size.width, rasterInput.size.height, f, d, v, z);
+
+ 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))));
+ }
+
+ return sdf;
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/tiny_sdf.hpp b/src/mbgl/util/tiny_sdf.hpp
new file mode 100644
index 0000000000..33c9280cbd
--- /dev/null
+++ b/src/mbgl/util/tiny_sdf.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+
+namespace mbgl {
+namespace util {
+
+/*
+ C++ port of https://github.com/mapbox/tiny-sdf, which is in turn based on the
+ Felzenszwalb/Huttenlocher distance transform paper (https://cs.brown.edu/~pff/papers/dt-final.pdf).
+ Note there exists an alternative C++ implementation from the paper’s authors at
+ https://cs.brown.edu/~pff/dt/, which this implementation is not based on.
+
+ Takes an alpha channel raster input and transforms it into an alpha channel
+ Signed Distance Field (SDF) output of the same dimensions.
+*/
+AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff);
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/parsedate/parsedate.c b/src/parsedate/parsedate.c
index 46acceed75..7228c4edbc 100644
--- a/src/parsedate/parsedate.c
+++ b/src/parsedate/parsedate.c
@@ -418,7 +418,7 @@ static time_t my_timegm(struct my_tm *tm)
{
static const int month_days_cumulative [12] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
- int month, year, leap_days;
+ int month_, year, leap_days;
if(tm->tm_year < 70)
/* we don't support years before 1970 as they will cause this function
@@ -426,14 +426,14 @@ static time_t my_timegm(struct my_tm *tm)
return -1;
year = tm->tm_year + 1900;
- month = tm->tm_mon;
- if(month < 0) {
- year += (11 - month) / 12;
- month = 11 - (11 - month) % 12;
+ month_ = tm->tm_mon;
+ if(month_ < 0) {
+ year += (11 - month_) / 12;
+ month_ = 11 - (11 - month_) % 12;
}
- else if(month >= 12) {
- year -= month / 12;
- month = month % 12;
+ else if(month_ >= 12) {
+ year -= month_ / 12;
+ month_ = month_ % 12;
}
leap_days = year - (tm->tm_mon <= 1);
@@ -441,7 +441,7 @@ static time_t my_timegm(struct my_tm *tm)
- (1969 / 4) + (1969 / 100) - (1969 / 400));
return ((((time_t) (year - 1970) * 365
- + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
+ + leap_days + month_days_cumulative [month_] + tm->tm_mday - 1) * 24
+ tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
}